【freertos】013-任務通知及其實現細節

2022-08-06 15:00:23

前言

參考:

13.1 任務通知實現原理個人構想

任務通知的實現機制和訊息佇列和事件標誌機制不一樣。

訊息佇列和事件標誌機制實現需要的資源,資料結構都是需要申請,建立的,獨立於核心,分離於任務的元件。

這些元件的實現構想都是拿個記憶體塊組成資料結構,一部分記憶體做資訊傳遞,一部分記憶體做阻塞連結串列等等,然後通過封裝各種API實現各種對這個資料結構的騷操作而形成的任務間通訊機制。

而任務通知,首先記憶體源自任務控制塊,這個記憶體的資料結構並未確定,所以我們能把這個記憶體做資訊傳遞。

阻塞部分,我們可以利用系統的延時連結串列,做個等待資訊阻塞。

不過沒有傳送阻塞,因為向某個任務傳送資訊只是向某個任務控制塊寫入資料的騷操作而已,沒有因為沒有空間而阻塞的這個說法,因為任務控制塊根本就沒有給被寫入失敗而阻塞的連結串列的記憶體。

明白了任務通知的實現原理,就知道這玩意是啥了。

13.2 任務通知概念

每個RTOS任務都有一個任務通知陣列。

每個任務通知都有一個通知狀態,可以是pending或者not pending,並且有一個32位元的通知值。

常數configTASK_NOTIFICATION_ARRAY_ENTRIES設定任務通知陣列中的索引數量。

注意:在FreeRTOS V10.4.0之前,任務只有一個任務通知,而不是一組通知。

任務的通知是直接傳送給任務的事件,而不是通過佇列、事件組或號誌等中間物件間接傳送給任務。

向任務傳送直接到任務的通知會將目標任務通知的狀態設定為pending

就像一個任務可以阻塞一箇中間物件,比如一個號誌,以等待該號誌可用一樣,一個任務也可以阻塞一個任務通知,以等待該通知的狀態變為pending

13.3 任務通知實現各種IPC

任務通知方式可以實現計數號誌,二值號誌,事件標誌組和訊息郵箱(訊息郵箱就是訊息佇列長度為1的情況)。

使用方法與前面章節講解的事件標誌組和號誌基本相同,只是換了不同的函數來實現。

任務通知方式實現的計數號誌,二值號誌,事件標誌組和訊息郵箱是通過修改任務對應任務控制塊的ulNotifiedValue陣列成員實現:

  • 覆蓋該值,無論接收任務是否讀取了被覆蓋的值。(訊息郵箱)
  • 僅當接收任務已讀取才能寫入。(訊息佇列)
  • 在值中設定一個或多個位。(事件組)
  • 增加(加1)值。(號誌)

呼叫xTaskNotifyWait()xTaskNotifyWaitIndexed()來讀取一個通知值,將該通知的狀態清除為not pending

通知狀態也可以通過呼叫xTaskNotifyStateClear()xTaskNotifyStateClearIndexed()顯式地設定為not pending

13.4 任務通知效能優勢

使用任務通知比通過號誌等ICP通訊方式解除阻塞的任務要快45%,並且更加省RAM記憶體空間。

13.5 任務通知使用注意

  1. freertos的任務通知功能預設是啟用的,可以通過在FreeRTOSConfig.h中將configUSE_TASK_NOTIFICATIONS設定為0來關閉。
  2. RTOS 任務通知只能在只有一個任務可以作為事件的接收者時使用。
  3. 陣列中的每個通知都是獨立操作的。一個任務一次只能阻塞陣列中的一個通知,並且不會被傳送到任何其他陣列索引的通知解除阻塞。
  4. 在使用 RTOS 任務通知代替佇列的情況下:雖然接收任務可以在阻塞狀態等待通知(因此不消耗任何 CPU 時間),但如果傳送不能立即完成,則傳送任務不能在阻塞狀態下等待傳送完成。
  5. FreeRTOS StreamMessage Buffers 在陣列索引 0 處使用任務通知。如果想在呼叫 StreamMessage Buffer API 函數時保持任務通知的狀態,則在陣列索引大於 0 處使用任務通知。

