FreeRTOS 任務通知
- 任務通知簡介
- 一 、發送通知
- 1.1 xTaskNotify()
- 1.2 xTaskNotifyFromISR()
- 1.3 xTaskNotifyGive()
- 1.4 xTaskNotifyAndQuery()
- 1.5 xTaskNotifyAndQueryFromISR()
- 二、接收通知
- 2.1 ulTaskNotifyTake()
- 2.2 xTaskNotifyWait()
- 三、清除通知狀態和值
- 3.1 xTaskNotifyStateClear()
- 3.2 ulTaskNotifyValueClear()
- 四、eAction 值和相關操作
FreeRTOS 中的任務通知 (Task Notifications) 是一種極其高效、輕量級的任務間通信 (IPC) 和同步機制。它允許一個任務或中斷服務程序 (ISR) 直接向另一個任務發送事件通知,并可選地附帶一個 32 位的值。相比于傳統的隊列、信號量、事件組等機制,任務通知通常更快且占用更少的內存,因為它利用了任務控制塊 (TCB) 中已有的字段。
大多數任務間通信方法借助中間對象,如隊列、信號量 或 事件組。發送任務寫入通信對象,而接收任務從 通信對象中讀取。使用直接任務通知時,顧名思義,發送 任務直接向接收任務發送通知,無需借助中間對象, 使用 FreeRTOS 任務通知替代信號量方案, RAM占用更小且速度快了高達 45%。
Each RTOS task has an array of task notifications. Each task notification has a notification state that can be either ‘pending’ or ‘not pending’, and a 32-bit notification value.
每個 RTOS 任務都有一組任務通知,每個任務通知都有一個 通知狀態,可以是“待處理”或“未待處理”,以及一個 32 位 通知值。
任務通知簡介
- 每個任務擁有一個通知值: 每個任務都有一個 32 位的
ulNotifiedValue
字段(在 TCB 中)。 - 通知狀態: 每個任務還有一個通知狀態字段 (
ucNotifyState
),可以是:taskNOT_WAITING_NOTIFICATION
: 任務沒有在等待通知。taskWAITING_NOTIFICATION
: 任務正在阻塞等待通知 (ulTaskNotifyTake
或xTaskNotifyWait
)。taskNOTIFICATION_RECEIVED
: 任務收到了一個通知(通知值已更新),但任務尚未取走它(對于某些 API 模式)。
- 發送者: 任務或 ISR 通過調用
xTaskNotifyGive()
,vTaskNotifyGiveFromISR()
,xTaskNotify()
,xTaskNotifyFromISR()
,xTaskNotifyAndQuery()
,xTaskNotifyAndQueryFromISR()
等 API 來更新目標任務的ulNotifiedValue
和ucNotifyState
。 - 接收者: 目標任務通過調用
ulTaskNotifyTake()
或xTaskNotifyWait()
來查詢、等待并消費通知。
?? ?在 FreeRTOS V10.4.0 之前,每項任務只有一個“通知值”, 所有任務通知 API 函數都只能操作這一個值。而從V10.4 開始,任務通知變為 了一組數組,用戶可以通過配置
configTASK_NOTIFICATION_ARRAY_ENTRIES
來決定了 每項任務的任務通知數組中的索引數。
在 V10.5.1 tskTaskControlBlock 結構體中可以查看到任務通知變量已變為了一個數組:
#if ( configUSE_TASK_NOTIFICATIONS == 1 )volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
#endif
而在V10.3.1 中,tskTaskControlBlock 中任務通知變量聲明如下:
#if( configUSE_TASK_NOTIFICATIONS == 1 )volatile uint32_t ulNotifiedValue;volatile uint8_t ucNotifyState;
#endif
xTaskNotify()
是原始 API 函數, 為保持向后兼容, 調用xTaskNotify()
相當于調用xTaskNotifyIndexed()
, 其uxIndexToNotify
參數設置為 0,其他的函數同此類似。
一 、發送通知
通知發送函數如下:
函數 | 描述 |
---|---|
xTaskNotify() / xTaskNotifyIndexed() | 任務中發送通知,攜帶通知值并且不保留接收任務原來的通知值 |
xTaskNotifyFromISR() / xTaskNotifyFromISRIndexed() | 中斷中發送通知, |
xTaskNotifyGive() / xTaskNotifyGiveFromISR() | 任務中發送通知,不帶通知值并且不保留接收任務原來的通知值, |
xTaskNotifyAndQuery() / xTaskNotifyAndQueryIndexed() | 任務中發送通知,帶有通知量并且保留接收任務的原來的通知值 |
xTaskNotifyAndQueryFromISR / xTaskNotifyAndQueryFromISRIndexed() | xTaskNotifyAndQuery() 的中斷版本 |
1.1 xTaskNotify()
xTaskNotify()
和 xTaskNotifyIndexed()
是等效函數,唯一區別在于 xTaskNotifyIndexed()
可以操作任務通知數組中的任何任務通知,而 xTaskNotify()
總是操作 數組中索引為 0 的任務通知。
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, // 任務句柄uint32_t ulValue, // 通知值eNotifyAction eAction ); // 通知狀態
參數:
-
xTaskToNotify
接收通知的 RTOS 任務的句柄,通知值會遞增。可通過以下方法獲取任務句柄: 使用
xTaskCreate()
創建任務,并通過pxCreatedTask
參數獲取句柄; 使用xTaskCreateStatic()
創建任務,并返回值作為句柄; 調用xTaskGetHandle()
,通過任務名稱獲取句柄。當前正在執行的 RTOS 任務的句柄 由xTaskGetCurrentTaskHandle()
API 函數返回。 -
ulValue:用于更新目標任務的通知值。
-
eAction 任務通知更新的方法。枚舉類型,可以取下列任一值,以執行相關操作
在task.h
中有如下的定義:
/* Actions that can be performed when vTaskNotify() is called. */
typedef enum
{eNoAction = 0, /* Notify the task without updating its notify value. */eSetBits, /* Set bits in the task's notification value. */eIncrement, /* Increment the task's notification value. */eSetValueWithOverwrite, /* Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */eSetValueWithoutOverwrite /* Set the task's notification value if the previous value has been read by the task. */
} eNotifyAction;
對應的解釋如下:
eNoAction | 不會更改通知值(只發信號,不傳數據) |
---|---|
eSetBits | 設置通知值的指定bit置一,類似事件組的用法 |
eIncrement | 通知值加一,類似計數信號量 |
eSetValueWithOverwrite | 覆蓋之前通知值的方式更新通知值 |
eSetValueWithoutOverwrite | 不覆蓋之前的通知值 |
返回值:
如果 eAction
設置為 eSetValueWithoutOverwrite ,且目標任務已有通知pending(待處理),則其通知值不會更新, 以免之前的值在使用前被覆蓋。在這種情況下,調用 xTaskNotify() 會失敗, 返回 pdFALSE。通過這種方式,RTOS 任務通知機制可以 在長度為 1 的隊列上作為 xQueueSend()
的輕量級替代方案。
其他情況下均返回 pdPASS。
BaseType_t xTaskNotifyIndexed( TaskHandle_t xTaskToNotify,UBaseType_t uxIndexToNotify,uint32_t ulValue,eNotifyAction eAction );
xTaskNotifyIndexed
比 xTaskNotify()
多了一個 uxIndexToNotify
的參數,可以指定到目標任務的通知數組具體索引。
1.2 xTaskNotifyFromISR()
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction,BaseType_t *pxHigherPriorityTaskWoken );
作為 xTaskNotify()
的中斷版本,可以用在中斷函數中。
參數:
-
pxHigherPriorityTaskWoken
*pxHigherPriorityTaskWoken 必須初始化為 pdFALSE。 如果發送通知導致任務解除阻塞,并且解除阻塞的任務的優先級高于當前正在運行的任務, 則 xTaskNotifyFromISR() 會將 *pxHigherPriorityTaskWoken 設置為 pdTRUE。如果 xTaskNotifyFromISR() 將此值設置為 pdTRUE,則應在退出中斷前 請求上下文切換。pxHigherPriorityTaskWoken 是可選參數, 可設置為 NULL。
返回值:
如果 eAction
設置為 eSetValueWithoutOverwrite ,且目標任務已有通知在pending(待處理),則其通知值不會更新, 以免之前的值在使用前被覆蓋。在這種情況下,調用 xTaskNotify() 會失敗, 返回 pdFALSE。通過這種方式,RTOS 任務通知機制可以 在長度為 1 的隊列上作為 xQueueSend()
的輕量級替代方案。
其他情況下均返回 pdPASS。
同樣的,當任務通知擴展到數組時,下面這個函數和 xTaskNotifyFromISR()
等價。
BaseType_t xTaskNotifyIndexedFromISR( TaskHandle_t xTaskToNotify,UBaseType_t uxIndexToNotify,uint32_t ulValue,eNotifyAction eAction,BaseType_t *pxHigherPriorityTaskWoken );
參數:
-
uxIndexToNotify
指定發送到目標任務的通知數組具體索引處。 uxIndexToNotify 必須小于
configTASK_NOTIFICATION_ARRAY_ENTRIES
。 xTaskNotifyFromISR() 沒有此參數,并且總是將通知發送到索引 0。
1.3 xTaskNotifyGive()
xTaskNotifyGive() 宏可視為速度更快的輕量級二進制或計數信號量的替代方案。
?? 當任務通知值用作二進制或計數信號量的等效物時, 接收通知的任務應該使用
ulTaskNotifyTake()
API 函數來等待通知, 而不是使用xTaskNotifyWait()
API 函數。
在任務中發送通知
xTaskNotifyGive()
與 xTaskNotifyGiveIndexed()
是等效宏,唯一區別在于 xTaskNotifyGiveIndexed()
可以操作數組中的任何任務通知,而 xTaskNotifyGive()
總是操作數組中索引為 0 的任務通知。
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
BaseType_t xTaskNotifyGiveIndexed( TaskHandle_t xTaskToNotify, UBaseType_t uxIndexToNotify );
-
xTaskToNotify
接收通知的 RTOS 任務的句柄,通知值會遞增。可通過以下方法獲取任務句柄: 使用
xTaskCreate()
創建任務,并通過pxCreatedTask
參數獲取句柄; 使用xTaskCreateStatic()
創建任務,并返回值作為句柄; 調用xTaskGetHandle()
,通過任務名稱獲取句柄。當前正在執行的 RTOS 任務的句柄 由xTaskGetCurrentTaskHandle()
API 函數返回。 -
uxIndexToNotify
要向目標任務的通知值數組中發送的通知索引。
uxIndexToNotify
必須小于configTASK_NOTIFICATION_ARRAY_ENTRIES
。xTaskNotifyGive()
沒有此參數,并且總是將通知發送到索引 0。
中斷版本
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify,BaseType_t *pxHigherPriorityTaskWoken );void vTaskNotifyGiveIndexedFromISR( TaskHandle_t xTaskHandle, UBaseType_t uxIndexToNotify, BaseType_t *pxHigherPriorityTaskWoken );
可在中斷服務程序 (ISR) 中使用的 xTaskNotifyGive()
和 xTaskNotifyGiveIndexed()
版本 。
1.4 xTaskNotifyAndQuery()
BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction,uint32_t *pulPreviousNotifyValue );BaseType_t xTaskNotifyAndQueryIndexed( TaskHandle_t xTaskToNotify,UBaseType_t uxIndexToNotify,uint32_t ulValue,eNotifyAction eAction,uint32_t *pulPreviousNotifyValue );
xTaskNotifyAndQueryIndexed()
執行的操作與 xTaskNotifyIndexed()
相同, 另外還可通過額外的 pulPreviousNotifyValue
參數返回目標任務之前的通知值 (函數被調用時的通知值,而不是函數返回時的通知值) 。
xTaskNotifyAndQuery()
執行的操作與 xTaskNotify()
相同, 另外還可通過額外的 pulPreviousNotifyValue
參數返回目標任務之前的通知值 (函數被調用時的通知值,而不是函數返回時的通知值) 。
參數:
-
pulPreviousNotifyValue
可用于在
xTaskNotifyAndQuery()
修改任何位之前傳出目標任務的通知值。pulPreviousNotifyValue
是可選參數,如果不需要,可設置為 NULL。如果不使用pulPreviousNotifyValue
, 可以考慮使用xTaskNotify()
替代xTaskNotifyAndQuery()
。 -
返回值:
如果
eAction
設置為 eSetValueWithoutOverwrite ,且此時目標任務已有的通知pending,則其通知值不會更新, 以免之前的值在使用前被覆蓋。在這種情況下,調用xTaskNotify()
會失敗, 返回 pdFALSE。通過這種方式,RTOS 任務通知機制可以 在長度為 1 的隊列上作為xQueueSend()
的輕量級替代方案。其他情況下均返回 pdPASS。
1.5 xTaskNotifyAndQueryFromISR()
BaseType_t xTaskNotifyAndQueryFromISR(TaskHandle_t xTaskToNotify,uint32_t ulValue,eNotifyAction eAction,uint32_t *pulPreviousNotifyValue,BaseType_t *pxHigherPriorityTaskWoken );BaseType_t xTaskNotifyAndQueryIndexedFromISR(TaskHandle_t xTaskToNotify,UBaseType_t uxIndexToNotifyuint32_t ulValue,eNotifyAction eAction,uint32_t *pulPreviousNotifyValue,BaseType_t *pxHigherPriorityTaskWoken );
xTaskNotifyAndQueryFromISR()
執行的操作與 xTaskNotifyFromISR()
相同, 另外還可通過額外的 pulPreviousNotifyValue
參數返回目標任務之前的通知值 (函數被調用時的通知值,而不是函數返回時的通知值) 。
二、接收通知
任務通知接收函數如下:
函數 | 描述 |
---|---|
ulTaskNotifyTake / ulTaskNotifyTakeIndexed | 獲取任務通知,可設置在退出此函數時將任務通知值清零或者減一。當任務通知用作二值信號量或者計數信號量的時候使用此函數來獲取信號量。 |
xTaskNotifyWait / xTaskNotifyWaitIndexed | 等待任務通知,功能比 ulTaskNotifyTake() 更齊全。 |
2.1 ulTaskNotifyTake()
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,TickType_t xTicksToWait );uint32_t ulTaskNotifyTakeIndexed( UBaseType_t uxIndexToWaitOn, BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
ulTaskNotifyTake()
和 ulTaskNotifyTakeIndexed()
是等效的, 唯一的區別 是 ulTaskNotifyTakeIndexed()
可以在操作數組中的任何任務通知, 而 ulTaskNotifyTake()
只能操作數組索引 0 處的任務通知。
參數:
-
uxIndexToWaitOn
調用任務的通知值數組中的索引, 調用任務將在該索引上等待非零通知。
uxIndexToWaitOn
必須小于configTASK_NOTIFICATION_ARRAY_ENTRIES
。xTaskNotifyTake()
沒有此參數,默認在索引 0 處等待通知。 -
xClearCountOnExit
如果收到 RTOS 任務通知,且
xClearCountOnExit
設置為pdFALSE
,那么 RTOS 任務的 通知值將在ulTaskNotifyTake()
退出前遞減。這相當于 成功調用
xSemaphoreTake()
后,計數信號量的值被遞減。如果收到 RTOS 任務通知 且xClearCountOnExit
設置為pdTRUE,則 RTOS 任務的通知值 將在
ulTaskNotifyTake()
退出前重置為 0。這等同于 在成功調用xSemaphoreTake()
后,將二進制信號量的值保留為 0。
-
xTicksToWait
表示如果調用
ulTaskNotifyTake()
時尚未收到通知,在阻塞狀態下等待收到通知的最長時間。處于阻塞狀態的 RTOS 任務不會消耗 任何 CPU 時間。時間以 RTOS 滴答周期為單位。pdMS_TO_TICKS()
宏可用于 將以毫秒為單位的時間轉換為以滴答為單位的時間。
返回:
- 被遞減或清除之前的任務通知值的值(原來的任務通知值)
2.2 xTaskNotifyWait()
該函數比 ulTaskNotifyTake()
功能更為強大,不管任務通知用作二值信號量、計數信號量、隊列和事件標志組中的哪一種,都可以使用此函數來獲取任務通知。
xTaskNotifyWait()
和xTaskNotifyWaitIndexed()
是等效宏,唯一區別在于 xTaskNotifyWaitIndexed()
可以操作數組中的任何任務通知, 而 xTaskNotifyWait()
只能操作數組中索引為 0 的任務通知。
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit,uint32_t *pulNotificationValue,TickType_t xTicksToWait );BaseType_t xTaskNotifyWaitIndexed( UBaseType_t uxIndexToWaitOn,uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit,uint32_t *pulNotificationValue,TickType_t xTicksToWait );
參數:
-
ulBitsToClearOnEntry
接收任務通知之前,先進行按位清除通知值。當沒有接收到任務通知的時候,將任務通知值與此參數的取反值進行按位與運算。當此參數設為0xffffffff 或者 ULONG_MAX 的時候,就會將任務通知值清零。
-
ulBitsToClearOnExit
退出此函數之前,先按位清除通知值。如果接收到任務通知,在退出此函數之前,將任務通知值與此參數的取反值進行按位與運算。當此參數設為0xffffffff 或者 ULONG_MAX 的時候,就會將任務通知值清零。
-
*pulNotificationValue
用于傳出 RTOS 任務的通知值,復制到 *pulNotificationValue 的值是 RTOS 任務的通知值,該值是在應用 ulBitsToClearOnExit 設置清除任何位 之前的值。如果無需保存通知值,可以將 pulNotificationValue 設置為 NULL。
-
xTicksToWait 阻塞時間
返回:
如果收到了通知,或者在調用 xTaskNotifyWait()
時通知已經在pending(待處理)狀態, 則返回 pdTRUE。
如果調用 xTaskNotifyWait()
超時且在超時前沒有收到通知, 則返回 pdFALSE。
三、清除通知狀態和值
3.1 xTaskNotifyStateClear()
在 FreeRTOS 中,xTaskNotifyStateClear()
是一個用于管理任務通知狀態的重要函數。它的核心作用是:清除目標任務的通知pending (待處理)狀態,而不會修改通知值本身。
BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask );BaseType_t xTaskNotifyStateClearIndexed( TaskHandle_t xTask, UBaseType_t uxIndexToClear );
3.2 ulTaskNotifyValueClear()
用于清除任務通知的指定標志位ulBitsToClear
uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear );uint32_t ulTaskNotifyValueClearIndexed( TaskHandle_t xTask, UBaseType_t uxIndexToClear,uint32_t ulBitsToClear );
ulTaskNotifyValueClear()
和 ulTaskNotifyValueClearIndexed()
是等效的宏。唯一的區別 是 ulTaskNotifyValueClearIndexed()
可以指定任務通知數組任意索引處, 而ulTaskNotifyValueClear()
始終在數組索引 0 處的任務通知上運行。
- 返回值:
ulBitsToClear
指定位清零前目標任務的通知值。
四、eAction 值和相關操作
-
eNoAction
目標任務接收事件,但其通知值不會更新。在這種情況下, 不會使用 ulValue。
-
eSetBits
目標任務的通知值將與 ulValue 進行按位“或”操作。例如,如果 ulValue 設置為 0x01,則目標任務通知值中的第 0 位將被設置。同樣,如果 ulValue 設置為 0x04,則目標任務通知值中的第 2 位將被設置。通過這種方式,RTOS 任務 通知機制可以作為事件組的輕量級替代方案。
-
eIncrement
目標任務的通知值將增加 1,這樣調用 xTaskNotifyFromISR() 相當于調用 vTaskNotifyGiveFromISR()。在這種情況下,不會使用 ulValue。
-
eSetValueWithOverwrite
目標任務的通知值無條件設置為 ulValue。通過這種方式,RTOS 任務 通知機制可以作為 xQueueOverwrite() 的輕量級替代方案。
-
eSetValueWithoutOverwrite
如果目標任務當前沒有通知pending,則其通知值 將設置為 ulValue。 如果目標任務已有通知在pending,則其通知值 不會更新,以免之前的值在使用前被覆蓋。在這種情況下, 調用 xTaskNotify() 會失敗,返回 pdFALSE。 通過這種方式,RTOS 任務通知機制可以 在長度為 1 的隊列上作為 xQueueSend() 的輕量級替代方案。