FreeRTOS源码分析:任务通知-TaskNotify

FreeRTOS任务通知机制详解

FreeRTOS任务通知(Task Notifiy)概述

任务通知是FreeRTOS提供的一种轻量级通信机制,允许任务间直接发送事件或数据。相比队列、信号量等传统IPC机制,任务通知更高效,因为其直接操作任务控制块(TCB)中的通知字段,无需额外数据结构。

任务通知的核心机制

每个任务都有一个32位的通知值(ulNotifiedValue)和一个通知状态(eNotifyState)字段,存储在TCB中。通知状态包括:

  • eNotWaitingNotification:任务未等待通知。
  • eWaitingNotification:     任务在等待通知。
  • eNotifificationReceived:任务已收到通知。

主要API函数分析

TCB:

typedef struct tskTaskControlBlock       
{
    volatile StackType_t * pxTopOfStack;
    

    ListItem_t xStateListItem;                 
    ListItem_t xEventListItem;                 
    UBaseType_t uxPriority;                    
    StackType_t * pxStack;                      
    char pcTaskName[ configMAX_TASK_NAME_LEN ]; 
                                    .
                                    .
                                    .
                                    .
/***********************任务通知就设置/读取这两个值*********************************/                                    
    #if ( configUSE_TASK_NOTIFICATIONS == 1 )
        volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
        volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
    #endif
/*********************************************************************************/
                                    .
                                    .
                                    .
                                    .
    
} tskTCB;

通知状态:

/* Values that can be assigned to the ucNotifyState member of the TCB. */
#define taskNOT_WAITING_NOTIFICATION              ( ( uint8_t ) 0 )      //不等待通知
#define taskWAITING_NOTIFICATION                  ( ( uint8_t ) 1 )      //等待通知
#define taskNOTIFICATION_RECEIVED                 ( ( uint8_t ) 2 )      //收到通知,但未处理

通知方式:

/* Actions that can be performed when vTaskNotify() is called. */
typedef enum
{
    eNoAction = 0,            /* Notify the task without updating its notify value. */
    eSetBits,                 /* Set bits in the task's notification value. */
    eIncrement,               /* Increment the task's notification value. */
    eSetValueWithOverwrite,   /* Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */
    eSetValueWithoutOverwrite /* Set the task's notification value if the previous value has been read by the task. */
} eNotifyAction;

支持五种操作模式:

  • eNoAction:仅更新通知状态,不修改值。
  • eSetBits:按位设置(类似事件组)。
  • eIncrement:递增通知值。
  • eSetValueWithOverwrite / eSetValueWithoutOverwrite:覆盖或保留原值。

1. xTaskNotifyGive() / xTaskNotify()

/**************xTaskNotifyGive()实际为函数xTaskGenericNotify()********************/
#define xTaskNotifyGive( xTaskToNotify ) 
    xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( 0 ), eIncrement, NULL )

/****************xTaskNotify()实际为函数xTaskGenericNotify()*********************/
#define xTaskNotify( xTaskToNotify, ulValue, eAction ) \
    xTaskGenericNotify( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( ulValue ), ( eAction ), NULL )
/********************************************************************************/




/*
参数:
    1.要送送给谁?->任务句柄
    2.写value的位置
    3.
    4.通知的方式(枚举)
    5.以前的通知值存储位置
*/
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;
        
/*****************断言:1.写value位置不能越界 2.任务句柄不能为空********************/
        configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES );
        configASSERT( xTaskToNotify );
        pxTCB = xTaskToNotify;

        taskENTER_CRITICAL();                            //进入临界保护区(关中断)
        {
/***************************写入之前是否保存原值**********************************/
            if( pulPreviousNotificationValue != NULL )
            {
                *pulPreviousNotificationValue = pxTCB->ulNotifiedValue[ uxIndexToNotify ];
            }

/***************************写入之前读出前一个通知的状态**************************/
            ucOriginalNotifyState = pxTCB->ucNotifyState[ uxIndexToNotify ];

/***************************写入新的通知状态-RECV********************************/
            pxTCB->ucNotifyState[ uxIndexToNotify ] = taskNOTIFICATION_RECEIVED;

/*************************按照不同的通知方式,给value赋值*************************/
/*
1. 类似事件组,'|'形式写入(按位操作)
2. 类似计数信号量,value++
3. 覆盖写入(无论上一个数据是否处理)
4. 覆盖写入(上一个数据未处理不写入且退出,返回错误;已处理则写入)
5. 之前的通知值和新的通知值一样,不用做操作
*/
            switch( eAction )
            {
                case eSetBits:
                    pxTCB->ulNotifiedValue[ uxIndexToNotify ] |= ulValue;
                    break;

                case eIncrement:
                    ( 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:
                    configASSERT( xTickCount == ( TickType_t ) 0 );
                    break;
            }

            traceTASK_NOTIFY( uxIndexToNotify );
/**********************************************************************************/


/***************如果接受通知的任务因为等待通知处于阻塞状态,释放它*********************/            
            if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
            {
                listREMOVE_ITEM( &( 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 )
//                {
//                    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;
    }

  • xTaskNotify()比较灵活,有更多的可配置选项,可以模仿信号量/事件组/消息队列。
  • xTaskNotifyGive()只能模仿信号量。

2. vTaskNotifyGiveFromISR() / xTaskNotifyFromISR()

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


/***************************
参数:
    1.通知任务的句柄
    2.写入value/status的位置
    3.写入的值
    4.写入方式
    5.先前值是否保存,保存位置
    6.是否唤醒高优先级任务
***************************/

BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify,
                                          UBaseType_t uxIndexToNotify,
                                          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 );
        configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES );
/***************************************************************************************/
       
        portASSERT_IF_INTERRUPT_PRIORITY_INVALID();//检查此中断是否是可管理,如果不是->卡死
        pxTCB = xTaskToNotify;
        
        //这句没看懂,好像是提高此中断优先级到可管理的最高优先级
        uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
        {
/***************************和非终端的函数操作一样,参考上面就行了************************/
            if( pulPreviousNotificationValue != 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:
                    ( 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_FROM_ISR( uxIndexToNotify );

          
            if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
            {
                /* The task should not have been on an event list. */
                configASSERT( listLIST_ITEM_CONTAINER( &(pxTCB->xEventListItem))==NULL );
/****************************以此网上和非中断得出函数一样******************************/
                //如果调度器没有挂起
                if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )  
                {
                    listREMOVE_ITEM( &( pxTCB->xStateListItem ) );
                    prvAddTaskToReadyList( pxTCB );
                }
                //如果调度器挂起,DelayList和ReadyList无法访问,放入另一个链表,调度回复后放入            
                //ReadyList
                else
                {
                    listINSERT_END( &( xPendingReadyList ), &( pxTCB->xEventListItem ) );
                }
/**************************当前任务和通知任务优先级对比******************************/
                if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )  //被通知的任务优先级高
                {
                 
                    if( pxHigherPriorityTaskWoken != NULL )
                    {
                        *pxHigherPriorityTaskWoken = pdTRUE;   //存储-发生了更高优先级唤醒
                    }

                    xYieldPending = pdTRUE;
                }
                else                                                //被通知任务优先级低
                {
                    mtCOVERAGE_TEST_MARKER();                       //继续当前任务
                }
            }
        }
/****************应该是中断优先级降会原来的优先级************************************/
        portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );

        return xReturn;
    }







