FreeRTOS 任务通知(Task Notification)详解

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 函数分类

类别专业版简化版
发出通知xTaskNotifyxTaskNotifyFromISRxTaskNotifyGivevTaskNotifyGiveFromISR
取出通知xTaskNotifyWaitulTaskNotifyTake

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 注意事项

  • 使用任务通知时需要仔细管理通知状态,避免数据丢失

  • 对于复杂通信需求,仍需要结合队列、事件组等其他机制使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值