1、任务通知理论
1.1 任务通知的核心
理解任务通知的核心在于TCB结构体中有这两项:
ucNotifyState有3种取值:
- taskNOT_WAITING_NOTIFICATION:任务没有在等待通知
- taskWAITING_NOTIFICATION:任务在等待通知
- taskNOTIFICATION_RECEIVED:任务接收到了通知,也被称为pending(有数据了,待处理)
1.2 使用流程
使用流程:使用ucNotifyState来切换任务状态(阻塞、就绪),使用ulNotifiedValue来传递信息。
- 任务A被创建出来时,ucNotifyState为
taskNOT_WAITING_NOTIFICATION
- 它想等待通知的话
- 调用
ulTaskNotifyTake
或xTaskNotifyWait
,进入taskWAITING_NOTIFICATION
- 表示在等待通知,任务进入阻塞状态
- 调用
- 任务B可以调用这两个函数来通知A:
xTaskNotifyGive
或xTaskNotify
- 任务A的ucNotifyState就变为
taskNOTIFICATION_RECEIVED
- 表示收到了通知,待处理
- 任务A的ucNotifyState就变为
- 任务A从阻塞状态变为就绪态,它运行时
- 从
ulTaskNotifyTake
或xTaskNotifyWait
得到数值并返回 - 返回之前把ucNotifyState恢复为
taskNOT_WAITING_NOTIFICATION
- 从
1.3 怎么传递信息
函数原型如下:
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait );
发送函数 | 作用 | 接收函数 | 作用 |
---|---|---|---|
xTaskNotifyGive | val++ | ulTaskNotifyTake | val-- 或 val = 0 |
xTaskNotify | xTaskNotifyWait | ||
不使用val,只起通知作用 | 可以在函数进入时清除val的某些位 可以在函数退出前清除val的某些位 可以取得val的值 | ||
val |= (bits) | |||
val++ | |||
val = xxx 不覆盖, 当ucNotifyState表示在等待才起效 | |||
val = xxx 覆盖 |
eNotifyAction参数说明:
eNotifyAction取值 | 说明 |
---|---|
eNoAction | 仅仅是更新通知状态为"pending",未使用ulValue。 这个选项相当于轻量级的、更高效的二进制信号量。 |
eSetBits | 通知值 = 原来的通知值 | ulValue,按位或。 相当于轻量级的、更高效的事件组。 |
eIncrement | 通知值 = 原来的通知值 + 1,未使用ulValue。 相当于轻量级的、更高效的二进制信号量、计数型信号量。 相当于 xTaskNotifyGive() 函数。 |
eSetValueWithoutOverwrite | 不覆盖。 如果通知状态为"pending"(表示有数据未读), 则此次调用xTaskNotify不做任何事,返回pdFAIL。 如果通知状态不是"pending"(表示没有新数据), 则:通知值 = ulValue。 |
eSetValueWithOverwrite | 覆盖。 无论如何,不管通知状态是否为"pending", 通知值 = ulValue。 |
2. 任务通知使用_轻量级信号量
函数对比:
信号量 | 使用任务通知实现信号量 | |
---|---|---|
创建 | SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount ); | 无 |
Give | xSemaphoreGive( SemaphoreHandle_t xSemaphore ); | BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); |
Take | xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xBlockTime ); | uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ); |
信号量:give N次,take N次
任务通知:
- pdTrue:val = 0 ,take 1次
- pdFalse:val-- , take N次(与信号量相同)
3. 任务通知使用_轻量级队列
函数对比:
队列 | 使用任务通知实现队列 | |
---|---|---|
创建 | QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, UBaseType_t uxItemSize ); | 无 |
发送 | BaseType_t xQueueSend( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait ); | BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction ); |
接收 | BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ); | BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ); |
轻量级队列(任务通知):
- 覆盖:多次发送,接收的是最后一次数据
- 不覆盖:多次发送,只有最开始那次成功
4. 任务通知使用_轻量级事件组
假设有3个任务:
- 任务1做事件1
- 任务2做事件2
- 任务3等待事件1、事件2都发生
可以使用事件组来编写程序,也可以使用任务通知来编写程序。
函数对比:
事件组 | 使用任务通知实现事件组 | |
---|---|---|
创建 | EventGroupHandle_t xEventGroupCreate( void ) | 无 |
设置事件 | EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet ); | BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction ); |
等待事件 | EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait ); | BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ); |
轻量级任务组(任务通知):一旦设置事件,一定会唤醒目标任务,无法指定哪些事件(无法实现真正的任务组)
队列、信号量、事件组与任务通知的区别
使用队列、信号量、事件组时,我们都要事先创建对应的结构体,双方通过中间的结
构体通信:
使用任务通知时,任务结构体 TCB 中就包含了内部对象,可以直接接收别人发过来的
“通知”:(多对一的关系)