FreeRTOS202212‐xTaskNotify()与xTaskNotifyIndexed()
xTaskNotify()与xTaskNotifyIndexed() 可以理解为 xTaskNotifyGive() 与 xTaskNotifyGiveIndexed() 的升级版本(开放了更多的参数传递接口),能够实现更复杂的功能比如模拟事件组及模拟队列等。
以上四个函数宏展开后皆是调用 xTaskGenericNotify() 。
xTaskNotify()功能为向一个任务通知数组的首个元素发送一条任务通知。
xTaskNotifyIndexed()相比于xTaskNotify()增加了索引参数,功能为向一个任务数组的指定索引位置发送一条任务通知。
任务通知具体执行过程由传入的eAction参数确定。
流程图按红色框图执行(switch分支选择取决与函数传入的eAction参数)
#define xTaskNotify( xTaskToNotify , ulValue , eAction ) \
xTaskGenericNotify ((xTaskToNotify),(tskDEFAULT_INDEX_TO_NOTIFY),(ulValue),(eAction),NULL)
#define xTaskNotifyIndexed( xTaskToNotify , uxIndexToNotify , ulValue , eAction ) \
xTaskGenericNotify ((xTaskToNotify),(uxIndexToNotify),(ulValue),(eAction),NULL)
/*‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐*/
#if (configUSE_TASK_NOTIFICATIONS == 1 )
BaseType_t xTaskGenericNotify ( TaskHandle_t xTaskToNotify , /*要通知的任务的句柄*/
UBaseType_t uxIndexToNotify , /*任务通知数组的索引值(任务通知相关数组下标,默认为0,使用通知数组第一个元素)*/
uint32_t ulValue , /*要传递的通知值*/
eNotifyAction eAction , /*指定通知的动作*/
uint32_t * pulPreviousNotificationValue ) /*指向存储上一个通知值的变量的指针,如果不需要回传上一个通知值,则可以传递NULL*/
{
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 ];
/*将负责接收直达任务通知的TCB(任务控制块)中的任务通知状态设置为已收到任务通知态*/
pxTCB ‐> ucNotifyState [ uxIndexToNotify ]= taskNOTIFICATION_RECEIVED ;
switch ( eAction )
{
/*模拟事件组:将通知值的某些位置1*/
case eSetBits :
pxTCB ‐> ulNotifiedValue [ uxIndexToNotify ]|= ulValue ;
break ;
/*模拟计数型信号量:将通知值+1*/
case eIncrement :
( pxTCB ‐> ulNotifiedValue [ uxIndexToNotify ])++;
break ;
/*模拟队列:覆写通知值*/
case eSetValueWithOverwrite :
pxTCB ‐> ulNotifiedValue [ uxIndexToNotify ]= ulValue ;
break ;
/*模拟队列:(不覆盖)写通知值*/
case eSetValueWithoutOverwrite :
/*如果不处于收到任务通知状态,说明通知值已被读走,可以写入*/
if ( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
{
pxTCB ‐> ulNotifiedValue [ uxIndexToNotify ]= ulValue ;
}
else
{
/*Thevaluecouldnotbewrittentothetask.*/
/*无法将值写入任务*/
xReturn = pdFAIL ; /*通知值未被读走,不能覆写*/
}
break ;
/*只将任务标记为等待接收通知状态,并不修改通知值*/
case eNoAction :
/*Thetaskisbeingnotifiedwithoutitsnotifyvaluebeingupdated.*/
/*正在通知任务,但未更新其通知值*/
break ;
default :
/*Shouldnotgethereifallenumsarehandled.Artificiallyforceanassertbytestingavaluethecompilercan'tassumeisconst.*/
/*如果处理了所有枚举,则不应到达此处。通过测试编译器不能假定为常量的值来人为地强制断言*/
configASSERT ( xTickCount ==( TickType_t ) 0 );
break ;
}
traceTASK_NOTIFY ( uxIndexToNotify );
/*Ifthetaskisintheblockedstatespecificallytowaitforanotificationthenunblockitnow.*/
/*如果任务处于阻止状态,专门用于等待通知,则立即取消阻止*/
if ( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) /*如果在此之前,任务因等待任务通知而被阻塞(该任务为等待通知状态),则现在解除阻塞*/
{
/*将任务从所在任务状态链表(延时/挂起链表)中移出*/
listREMOVE_ITEM (&( pxTCB ‐> xStateListItem ));
/*将任务添加到就绪任务链表中*/
prvAddTaskToReadyList ( pxTCB );
/*Thetaskshouldnothavebeenonaneventlist.*/
/*该任务不应出现在事件链表中*/
configASSERT ( listLIST_ITEM_CONTAINER (&( pxTCB ‐> xEventListItem ))== NULL );
#if (configUSE_TICKLESS_IDLE != 0 )
{
/*IfataskisblockedwaitingforanotificationthenxNextTaskUnblockTimemightbesettotheblockedtask'stimeouttime.
IfthetaskisunblockedforareasonotherthanatimeoutxNextTaskUnblockTimeisnormallyleftunchanged,becauseitwillautomaticallygetresettoanewvaluewhenthetickcountequalsxNextTaskUnblockTime.
Howeverifticklessidlingisuseditmightbemoreimportanttoentersleepmodeattheearliestpossibletime‐soresetxNextTaskUnblockTimeheretoensureitisupdatedattheearliestpossibletime.*/
/*如果任务被阻止等待通知,则xNextTaskUnblockTime可能会设置为被阻止任务的超时时间。
如果由于超时以外的原因取消阻止任务,xNextTaskUnblockTime通常保持不变,因为当勾号计数等于xNextTTaskUnblocktime时,它将自动重置为新值。
然而,如果使用了无障碍空转,那么尽早进入睡眠模式可能更重要——因此,请在此处重置xNextTaskUnblockTime,以确保它在尽早更新*/
prvResetNextTaskUnblockTime (); /*更新下一个解除阻塞的任务*/
}
#endif
/*有任务解除阻塞后,就应该判断是否需要进行任务切换*/
if ( pxTCB ‐> uxPriority > pxCurrentTCB ‐> uxPriority )
{
/*Thenotifiedtaskhasapriorityabovethecurrentlyexecutingtasksoayieldisrequired.*/
/*通知的任务的优先级高于当前执行的任务,因此需要切换*/
taskYIELD_IF_USING_PREEMPTION (); /*悬起PendSV中断,准备进行任务切换*/
}
else
{
mtCOVERAGE_TEST_MARKER ();
}
}
else
{
mtCOVERAGE_TEST_MARKER ();
}
}
taskEXIT_CRITICAL ();
return xReturn ;
}
#endif /*configUSE_TASK_NOTIFICATIONS*/
/*‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐*/
xTaskGenericNotify ()
定义任务块指针
TCB_t * pxTCB ;
初始化返回值为pdPASS
BaseType_t xReturn = pdPASS ;
定义原始通知状态变量
uint8_t ucOriginalNotifyState ;
任务块指针指向要通知的任务句柄
pxTCB = xTaskToNotify ;
*
pxTCB
进入临界段
taskENTER_CRITICAL ();
栈顶*pxTopOfStack
volatile(portSTACK_TYPE)uint32_t(StackType_t)
xStateListItem
xEventListItem
任务栈起始地址*pxStack
任务名称,仅用于调试,字符串形式pcTaskName[configMAX_TASK_NAME_LEN]
uint8_t(char)
ulNotifiedValue [configTASK_NOTIFICATION_ARRAY_ENTRIES]
任务通知功能
volatileuint32_t
ucNotifyState [configTASK_NOTIFICATION_ARRAY_ENTRIES]
任务通知功能
volatileuint8_t
ucStaticallyAllocated
支持静态&动态内存
uint8_t
*pxTopOfStack
xLIST_ITEM(ListItem_t)
xStateListItem
xLIST_ITEM(ListItem_t)
xEventListItem
UBaseType_t(unsignedlong)
优先级
uxPriority=
*pxStack
volatile(portSTACK_TYPE)uint32_t(StackType_t)
pcTaskName [configMAX_TASK_NAME_LEN]
xItemValue
*pxNext
*pxPrevious
*pvOwner
*pvContainer
内核对象
链表对象
void
void
structxLIST_ITEM
structxLIST_ITEM
uint32_t/uint16_t(TickType_t)
节点排序值
tskTaskControlBlock(tskTCB)(TCB_t)
xMPUSettings
#if(portUSING_MPU_WRAPPERS==1)
xItemValue
*pxNext
*pxPrevious
*pvOwner
*pvContainer
内核对象
链表对象
void
void
structxLIST_ITEM
structxLIST_ITEM
uint32_t/uint16_t(TickType_t)
节点排序值
指向堆栈的最高有效地址
volatile(portSTACK_TYPE)uint32_t(StackType_t)
*pxEndOfStack
#if(portSTACK_GROWTH>0)
UBaseType_t(unsignedlong)
uxCriticalNesting
portCRITICAL_NESTING_IN_TCB==1
UBaseType_t(unsignedlong)
uxTCBNumber
#if(configUSE_TRACE_FACILITY==1)
UBaseType_t(unsignedlong)
uxTaskNumber
#if(configUSE_TRACE_FACILITY==1)
UBaseType_t(unsignedlong)
uxBasePriority
#if(configUSE_MUTEXES==1)
UBaseType_t(unsignedlong)
xxx
uxMutexesHeld
#if(configUSE_MUTEXES==1)
函数指针
(*)pxTaskTag
#if(configUSE_APPLICATION_TASK_TAG==1)
typedefBaseType_t(*TaskHookFunction_t)(void*)
xxx
*pvThreadLocalStoragePointers[configNUM_THREAD_LOCAL_STORAGE_POINTERS]
#if(configNUM_THREAD_LOCAL_STORAGE_POINTERS>0)
void
存储任务在“正在运行”状态下花费的时间
ulRunTimeCounter
#if(configGENERATE_RUN_TIME_STATS==1)
uint32_t
xxx
configTLS_BLOCK_TYPE xTLSBlock; /*
为任务分配一个
Newlibreent
结构体变量。
Newlib
是一个
C
库函数,并非
FreeRTOS
维护,
FreeRTOS
也不对使用结果负责。如果用户使用
Newlib
,必须熟知
Newlib
的细节
*/
#if((configUSE_NEWLIB_REENTRANT==1)||(configUSE_C_RUNTIME_TLS_SUPPORT==1))
struct_reent
xNewLib_reent
#if(configUSE_TASK_NOTIFICATIONS==1)
#if(configUSE_TASK_NOTIFICATIONS==1)
#if(tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE!=0)
ucDelayAborted
xxx
uint8_t
#if(INCLUDE_xTaskAbortDelay==1)
ulRLAR
ulRBAR
structMPURegionSettings(MPURegionSettings_t)
uint32_t
structMPU_SETTINGS(xMPU_SETTINGS)
ulMAIR0
uint32_t
uint32_t
xRegionsSettings[portTOTAL_NUM_REGIONS]
指向链表下一个节点
指向链表前一个节点
指向链表下一个节点
指向链表前一个节点
临界区嵌套深度
存储每次创建TCB时递增的数字
存储专门供第三方跟踪代码使用的数字
上次分配给任务的优先级‐由优先级继承机制使用
iTaskErrno
xxx
int
#if(configUSE_POSIX_ERRNO==1)
判断是否需要通知前的通知值 ?
if ( pulPreviousNotificationValue != NULL )
YES
NO
获取发送通知前的通知值
* pulPreviousNotificationValue = pxTCB ‐> ulNotifiedValue [ uxIndexToNotify ];
记录发送通知前的任务通知状态
ucOriginalNotifyState = pxTCB ‐> ucNotifyState [ uxIndexToNotify ];
接收通知的TCB中的任务通知状态设置为已收到任务通知态
pxTCB ‐> ucNotifyState [ uxIndexToNotify ]= taskNOTIFICATION_RECEIVED ;
选择条件
switch ( eAction )
模拟事件组:
case eSetBits :
No
Yes
将通知值的某些位置1
pxTCB ‐> ulNotifiedValue [ uxIndexToNotify ]|= ulValue ;
模拟计数型信号量:
case eIncrement :
No
Yes
break ;
将通知值+1
( pxTCB ‐> ulNotifiedValue [ uxIndexToNotify ])++;
break ;
模拟可覆盖队列:
case eSetValueWithOverwrite :
No
Yes
覆写通知值
pxTCB ‐> ulNotifiedValue [ uxIndexToNotify ]= ulValue ;
break ;
模拟不可覆盖队列:
case eSetValueWithoutOverwrite :
No
Yes
写入通知值
pxTCB ‐> ulNotifiedValue [ uxIndexToNotify ]= ulValue ;
break ;
不处于收到任务通知状态?
if ( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED )
No
Yes
复位返回值(未能写入)
xReturn = pdFAIL ;
不更新通知值:
case eNoAction :
No
Yes
break ;
默认 :
default :
Yes
break ;
任务正在等待此通知
if ( ucOriginalNotifyState == taskWAITING_NOTIFICATION )
No
Yes
将任务从延时/挂起链表中移出
listREMOVE_ITEM (&( pxTCB ‐> xStateListItem ));
将任务插入到就绪链表中
prvAddTaskToReadyList ( pxTCB );
#启用低功耗模式
#if (configUSE_TICKLESS_IDLE != 0 )
No
Yes
重置延时链表下一个任务的解锁时间
prvResetNextTaskUnblockTime ();
此任务优先级高于当前任务优先级
if ( pxTCB ‐> uxPriority > pxCurrentTCB ‐> uxPriority )
No
Yes
悬起PendSV中断,准备进行任务切换
taskYIELD_IF_USING_PREEMPTION ();
退出临界区
taskEXIT_CRITICAL ();
结束返回
return xReturn ;
#define xTaskNotify( xTaskToNotify , ulValue , eAction ) \
xTaskGenericNotify ((xTaskToNotify),(tskDEFAULT_INDEX_TO_NOTIFY),(ulValue),(eAction),NULL)
#define xTaskNotifyIndexed( xTaskToNotify , uxIndexToNotify , ulValue , eAction ) \
xTaskGenericNotify ((xTaskToNotify),(uxIndexToNotify),(ulValue),(eAction),NULL)
BaseType_t xTaskGenericNotify ( TaskHandle_t xTaskToNotify , /*要通知的任务的句柄*/
UBaseType_t uxIndexToNotify , /*任务的指定通知(任务通知相关数组下标,默认为0,使用数组第一个元素当做通知)*/
uint32_t ulValue , /*要传递的通知值*/
eNotifyAction eAction , /*指定通知的动作*/
uint32_t * pulPreviousNotificationValue ) /*指向存储上一个通知值的变量的指针,如果不需要回传上一个通知值,则可以传递NULL*/
功能
xTaskNotify()用于将事件直接发送到RTOS任务,并可能取消该任务的阻塞状态.
可选择以下列方式更新接收任务的通知值:
将32位数字写入通知值
添加一个(递增)通知值
设置通知值中的一个或多个位
保持通知值不变
参数
xTaskToNotify
正被通知的RTOS任务的句柄,且该任务的通知值正在递增。
要获取任务句柄,请使用xTaskCreate()创建任务并使用pxCreatedTask参数,或使用返回值创建任务xTaskCreateStatic()并存储该值,或在调用
xTaskGetHandle()中使用任务的名称。
当前执行的RTOS任务的句柄通过以下方式由xTaskGetCurrentTaskHandle()API函数返回。
uxIndexToNotify
目标任务通知值数组中的索引,通知值将发送给该索引。
uxIndexToNotify必须小于configTASK_NOTIFICATION_ARRAY_ENTRIES。
xTaskNotify()没有此参数,并且总是向索引0发送通知。
ulValue
用于更新目标任务的通知值。请参阅下面eAction参数的说明。
eAction
一种枚举类型,可以采用下表中记录的值之一来执行相关操作。
eNoAction
目标任务接收事件,但其通知值未更新。在这种情况下,不使用ulValue。
eSetBits
目标任务的通知值使用ulValue按位或运算。
例如,如果ulValue设置为0x01,则将在目标任务的通知值中设置位0。
如果ulValue设置为0x04,则将在目标任务的通知值中设置位2。
通过这种方式,RTOS任务通知机制可以用作事件组的轻量级替代方案。
eIncrement 目标任务的通知值自增1,使得调用xTaskNotify()相当于调用xTaskNotifyGive()。在这种情况下,不使用ulValue。
eSetValueWithOverwrite
目标任务的通知值无条件设置为ulValue。因此,RTOS任务通知机制可用作xQueueOverwrite()的轻量级替代方案。
eSetValueWithoutOrwrite
如果目标任务没有挂起的通知,则其通知值将设置为ulValue。
如果目标任务已经有挂起的通知,则不会更新其通知值,因为这样做会覆盖之前使用的值。
在这种情况下,调用xTaskNotify()失败,返回pdFALSE。
因此,RTOS任务通知机制可在长度为1的队列上用作xQueueSend()在长度为的轻量级替代方案。
pulPreviousNotificationValue= NULL
指向存储上一个通知值的变量的指针,如果不需要回传上一个通知值,则可以传递NULL
返回值
除了eAction被设置为eSetValueWithoutOverwrite,且目标任务的通知值因目标任务已有挂起的通知而无法更新之外,在所有情况下均返回pdPASS。
[如果你使用RTOS任务通知来实现二进制或计数信号量类型行为,请使用更简单的xTaskNotifyGive()API函数,而不是xTaskNotify()]
每个任务都有一个“任务通知”数组(或简称为“通知”),每个通知都包含状态和一个32位值。直达任务通知是直接发送至任务的事件,可以取消阻塞接收任务,并可以选择通过多种方式更新接收任务的某个通知值。
例如,通知可覆盖接收任务的通知值中的一个,或仅设置接收任务的通知值中的一个或多个比特位。
xTaskNotify()用于将事件直接发送到RTOS任务并可能取消该任务的阻塞状态,
可选择以下列方式更新接收任务的通知值:
将32位数字写入通知值
添加一个(递增)通知值
设置通知值中的一个或多个位
保持通知值不变
xTaskNotify()和xTaskNotifyIndexed()是等效的函数‐唯一的区别是xTaskNotifyIndexed()可以对数组内的任何任务通知进行操作,而xTaskNotify()始终对数组索引0处的任务通知进行操作。
不得从中断服务例程(ISR)调用此函数。使用xTaskNotifyFromISR()代替。
configUSE_TASK_NOTIFICATIONS必须在FreeRTOSConfig.h中设置为1(或保留为未定义)以使这些函数可用。常量configTASK_NOTIFICATION_ARRAY_ENTRIES设置任务通知的每个任务数组中的索引数量。
向后兼容性信息:
在FreeRTOSV10.4.0之前,每个任务有一个单一的“通知值”,且所有任务通知API函数都在该值上运行。用通知值的数组更换单个通知值需要新的API函数集,该函数集应能在数组内处理数组中的特定通知。
xTaskNotify()是原始API函数,并且通过始终对数组中索引0处的通知值进行操作来保持向后兼容。调用xTaskNotify()相当于调用xTaskNotifyIndexed(),其uxIndexToNotify参数设置为0。