FreeRTOS STM32CubeMX配置 内存管理 任务管理
上节介绍了用STM32CubeMX生成带FreeRTOS的工程, 细心的同学可能发现, 已创建任务的函数为例, FreeRTOS官方是xTaskCreate()
, 到之前CubeMX生成cmsis_os
中的osThreadCreate
, 再到最近CubeMX生成cmsisi_os2
中的osThreadNew
, 不用疑惑, 跳转到CubeMX生成的函数定义可以看到, 其实后者是对FreeRTOS函数的进一步封装, osThreadNew
封装了xTaskCreate()
和xTaskCreateStatic
, 类似的, osMessageQueueGet
封装了xQueueReceive
和xQueueReceiveFromISR
, 用起来框架上没有什么大的不同, 类似于NXP MCU中PAL对HAL的进一步封装, 甚至更好用了, 所以, 放心用CubeMX生成的函数就好.
FreeRTOS的任务间通信有队列, 信号量和互斥量, 下面一一介绍.
队列的作用:
队列相关的函数在cmsisi_os2.h
中列了出来:
// ==== Message Queue Management Functions ====
/// Create and Initialize a Message Queue object.
/// \param[in] msg_count maximum number of messages in queue.
/// \param[in] msg_size maximum message size in bytes.
/// \param[in] attr message queue attributes; NULL: default values.
/// \return message queue ID for reference by other functions or NULL in case of error.
osMessageQueueId_t osMessageQueueNew (uint32_t msg_count, uint32_t msg_size, const osMessageQueueAttr_t *attr);
/// Get name of a Message Queue object.
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
/// \return name as NULL terminated string.
const char *osMessageQueueGetName (osMessageQueueId_t mq_id);
/// Put a Message into a Queue or timeout if Queue is full.
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
/// \param[in] msg_ptr pointer to buffer with message to put into a queue.
/// \param[in] msg_prio message priority.
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
/// \return status code that indicates the execution status of the function.
osStatus_t osMessageQueuePut (osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, uint32_t timeout);
/// Get a Message from a Queue or timeout if Queue is empty.
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
/// \param[out] msg_ptr pointer to buffer for message to get from a queue.
/// \param[out] msg_prio pointer to buffer for message priority or NULL.
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
/// \return status code that indicates the execution status of the function.
osStatus_t osMessageQueueGet (osMessageQueueId_t mq_id, void *msg_ptr, uint8_t *msg_prio, uint32_t timeout);
/// Get maximum number of messages in a Message Queue.
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
/// \return maximum number of messages.
uint32_t osMessageQueueGetCapacity (osMessageQueueId_t mq_id);
/// Get maximum message size in a Memory Pool.
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
/// \return maximum message size in bytes.
uint32_t osMessageQueueGetMsgSize (osMessageQueueId_t mq_id);
/// Get number of queued messages in a Message Queue.
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
/// \return number of queued messages.
uint32_t osMessageQueueGetCount (osMessageQueueId_t mq_id);
/// Get number of available slots for messages in a Message Queue.
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
/// \return number of available slots for messages.
uint32_t osMessageQueueGetSpace (osMessageQueueId_t mq_id);
/// Reset a Message Queue to initial empty state.
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
/// \return status code that indicates the execution status of the function.
osStatus_t osMessageQueueReset (osMessageQueueId_t mq_id);
/// Delete a Message Queue object.
/// \param[in] mq_id message queue ID obtained by \ref osMessageQueueNew.
/// \return status code that indicates the execution status of the function.
osStatus_t osMessageQueueDelete (osMessageQueueId_t mq_id);
依然是上篇中的两个LED, 创建两个任务, 一个生产者一个消费者, 并且创建一个队列:
注意Item Size里面那个数据类型设置的uint8_t
, 这个是手写的, 可以是任意数据类型, 结构体之类的都可以, 只要自己实现就行.
生成代码后再freertos中补充:
/* USER CODE BEGIN Variables */
uint8_t queue_msg0[] = {0x00}; //use for producer
uint8_t queue_msg1[1]; //use for consumer
/* USER CODE END Variables */
void Entry_Producer(void *argument)
{
/* USER CODE BEGIN Entry_Producer */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
osMessageQueuePut(myQueue01Handle, queue_msg0, 1, 100);
++queue_msg0[0];
osDelay(1000);
}
/* USER CODE END Entry_Producer */
}
void Entry_Consumer(void *argument)
{
/* USER CODE BEGIN Entry_Consumer */
/* Infinite loop */
for(;;)
{
osMessageQueueGet(myQueue01Handle, queue_msg1, (uint8_t *)1, osWaitForever);
if(queue_msg1[0] % 2 == 0) {
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
}
}
/* USER CODE END Entry_Consumer */
}
编译下载, 可以看到LED1, LED2同时亮, LED1的翻转周期是1s, 而LED2是2s.
信号量的主要作用:
信号量的类型:
信号量相关的函数在cmsisi_os2.h
中列了出来:
// ==== Semaphore Management Functions ====
/// Create and Initialize a Semaphore object.
/// \param[in] max_count maximum number of available tokens.
/// \param[in] initial_count initial number of available tokens.
/// \param[in] attr semaphore attributes; NULL: default values.
/// \return semaphore ID for reference by other functions or NULL in case of error.
osSemaphoreId_t osSemaphoreNew (uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr);
/// Get name of a Semaphore object.
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
/// \return name as NULL terminated string.
const char *osSemaphoreGetName (osSemaphoreId_t semaphore_id);
/// Acquire a Semaphore token or timeout if no tokens are available.
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
/// \return status code that indicates the execution status of the function.
osStatus_t osSemaphoreAcquire (osSemaphoreId_t semaphore_id, uint32_t timeout);
/// Release a Semaphore token up to the initial maximum count.
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
/// \return status code that indicates the execution status of the function.
osStatus_t osSemaphoreRelease (osSemaphoreId_t semaphore_id);
/// Get current Semaphore token count.
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
/// \return number of tokens available.
uint32_t osSemaphoreGetCount (osSemaphoreId_t semaphore_id);
/// Delete a Semaphore object.
/// \param[in] semaphore_id semaphore ID obtained by \ref osSemaphoreNew.
/// \return status code that indicates the execution status of the function.
osStatus_t osSemaphoreDelete (osSemaphoreId_t semaphore_id);
新建两个相同优先级的任务:
新建一个二进制信号量:
生成代码, freertos.c
中补充:
void Entry_LED1(void *argument)
{
/* USER CODE BEGIN Entry_LED1 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
osDelay(1000);
osSemaphoreRelease(myBinarySem01Handle);
}
/* USER CODE END Entry_LED1 */
}
void Entry_LED2(void *argument)
{
/* USER CODE BEGIN Entry_LED2 */
/* Infinite loop */
for(;;)
{
osSemaphoreAcquire(myBinarySem01Handle, osWaitForever);
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
}
/* USER CODE END Entry_LED2 */
}
现象是LED1, LED2同时亮灭.
计数信号量用法和二进制信号量差不太多, 这里创建三个同优先级的任务:
添加一个计数信号量:
生成代码, 补充freertos.c
:
void Entry_LED1(void *argument)
{
/* USER CODE BEGIN Entry_LED1 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
osDelay(1000);
osSemaphoreRelease(myCountingSem01Handle);
}
/* USER CODE END Entry_LED1 */
}
void Entry_LED2(void *argument)
{
/* USER CODE BEGIN Entry_LED2 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
osDelay(1000);
osSemaphoreRelease(myCountingSem01Handle);
}
/* USER CODE END Entry_LED2 */
}
void Entry_LED3(void *argument)
{
/* USER CODE BEGIN Entry_LED3 */
/* Infinite loop */
for(;;)
{
osSemaphoreAcquire(myCountingSem01Handle, osWaitForever);
osSemaphoreAcquire(myCountingSem01Handle, osWaitForever);
HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);
}
/* USER CODE END Entry_LED3 */
}
这样, 3个LED同时亮灭.
互斥量用于控制在两个或多个任务间访问共享资源.
互斥量相关的管理函数在cmsis_os2.h
中的声明:
// ==== Mutex Management Functions ====
/// Create and Initialize a Mutex object.
/// \param[in] attr mutex attributes; NULL: default values.
/// \return mutex ID for reference by other functions or NULL in case of error.
osMutexId_t osMutexNew (const osMutexAttr_t *attr);
/// Get name of a Mutex object.
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
/// \return name as NULL terminated string.
const char *osMutexGetName (osMutexId_t mutex_id);
/// Acquire a Mutex or timeout if it is locked.
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
/// \param[in] timeout \ref CMSIS_RTOS_TimeOutValue or 0 in case of no time-out.
/// \return status code that indicates the execution status of the function.
osStatus_t osMutexAcquire (osMutexId_t mutex_id, uint32_t timeout);
/// Release a Mutex that was acquired by \ref osMutexAcquire.
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
/// \return status code that indicates the execution status of the function.
osStatus_t osMutexRelease (osMutexId_t mutex_id);
/// Get Thread which owns a Mutex object.
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
/// \return thread ID of owner thread or NULL when mutex was not acquired.
osThreadId_t osMutexGetOwner (osMutexId_t mutex_id);
/// Delete a Mutex object.
/// \param[in] mutex_id mutex ID obtained by \ref osMutexNew.
/// \return status code that indicates the execution status of the function.
osStatus_t osMutexDelete (osMutexId_t mutex_id);
这里以串口打印为例, 两个任务都用串口3进行printf打印. 新建两个相同优先级的任务:
创建互斥量:
生成代码, freertos.c
补充:
void Entry_Print1(void *argument)
{
/* USER CODE BEGIN Entry_Print1 */
/* Infinite loop */
for(;;)
{
osDelay(100);
osMutexAcquire(myMutex01Handle, 1000);
printf("task1\r\n");
osMutexRelease(myMutex01Handle);
}
/* USER CODE END Entry_Print1 */
}
void Entry_Print2(void *argument)
{
/* USER CODE BEGIN Entry_Print2 */
/* Infinite loop */
for(;;)
{
osDelay(100);
osMutexAcquire(myMutex01Handle, 1000);
printf("task2\r\n");
osMutexRelease(myMutex01Handle);
}
/* USER CODE END Entry_Print2 */
}
编译运行, 在串口助手中:
[23:28:09.944]收←◆task2
task1
[23:28:10.044]收←◆task2
task1
[23:28:10.144]收←◆task2
task1
[23:28:10.244]收←◆task2
task1
[23:28:10.344]收←◆task2
task1
[23:28:10.444]收←◆task2
task1
上面图中还有一个Recursive Mutexes
递归互斥锁, 这是个很有用的东西, 有兴趣的可以去了解一下.
欢迎扫描二维码关注本人微信公众号, 及时获取或者发送给我最新消息: