2025最新超详细FreeRTOS入门教程:第八章 FreeRTOS任务通知
FreeRTOS任务通知是最轻量级的任务通信机制,每个任务自带32位通知值,无需额外内存开销。它支持二值信号量、计数信号量、事件标志和消息传递等多种用途,特别适合ISR与任务间的高效通信。关键API包括xTaskNotify()发送通知和xTaskNotifyWait()接收通知。相比队列和信号量,任务通知速度最快、内存占用最少,但不支持多任务等待。典型应用场景包括中断触发任务、轻量级任务间通信等
·
2025最新超详细FreeRTOS入门教程:第八章 FreeRTOS任务通知
摘要
在前几章中,我们依次学习了 队列、信号量、互斥量、事件组 等任务间通信与同步机制。虽然这些工具功能强大,但在某些轻量级场景下,它们可能显得“过于复杂”,比如:
- 任务之间只需要传递一个整数值
- ISR 只需要快速通知任务执行一次操作
- 需要更快、更轻量的同步方式
此时,使用 任务通知(Task Notification) 是最佳选择。
任务通知是 FreeRTOS 提供的最轻量级任务通信机制,它不需要额外的内存开销,比队列和信号量更高效。
文章目录
一、任务通知的基本概念
1. 定义
- 每个任务都有一个 32 位通知值,由 FreeRTOS 内核维护
- 任务可以等待通知、发送通知
- 通知既可以当作 二值信号量 使用,也可以当作 计数信号量/事件标志/消息传递 使用
2. 特点
- 每个任务最多有一个通知值(可扩展为多个通知数组,FreeRTOS V10.4+ 引入
TaskNotifyTakeIndexed
) - 不需要额外的句柄对象(不像队列、信号量需要额外 RAM)
- 速度最快,适合 ISR 与任务之间的通信
二、任务通知 API
1. 发送任务通知
BaseType_t xTaskNotify(
TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction
);
eAction
取值:eSetBits
:对通知值执行按位或eIncrement
:通知值自增eSetValueWithOverwrite
:覆盖写入eSetValueWithoutOverwrite
:不覆盖已有通知
2. 等待任务通知
BaseType_t xTaskNotifyWait(
uint32_t ulBitsToClearOnEntry,
uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue,
TickType_t xTicksToWait
);
ulBitsToClearOnEntry
:进入等待前清除的位ulBitsToClearOnExit
:退出等待后清除的位pulNotificationValue
:输出通知值xTicksToWait
:阻塞等待时间
3. 类似信号量的 API
uint32_t ulTaskNotifyTake(BaseType_t xClearCountOnExit,
TickType_t xTicksToWait);
- 用于实现 二值/计数信号量
xClearCountOnExit = pdTRUE
:退出时清零- 返回值:成功接收到的次数
4. ISR 中发送任务通知
BaseType_t xTaskNotifyFromISR(
TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction,
BaseType_t *pxHigherPriorityTaskWoken
);
三、任务通知使用示例
示例1:ISR 通知任务
#include "FreeRTOS.h"
#include "task.h"
TaskHandle_t xHandleTask;
void vTaskHandler(void *pvParameters)
{
uint32_t ulNotifiedValue;
for(;;)
{
if(xTaskNotifyWait(0x00, 0xFFFFFFFF, &ulNotifiedValue, portMAX_DELAY) == pdTRUE)
{
printf("任务被中断唤醒, 值=%lu\n", ulNotifiedValue);
}
}
}
void EXTI0_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(xHandleTask, 1, eIncrement, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
xTaskCreate(vTaskHandler, "Handler", 128, NULL, 2, &xHandleTask);
vTaskStartScheduler();
}
运行结果:
- 每次中断触发,任务收到通知并打印计数
示例2:任务间通信(传递整数)
TaskHandle_t xTaskSender, xTaskReceiver;
void vTaskSender(void *pvParameters)
{
uint32_t count = 0;
for(;;)
{
count++;
xTaskNotify(xTaskReceiver, count, eSetValueWithOverwrite);
vTaskDelay(1000);
}
}
void vTaskReceiver(void *pvParameters)
{
uint32_t ulValue;
for(;;)
{
if(xTaskNotifyWait(0, 0, &ulValue, portMAX_DELAY) == pdTRUE)
{
printf("接收值=%lu\n", ulValue);
}
}
}
四、任务通知的工作机制
五、任务通知与队列/信号量对比
特性 | 任务通知 | 队列 | 信号量 |
---|---|---|---|
速度 | ✅ 最快 | ❌ 较慢 | 中等 |
内存占用 | ✅ 无额外对象 | ❌ 队列结构体 | ❌ 信号量对象 |
传递数据 | ✅ 支持整数/位图 | ✅ 任意数据 | ❌ |
ISR 使用 | ✅ | ✅ | ✅ |
多任务等待 | ❌ 仅单任务 | ✅ | ✅ |
六、常见应用场景
- 任务与中断同步
- ISR 中快速通知任务处理事件
- 任务间轻量通信
- 不需要完整队列机制,只需传递整数
- 任务事件触发
- 使用按位通知,代替事件组的轻量版本
七、调试与监控
- 使用
uxTaskGetStackHighWaterMark()
检查任务是否正常运行 - 使用
vTaskGetInfo()
获取任务通知状态 - 注意避免丢失通知(覆盖写模式可能导致数据丢失)
八、常见问题与解决方法
问题 | 可能原因 | 解决方法 |
---|---|---|
通知丢失 | 使用覆盖写模式 | 改用 eIncrement 或事件组 |
ISR 报错 | 使用了错误 API | 改用 xTaskNotifyFromISR |
多任务等待通知失败 | 每个任务只能有一个通知值 | 使用事件组/队列替代 |
通知无法清零 | xTaskNotifyWait 参数设置错误 |
检查清除位参数 |
九、经验总结
📌 开发建议
- 若只是 ISR 与任务间的 单一事件触发,优先使用任务通知
- 若需要多个任务等待,使用事件组或队列
- 若需要传递复杂数据结构,使用队列
- 在高实时性场景,任务通知是最快的通信方式
十、总结
通过本章学习,你已经掌握:
- 任务通知的概念与机制
- 发送与接收任务通知的 API
- 在 ISR 与任务之间使用任务通知
- 与队列/信号量的对比和适用场景
任务通知是 FreeRTOS 中最轻量的同步与通信工具,在性能要求高的嵌入式系统中非常有价值。
🔗 FreeRTOS专栏👉 下一章:2025最新超详细FreeRTOS入门教程:第九章 FreeRTOS软件定时器 ——将学习如何在任务之外调度定时操作。
更多推荐
所有评论(0)