1. 任务通知的基本概念
任务通知是 FreeRTOS 中一种高效的进程间通信机制,它直接通过任务控制块(TCB)进行通信,无需创建额外的通信对象。
与其他通信机制对比:
-
队列、信号量、事件组:需要先创建对应的结构体,作为中介在生产者和消费者之间传递数据
-
任务通知:直接通过任务本身的 TCB 进行通信,无需额外结构体
2. 任务通知的优势与限制
2.1 优势
-
效率更高:比队列、信号量、事件组都有显著性能优势
-
更节省内存:无需创建额外的通信结构体
2.2 限制
-
不能发送数据给 ISR:ISR 没有任务结构体,无法接收任务通知(但 ISR 可以发送通知给任务)
-
数据只能给该任务独享:通知值存储在目标任务 TCB 中,只有该任务能访问
-
无法缓冲数据:每个任务只能保持一个通知值,无法像队列那样缓冲多个数据
-
无法广播给多个任务:一次只能通知一个任务
-
发送受阻时无法阻塞等待:发送方只能立即返回错误,不能阻塞等待
3. 任务通知的内部机制
3.1 任务控制块中的通知相关成员
每个任务的 TCB 中包含两个关键成员:
c
typedef struct tskTaskControlBlock {
// ...
volatile uint32_t ulNotifiedValue[configTASK_NOTIFICATION_ARRAY_ENTRIES];
volatile uint8_t ucNotifyState[configTASK_NOTIFICATION_ARRAY_ENTRIES];
// ...
} tskTCB;
3.2 通知状态的三种取值
-
taskNOT_WAITING_NOTIFICATION(0):任务没有在等待通知(初始状态) -
taskWAITING_NOTIFICATION(1):任务正在等待通知 -
taskNOTIFICATION_RECEIVED(2):任务已接收到通知(有数据待处理)
3.3 通知值的两种用途
-
计数值:类似计数信号量
-
位图:类似事件组,可以按位操作
4. 任务通知的工作场景分析
4.1 场景一:任务非等待通知状态
-
任务 B 处于
taskNOT_WAITING_NOTIFICATION状态(非因等待通知而阻塞) -
任务 A 发送通知:将 B 的状态改为
taskNOTIFICATION_RECEIVED -
B 不会被立即唤醒,但当 B 被其他方式唤醒后,如果调用等待通知函数,会立即接收通知
4.2 场景二:任务等待通知状态
-
任务 B 因等待通知进入
taskWAITING_NOTIFICATION状态 -
任务 A 发送通知:B 被唤醒,状态变为
taskNOTIFICATION_RECEIVED -
B 的 TCB 中的通知值和状态被修改
-
B 处理完通知后,状态恢复为
taskNOT_WAITING_NOTIFICATION
5. 任务通知的 API 函数分类
| 类别 | 专业版 | 简化版 |
|---|---|---|
| 发出通知 | xTaskNotify, xTaskNotifyFromISR | xTaskNotifyGive, vTaskNotifyGiveFromISR |
| 取出通知 | xTaskNotifyWait | ulTaskNotifyTake |
6. 简化版函数详解
6.1 发出通知函数
-
xTaskNotifyGive/vTaskNotifyGiveFromISR:使目标任务的通知值加 1(类似信号量操作)
6.2 取出通知函数
-
ulTaskNotifyTake:-
如果任务因等待通知而阻塞(
taskWAITING_NOTIFICATION),会被唤醒并使通知值减 1 -
如果任务未等待通知,仅将状态改为
taskNOTIFICATION_RECEIVED,不影响任务执行
-
7. 专业版函数详解
7.1 xTaskNotify 函数
c
BaseType_t xTaskNotify(TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction);
参数说明:
-
xTaskToNotify:目标任务句柄 -
ulValue:通知值 -
eAction:通知动作,决定如何处理通知值
7.2 eNotifyAction 取值及含义
| 动作 | 说明 |
|---|---|
eNoAction | 仅更新通知状态为 "pending",不使用 ulValue。相当于轻量级二进制信号量 |
eSetBits | 通知值 = 原通知值 | ulValue(按位或)。相当于轻量级事件组 |
eIncrement | 通知值 = 原通知值 + 1,不使用 ulValue。相当于轻量级计数信号量,功能同 xTaskNotifyGive() |
eSetValueWithoutOverwrite | 不覆盖:如果通知状态为 "pending"(有未读数据),则不做任何操作并返回 pdFAIL |
eSetValueWithOverwrite | 覆盖:无论如何都设置通知值 = ulValue |
7.3 xTaskNotifyWait 函数
c
BaseType_t xTaskNotifyWait(uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait);
参数说明:
-
ulBitsToClearOnEntry:在进入函数时清除通知值的指定位(如果当前不是taskNOTIFICATION_RECEIVED状态) -
ulBitsToClearOnExit:在接收到通知并保存值后,清除通知值的指定位 -
pulNotificationValue:用于保存接收到的通知值 -
xTicksToWait:超时时间
8. 补充说明
8.1 使用建议
-
适合一对一的通信场景,特别是从多个数据源向单个任务发送数据的场景
-
不适合需要数据缓冲、广播或多任务共享数据的场景
8.2 性能考虑
-
任务通知是 FreeRTOS 中最快的进程间通信机制
-
在资源受限的嵌入式系统中,优先考虑使用任务通知替代队列/信号量
8.3 注意事项
-
使用任务通知时需要仔细管理通知状态,避免数据丢失
-
对于复杂通信需求,仍需要结合队列、事件组等其他机制使用
1790

被折叠的 条评论
为什么被折叠?



