FreeRTOS 原理 --- 任务通知

发布时间 2023-10-03 17:35:58作者: 流水灯

简介

任务通知核心包含是一个32位的无符号整数和一个8位的通知状态,这两个在任务控制块中,通知任务就是一个任务或者中断改写另外一个任务中的32位的无符号整数,改写这个整数的方式可以有所不同

  • 可以让这个整数加1,模拟信号量
  • 设置该整数的指定的某些位,模拟事件组
  • 直接选择覆盖或者不覆盖写入,模拟消息队列
        volatile uint32_t ulNotifiedValue;
        volatile uint8_t ucNotifyState;

 

任务的通知状态:任务通知有三种状态
未等待通知状态:就是任务的初始状态

等待通知状态:当任务在没有通知的时候接收通知时(也就是任务没有接收到通知的时候调用了接收通知的函数,则此时必定接收不到通知,把该任务标记为等待通知状态(去等别的任务发给我通知),任务进入阻塞态),这样做的用处是什么呢? 答:当另外一个任务发通知给该任务时,此时发现任务处于等待通知的状态,然后就可以即可把该任务唤醒。

等待接收通知状态:当有其他任务向任务发送通知,但任务还未接收这一通知的这段期间内(当其他任务给该任务发了通知,但是该任务还没有接收,则将该任务标记为等待接收通知状态),这样做的用处就是当该任务调用了接收通知的函数,发现自身的状态为等待接收通知状态,则不用进入阻塞,直接接收通知值。

那为什么任务通知不能以通知值是否为0判断是否有消息呢?
确实模拟信号量确实是怎么做的,但是如果是模拟队列的话,就不能怎么搞了,因为我发送一个0,0也算是数据

相关的API

xTaskNotify()

ulTaskNotifyTake() // 模拟信号量

xTaskNotifyWait() // 模拟队列、事件组

 

源码分析

xTaskGenericNotify() 发生通知
    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 )
            {
                *pulPreviousNotificationValue = pxTCB->ulNotifiedValue;
            }

            ucOriginalNotifyState = pxTCB->ucNotifyState;

            pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; // 更新任务的通知状态

            switch( eAction ) // 根据入参指定的行为,修改任务通知值
            {
                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
                    {
                        /* 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;
            }

            traceTASK_NOTIFY();

            /* If the task is in the blocked state specifically to wait for a
            notification then unblock it now. */
            if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) // 如果任务当前正在等通知,说明进入阻塞,唤醒它
            {
                ( void ) uxListRemove( &( pxTCB->xStateListItem ) );
                prvAddTaskToReadyList( pxTCB );

                /* The task should not have been on an event list. */
                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

                if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
                {
                    /* The notified task has a priority above the currently
                    executing task so a yield is required. */
                    taskYIELD_IF_USING_PREEMPTION();
                }
                else
                {
                    mtCOVERAGE_TEST_MARKER();
                }
            }
            else
            {
                mtCOVERAGE_TEST_MARKER();
            }
        }
        taskEXIT_CRITICAL();

        return xReturn;
    }