任务通知可用来代替信号量、消息队列、事件标志位,而且使用任务通知的形式效率会更高,它不需要像信号量那样创建队列和操作队列,任务通知的存储变量来自任务控制块中,当宏 configUSE_TASK_NOTIFICATIONS 被定义为1时就可以使用任务通知相关的函数了。任务通知的接口函数同样存在发送和接收,发送的接口函数如下:
/* 发送通知,有通知值但不保留接收任务原通知值 */
xTaskNotify( xTaskToNotify, ulValue, eAction )
xTaskNotifyFromISR(xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken)
/* 发送通知(可代替计数型信号量),没有通知值并且不保留接收任务的通知值,会将接收任务的通知值加一 */
xTaskNotifyGive(xTaskToNotify)
vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken )
/* 发送通知,有通知值并且保留接收任务的原通知值 */
xTaskNotifyAndQuery(xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue)
xTaskNotifyAndQueryFromISR(xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken)
这几个发送通知的接口实际上都是宏,定义如下:
#define xTaskNotify( xTaskToNotify, ulValue, eAction ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL )
#define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) )
#define xTaskNotifyGive( xTaskToNotify ) xTaskGenericNotify( ( xTaskToNotify ), ( 0 ), eIncrement, NULL )
#define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) )
#define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) )
有分任务之间的接口和中断的接口,核心主要就是这两个函数:
BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue )
BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken )
任务通知的写入方式有以下几种,使用一个枚举来定义:
typedef enum
{
eNoAction = 0, /* 不升级通知值 */
eSetBits, /* 更新指定的bit */
eIncrement, /* 通知值加一 */
eSetValueWithOverwrite, /* 覆写的方式更新通知值 */
eSetValueWithoutOverwrite /* 不覆盖通知值 */
} eNotifyAction;
xTaskGenericNotify 通用通知函数的源码分析如下:
#if( configUSE_TASK_NOTIFICATIONS == 1 )
/* 形参:
* xTaskToNotify:任务句柄(要通知的任务)
* ulValue:任务通知值
* eAction:任务通知更新的方法
* pulPreviousNotificationValue:用于保存任务更新前的任务通知值
*/
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 = 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
{
/* 如果任务处于接收状态的话就不能被写入,因为使用的不覆写的方式,返回错误 */
xReturn = pdFAIL;
}
break;
case eNoAction:
break;
default:
configASSERT( pxTCB->ulNotifiedValue == ~0UL );
break;
}
traceTASK_NOTIFY();
/* 如果被通知任务原来的状态为等待通知而进入阻塞,这里解锁阻塞 */
if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
{
/* 将任务从原来的状态列表中移除 */
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
/* 将任务添加到就绪列表中 */
prvAddTaskToReadyList( pxTCB );
configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );
#if( configUSE_TICKLESS_IDLE != 0 )
{
prvResetNextTaskUnblockTime();
}
#endif
/* 如果任务的优先级高于当前优先级则进行一次任务切换 */
if( pxTCB->uxPriority > pxCurrentTCB->uxPriority )
{
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 退出临界区 */
taskEXIT_CRITICAL();
return xReturn;
}
#endif /* configUSE_TASK_NOTIFICATIONS */
在中断中使用的发送函数 xTaskGenericNotifyFromISR 分析如下:
#if( configUSE_TASK_NOTIFICATIONS == 1 )
/* 形参:
* xTaskToNotify:任务句柄
* ulValue:任务通知值
* eAction:任务通知更新的方法
* pulPreviousNotificationValue:用于保存任务更新前的任务通知值
* pxHigherPriorityTaskWoken:使用变量记退出此函数以后是否进行任务切换,用于需要提供一个变量来保存这个值
*/
BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, 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 );
portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
pxTCB = xTaskToNotify;
/* 进入中断临界区 */
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
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
{
/* 如果任务处于接收状态的话就不能被写入,返回错误 */
xReturn = pdFAIL;
}
break;
case eNoAction :
break;
default:
configASSERT( pxTCB->ulNotifiedValue == ~0UL );
break;
}
traceTASK_NOTIFY_FROM_ISR();
/* 如果被通知任务原来的状态为等待通知而进入阻塞,这里解锁阻塞 */
if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
{
configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL );
/* 如果调度器休眠没休眠,执行这个分支 */
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
/* 将任务从原来的状态列表中移除 */
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
/* 将任务添加到就绪列表中 */
prvAddTaskToReadyList( pxTCB );
}
/* 如果调度器休眠了,执行这个分支 */
else
{
/* 将任务插入到挂机就绪列表,调度器恢复后再放入就绪列表 */
vListInsertEnd( &( 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;
}
#endif
下面获取任务通知的接口:
/* 获取任务通知,可以设置在退出此函数的时候将任务通知值清零或减一,用于代替二值信号和计数型信号量 */
ulTaskNotifyTake(BaseType_t xClearCountOnExit, TickType_t xTicksToWait)
/* 获取任务通知,等待任务通知,比ulTaskNotifyTake强大,全功能版任务通知获取函数 */
xTaskNotifyWait(xTaskToNotify, ulValue, eAction)
ulTaskNotifyTake 的源码分析如下:
#if( configUSE_TASK_NOTIFICATIONS == 1 )
/* 形参:
* xClearCountOnExit:为 pdFALSE 的话在退出时通知值减一,类似计数型信号量,
* 为 pdTURE 的话在退出时通知值清零,类似二值信号量
* xTicksToWait:阻塞时间
*/
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
{
uint32_t ulReturn;
/* 进入临界区 */
taskENTER_CRITICAL();
{
/* 如果当前任务通知值为0 */
if( pxCurrentTCB->ulNotifiedValue == 0UL )
{
/* 任务的通知状态标记为等待通知 */
pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;
/* 如果任务的等待时间大于0 */
if( xTicksToWait > ( TickType_t ) 0 )
{
/* 将任务添加到延时任务列表 */
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
traceTASK_NOTIFY_TAKE_BLOCK();
/* 进行一次任务调度 */
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 退出临界区 */
taskEXIT_CRITICAL();
/* 进入临界区,执行到这里说明接收到了来自其它任务的通知或超时 */
taskENTER_CRITICAL();
{
traceTASK_NOTIFY_TAKE();
/* 获取当前任务的通知值 */
ulReturn = pxCurrentTCB->ulNotifiedValue;
/* 通知值不为0 */
if( ulReturn != 0UL )
{
if( xClearCountOnExit != pdFALSE )
{
/* 类似二值信号量,获取到后清零 */
pxCurrentTCB->ulNotifiedValue = 0UL;
}
else
{
/* 类似计数信号量,获取到后减一 */
pxCurrentTCB->ulNotifiedValue = ulReturn - ( uint32_t ) 1;
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 当前任务的通知状态改为等待通知 */
pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
/* 退出临界区 */
taskEXIT_CRITICAL();
return ulReturn;
}
#endif
xTaskNotifyWait 源码分析如下:
#if( configUSE_TASK_NOTIFICATIONS == 1 )
/* 形参:
* ulBitsToClearOnEntry:进入函数时是否清除任务bit,没接收到任务通知的时候将任务
* 通知值与此参数的取反值进行按位与运算,当此参数
* 为0xffffffff时会将任务的通知值清零
* xClearCountOnExit:为 pdFALSE 的话在退出时通知值减一,类似计数型信号量,
* 为 pdTURE 的话在退出时通知值清零,类似二值信号量
* pulNotificationValue:用于保存任务通知值
* xTicksToWait:阻塞时间
*/
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait )
{
BaseType_t xReturn;
/* 进入临界区 */
taskENTER_CRITICAL();
{
/* 当前任务的通知状态不是接收状态,即还没接收到任务通知 */
if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED )
{
/* 清除指定的bit */
pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry;
/* 任务的通知状态标记为等待通知 */
pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION;
/* 用户设置的超时时间大于0 */
if( xTicksToWait > ( TickType_t ) 0 )
{
/* 将任务添加到延时列表中 */
prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );
traceTASK_NOTIFY_WAIT_BLOCK();
/* 进行一次任务调度 */
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 退出临界区 */
taskEXIT_CRITICAL();
/* 进入临界区,执行到这里说明接收到了来自其它任务的通知或超时 */
taskENTER_CRITICAL();
{
traceTASK_NOTIFY_WAIT();
/* 用户传入的用于保存通知值的变量不为空 */
if( pulNotificationValue != NULL )
{
/* 返回任务当前通知值 */
*pulNotificationValue = pxCurrentTCB->ulNotifiedValue;
}
/* 未接收到任务通知返回 pdFALSE */
if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED ) {
xReturn = pdFALSE;
}
/* 接收到任务通知返回 pdTRUE */
else
{
/* 清除指定的bit */
pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit;
xReturn = pdTRUE;
}
/* 任务的通知状态标记为等待通知 */
pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION;
}
/* 退出临界区 */
taskEXIT_CRITICAL();
return xReturn;
}
#endif
关于任务通知的代码相对比较简单,都是通过直接赋值的形式,不存在像信号量那样的入队和出队过程。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)