FreeRTOS任务通知

——————(正点原子FreeRTOS学习笔记)
开始学习FreeRTOS,学习参考书籍和视频来自正点原子FreeRTOS源码详解与应用开发,北京航空航天大学出版社

1    任务通知简介
        任务通知在 FreeRTOS 中是一个可选的功能,要使用任务通知的话就需要将宏configUSE_TASK_NOTIFICATIONS 定义为 1。FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue就是这个通知值。任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态。也可以更新接收任务的任务通知值,任务通知可以通过如下方法更新接收任务的通知值:
● 不覆盖接收任务的通知值(如果上次发送给接收任务的通知还没被处理)。
● 覆盖接收任务的通知值。
● 更新接收任务通知值的一个或多个 bit。
● 增加接收任务的通知值。
        合理、灵活的使用上面这些更改任务通知值的方法可以在一些场合中替代队列、二值信号量、计数型信号量和事件标志组。使用任务通知来实现二值信号量功能的时候,解除任务阻塞的时间比直接使用二值信号量要快 45%(FreeRTOS 官方测试结果,使用 v8.1.2 版本中的二值信号量,GCC 编译器,-O2 优化的条件下测试的,没有使能断言函数 configASSERT()),并且使用的 RAM 更少!
        任务通知的发送使用函数 xTaskNotify()或者 xTaskNotifyGive()(还有此函数的中断版本)来完成,这个通知值会一直被保存着,直到接收任务调用函数 xTaskNotifyWait()或者ulTaskNotifyTake()来获取这个通知值。假如接收任务因为等待任务通知而阻塞的话那么在接收到任务通知以后就会解除阻塞态。
        任务通知虽然可以提高速度,并且减少 RAM 的使用,但是任务通知也是有使用限制的:
● FreeRTOS 的任务通知只能有一个接收任务,其实大多数的应用都是这种情况。
● 接收任务可以因为接收任务通知而进入阻塞态,但是发送任务不会因为任务通知发送失败而阻塞。

2    发送任务通知
            任务通知发送函数有 6 个,如表 17.2.1 所示:
FreeRTOS任务通知
1 、函数 xTaskNotify()
        此函数用于发送任务通知,此函数发送任务通知的时候带有通知值,此函数是个宏,真正执行的函数 xTaskGenericNotify(),函数原型如下:
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,
                                        uint32_t ulValue,
                                        eNotifyAction eAction )
参数:
        xTaskToNotify : 任务句柄,指定任务通知是发送给哪个任务的。
        ulValue : 任务通知值。
        eAction : 任务通知更新的方法,eNotifyAction 是个枚举类型,在文件 task.h 中有如下
定义:
        typedef enum
        {
                eNoAction = 0,
                eSetBits, //更新指定的 bit
                eIncrement,  //通知值加一
                eSetValueWithOverwrite,  //覆写的方式更新通知值
                eSetValueWithoutOverwrite //不覆写通知值
        } eNotifyAction;
        此参数可以选择枚举类型中的任意一个,不同的应用环境其选择也不同。
返回值:
        pdFAIL:     当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有更新成功就返回 pdFAIL。
        pdPASS:     eAction 设置为其他选项的时候统一返回 pdPASS。

2 、函数 xTaskNotifyFromISR()
        此函数用于发送任务通知,是函数 xTaskNotify()的中断版本,此函数是个宏,真正执行的是函数 xTaskGenericNotifyFromISR(),此函数原型如下:
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify,
                                                    uint32_t ulValue,
                                                    eNotifyAction eAction,
                                                    BaseType_t * pxHigherPriorityTaskWoken );
参数:
        xTaskToNotify : 任务句柄,指定任务通知是发送给哪个任务的。
        ulValue : 任务通知值。
        eAction : 任务通知更新的方法。
        pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这
个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。

返回值:
        pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有更新成功就返回 pdFAIL。
        pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。

3 、函数 xTaskNotifyGive()
        发送任务通知,相对于函数 xTaskNotify(),此函数发送任务通知的时候不带有通知值。此函数只是将任务通知值简单的加一,此函数是个宏,真正执行的是函数 xTaskGenericNotify(),此函数原型如下:
        BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
参数:
        xTaskToNotify : 任务句柄,指定任务通知是发送给哪个任务的。
