FreeRTOS 第十一章 通知

从v8.2.0版本开始,FreeRTOS新增了任务通知(Task Notification)功能。可以使用任务通知拉代替信号量、消息队列、事件标志等。使用任务通知效率会高些。

任务通知是一个可选的功能,FreeRTOS的每个任务都有一个32位的通知值。任务控制块的成员变量ulBotifiedValue就是这个值。任务通知是一个事件,假设某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务通知以后就会解除这个任务的阻塞状态。也可以更新接收任务的任务通知值,任务通知值可以通过以下方法更新接收任务的通知值。

  • 不覆盖接收任务的通知值(如果上次发送给接收任务的通知还没有被处理)
  • 覆盖接收任务的通知值。
  • 更新接收任务通知值的一个或者多个bit。
  • 增加接收任务的通知值。

任务通知的发送使用函数xTaskNotify()或者xTaskNotifyGive()来完成,这个通知值会一直保存着。直到接收任务调用函数xTaskNotifyWait()或者ulTaskNotifyTake()来获取这个值。

任务通知虽然可以提供速度,并且减少RAM的使用,但是任务通知也是有使用限制的:

1、任务通知只能有一个接收任务。

2、接收任务可以因为接收任务通知而进入阻塞状态,但是发送任务不会因为任务通知发送失败而阻塞。

发送通知

xTaskNotify():发送通知,带有通知值并且不会保留接收任务的通知值。

xTaskNotifyGive():发送通知,不带通知值并且不保留接收任务的通知值,此函数会在接收任务的通知值加一。

xTaskNotifyAndQuery():发送通知,带有通知值并且保留接收任务的通知值。

xTaskNotify:

#define xTaskNotify( xTaskToNotify, ulValue, eAction ) \
    xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL )
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, //要通知的任务句柄
                                   UBaseType_t uxIndexToNotify, //task支持多个通知,默认使用index0
                                   uint32_t ulValue, //通知值
                                   eNotifyAction eAction, //通知的类型
                                   uint32_t * pulPreviousNotificationValue ) //先前的通知值
    {
        TCB_t * pxTCB;
        BaseType_t xReturn = pdPASS;
        uint8_t ucOriginalNotifyState;

        traceENTER_xTaskGenericNotify( xTaskToNotify, uxIndexToNotify, ulValue, eAction, pulPreviousNotificationValue );

        configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES );
        configASSERT( xTaskToNotify );
        //任务句柄就是任务pcb。
        pxTCB = xTaskToNotify;

        taskENTER_CRITICAL();
        {
            if( pulPreviousNotificationValue != NULL )//如果先前值指针不为NULL,就将先前值赋值指针,供返回使用。
            {
                *pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ];
            }
            //获取任务通知状态。
            ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ];
            //新的状态位接收
            pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED;
            //任务通知的动作类型
            switch( eAction )
            {
                case eSetBits://或上以前的值
                    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
                    {
                        //如果已经是接收状态了,说明不能重写通知值。因此什么都不做
                        /* The value could not be written to the task. */
                        xReturn = pdFAIL;
                    }

                    break;

                case eNoAction://通知任务,不更新通知值

                    /* The task is being notified without its notify value being
                     * updated. */
                    break;

                default:

                    /* Should not get here if all enums are handled.
                     * Artificially force an assert by testing a value the
                     * compiler can't assume is const. */
                    configASSERT( xTickCount == ( TickType_t ) 0 );

                    break;
            }

            traceTASK_NOTIFY( uxIndexToNotify );

            /* If the task is in the blocked state specifically to wait for a
             * notification then unblock it now. */
            //如果task以前的状态是等待通知阻塞
            if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
            {
                listREMOVE_ITEM( &( pxTCB->xStateListItem ) );
                //将任务添加到就绪列表
                prvAddTaskToReadyList( pxTCB );

                /* The task should not have been on an event list. */
                //task现在没有因为事件而阻塞
                configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );

                #if ( configUSE_TICKLESS_IDLE != 0 )
                {
                    /* If a task is blocked waiting for a notification then
                     * xNextTaskUnblockTime might be set to the blocked task's time
                     * out time.  If the task is unblocked for a reason other than
                     * a timeout xNextTaskUnblockTime is normally left unchanged,
                     * because it will automatically get reset to a new value when
                     * the tick count equals xNextTaskUnblockTime.  However if
                     * tickless idling is used it might be more important to enter
                     * sleep mode at the earliest possible time - so reset
                     * xNextTaskUnblockTime here to ensure it is updated at the
                     * earliest possible time. */
                    prvResetNextTaskUnblockTime();
                }
                #endif

                /* Check if the notified task has a priority above the currently
                 * executing task. */
                //如果解除阻塞态的任务优先级比当前任务优先级高,就执行一次任务切换。
                taskYIELD_ANY_CORE_IF_USING_PREEMPTION( pxTCB );
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        taskEXIT_CRITICAL();

        traceRETURN_xTaskGenericNotify( xReturn );

        return xReturn;
    }

xTaskNotifyGive()其实没有什么特别之后,也是调用xTaskGenericNotify()只不过参数传递的不一样而已。 eIncrement表示只通知任务,不改变通知值。

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

获取通知

xTaskNotifyTake():获取任务通知,可以设置在退出此函数的时候将任务通知值清0或者减1.当任务通知用作二值信号量的时候使用此函数来获取信号量。