13.6 任務通知資料結構

任務通知是任務控制塊的資源。

/* 任務控制塊 */
typedef struct tskTaskControlBlock
{
    volatile StackType_t * pxTopOfStack; /*< 指向放在任務堆疊上的最後一項的位置。這必須是TCB結構體的第一個成員。 */

    #if ( portUSING_MPU_WRAPPERS == 1 )
        xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer.  THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */
    #endif

    ListItem_t xStateListItem;                  /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */
    ListItem_t xEventListItem;                  /*< Used to reference a task from an event list. */
    UBaseType_t uxPriority;                     /*< The priority of the task.  0 is the lowest priority. */
    StackType_t * pxStack;                      /*< Points to the start of the stack. */
    char pcTaskName[ configMAX_TASK_NAME_LEN ]; /*< Descriptive name given to the task when created.  Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */

    #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) )
        StackType_t * pxEndOfStack; /*< Points to the highest valid address for the stack. */
    #endif

    #if ( portCRITICAL_NESTING_IN_TCB == 1 )
        UBaseType_t uxCriticalNesting; /*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */
    #endif

    #if ( configUSE_TRACE_FACILITY == 1 )
        UBaseType_t uxTCBNumber;  /*< Stores a number that increments each time a TCB is created.  It allows debuggers to determine when a task has been deleted and then recreated. */
        UBaseType_t uxTaskNumber; /*< Stores a number specifically for use by third party trace code. */
    #endif

    #if ( configUSE_MUTEXES == 1 )
        UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */
        UBaseType_t uxMutexesHeld;
    #endif

    #if ( configUSE_APPLICATION_TASK_TAG == 1 )
        TaskHookFunction_t pxTaskTag;
    #endif

    #if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
        void * pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
    #endif

    #if ( configGENERATE_RUN_TIME_STATS == 1 )
        configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */
    #endif

    #if ( configUSE_NEWLIB_REENTRANT == 1 )

        /* Allocate a Newlib reent structure that is specific to this task.
         * Note Newlib support has been included by popular demand, but is not
         * used by the FreeRTOS maintainers themselves.  FreeRTOS is not
         * responsible for resulting newlib operation.  User must be familiar with
         * newlib and must provide system-wide implementations of the necessary
         * stubs. Be warned that (at the time of writing) the current newlib design
         * implements a system-wide malloc() that must be provided with locks.
         *
         * See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html
         * for additional information. */
        struct  _reent xNewLib_reent;
    #endif

    #if ( configUSE_TASK_NOTIFICATIONS == 1 )
        volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
        volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
    #endif

    /* See the comments in FreeRTOS.h with the definition of
     * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */
    #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */
        uint8_t ucStaticallyAllocated;                     /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */
    #endif

    #if ( INCLUDE_xTaskAbortDelay == 1 )
        uint8_t ucDelayAborted;
    #endif

    #if ( configUSE_POSIX_ERRNO == 1 )
        int iTaskErrno;
    #endif
} tskTCB;

ulNotifiedValue

  • 任務通知值陣列。

ucNotifyState:

  • 任務通知狀態陣列。
  • 與任務通知值一一對應。

13.7 任務通知為什麼沒有寫阻塞?

通過任務通知的資料結構就知道,其資料結構嵌入在任務控制塊中,一個通知對應一個通知狀態。

13.7.1 讀阻塞實現

讀阻塞很容易實現,讀取當前任務通知不滿足條件時,可以將其阻塞到延時連結串列或者掛起連結串列,當有任務往這個任務通知傳送資料時,滿足當前任務通知要求,可以將當前任務通知從延時連結串列或者掛起連結串列解除阻塞。

13.7.2 寫阻塞實現(修改核心元件實現)

先按照當前官方任務通知資料結構分析,並不能實現。

試想下,如果不能寫,就進入阻塞,可以參考讀阻塞一樣先插入延時連結串列或掛起連結串列,但是怎麼喚醒呢?當任務通知可以寫時,通過什麼方式找到這個寫阻塞的任務呢?