#define vTaskNotifyGiveFromISR( xTaskToNotify, pxHigherPriorityTaskWoken ) \
    vTaskGenericNotifyGiveFromISR( ( xTaskToNotify ), ( tskDEFAULT_INDEX_TO_NOTIFY ), ( pxHigherPriorityTaskWoken ) )


/************就是上面xTaskGenericNotifyFromISR()函数的删减版,参考上面就可以了*********/
void vTaskGenericNotifyGiveFromISR( TaskHandle_t xTaskToNotify,
                                        UBaseType_t uxIndexToNotify,
                                        BaseType_t * pxHigherPriorityTaskWoken )
    {
    ..................................................
    }
/***********************************************************************************/




3. xTaskNotifyWait()

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


/*
参数:
    1.等待那个位置的数据
    2.进入时清除哪些位的数据
    3.退出时清除哪些位的数据
    4.当前值存储位置    
    5.阻塞时间
/*

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 )
            {
                //清空原先的通知值
                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 );

/**************************如果存储等待值位置不为NULL*******************************/
            if( pulNotificationValue != NULL )
            {
                 //将任务通知值存入pulNotificationValue 
                *pulNotificationValue = pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ];
            }

/************************如果不是因为收到通知推出的阻塞*****************************/
            if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ]!= taskNOTIFICATION_RECEIVED )
            {
                  xReturn = pdFALSE;
            }
/************************通知已挂起或在任务等待期间收到通知*************************/
            else
            {
                //清除掉与ulBitsToClearOnExit对应的位
                pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnExit;
                xReturn = pdTRUE;
            }
/*****************************设置任务通知状态************************************/
            pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskNOT_WAITING_NOTIFICATION;
        }
        taskEXIT_CRITICAL();        //退出临界区

        return xReturn;
    }

实现原理

  1. 发送流程

    • 检查目标任务状态,若为eWaitingNotification,则唤醒任务。
    • 根据eAction修改ulNotifiedValue
    • 触发任务调度(若优先级更高)。
  2. 接收流程

    • 若通知已到达(eNotifificationReceived),直接返回通知值。
    • 若未到达,任务进入阻塞状态,直到通知到来或超时。

性能优势

  1. 速度更快:相比队列,省去了数据拷贝和临界区操作。
  2. 内存更少:无需额外存储对象,仅占用TCB的少量字段。
  3. 灵活性:    支持数值传递、位操作和计数信号量模式。

使用场景

  1. 替代二值信号量:通过xTaskNotifyGive()实现。

    // 发送端
    xTaskNotifyGive(xTaskHandle);
    
    // 接收端
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    
  2. 事件标志组:使用xTaskNotify()实现,由eSetBits动作。

    // 发送事件标志
    xTaskNotify(xTaskHandle, 0x01, eSetBits);
    
    // 等待事件
    xTaskNotifyWait(0, 0xFFFFFFFF, &ulValue, portMAX_DELAY);
    
  • 消息队列:使用xTaskNotify()实现,通过eSetValueWithOverwrite发送32位数据。

    xTaskNotify(xTaskHandle, data, eSetValueWithOverwrite);
    

可以参考我另一篇文章:

​​​​​FreeRTOS任务通知使用以及例程-CSDN博客

注意事项

  1. 单接收者限制:每个通知只能唤醒一个任务(与消息队列不同)。
  2. 非队列特性:   仅保存最后一次通知值,历史数据可能丢失。
  3. ISR安全:        需使用FromISR版本并处理上下文切换标志。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值