10.FreeRTOS任务通知的简易分析
FreeRTOS任务通知
- 架构:Cortex-M3
- 版本:FreeRTOS V9.0.0
- 前言:任务通知的出现,是为了代替有些场景队列和信号量的使用,为什么要替换?为什么可以替换?接下来是分析任务通知有什么优势。
- FreeRTOS任务通知
- 1.任务通知的创建
- 2.任务通知的发送
- 3.任务通知的接收
1.任务通知的创建
任务通知并没有专门的函数创建,而是在每个任务创建的时候,任务通知就已经创建了
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
{
pxNewTCB->ulNotifiedValue = 0;
pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
#endif
? 任务通知有两个属性,一个是通知值被初始化为0,另一个是通知状态被初始化为NOT_WAITING_NOTIFICATION。
? 可以看到,任务通知只用了两个四字节的变量就实现了,而前面分析过的队列和信号量,初始化过程不仅相对复杂,并且占用的内存也相较更大。
2.任务通知的发送
发送任务一共有三种方式:
- xTaskNotifyGive( xTaskToNotify ) 这个发送通知不会对ulNotifiedValue操作
- xTaskNotify( xTaskToNotify, ulValue, eAction ) 这个发送通知会根据eAction来对ulNotifiedValue进行自加、按位或、覆盖的操作。
- xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) 同上,但是多了一步,可以查询ulNotifiedValue的值,ulNotifiedValue被放在pulPreviousNotifyValue 中
其实以上三个函数都是宏定义,真正被调用的是xTaskGenericNotify
具体分析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;
}
? 首先查看是否需要返回ulNotifiedValue
,要的话就赋值到pulPreviousNotificationValue
中。然后是保存ucNotifyState
,然后根据eAction
对ulNotifiedValue
进行操作。检查被发送任务通知的任务是否因为等待任务通知而阻塞,如果是就挂载到等待链表中,如果被发送任务通知的任务优先级大于当前任务,则切换到被发送任务通知的任务。
3.任务通知的接收
等待任务通知有两个函数:
3.1 uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
,第一个参数是要不是把ulNotifiedValue
清0,第二个参数是阻塞的时间
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
{
uint32_t ulReturn;
taskENTER_CRITICAL();
{
/* Only block if the notification count is not already non-zero. */
if( pxCurrentTCB->ulNotifiedValue == 0UL )
{
/* Mark this task as waiting for a notification. */
pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;
if( xTicksToWait > ( TickType_t ) 0 )
{
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
traceTASK_NOTIFY_TAKE_BLOCK();
/* All ports are written to allow a yield in a critical
section (some will yield immediately, others wait until the
critical section exits) - but it is not something that
application code should ever do. */
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
taskENTER_CRITICAL();
{
traceTASK_NOTIFY_TAKE();
ulReturn = pxCurrentTCB->ulNotifiedValue;
if( ulReturn != 0UL )
{
if( xClearCountOnExit != pdFALSE )
{
pxCurrentTCB->ulNotifiedValue = 0UL;
}
else
{
pxCurrentTCB->ulNotifiedValue = ulReturn - 1;
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
taskEXIT_CRITICAL();
return ulReturn;
}
如果当前没有收到通知(ulNotifiedValue
为0),并且阻塞时间大于0,那么就将当前任务插入到DelayedList
中,然后切换其它任务。如果此时收到通知(ulNotifiedValue
不为0),根据xClearCountOnExit
的值,ulNotifiedValue
要么清零,要么减1。
3.2 BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait )
第一个参数是在进入该函数的时候,要把ulNotifiedValue
初始化为某个值,第二个参数是在函数结束的时候要把ulNotifiedValue
赋为什么值。第三个参数是返回的是结束时ulNotifiedValue
,第四个参数是阻塞的时间。
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait )
{
BaseType_t xReturn;
taskENTER_CRITICAL();
{
/* Only block if a notification is not already pending. */
if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED )
{
/* Clear bits in the task's notification value as bits may get
set by the notifying task or interrupt. This can be used to
clear the value to zero. */
pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry;
/* Mark this task as waiting for a notification. */
pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;
if( xTicksToWait > ( TickType_t ) 0 )
{
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
traceTASK_NOTIFY_WAIT_BLOCK();
/* All ports are written to allow a yield in a critical
section (some will yield immediately, others wait until the
critical section exits) - but it is not something that
application code should ever do. */
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
taskENTER_CRITICAL();
{
traceTASK_NOTIFY_WAIT();
if( pulNotificationValue != NULL )
{
/* Output the current notification value, which may or may not
have changed. */
*pulNotificationValue = pxCurrentTCB->ulNotifiedValue;
}
/* If ucNotifyValue is set then either the task never entered the
blocked state (because a notification was already pending) or the
task unblocked because of a notification. Otherwise the task
unblocked because of a timeout. */
if( pxCurrentTCB->ucNotifyState == taskWAITING_NOTIFICATION )
{
/* A notification was not received. */
xReturn = pdFALSE;
}
else
{
/* A notification was already pending or a notification was
received while the task was waiting. */
pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit;
xReturn = pdTRUE;
}
pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
taskEXIT_CRITICAL();
return xReturn;
}
如果当前没有收到通知,那么就将ulNotifiedValue
&取反ulBitsToClearOnEntry
,如果设置的阻塞时间大于0,就把当前任务插入到DelayedList
中。如果收到了通知,则把ulNotifiedValue
&取反ulBitsToClearOnExit