任務通知這個物件嵌入到任務控制塊中,歸屬於某個任務,其它任務往這裡寫,發生阻塞,當可寫時,需要解除阻塞時,是找不到這個寫任務並將其喚醒的。

以上就是按照freertos任務通知資料結構基礎來演進分析。

其實解決了寫阻塞的喚醒,就可以解決寫阻塞這個問題了。
也很簡單,在任務通知這個資料結構中再新增一個寫阻塞連結串列即可。有興趣的同學可以自己改寫下核心原始碼實現。

13.8 任務通知型別

typedef enum
{
    eNoAction = 0,            /* 通知任務而不更新其通知值 */
    eSetBits,                 /* 事件組。按位元或設定。 */
    eIncrement,               /* 號誌。自增。 */
    eSetValueWithOverwrite,   /* 郵箱。覆蓋希寫入 */
    eSetValueWithoutOverwrite /* 佇列。對應通知為空時才寫入。 */
} eNotifyAction;

13.9 任務通知狀態

/* 任務通知狀態 */
#define taskNOT_WAITING_NOTIFICATION              ( ( uint8_t ) 0 ) /* 初始值為0,所以預設態也應該為0 */
#define taskWAITING_NOTIFICATION                  ( ( uint8_t ) 1 )
#define taskNOTIFICATION_RECEIVED                 ( ( uint8_t ) 2 )

taskNOT_WAITING_NOTIFICATION:任務沒有在等待通知。

taskWAITING_NOTIFICATION:任務在等待通知。

taskNOTIFICATION_RECEIVED:任務接收到通知。

13.10 傳送任務通知基函數

在分析任務通知做號誌前先分析一個任務通知基函數xTaskGenericNotify(),任務訊息、任務郵箱、任務事件這些傳送端都是通過封裝該API實現的。

xTaskGenericNotify()

  • xTaskToNotify:任務控制程式碼。即是任務通知的物件。

  • uxIndexToNotify

    • 通知將傳送到的目標任務通知值陣列中的索引。
    • uxIndexToNotify必須小於configTASK_NOTIFICATION_ARRAY_ENTRIES
  • ulValue:用於更新目標任務的通知值。

  • pulPreviousNotificationValue:任務原本的通知值返回。(回傳)

#if ( configUSE_TASK_NOTIFICATIONS == 1 )
    BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify,
                                   UBaseType_t uxIndexToNotify,
                                   uint32_t ulValue,
                                   eNotifyAction eAction,
                                   uint32_t * pulPreviousNotificationValue )
    {
        TCB_t * pxTCB;
        BaseType_t xReturn = pdPASS;
        uint8_t ucOriginalNotifyState;

        /* 任務通知索引不能溢位 */
        configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES );
        /* 引數校驗 */
        configASSERT( xTaskToNotify );
        pxTCB = xTaskToNotify;

        taskENTER_CRITICAL(); /* 進入臨界 */
        {
            if( pulPreviousNotificationValue != NULL ) /* 需要回傳 */
            {
                /* 回傳當前未處理的任務通知值 */
                *pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ];
            }
            /* 獲取該任務通知的狀態 */
            ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ];
            /* 更新為pendig狀態,表示該任務通知接收到通知了 */
            pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED;
            /* 按照不同通知型別進行操作 */
            switch( eAction )
            {
                case eSetBits: /* 事件組。bit處理。與原通知值按位元或。 */
                    pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue;
                    break;

                case eIncrement: /* 號誌。釋放號誌。原通知值+1 */
                    ( pxTCB->ulNotifiedValue[ uxIndexToNotify ] )++;
                    break;

                case eSetValueWithOverwrite: /* 郵箱。覆蓋寫入。 */
                    pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;
                    break;

                case eSetValueWithoutOverwrite: /* 佇列。沒有通知時才寫入。 */
                    if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
                    {
                        pxTCB->ulNotifiedValue[ uxIndexToNotify ] = ulValue;
                    }
                    else
                    {
                        /* 如果已有通知,則無法寫入。 */
                        xReturn = pdFAIL;
                    }
                    break;

                case eNoAction:
                    /* 只觸發通知,不更新內容 */
                    break;

                default:
                    /* 如果通知型別列舉錯誤,強制斷言 */
                    configASSERT( xTickCount == ( TickType_t ) 0 );
                    break;
            }

            traceTASK_NOTIFY( uxIndexToNotify );

            if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) /* 如果任務阻塞等待通知 */
            {
                /* 解除任務狀態 */
                listREMOVE_ITEM( &( pxTCB->xStateListItem ) );
                /* 重新插入就緒連結串列 */
                prvAddTaskToReadyList( pxTCB );

                /* 如果任務等待通知,邏輯上就不會阻塞在事件連結串列中 */
                configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );

                #if ( configUSE_TICKLESS_IDLE != 0 )
                    {
                        /* 解鎖任務後,更新下下次解鎖延時連結串列的節拍值,否則可能會誤導低功耗模式提前喚醒 */
                        prvResetNextTaskUnblockTime();
                    }
                #endif

                if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
                {
                    /* 解鎖的任務優先順序更高就需要觸發工作切換。(退出臨界後) */
                    taskYIELD_IF_USING_PREEMPTION();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        taskEXIT_CRITICAL(); /* 退出臨界 */

        return xReturn;
    }
