从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 ) )