freertos之tasknotify淺析
阿新 • • 發佈:2018-12-29
前言
freertos在V8.0.0版本之後就加入了task notify 的功能,據說比訊號量 佇列傳遞訊息等更快更高效且佔系統資源更少,在每個TCB結構體中多佔用5個位元組空間而已,相對於queue實現的訊號量等來說著實輕便了很多;但是有一個很大的問題,RTOS通知任務才可使用只有一個任務可以被該接收者的事件,雖然這種情況在大多數真實應用中。
可以通過一些特定引數設定來實現二值訊號量,二值訊號郵箱等操作。
程式碼實現
通知接收
task通過呼叫ulTaskNotifyTake
、xTaskNotifyWait
來等待任務通知,ulTaskNotifyTake的返回值是收到的任務通知值;xTaskNotifyWait的返回值是是否收到任務通知。
這兩個函式的用處還是有點不一樣的,注意從程式碼上去區分。
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) { uint32_t ulReturn; taskENTER_CRITICAL(); { /*只有當通知計數不是非零時才阻塞。只有當通知計數 是 零時才阻塞。*/ if( pxCurrentTCB->ulNotifiedValue == 0UL ){ /* 標註這個task正在等待通知*/ pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION; if( xTicksToWait > ( TickType_t ) 0 ) {//等待tick數大於0的話,需要加入 pxDelayedTaskList中去 prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); /* 執行可移植 導致任務切換的函式*/ portYIELD_WITHIN_API(); } } } taskEXIT_CRITICAL();//真正的·1任務切換髮生在這裡 taskENTER_CRITICAL(); { ulReturn = pxCurrentTCB->ulNotifiedValue; /*此處應該是已經返回,有兩種情況 1.有task2將通知傳送給本任務了,然後加入到readylists被執行了 2.xTicksToWait過完了*/ if( ulReturn != 0UL ){ if( xClearCountOnExit != pdFALSE ){//如果需要清空任務通知值的話 pxCurrentTCB->ulNotifiedValue = 0UL; } } /*修改任務通知狀態 為 不需要等待 任務通知*/ pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; } taskEXIT_CRITICAL(); return ulReturn; } BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ){ BaseType_t xReturn; taskENTER_CRITICAL();{ /* 只有在通知尚未掛起時才阻塞。*/ if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED ){ /*可能在傳送的通知的 task或中斷 中 ,清除任務通知的相應的bit 這可以用來清除 該位 為 0。*/ pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry; /*標註這個task正在等待通知*/ pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION; if( xTicksToWait > ( TickType_t ) 0 ){ prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); /* 執行可移植 導致任務切換的函式*/ portYIELD_WITHIN_API(); } } } taskEXIT_CRITICAL(); taskENTER_CRITICAL();{ if( pulNotificationValue != NULL ){ /* 輸出當前通知值,該值可以更改,也可以不更改。*/ *pulNotificationValue = pxCurrentTCB->ulNotifiedValue; } /* 如果設定了ucNotifyValue,那麼要麼任務從未輸入阻塞狀態 (因為通知已經掛起)或任務因通知解除阻塞。 否則,任務由於超時而解除阻塞*/ if( pxCurrentTCB->ucNotifyState == taskWAITING_NOTIFICATION ){ /* 沒有接收到任務通知 */ xReturn = pdFALSE; }else{/* 接收到任務通知的話 */ /* A notification was already pending or a notification was received while the task was waiting. 通知已經掛起或已經掛起在任務等待時收到。*/ pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit; xReturn = pdTRUE; } /*修改任務通知狀態 為 不需要等待 任務通知*/ pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; } taskEXIT_CRITICAL(); return xReturn; }
通知傳送
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ){ TCB_t * pxTCB; BaseType_t xReturn = pdPASS; uint8_t ucOriginalNotifyState; pxTCB = ( TCB_t * ) xTaskToNotify; taskENTER_CRITICAL(); {/*pulPreviousNotificationValue 和 ucOriginalNotifyState 儲存原來任務通知值和通知狀態*/ if( pulPreviousNotificationValue != NULL ){ *pulPreviousNotificationValue = pxTCB->ulNotifiedValue; } ucOriginalNotifyState = pxTCB->ucNotifyState; pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; switch( eAction ) {/*根據設定的任務通知操作來 操作 被通知任務的通知值*/ case eSetBits ://設定相應的位 pxTCB->ulNotifiedValue |= ulValue; break; case eIncrement ://僅僅給通知值+1 ( pxTCB->ulNotifiedValue )++; break; case eSetValueWithOverwrite ://不管原來任務通知值有沒有,直接覆蓋 pxTCB->ulNotifiedValue = ulValue; break; case eSetValueWithoutOverwrite : //如果原來任務已經接收到了通知值,那麼不操作返回通知失敗,否則就通知,即不覆蓋式操作 if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ){ pxTCB->ulNotifiedValue = ulValue; }else{ /* 通知值 通知不到 應該被通知的task */ xReturn = pdFAIL; } break; case eNoAction://不操作,僅僅告訴被通知任務 有任務通知了 而已 break; } /* 如果任務處於阻塞狀態,特別是等待通知,那麼現在就解除阻塞。 就是常規列表操作*/ if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) { ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); prvAddTaskToReadyList( pxTCB ); if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ){ /*被通知任務的優先順序高於當前執行任務,因此需要任務切換。*/ taskYIELD_IF_USING_PREEMPTION(); } } } taskEXIT_CRITICAL(); return xReturn; }
由於中斷不能有延時或者休眠操作,所以等待通知是不能在中斷中的,而freertos提供了中斷中的傳送任務通知的函式,其操作跟普通notify很相似,之後再分析所有系統api在中斷中的版本。