#endif 

13.11 任務事件等待通知基函數

需要明白任務通知並不是獨立的IPC通訊元件,而是基於任務控制塊的通訊元件,所以只能執行該任務時呼叫等待發給該任務的通知,所以上下文只能是執行緒形,沒有中斷形。這樣,實現的API只需要實現任務版即可。

接收任務通知的基函數是xTaskNotifyWaitIndexed():

  • uxIndexToWait:等待的通知索引。

  • ulBitsToClearOnEntry:沒有接到通知時才生效的引數。標記接收通知前清空通知。

    • 通知值=通知值& ~(ulBitsToClearOnEntry)
  • ulBitsToClearOnExit:收到通知後需要清除的通知的值。

    • 通知值=通知值& ~(ulBitsToClearOnExit)
  • pulNotificationValue:回傳退出清除通知前的通知的容器。

  • xTicksToWait:等待阻塞超時時間。

#if ( configUSE_TASK_NOTIFICATIONS == 1 )
    BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWait,
                                       uint32_t ulBitsToClearOnEntry,
                                       uint32_t ulBitsToClearOnExit,
                                       uint32_t * pulNotificationValue,
                                       TickType_t xTicksToWait )
    {
        BaseType_t xReturn;

        configASSERT( uxIndexToWait < configTASK_NOTIFICATION_ARRAY_ENTRIES );

        taskENTER_CRITICAL();
        {
            /* 當前沒有通知 */
            if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED )
            {
                /* 接收通知前需要清空通知對應的bit */
                pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnEntry;

                /* 標記下當前任務等待當前通知 */
                pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskWAITING_NOTIFICATION;

                if( xTicksToWait > ( TickType_t ) 0 ) /* 需要阻塞 */
                {
                    /* 從就緒連結串列遷移到延時連結串列或者掛起連結串列。即是進入阻塞態 */
                    prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
                    traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWait );

                    /* 觸發任務排程。(退出臨界後才執行實際的排程) */
                    portYIELD_WITHIN_API();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else /* 當前已經有通知了 */
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        taskEXIT_CRITICAL(); /* 退出臨界 */
  
        /* 如果前面進入了阻塞,再跑回這裡是喚醒後了 */

        taskENTER_CRITICAL(); /* 進入臨界 */
        {
            traceTASK_NOTIFY_WAIT( uxIndexToWait );

            if( pulNotificationValue != NULL ) /* 需要回傳 */
            {
                /* 回傳退出前的通知 */
                *pulNotificationValue = pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ];
            }

            if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED ) /* 沒有通知 */
            {
                /* 沒有通知,要麼沒有進入阻塞,要麼進入阻塞後因非通知原因而解鎖,如阻塞超時或被強制解鎖。 */
                xReturn = pdFALSE; /* 返回接收失敗 */
            }
            else /* 收到通知 */
            {
                /* 收到通知的可能:1. 接收前已經收到通知;2. 阻塞時收到通知。 */
                pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnExit;
                xReturn = pdTRUE; /* 返回接收成功 */
            }
            /* 更新通知狀態為初始態 */
            pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskNOT_WAITING_NOTIFICATION;
        }
        taskEXIT_CRITICAL(); /* 退出臨界 */

        return xReturn;
    }
