FreeRTOS 队列 信号量 互斥量

前言

FreeRTOS STM32CubeMX配置 内存管理 任务管理
上节介绍了用STM32CubeMX生成带FreeRTOS的工程, 细心的同学可能发现, 已创建任务的函数为例, FreeRTOS官方是xTaskCreate(), 到之前CubeMX生成cmsis_os中的osThreadCreate, 再到最近CubeMX生成cmsisi_os2中的osThreadNew, 不用疑惑, 跳转到CubeMX生成的函数定义可以看到, 其实后者是对FreeRTOS函数的进一步封装, osThreadNew封装了xTaskCreate()xTaskCreateStatic, 类似的, osMessageQueueGet封装了xQueueReceivexQueueReceiveFromISR, 用起来框架上没有什么大的不同, 类似于NXP MCU中PAL对HAL的进一步封装, 甚至更好用了, 所以, 放心用CubeMX生成的函数就好.

FreeRTOS的任务间通信有队列, 信号量和互斥量, 下面一一介绍.

Queue 队列

队列的作用:

  • 基于FreeRTOS的应用程序是由一系列独立或交互的任务构成的,每个任务都拥有自
    己相对独立的资源。
  • FreeRTOS中大多数通信和同步都是基于队列实现的。
  • 通常队列被作为FIFO使用,即数据由队列尾写入,从队列首读出。

队列相关的函数在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.

Semaphore 信号量

信号量的主要作用:

  • 用于做任务间的同步
  • 用于做中断与任务之间的同步

信号量的类型:

  • 二进制信号量
    • 仅有一个 ‘token’
    • 用于同步一个操作
  • 计数信号量
    • 拥有多个’tokens’
    • 同步多个操作

信号量相关的函数在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同时亮灭.

Mutex 互斥量

互斥量用于控制在两个或多个任务间访问共享资源.

互斥量相关的管理函数在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递归互斥锁, 这是个很有用的东西, 有兴趣的可以去了解一下.

微信公众号

欢迎扫描二维码关注本人微信公众号, 及时获取或者发送给我最新消息:
在这里插入图片描述

发布了203 篇原创文章 · 获赞 105 · 访问量 38万+

猜你喜欢

转载自blog.csdn.net/weifengdq/article/details/103377103