返回值:
        pdPASS: 此函数只会返回 pdPASS。

4 、函数 vTaskNotifyGiveFromISR()
        此函数为 xTaskNotifyGive()的中断版本,用在中断服务函数中,函数原型如下:
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle,
                                                BaseType_t *  pxHigherPriorityTaskWoken );
参数:
        xTaskToNotify : 任务句柄,指定任务通知是发送给哪个任务的。
        pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动
        设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
        无。

5 、函数 xTaskNotifyAndQuery()
        此函数和 xTaskNotify()很类似,此函数比 xTaskNotify()多一个参数,此参数用来保存更新前的通知值。此函数是个宏,真正执行的是函数 xTaskGenericNotify(),此函数原型如下:
BaseType_t xTaskNotifyAndQuery ( TaskHandle_t xTaskToNotify,
                                                        uint32_t ulValue,
                                                        eNotifyAction eAction
                                                        uint32_t * pulPreviousNotificationValue);
参数:
        xTaskToNotify : 任务句柄,指定任务通知是发送给哪个任务的。
        ulValue : 任务通知值。
        eAction : 任务通知更新的方法。
        pulPreviousNotificationValue:用来保存更新前的任务通知值。

返回值:
        pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有更新成功就返回 pdFAIL。
        pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。

6 、函数 xTaskNotifyAndQueryFromISR()
        此函数为 xTaskNorityAndQuery()的中断版本,用在中断服务函数中。此函数同样为宏,真正执行的是函数 xTaskGenericNotifyFromISR(),此函数的原型如下:
        BaseType_t xTaskNotifyAndQueryFromISR ( TaskHandle_t xTaskToNotify,
                                                                            uint32_t ulValue,
                                                                            eNotifyAction eAction,
                                                                            uint32_t * pulPreviousNotificationValue
                                                                            BaseType_t *  pxHigherPriorityTaskWoken );
参数:
        xTaskToNotify : 任务句柄,指定任务通知是发送给哪个任务的。
        ulValue : 任务通知值。
        eAction : 任务通知更新的方法。
        pulPreviousNotificationValue:用来保存更新前的任务通知值。
        pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动
        设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。
返回值:
        pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有更新成功就返回 pdFAIL。
        pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。

3  任务通知通用发送函数
3.1  任务级任务通知通用发送函数
        在 17.2 小节中我们学习了 3 个任务级任务通知发送函数:xTaskNotify()、xTaskNotifyGive()和 xTaskNotifyAndQuery(),这三个函数最终调用的都是函数 xTaskGenericNotify()!此函数在文件 tasks.c 中有如下定义,缩减后的函数如下:
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, //任务句柄
                                                    uint32_t ulValue, //任务通知值
                                                    eNotifyAction eAction, //任务通知更新方式
                                                    uint32_t *  pulPreviousNotificationValue )//保存更新前的