#endif /* configUSE_TASK_NOTIFICATIONS */

13.12 任務全功能通知函數

xTaskNotify()xTaskNotifyIndexed(),對比內部通用函數,只是沒有回傳功能。

#define xTaskNotify( xTaskToNotify, ulValue, eAction ) \
    xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL )
#define xTaskNotifyIndexed( xTaskToNotify, uxIndexToNotify, ulValue, eAction ) \
    xTaskGenericNotify( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), NULL )

中斷版:xTaskNotifyFromISR()xTaskNotifyIndexedFromISR(),對比內部通用函數中斷版,只是沒有回傳功能。

#define xTaskNotifyFromISR( xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) \
    xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) )
#define xTaskNotifyIndexedFromISR( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) \
    xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) )

13.13 任務通知全功能等待通知函數

xTaskNotifyWait()xTaskNotifyWaitIndexed()

#define xTaskNotifyWait( ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ) \
    xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY, ( ulBitsToClearOnEntry ), ( ulBitsToClearOnExit ), ( pulNotificationValue ), ( xTicksToWait ) )
#define xTaskNotifyWaitIndexed( uxIndexToWaitOn, ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ) \
    xTaskGenericNotifyWait( ( uxIndexToWaitOn ), ( ulBitsToClearOnEntry ), ( ulBitsToClearOnExit ), ( pulNotificationValue ), ( xTicksToWait ) )

13.14 任務通知之號誌

通過學習了上述兩個內部通用任務通知API後,我們可以通過封裝這兩個API來實現對應IPC通訊概念的專用API。

13.14.1 釋放號誌

任務通知之釋放號誌:xTaskNotifyGive()xTaskGenericNotify()

#define xTaskNotifyGive( xTaskToNotify ) \
    xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( 0 ), eIncrement, NULL )
#define xTaskNotifyGiveIndexed( xTaskToNotify, uxIndexToNotify ) \
    xTaskGenericNotify( ( xTaskToNotify ), ( uxIndexToNotify ), ( 0 ), eIncrement, NULL )

中斷版:(內部原始碼可以自己分析,參考上述通用API中的號誌邏輯,只是沒有阻塞機制)

#define vTaskNotifyGiveFromISR( xTaskToNotify, pxHigherPriorityTaskWoken ) \
    vTaskGenericNotifyGiveFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( pxHigherPriorityTaskWoken ) );
#define vTaskNotifyGiveIndexedFromISR( xTaskToNotify, uxIndexToNotify, pxHigherPriorityTaskWoken ) \
    vTaskGenericNotifyGiveFromISR( ( xTaskToNotify ), ( uxIndexToNotify ), ( pxHigherPriorityTaskWoken ) );

13.14.2 獲取號誌

ulTaskNotifyTake()ulTaskNotifyTakeIndexed()

如果引數xClearCountOnExit設定為pdTRUE則用於任務通知的二值號誌。

#define ulTaskNotifyTake( xClearCountOnExit, xTicksToWait ) \
    ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ), ( xClearCountOnExit ), ( xTicksToWait ) )
#define ulTaskNotifyTakeIndexed( uxIndexToWaitOn, xClearCountOnExit, xTicksToWait ) \
    ulTaskGenericNotifyTake( ( uxIndexToWaitOn ), ( xClearCountOnExit ), ( xTicksToWait ) )

13.15 任務通知之訊息郵箱

xTaskNotifyAndQuery()xTaskNotifyAndQueryIndexed()

#define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) \
    xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) )
#define xTaskNotifyAndQueryIndexed( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotifyValue ) \
    xTaskGenericNotify( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) )