xTaskNotifyWait():等待任务通知,功能更强大。

#define ulTaskNotifyTake( xClearCountOnExit, xTicksToWait ) \
    ulTaskGenericNotifyTake( ( tskDEFAULT_INDEX_TO_NOTIFY ), ( xClearCountOnExit ), ( xTicksToWait ) )
uint32_t ulTaskGenericNotifyTake( UBaseType_t uxIndexToWaitOn, //index索引值
                                      BaseType_t xClearCountOnExit, //返回的时候是否清通知值
                                      TickType_t xTicksToWait )//最大等到时间
    {
        uint32_t ulReturn;
        BaseType_t xAlreadyYielded;

        traceENTER_ulTaskGenericNotifyTake( uxIndexToWaitOn, xClearCountOnExit, xTicksToWait );

        configASSERT( uxIndexToWaitOn < configTASK_NOTIFICATION_ARRAY_ENTRIES );

        taskENTER_CRITICAL();

        /* Only block if the notification count is not already non-zero. */
        if( pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] == 0UL )//如果没有通知值
        {
            /* Mark this task as waiting for a notification. */
            //将task状态设置成等待状态
            pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskWAITING_NOTIFICATION;
            //最大等待时间
            if( xTicksToWait > ( TickType_t ) 0 )
            {
                traceTASK_NOTIFY_TAKE_BLOCK( uxIndexToWaitOn );

                /* We MUST suspend the scheduler before exiting the critical
                 * section (i.e. before enabling interrupts).
                 *
                 * If we do not do so, a notification sent from an ISR, which
                 * happens after exiting the critical section and before
                 * suspending the scheduler, will get lost. The sequence of
                 * events will be:
                 * 1. Exit critical section.
                 * 2. Interrupt - ISR calls xTaskNotifyFromISR which adds the
                 *    task to the Ready list.
                 * 3. Suspend scheduler.
                 * 4. prvAddCurrentTaskToDelayedList moves the task to the
                 *    delayed or suspended list.
                 * 5. Resume scheduler does not touch the task (because it is
                 *    not on the pendingReady list), effectively losing the
                 *    notification from the ISR.
                 *
                 * The same does not happen when we suspend the scheduler before
                 * exiting the critical section. The sequence of events in this
                 * case will be:
                 * 1. Suspend scheduler.
                 * 2. Exit critical section.
                 * 3. Interrupt - ISR calls xTaskNotifyFromISR which adds the
                 *    task to the pendingReady list as the scheduler is
                 *    suspended.
                 * 4. prvAddCurrentTaskToDelayedList adds the task to delayed or
                 *    suspended list. Note that this operation does not nullify
                 *    the add to pendingReady list done in the above step because
                 *    a different list item, namely xEventListItem, is used for
                 *    adding the task to the pendingReady list. In other words,
                 *    the task still remains on the pendingReady list.
                 * 5. Resume scheduler moves the task from pendingReady list to
                 *    the Ready list.
                 */
                vTaskSuspendAll();
                {
                    taskEXIT_CRITICAL();
                    //将task放到延时列表里。
                    prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
                }
                xAlreadyYielded = xTaskResumeAll();

                if( xAlreadyYielded == pdFALSE )//是否需要调度一次
                {
                    taskYIELD_WITHIN_API();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                taskEXIT_CRITICAL();
            }
        }
        else
        {
            taskEXIT_CRITICAL();
        }

        taskENTER_CRITICAL();
        {
            traceTASK_NOTIFY_TAKE( uxIndexToWaitOn );
            //任务得到运行,可能是超时时间到了,也可能是收到了通知
            ulReturn = pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ];

            if( ulReturn != 0UL )//收到了通知
            {
                if( xClearCountOnExit != pdFALSE )//离开后清掉通知
                {
                    pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] = 0UL;
                }
                else
                {    //通知值减1.
                    pxCurrentTCB->ulNotifiedValue[ uxIndexToWaitOn ] = ulReturn - ( uint32_t ) 1;
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
            //任务状态设置为等待状体
            pxCurrentTCB->ucNotifyState[ uxIndexToWaitOn ] = taskNOT_WAITING_NOTIFICATION;
        }
        taskEXIT_CRITICAL();

        traceRETURN_ulTaskGenericNotifyTake( ulReturn );

        return ulReturn;
    }

xTaskNotifyWait()流程和xTaskNotifyTake()基本一样,就是多了一些功能:比如进入函数就可以清掉标记位,可以返回上一次的通知值。

BaseType_t xTaskGenericNotifyWait( UBaseType_t uxIndexToWaitOn,
                                   uint32_t ulBitsToClearOnEntry,
                                   uint32_t ulBitsToClearOnExit,
                                   uint32_t * pulNotificationValue,
                                   TickType_t xTicksToWait ) PRIVILEGED_FUNCTION;
#define xTaskNotifyWait( ulBitsToClearOnEntry, ulBitsToClearOnExit, pulNotificationValue, xTicksToWait ) \
    xTaskGenericNotifyWait( tskDEFAULT_INDEX_TO_NOTIFY, ( ulBitsToClearOnEntry ), ( ulBitsToClearOnExit ), ( pulNotificationValue ), ( xTicksToWait ) )

 

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王涛的专栏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值