从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支持多个通知,默认使用index0uint32_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 ) )