xTaskNotifyAndQueryIndexed()執行與xTaskNotifyIndexed()相同的操作,另外它還在附加的pulPreviousNotifyValue引數中返回目標任務的之前的通知值(函數被呼叫時的通知值,而不是函數返回時的通知值)。(對應的xTaskNotifyAndQuery()xTaskNotify()差別也一樣)。

中斷版:

#define xTaskNotifyAndQueryIndexedFromISR( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) \
    xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( uxIndexToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) )
#define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) \
    xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) )

13.16 任務通知之事件組標誌

並沒有封裝出專門的事件標誌API,但是可以使用全能版的API實現。使用者也可以自己封裝。

13.17 清除任務通知狀態

如果一個通知被傳送到通知陣列中的一個索引,那麼該索引的通知被稱為pending,直到任務讀取它的通知值或通過呼叫xTaskNotifyStateClear()顯式地清除通知狀態為not pending

xTaskNotifyStateClear()xTaskNotifyStateClearIndexed()

#define xTaskNotifyStateClear( xTask ) \
    xTaskGenericNotifyStateClear( ( xTask ), ( tskDEFAULT_INDEX_TO_NOTIFY ) )
#define xTaskNotifyStateClearIndexed( xTask, uxIndexToClear ) \
    xTaskGenericNotifyStateClear( ( xTask ), ( uxIndexToClear ) )

內部實現:xTaskGenericNotifyStateClear()

#if ( configUSE_TASK_NOTIFICATIONS == 1 )
    BaseType_t xTaskGenericNotifyStateClear( TaskHandle_t xTask,
                                             UBaseType_t uxIndexToClear )
    {
        TCB_t * pxTCB;
        BaseType_t xReturn;

        configASSERT( uxIndexToClear < configTASK_NOTIFICATION_ARRAY_ENTRIES );

        /* 如果在這裡傳遞null,那麼當前任務的通知狀態被清除 */
        pxTCB = prvGetTCBFromHandle( xTask );

        taskENTER_CRITICAL(); /* 進入臨界 */
        {
            if( pxTCB->ucNotifyState[ uxIndexToClear ] == taskNOTIFICATION_RECEIVED )
            {
                pxTCB->ucNotifyState[ uxIndexToClear ] = taskNOT_WAITING_NOTIFICATION; /* 如果處於收到通知的狀態,則可清除,回到沒有等待通知的狀態 */

                xReturn = pdPASS;
            }
            else
            {
                xReturn = pdFAIL;
            }
        }
        taskEXIT_CRITICAL(); /* 退出臨界 */

        return xReturn;
    }
#endif

13.18 清除任務通知值

ulTaskNotifyValueClear()ulTaskNotifyValueClearIndexed():

清除任務通知值,返回的是清除前的任務通知值。

注意:是按bit清除。

#define ulTaskNotifyValueClear( xTask, ulBitsToClear ) \
    ulTaskGenericNotifyValueClear( ( xTask ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulBitsToClear ) )
#define ulTaskNotifyValueClearIndexed( xTask, uxIndexToClear, ulBitsToClear ) \
    ulTaskGenericNotifyValueClear( ( xTask ), ( uxIndexToClear ), ( ulBitsToClear ) )

內部通用函數:ulTaskGenericNotifyValueClear()

#if ( configUSE_TASK_NOTIFICATIONS == 1 )
    uint32_t ulTaskGenericNotifyValueClear( TaskHandle_t xTask,
                                            UBaseType_t uxIndexToClear,
                                            uint32_t ulBitsToClear )
    {
        TCB_t * pxTCB;
        uint32_t ulReturn;

        /* 如果在這裡傳遞null,那麼當前任務的通知值被清除 */
        pxTCB = prvGetTCBFromHandle( xTask );

        taskENTER_CRITICAL(); /* 進入臨界 */
        {
            /* 返回清除位之前的通知,然後清除位掩碼 */
            ulReturn = pxTCB->ulNotifiedValue[ uxIndexToClear ];
            pxTCB->ulNotifiedValue[ uxIndexToClear ] &= ~ulBitsToClear;
        }
        taskEXIT_CRITICAL(); /* 退出臨界 */

        return ulReturn;
    }
#endif /* configUSE_TASK_NOTIFICATIONS */