//任务通知值
{
    TCB_t * pxTCB;
    BaseType_t xReturn = pdPASS;
    uint8_t ucOriginalNotifyState;
    configASSERT( xTaskToNotify );
    pxTCB = ( TCB_t * ) xTaskToNotify;
    taskENTER_CRITICAL();
    {
        if( pulPreviousNotificationValue != NULL ) (1)
        {
            *pulPreviousNotificationValue = pxTCB->ulNotifiedValue; (2)
        }
        ucOriginalNotifyState = pxTCB->ucNotifyState; (3)
        pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED;  (4)
        switch( eAction )
        {
            case eSetBits :  (5)
            pxTCB->ulNotifiedValue |= ulValue;
            break;
            case eIncrement : (6)
            ( pxTCB->ulNotifiedValue )++;
            break;
            case eSetValueWithOverwrite :  (7)
            pxTCB->ulNotifiedValue = ulValue;
            break;
            case eSetValueWithoutOverwrite : (8)
            if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
            {
                pxTCB->ulNotifiedValue = ulValue;
            }
            else
            {
                xReturn = pdFAIL;
            }
            break;
            case eNoAction:
            break;
        }
        traceTASK_NOTIFY();
//如果任务因为等待任务通知而进入阻塞态的话就需要解除阻塞
        if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) (9)
        {
                ( void ) uxListRemove( &( pxTCB->xStateListItem ) );  (10)
                prvAddTaskToReadyList( pxTCB );  (11)
/********************省略相关的条件编译代码************************/
                if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) (12)
                {
                    //解除阻塞的任务优先级比当前运行的任务优先级高,所以需要进行
                    //任务切换。
                    taskYIELD_IF_USING_PREEMPTION();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    taskEXIT_CRITICAL();
    return xReturn;  (13)
}
(1)、判断参数 pulPreviousNotificationValue 是否有效,因为此参数用来保存更新前的任务通知值。
(2)、如果参数 pulPreviousNotificationValue 有效的话就用此参数保存更新前的任务通知值。
(3)、保存任务通知状态,因为下面会修改这个状态,后面我们要根据这个状态来确定是否将任务从阻塞态解除。
(4)、更新任务通知状态为 taskNOTIFICATION_RECEIVED。
(5)、根据不同的更新方式做不同的处理,如果为 eSetBits 的话就将指定的 bit 置 1。也就是更新接收任务通知值的一个或多个 bit。
(6)、如果更新方式为 eIncrement 的话就将任务通知值加一。
(7)、如果更新方式为 eSetValueWithOverwrite 的话就直接覆写原来的任务通知值。
(8)、如果更新方式为 eSetValueWithoutOverwrite 的话就需要判断原来的任务通知值是否被处理,如果已经被处理了就更新为任务通知值。如果此前的任务通知值话没有被处理的话就标记 xReturn 为 pdFAIL,后面会返回这个值。
(9)、根据(3)中保存的接收任务之前的状态值来判断是否有任务需要解除阻塞,如果在任务通知值被更新前任务处于 taskWAITING_NOTIFICATION 状态的话就说明有任务因为等待任务通知值而进入了阻塞态。
(10)、将任务从状态列表中移除。
(11)、将任务重新添加到就绪列表中。
(12)、判断刚刚解除阻塞的任务优先级是否比当前正在运行的任务优先级高,如果是的话需要进行一次任务切换。
(13)、返回 xReturn 的值,pdFAIL 或 pdPASS。

3.2  中断级任务通知发送函数
        中 断 级 任 务 通 知 发 送 函 数 也 有 三 个 , 分 别 为 : xTaskNotifyFromISR() 、xTaskNotifyAndQueryFromISR()和 vTaskNotifyGiveFromISR()。其中函数 xTaskNotifyFromISR()和 xTaskNotifyAndQueryFromISR()最终调用的都是函数 xTaskGenericNotifyFromISR(),此函数的原型如下:
BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify,
                                                                uint32_t ulValue,
                                                                eNotifyAction eAction,
                                                                uint32_t *  pulPreviousNotificationValue,
                                                                BaseType_t * pxHigherPriorityTaskWoken )
参数:
        xTaskToNotify : 任务句柄,指定任务通知是发送给哪个任务的。
        ulValue : 任务通知值。
        eAction : 任务通知更新的方法。
        pulPreviousNotificationValue:用来保存更新前的任务通知值。
        pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之前一定要进行一次任务切换。

返回值:
        pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有更新成功就返回 pdFAIL。
        pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。
        函数 xTaskGenericNotifyFromISR()在文件 tasks.c 中有定义,函数源码如下:
BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify,
                                                                uint32_t ulValue,
                                                                eNotifyAction eAction,
                                                                uint32_t *  pulPreviousNotificationValue,
                                                                BaseType_t * pxHigherPriorityTaskWoken )
{
        TCB_t * pxTCB;
        uint8_t ucOriginalNotifyState;
        BaseType_t xReturn = pdPASS;
        UBaseType_t uxSavedInterruptStatus;
        configASSERT( xTaskToNotify );
        portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
        pxTCB = ( TCB_t * ) xTaskToNotify;
        uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
        {
                if( pulPreviousNotificationValue != NULL ) (1)
                {
                    *pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
                }
                ucOriginalNotifyState = pxTCB->ucNotifyState; (2)
                pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED;  (3)
                switch( eAction )  (4)
                {
                    case eSetBits :
                    pxTCB->ulNotifiedValue |= ulValue;
                    break;
                    case eIncrement  :
                    ( pxTCB->ulNotifiedValue )++;
                    break;
                    case eSetValueWithOverwrite :
                    pxTCB->ulNotifiedValue = ulValue;
                    break;
                    case eSetValueWithoutOverwrite :
                            if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
                            {
                                pxTCB->ulNotifiedValue = ulValue;
                            }
                            else
                            {
                                xReturn = pdFAIL;
                            }
                            break;
                    case eNoAction :
                            break;
                }
                traceTASK_NOTIFY_FROM_ISR();
                //如果任务因为等待任务通知而进入阻塞态的话就需要解除阻塞
                if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) (5)
                {
                    configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) ==NULL );
                    if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) (6)
                    {
                            ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
                            prvAddTaskToReadyList( pxTCB );
                    }
                    else (7)
                    {
                        vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) );
                    }
                    if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) (8)
                    {
                        //解除阻塞的任务优先级比当前运行任务的优先级高,所以需要标记
                        //在退出中断服务函数的时候需要做任务切换。
                        if( pxHigherPriorityTaskWoken != NULL )
                        {
                                *pxHigherPriorityTaskWoken = pdTRUE;
                        }
                        else
                        {
                            xYieldPending = pdTRUE;
                        }
                    }
                    else
                    {
                        mtCOVERAGE_TEST_MARKER();
                    }
                }
        }
        portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
        return xReturn;
 }
