tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第14章 任务通知" author: "尚硅谷研究院" date: 2026-07-17
用生活理解:任务通知就像同事直接走到你工位前告诉你一件事,不需要通过公司广播(队列)、公告栏(信号量)或旗语系统(事件标志组)。直接、高效,但因为是一对一,没法同时通知所有人。
任务通知(Task Notification)是 FreeRTOS V8.2.0 引入的轻量级任务间通信机制。每个 TCB 中内置了一个 32 位通知值和 8 位通知状态,无需创建额外的内核对象。
任务通知支持 3 种使用模式:
| 模式 | 发送函数 | 接收函数 | 行为 |
|---|---|---|---|
| 信号量模式 | xTaskNotifyGive() |
ulTaskNotifyTake() |
通知值递增/递减,模拟二值/计数信号量 |
| 消息邮箱模式 | xTaskNotify(handle, value, eSetValueWithOverwrite) |
xTaskNotifyWait() |
直接覆盖通知值,模拟消息邮箱 |
| 事件标志组模式 | xTaskNotify(handle, bits, eSetBits) |
xTaskNotifyWait() |
按位 OR,模拟事件标志组 |
| Function | Params | Description |
|---|---|---|
xTaskNotifyGive() |
xTaskToNotify |
发送通知(信号量模式),通知值 +1 |
ulTaskNotifyTake() |
xClearCountOnExit, xTicksToWait |
接收通知(信号量模式),返回通知值 |
xTaskNotify() |
xTaskToNotify, ulValue, eAction |
发送通知(带值),eAction 控制写/OR/覆写行为 |
xTaskNotifyWait() |
ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait |
接收通知(带值),可获取通知值和清除指定位 |
xTaskNotifyFromISR() |
同上+ISR 版本 | 中断中发送通知 |
vTaskNotifyGiveFromISR() |
xTaskToNotify, pxHigherPriorityTaskWoken |
中断中发送(信号量模式) |
Requirements: KEY1 → xTaskNotifyGive(task2),task2 通过 ulTaskNotifyTake() 接收。
File: P16/16_任务通知模拟信号量/Core/Src/freertos_demo.c
/* task1:KEY1按下,发送任务通知(信号量模式) */
void Task1(void *pvParameters)
{
uint8_t key = 0;
while (1) {
key = Key_Detect();
if (key == KEY1_PRESS) {
printf("任务通知模拟二值信号量释放\r\n");
/* 向task2发送任务通知,通知值+1 */
xTaskNotifyGive(task2_handler);
}
vTaskDelay(10);
}
}
/* task2:接收任务通知(信号量模式) */
void Task2(void *pvParameters)
{
uint32_t rev = 0;
while (1) {
/* 等待任务通知,pdTRUE表示退出时清零通知值 */
rev = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (rev != 0) {
printf("接收任务通知成功,模拟获取二值信号量\r\n");
}
}
}
Requirements: KEY1/KEY2 → xTaskNotify(task2, key, eSetValueWithOverwrite),task2 通过 xTaskNotifyWait() 接收并处理。
File: P17/17_任务通知模拟消息邮箱/Core/Src/freertos_demo.c
/* task1:扫描按键,通过任务通知发送键值 */
void Task1(void *pvParameters)
{
uint8_t key = 0;
while (1) {
key = Key_Detect();
if ((key != 0) && (task2_handler != NULL)) {
printf("任务通知模拟消息邮箱发送,发送的键值为:%d\r\n", key);
/* 直接覆写task2的通知值(消息邮箱模式) */
xTaskNotify(task2_handler, key, eSetValueWithOverwrite);
}
vTaskDelay(10);
}
}
/* task2:接收通知值,根据键值做不同动作 */
void Task2(void *pvParameters)
{
uint32_t notify_val = 0;
while (1) {
/* 等待任务通知,参数:入口不清零,出口全清零 */
xTaskNotifyWait(0, 0xFFFFFFFF, ¬ify_val, portMAX_DELAY);
switch (notify_val) {
case KEY1_PRESS: {
printf("接收到的通知值为:%d\r\n", notify_val);
LED_Toggle(LED1_Pin);
break;
}
case KEY2_PRESS: {
printf("接收到的通知值为:%d\r\n", notify_val);
LED_Toggle(LED2_Pin);
break;
}
default:
break;
}
}
}
Requirements: KEY1 → 置 EVENTBIT_0,KEY2 → 置 EVENTBIT_1,task2 等待两个位都置位。
File: P18/18_任务通知模拟事件标志组/Core/Src/freertos_demo.c
#define EVENTBIT_0 (1 << 0)
#define EVENTBIT_1 (1 << 1)
/* task1:KEY1 -> 设置EVENTBIT_0,KEY2 -> 设置EVENTBIT_1 */
void Task1(void *pvParameters)
{
uint8_t key = 0;
while (1) {
key = Key_Detect();
if (key == KEY1_PRESS) {
printf("将bit0位置1\r\n");
/* 使用eSetBits模式,按位OR设置通知值 */
xTaskNotify(task2_handler, EVENTBIT_0, eSetBits);
} else if (key == KEY2_PRESS) {
printf("将bit1位置1\r\n");
xTaskNotify(task2_handler, EVENTBIT_1, eSetBits);
}
vTaskDelay(10);
}
}
/* task2:等待bit0和bit1都置位 */
void Task2(void *pvParameters)
{
uint32_t notify_val = 0, event_bit = 0;
while (1) {
/* 等待任务通知 */
xTaskNotifyWait(0, 0xFFFFFFFF, ¬ify_val, portMAX_DELAY);
if (notify_val & EVENTBIT_0) {
event_bit |= EVENTBIT_0;
}
if (notify_val & EVENTBIT_1) {
event_bit |= EVENTBIT_1;
}
if (event_bit == (EVENTBIT_0 | EVENTBIT_1)) {
printf("任务通知模拟事件标志组接收成功\r\n");
event_bit = 0; /* 手动清除 */
}
}
}
/* FreeRTOSConfig.h */
#define configUSE_TASK_NOTIFICATIONS 1 /* 使能任务通知(默认已使能) */
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 1 /* 通知数组大小,默认1 */
| Function | Parameters | Return | Description |
|---|---|---|---|
BaseType_t xTaskNotifyGive(xTaskToNotify) |
目标任务句柄 | pdPASS/pdFAIL | 发送通知(信号量模式,通知值+1) |
uint32_t ulTaskNotifyTake(xClearCountOnExit, xTicksToWait) |
退出时清零?, 超时 | 通知值 | 接收通知(信号量模式,阻塞) |
BaseType_t xTaskNotify(xTaskToNotify, ulValue, eAction) |
目标任务, 值, 动作(覆写/OR/增量等) | pdPASS/pdFAIL | 发送通知(带值) |
BaseType_t xTaskNotifyWait(ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait) |
入口清除, 出口清除, 值指针, 超时 | pdPASS/pdFAIL | 接收通知(阻塞等待) |
BaseType_t xTaskNotifyFromISR(xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken) |
同 xTaskNotify+唤醒标记 | pdPASS/pdFAIL | ISR中发送通知 |
void vTaskNotifyGiveFromISR(xTaskToNotify, pxHigherPriorityTaskWoken) |
目标任务, 唤醒标记 | 无 | ISR中信号量模式发送 |
BaseType_t xTaskNotifyAndQuery(xTaskToNotify, ulValue, eAction, pulPreviousValue) |
同 xTaskNotify+原值 | pdPASS/pdFAIL | 发送并获取原通知值 |
eAction 枚举值:
eNoAction:仅发送通知(不修改值)eSetBits:与现有值按位 OReIncrement:通知值 +1eSetValueWithOverwrite:无条件覆写eSetValueWithoutOverwrite:仅当接收方未读取时覆写(否则返回 pdFAIL)xTaskNotifyWaitFromISR() 函数。接收任务通知只能在任务上下文中进行。xTaskNotifyGive() 使 32 位通知值递增,溢出后归零且无错误提示。pdTRUE:读取后清零(适合二值信号量)pdFALSE:读取后减 1(适合计数信号量),ulTaskNotifyTake() 返回减 1 前的值eSetValueWithOverwrite 时,如果发送者在接收者读取前多次发送,只有最后一次的值会被保留。