目录
FreeRTOS 从版本 V8.2.0 开始提供任务通知这个功能,每个任务都有一个 32 位的通知值。按照 FreeRTOS 官方的说法,使用消息通知比通过二进制信号量方式解除阻塞任务快 45%, 并且更加 省内存(无需创建队列)。
在大多数情况下,任务通知可以替代二值信号量、计数信号量、事件标志组,可以替代长度为 1 的队列(可以保存一个 32 位整数或指针值),并且任务通知速度更快、使用的RAM更少!
FreeRTOS 提供以下几种方式发送通知给任务 :
通过对以上方式的合理使用,可以在一定场合下替代原本的队列、信号量、事件标志组等。
函数 | 描述 |
xTaskNotify() | 发送通知,带有通知值 |
xTaskNotifyAndQuery() | 发送通知,带有通知值并且保留接收任务的原通知值 |
xTaskNotifyGive() | 发送通知,不带通知值 |
xTaskNotifyFromISR() | 在中断中发送任务通知 |
xTaskNotifyAndQueryFromISR() | 在中断中发送任务通知 |
vTaskNotifyGiveFromISR() | 在中断中发送任务通知 |
- BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,
- uint32_t ulValue,
- eNotifyAction eAction );
参数:
xTaskToNotify:
ulValue:
eAction:
枚举值 | 描述 |
eNoAction | 发送通知,但不更新值(参数ulValue未使用) |
eSetBits | 被通知任务的通知值按位或ulValue。(某些场景下可代替事 件组,效率更高) |
eIncrement | 被通知任务的通知值增加1(参数ulValue未使用),相当于 xTaskNotifyGive |
eSetValueWithOverwrite | 被通知任务的通知值设置为 ulValue。(某些场景下可代替 xQueueOverwrite ,效率更高) |
eSetValueWithoutOverwrite | 如果被通知的任务当前没有通知,则被通知的任务的通知值设为ulValue。 如果被通知任务没有取走上一个通知,又接收到了一个通 知,则这次通知值丢弃,在这种情况下视为调用失败并返回 pdFALSE (某些场景下可代替 xQueueSend ,效率更高) |
返回值:
- BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify,
- uint32_t ulValue,
- eNotifyAction eAction,
- uint32_t *pulPreviousNotifyValue );
参数:
xTaskToNotify:
ulValue:
eAction:
pulPreviousNotifyValue:
返回值:
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
参数:
返回值:
等待通知API函数只能用在任务,不可应用于中断中!
函数 | 描述 |
ulTaskNotifyTake() | 获取任务通知,可以设置在退出此函数的时候将任务通知值清零或者减 一。当任务通知用作二值信号量或者计数信号量的时候,使用此函数来 获取信号量。 |
xTaskNotifyWait() | 获取任务通知,比 ulTaskNotifyTake()更为复杂,可获取通知值和清除通知值的指定位 |
- uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,
- TickType_t xTicksToWait );
参数:
xClearCountOnExit:
xTicksToWait:阻塞等待任务通知值的最大时间;
返回值:
- BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,
- uint32_t ulBitsToClearOnExit,
- uint32_t *pulNotificationValue,
- TickType_t xTicksToWait );
参数:
ulBitsToClearOnEntry:
ulBitsToClearOnExit:
pulNotificationValue:
xTicksToWait:等待消息通知的最大等待时间。
首先得打开CubeMX,将FreeRTOS移植到STM32F103C8T6,具体看我之前写过的文章
(1)创建两个任务和设置按键引脚为输入
(2)设置两个按键分别发送和接收二值信号量
用到函数
- void StartTaskSend(void const * argument)
- {
- for(;;)
- {
- if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
- {
- osDelay(20);
- if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
- {
- xTaskNotifyGive(TaskReceiveHandle);
- printf("任务通知:模拟二值信号量发送成功\r\n");
- }
- while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
- }
- osDelay(10);
- }
- }
-
- void StartTaskReceive(void const * argument)
- {
- uint32_t rev = 0;
- for(;;)
- {
- if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
- {
- osDelay(20);
- if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
- {
- rev = ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
- if(rev != 0)
- printf("任务通知:模拟二值信号量接受成功\r\n");
- }
- while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);
- }
- osDelay(10);
- }
- }
(3)打开串口助手,查看结果
模拟计数型信号量跟模拟二值信号量基本相同:
将ulTaskNotifyTake()函数中第一个参数从pdTRUE改为pdFALSE
(1)代码示例:
用到函数
- void StartTaskSend(void const * argument)
- {
- for(;;)
- {
- if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
- {
- osDelay(20);
- if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
- {
- xTaskNotifyGive(TaskReceiveHandle);
- printf("任务通知:模拟计数型信号量发送成功\r\n");
- }
- while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
- }
- osDelay(10);
- }
- }
-
- void StartTaskReceive(void const * argument)
- {
- uint32_t rev = 0;
- for(;;)
- {
- if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
- {
- osDelay(20);
- if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
- {
- rev = ulTaskNotifyTake(pdFALSE,portMAX_DELAY);
- if(rev != 0)
- printf("任务通知:模拟计数型信号量接受成功,rev = %d\r\n",rev);
- }
- while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);
- }
- osDelay(10);
- }
- }
(2)打开串口助手,查看结果
(1)代码示例:
用到函数
- void StartTaskSend(void const * argument)
- {
- for(;;)
- {
- if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
- {
- osDelay(20);
- if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
- {
- printf("将bit0位置1\r\n");
- xTaskNotify(TaskReceiveHandle,0x01,eSetBits);
- }
- while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
- }
- if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
- {
- osDelay(20);
- if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
- {
- printf("将bit1位置1\r\n");
- xTaskNotify(TaskReceiveHandle,0x02,eSetBits);
- }
- while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);
- }
- osDelay(10);
- }
- }
-
- void StartTaskReceive(void const * argument)
- {
- uint32_t notify_rev = 0, event_bit = 0;
- for(;;)
- {
- xTaskNotifyWait(0,0xFFFFFFFF,¬ify_rev,portMAX_DELAY);
- if(notify_rev & 0x01)
- {
- event_bit |= 0x01;
- }
- if(notify_rev & 0x02)
- {
- event_bit |= 0x02;
- }
- if(event_bit == (0x01 | 0x02))
- {
- printf("任务通知模拟事件标志组接收成功\r\n");
- event_bit = 0;
- }
- osDelay(10);
- }
- }
(2)打开串口助手,查看结果
模拟邮箱大概就是向任务发送数据,但是与队列不同,任务邮箱发送消息受到了很多限制。
(1)代码示例:
用到函数
- void StartTaskSend(void const * argument)
- {
- for(;;)
- {
- if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
- {
- osDelay(20);
- if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
- {
- printf("按键1按下\r\n");
- xTaskNotify(TaskReceiveHandle,1,eSetValueWithOverwrite);
- }
- while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
- }
- if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
- {
- osDelay(20);
- if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET)
- {
- printf("按键2按下\r\n");
- xTaskNotify(TaskReceiveHandle,2,eSetValueWithOverwrite);
- }
- while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET);
- }
- osDelay(10);
- }
- }
-
- void StartTaskReceive(void const * argument)
- {
- uint32_t notify_rev = 0;
- for(;;)
- {
- xTaskNotifyWait(0,0xFFFFFFFF,¬ify_rev,portMAX_DELAY);
- printf("接收到的通知值为:%d\r\n",notify_rev);
- osDelay(10);
- }
- }
(2)打开串口助手,查看结果