FreeRTOS 任务通知详解

张开发
2026/4/12 9:50:27 15 分钟阅读

分享文章

FreeRTOS 任务通知详解
FreeRTOS 任务通知详解一、 任务通知详解1、 什么是任务通知2、 任务通知的工作原理3、任务通知的 API 函数详解4、使用场景和示例5、总结二、代码示例一、 任务通知详解FreeRTOS 的任务通知Task Notification是一种高效的任务间通信机制它允许任务直接发送通知给其他任务而无需使用额外的队列、信号量或事件组。任务通知通过每个任务内置的一个 32 位通知值notification value实现这个值可以用于传递事件、信号或简单数据。任务通知的优势在于资源消耗低不占用额外内存和延迟小特别适合嵌入式实时系统。在本详解中我将逐步介绍任务通知的原理、API、使用场景、优缺点并提供代码示例。内容基于 FreeRTOS 官方文档确保真实可靠。1、 什么是任务通知任务通知是 FreeRTOS 提供的一种轻量级同步机制。每个任务在创建时都会分配一个 32 位整数作为通知值初始值为 0。其他任务可以通过 API 函数直接修改或读取这个值从而实现事件通知、信号量功能或简单数据传输。任务通知的核心优势在于它避免了中间对象如队列的开销减少了内存占用和上下文切换时间。2、 任务通知的工作原理任务通知的工作依赖于通知值的操作。通知值是一个 32 位无符号整数类型为uint32_t可以被视为一个多功能状态变量。其工作原理包括以下关键点发送通知一个任务发送者调用 API 函数修改目标任务的 32 位通知值。修改方式包括设置位、增加计数或覆盖值。接收通知目标任务调用 API 函数等待通知并可以指定等待条件如超时或特定值。当通知到达时接收任务被唤醒。通知类型通知值可以用于多种用途事件标志使用位掩码表示不同事件例如0 x 01 0x010x01表示事件 A0 x 02 0x020x02表示事件 B。计数信号量通知值作为计数器每次通知增加或减少值。数据传递直接传递一个 32 位整数。任务通知的等待机制基于任务阻塞状态。如果目标任务在等待通知时通知值不满足条件任务会进入阻塞状态Blocked State直到通知到达或超时。FreeRTOS 内部使用任务控制块Task Control Block管理通知值确保操作的原子性。3、任务通知的 API 函数详解FreeRTOS 提供了一系列 API 函数来操作任务通知。以下是常用函数的详细解释基于 FreeRTOS V10.x 及以上版本。所有函数都需要包含头文件FreeRTOS.h和task.h。BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify )功能发送一个通知给目标任务增加通知值的计数通常用于计数信号量。通知值增加 1。参数xTaskToNotify是目标任务句柄。返回值总是返回pdPASS表示成功。使用场景当实现二进制信号量或轻量级信号量时。uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )功能任务等待通知并获取通知值。如果通知值为 0任务阻塞否则减少通知值。参数xClearCountOnExit如果设置为pdTRUE则退出时清零通知值pdFALSE则只减少值。xTicksToWait等待超时时间单位是 tick时钟节拍使用portMAX_DELAY表示无限等待。返回值返回获取前的通知值。使用场景配合xTaskNotifyGive()实现信号量功能。BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction )功能灵活发送通知支持多种操作模式。参数xTaskToNotify目标任务句柄。ulValue要设置或修改的值。eAction操作类型枚举值eNoAction只唤醒任务不修改通知值。eSetBits设置通知值的指定位位或操作例如设置位0 x 01 0x010x01使用u l V a l u e 0 x 01 ulValue 0x01ulValue0x01。eIncrement增加通知值 1类似xTaskNotifyGive()。eSetValueWithOverwrite覆盖通知值为ulValue。eSetValueWithoutOverwrite如果通知值为 0则设置为ulValue否则失败。返回值pdPASS表示成功pdFAIL表示失败例如在eSetValueWithoutOverwrite模式下通知值非零。使用场景通用通知发送支持事件标志、数据传递等。BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait )功能任务等待通知并处理通知值。参数ulBitsToClearOnEntry在等待前清除通知值的指定位位与操作。ulBitsToClearOnExit在退出时清除通知值的指定位。pulNotificationValue指针用于存储接收到的通知值。xTicksToWait等待超时时间。返回值pdTRUE表示收到通知pdFALSE表示超时。使用场景等待特定事件或数据。uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear )功能清除目标任务通知值的指定位。参数xTask是任务句柄ulBitsToClear是要清除的位掩码。返回值清除前的通知值。使用场景手动管理通知状态。API 函数功能描述参数说明xTaskNotify()向指定任务发送通知可选择覆盖或更新通知值并可设置通知状态为 pending。xTaskToNotify: 目标任务句柄ulValue: 通知值eAction: 操作类型覆盖、更新、增/减等。xTaskNotifyFromISR()xTaskNotify()的中断安全版本。同上增加pxHigherPriorityTaskWoken用于指示是否需上下文切换。xTaskNotifyGive()简化版通知发送递增目标任务的通知值并设置通知状态为 pending无操作类型。xTaskToNotify: 目标任务句柄。vTaskNotifyGiveFromISR()xTaskNotifyGive()的中断安全版本。同上增加pxHigherPriorityTaskWoken。ulTaskNotifyTake()等待通知阻塞直至通知到达或超时可选择清零通知值或递减。xClearCountOnExit: 是否清零通知值xTicksToWait: 超时时间Tick。xTaskNotifyAndQuery()发送通知并获取目标任务当前的通知值原子操作。xTaskToNotify: 目标任务句柄ulValue: 新通知值eAction: 操作类型pulPreviousValue: 返回原通知值。xTaskNotifyAndQueryFromISR()xTaskNotifyAndQuery()的中断安全版本。同上增加pxHigherPriorityTaskWoken。xTaskNotifyStateClear()清除当前任务自身的通知状态pending 状态。无参数。ulTaskNotifyValueClear()清除当前任务自身的通知值。ulBitsToClear: 需清零的位掩码。说明操作类型 (eAction)可选值包括eNoAction仅更新状态、eSetBits按位或、eIncrement递增、eSetValueWithOverwrite覆盖、eSetValueWithoutOverwrite仅当未读时覆盖。返回值部分函数如xTaskNotify()返回pdPASS/pdFAIL表示通知是否成功发送ulTaskNotifyTake()返回实际接收到的通知值。4、使用场景和示例任务通知适用于多种场景轻量级信号量替代二进制或计数信号量减少内存占用。事件通知使用位操作表示多个事件例如按键事件0 x 01 0x010x01、定时器事件0 x 02 0x020x02。数据传递传递小数据如状态码但仅限于 32 位值。任务同步唤醒阻塞任务实现简单同步。优点高效操作直接在任务控制块中完成没有额外对象创建开销。低延迟上下文切换少适合实时性要求高的应用。内存节省每个任务自带通知值不分配额外内存。缺点值大小限制通知值仅 32 位不适合大数据传递。单一接收者通知只能发送给特定任务不能广播。优先级反转风险如果高优先级任务等待低优先级任务的通知可能发生优先级反转需使用优先级继承机制缓解。5、总结任务通知是 FreeRTOS 中强大的轻量级通信工具适用于资源受限的嵌入式系统。通过直接操作任务的 32 位通知值它能高效实现信号量、事件标志等功能。使用时优先考虑其低开销特性但注意其值大小限制和单任务接收约束。结合 API 函数如xTaskNotify()和ulTaskNotifyTake()可以构建灵活的任务同步机制。二、代码示例以下是一个简单示例展示如何使用任务通知实现事件标志功能任务 A 发送事件通知给任务 B任务 B 等待事件。#includeFreeRTOS.h#includetask.h// 定义事件标志#defineEVENT_BUTTON_PRESSED(10)// $0x01$ 表示按钮按下事件#defineEVENT_TIMER_EXPIRED(11)// $0x02$ 表示定时器到期事件// 任务 B事件处理任务voidvTaskB(void*pvParameters){uint32_tulNotificationValue;for(;;){// 等待事件通知超时 1000 ticksif(xTaskNotifyWait(0,0,ulNotificationValue,pdMS_TO_TICKS(1000))pdTRUE){// 检查事件if(ulNotificationValueEVENT_BUTTON_PRESSED){// 处理按钮事件}if(ulNotificationValueEVENT_TIMER_EXPIRED){// 处理定时器事件}}else{// 超时处理}}}// 任务 A事件发送任务voidvTaskA(void*pvParameters){TaskHandle_t xTaskBHandle(TaskHandle_t)pvParameters;// 假设任务 B 句柄已传递for(;;){// 模拟事件发生vTaskDelay(pdMS_TO_TICKS(500));// 延时 500ms// 发送按钮事件通知xTaskNotify(xTaskBHandle,EVENT_BUTTON_PRESSED,eSetBits);}}// 主函数创建任务intmain(void){TaskHandle_t xTaskBHandle;xTaskCreate(vTaskB,TaskB,configMINIMAL_STACK_SIZE,NULL,2,xTaskBHandle);xTaskCreate(vTaskA,TaskA,configMINIMAL_STACK_SIZE,xTaskBHandle,1,NULL);vTaskStartScheduler();return0;}代码说明任务 B 使用xTaskNotifyWait()等待通知并检查通知值的位标志。任务 A 使用xTaskNotify()以eSetBits模式发送事件设置相应位。事件标志使用位掩码定义如E V E N T _ B U T T O N _ P R E S S E D 0 x 01 EVENT\_BUTTON\_PRESSED 0x01EVENT_BUTTON_PRESSED0x01。此示例演示了事件通知的基本流程实际应用中需处理错误和超时。

更多文章