基本概念
任务通知是 FreeRTOS 从 V8.2.0版本开始推出的新功能,每个任务都有一个32位的通知值,在大多数情况下,任务通知可以替代二值信号量、计数信号量、时间组、长度为1 的队列。
任务通知比信号量等IPC通信方式解决阻塞的任务要快45%,并且更加节省RAM内存空间,任务通知无需创建队列。但也存在一定的局限性:
只能有一个任务接收通知,即必须指定接受通知的任务;
只有等待通知的任务可以阻塞,发送通知的任务,在任何情况下都不会因为发送失败而进入阻塞。
运作机制
任务通知之所以更加节省空间,是因为其是属于任务中附带的资源,在任务被创建的时候同时对其进行初始化,不需要额外的创建。可以将等待通知的任务比作消费者,发送通知的任务或中断服务函数看作生产者,模型如下图所示:
数据结构
任务通知属于任务控制块的资源,是任务控制块中的成员变量
typedef struct tskTaskControlBlock /* The old naming convention is used to prevent breaking kernel aware debuggers. */
{
...
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
volatile uint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
#endif
...
} tskTCB;
ulNotifiedValue:任务通知的值,可以保存一个32位的整数或指针值。
ucNotifyState:任务通知状态,用于标识任务是否在等待通知。
任务通知API
发送任务通知API
以xTaskGenericNotify()为例进行分析
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;
configASSERT( uxIndexToNotify < configTASK_NOTIFICATION_ARRAY_ENTRIES );
configASSERT( xTaskToNotify );
pxTCB = xTaskToNotify;
taskENTER_CRITICAL();
{
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( uxIndexToNotify );
/* If the task is in the blocked state specifically to wait for a
* notification then unblock it now. */
/* 如果被通知任务由于等待任务通知而挂起 */
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 )
{
/* 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;
}
任务通知值状态表
eAction取值 | 含义 |
---|---|
eNoAction | 对象任务接收任务通知,但任务自身的任务通知值不变,即形参 ulValue 没有用。 |
eSetBits | 对象任务接收任务通知,同时任务自身的任务通知值与ulValue 按位或。如果 ulValue 设置为 0x01,那么任务的通知值的位 0 将被置为 1。同样的如果 ulValue 设置为 0x04,那么任务的通知值的位 2 将被置为 1。 |
eIncrement | 对象任务接收任务通知,任务自身的任务通知值加 1,即形参ulValue 没有用。 |
eSetValueWithOverwrite | 对象任务接收任务通知,且任务自身的任务通知值会无条件的被设置为 ulValue。 |
eSetValueWithoutOverwrite | 对象任务接收任务通知,且对象任务没有通知值,那么通知值就会被设置为 ulValue。对象任务接收任务通知,但是上一次接收到的通知值并没有取走,那么本次的通知值将不会更新,同时函数返回pdFALSE。 |
获取任务通知API
获取任务通知API有两个,分别是ulTaskNotifyTake() 和xTaskGenericNotifyWait()。
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();
{
/* Only block if a notification is not already pending. */
/* 只有任务当前没有收到任务通知,才会将任务阻塞 */
if( pxCurrentTCB->ucNotifyState[ uxIndexToWait ] != 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. */
/* 使用任务通知值之前,根据用户指定参数 ulBitsToClearOnEntryClear将通知值的某些或全部位清零 */
pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnEntry;
/* Mark this task as waiting for a notification. */
/* 设置任务状态标识:等待通知 */
pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskWAITING_NOTIFICATION;
/* 挂起任务等待通知或者进入阻塞态 */
if( xTicksToWait > ( TickType_t ) 0 )
{
/* 根据用户指定超时时间将任务添加到延时列表 */
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
traceTASK_NOTIFY_WAIT_BLOCK( uxIndexToWait );
/* 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( uxIndexToWait );
if( pulNotificationValue != NULL )
{
/* Output the current notification value, which may or may not
* have changed. */
/* 返回当前通知值,通过指针参数传递 */
*pulNotificationValue = pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ];
}
/* 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[ uxIndexToWait ] != taskNOTIFICATION_RECEIVED )
{
/* A notification was not received. */
xReturn = pdFALSE;
}
else
{
/* A notification was already pending or a notification was
* received while the task was waiting. */
/* 收到任务值,先将参数 ulBitsToClearOnExit 取反后与通知值位做按位与运算在退出函数前,将通知值的某些或者全部位清零. */
pxCurrentTCB->ulNotifiedValue[ uxIndexToWait ] &= ~ulBitsToClearOnExit;
xReturn = pdTRUE;
}
//重新设置任务通知状态
pxCurrentTCB->ucNotifyState[ uxIndexToWait ] = taskNOT_WAITING_NOTIFICATION;
}
taskEXIT_CRITICAL();
return xReturn;
}