(1)、判断参数 pulPreviousNotificationValue 是否有效,因为此参数用来保存更新前的任务通知值。如果参数 pulPreviousNotificationValue 有效的话就用此参数保存更新前的任务通知值。
(2)、保存任务通知状态,因为下面会修改这个状态,后面我们要根据这个状态来确定是否将任务解除阻塞态。
(3)、更新任务通知状态 taskNOTIFICATION_RECEIVED。
(4)、根据不同的通知值更新方式来做不同的处理,与函数 xTaskGenericNotify()的处理过程一样。
(5)、根据(2)中保存的接收任务之前的状态值来判断是否有任务需要解除阻塞,如果在任务通知值被更新前任务处于 taskWAITING_NOTIFICATION 状态的话就说明有任务因为等待任务通知值而进入了阻塞态。
(6)、判断任务调度器是否上锁,如果调度器没有上锁的话就将任务从状态列表中移除,然后重新将任务添加到就绪列表中。
(7)、如果任务调度器上锁了的话就将任务添加到列表 xPendingReadyList 中。
(8)、判断任务解除阻塞的任务优先级是否比当前任务优先级高,如果是的话就将pxHigherPriorityTaskWoken 标记 pdTRUE。如果参数 pxHigherPriorityTaskWoken 无效的话就将全局变量 xYieldPending 标记为 pdTRUE。还有另外一个用于中断服务函数的任务通知发送函数 vTaskNotifyGiveFromISR(),此函数和xTaskGenericNotifyFromISR()极其类似。此函数用于将任务通知值加一,大家可以自行分析一下此函数。

4  获取任务通知
        获取任务通知的函数有两个,如表 17.4.1 所示:
FreeRTOS任务通知

1 、函数 ulTaskNotifyTake()
        此函数为获取任务通知函数,当任务通知用作二值信号量或者计数型信号量的时候可以使用此函数来获取信号量,函数原型如下:
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,
                                            TickType_t xTicksToWait );
参数:
        xClearCountOnExit : 参数为 pdFALSE 的话在退出函数 ulTaskNotifyTake()的时候任务通知值减一,类似计数型信号量。当此参数为 pdTRUE 的话在退出函数的时候任务任务通知值清零,类似二值信号量。
xTickToWait:  阻塞时间。
返回值:
        任何值  : 任务通知值减少或者清零之前的值。
        此函数在文件 tasks.c 中有定义,代码如下:
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
{
    uint32_t ulReturn;
    taskENTER_CRITICAL();
    {
        if( pxCurrentTCB->ulNotifiedValue == 0UL )  (1)
        {
            pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION; (2)
            if( xTicksToWait > ( TickType_t ) 0 )  (3)
            {
                prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
                traceTASK_NOTIFY_TAKE_BLOCK();
                portYIELD_WITHIN_API();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    taskEXIT_CRITICAL();
    taskENTER_CRITICAL();
    {
        traceTASK_NOTIFY_TAKE();
        ulReturn = pxCurrentTCB->ulNotifiedValue;  (4)
        if( ulReturn != 0UL )  (5)
        {
            if( xClearCountOnExit != pdFALSE ) (6)
            {
                pxCurrentTCB->ulNotifiedValue = 0UL;
            }
            else
            {
                pxCurrentTCB->ulNotifiedValue = ulReturn - 1; (7)
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
        pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; (8)
    }
        taskEXIT_CRITICAL();
        return ulReturn;
}
(1)、判断任务通知值是否为 0,如果为 0 的话说明还没有接收到任务通知。
(2)、修改任务通知状态为 taskWAITING_NOTIFICATION。
(3)、如果阻塞时间不为 0 的话就将任务添加到延时列表中,并且进行一次任务调度。
(4)、如果任务通知值不为 0 的话就先获取任务通知值。
(5)、任务通知值大于 0。
(6)、参数 xClearCountOnExit 不为 pdFALSE,那就将任务通知值清零。
(7)、如果参数 xClearCountOnExit 为 pdFALSE 的话那就将任务通知值减一。
(8)、更新任务通知状态为 taskNOT_WAITING_NOTIFICATION。

2 、函数 xTaskNotifyWait()
        此函数也是用来获取任务通知的,不过此函数比 ulTaskNotifyTake()更为强大,不管任务通知用作二值信号量、计数型信号量、队列和事件标志组中的哪一种,都可以使用此函数来获取任务通知。但是当任务通知用作位置信号量和计数型信号量的时候推荐使用函数ulTaskNotifyTake()。此函数原型如下:
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,
                                            uint32_t ulBitsToClearOnExit,
                                            uint32_t * pulNotificationValue,
                                            TickType_t xTicksToWait );
参数:
    ulBitsToClearOnEntry: :当没有接收到任务通知的时候将任务通知值与此参数的取反值进行按位与运算,当此参数为 0xffffffff 或者 ULONG_MAX 的时候就会将任务
通知值清零。
    ulBitsToClearOnExit: :如果接收到了任务通知,在做完相应的处理退出函数之前将任务通知值与此参数的取反值进行按位与运算,当此参数为 0xffffffff 或者ULONG_MAX 的时候就会将任务通知值清零。
    pulNotificationValue :此参数用来保存任务通知值。
    xTickToWait:  阻塞时间。
返回值:
    pdTRUE : 获取到了任务通知。
    pdFALSE : 任务通知获取失败。
    此函数在文件 tasks.c 中有定义,代码如下:
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 ) (1)
        {
            pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry; (2)
            pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION; (3)
            if( xTicksToWait > ( TickType_t ) 0 )  (4)
            {
                prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
                traceTASK_NOTIFY_WAIT_BLOCK();
                portYIELD_WITHIN_API();
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        else
        {
            mtCOVERAGE_TEST_MARKER();
        }
    }
    taskEXIT_CRITICAL();
    taskENTER_CRITICAL();
    {
        traceTASK_NOTIFY_WAIT();
        if( pulNotificationValue != NULL )  (5)
        {
            *pulNotificationValue = pxCurrentTCB->ulNotifiedValue;
        }
        if( pxCurrentTCB->ucNotifyState == taskWAITING_NOTIFICATION ) (6)
        {
            xReturn = pdFALSE;
        }
        else
        {
            pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit;  (7)
            xReturn = pdTRUE;
        }
        pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; (8)
    }
    taskEXIT_CRITICAL();
    return xReturn;
}
(1)、任务同通状态不为 taskNOTIFICATION_RECEIVED。
(2)、将任务通知值与参数 ulBitsToClearOnEntry 的取反值进行按位与运算。
(3)、任务通知状态改为 taskWAITING_NOTIFICATION。
(4)、如果阻塞时间大于 0 的话就要将任务添加到延时列表中,并且进行一次任务切换。
(5)、如果任务通知状态为 taskNOTIFICATION_RECEIVED,并且参数 pulNotificationValue有效的话就保存任务通知值。
(6)、如果任务通知的状态又变为 taskWAITING_NOTIFICATION 的话就标记 xRetur 为pdFALSE。
(7)、如果任务通知的状态一直为 taskNOTIFICATION_RECEIVED 的话就将任务通知的值与参数 ulBitsToClearOnExit 的取反值进行按位与运算,并且标记 xReturn 为 pdTRUE,表示获取任务通知成功。
(8)、标记任务通知的状态为 taskNOT_WAITING_NOTIFICATION。