FreeRTOS任务通知
FreeRTOS 新增了任务通知(Task Notifictions)这个功能,可以使用任务通知来代替信号量、消息队列、事件标志组等这些东西。使用任务通知的话效率会更高,任务通知在 FreeRTOS 中是一个可选的功能,
使用队列、信号量、事件标志组时都需另外创建一个结构体,通过中间的结构体进行间接通信!过程如下图所示:
要使用任务通知的话就需要将宏configUSE_TASK_NOTIFICATIONS 定义为 1。FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue就是这个通知值。过程如下图所示:
任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态。也可以更新接收任务的任务通知值,任务通知可以通过如下方法更新接收任务的通知值:
● 不覆盖接收任务的通知值(如果上次发送给接收任务的通知还没被处理)。
● 覆盖接收任务的通知值。
● 更新接收任务通知值的一个或多个 bit。
● 增加接收任务的通知值。
所以根据以上四种情况可以在一些情况下合理替代队列、二值信号量、计数型信号量、事件标志组。任务通知虽然可以提高速度,并且减少RAM的使用,并且任务通知是有使用限制的:
1:RTOS的任务通知只能有一个接收任务。
2:接收任务可以因为接受任务通知而进入阻塞态,但是发送任务不会因为任务通知发送失败而阻塞。
上图为任务结构体存储的部分内容,从上图我们可以RTOS中默认数组的大小就是1,也可以看到一个是 uint32_t 类型,用来表示通知值,一个是 uint8_t 类型,用来表示通知状态。
任务通知值
任务通知值的更新方式有多种类型:
1 计数值(数值累加,类似与信号量)
2 相应位置1(类似事件标志组)
3 任意数值(支持覆写和不覆写,类似队列)
任务通知状态
任务通知发送函数
任务通知发送函数有以下6个:
此函数用于发送任务通知,此函数发送任务通知的时候带有通知值,此函数是个宏,真正执行的函数
xTaskGenericNotify()
函数原型如下:
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction )
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
ulValue: 任务通知值。
eAction: 任务通知更新的方法,eNotifyAction 是个枚举类型,在文件 task.h 中有如下定义:
返回值:
pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有
更新成功就返回 pdFAIL。
pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。
eAction如下图枚举的类型一样,依次为:
eSetBits更新指定的Bit,eIncrement通知值加1,eSetValueWithOverwrite,覆写的方式更新通知值。
eSetValueWithoutOverwrite,不覆写通知值。
函数 xTaskNotifyFromISR()
此函数用于发送任务通知,是函数 xTaskNotify()的中断版本,此函数是个宏,真正执行的是函数 xTaskGenericNotifyFromISR(),此函数原型如下:
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction,
BaseType_t * pxHigherPriorityTaskWoken );
参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
ulValue: 任务通知值。
eAction: 任务通知更新的方法。
pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,用户不用
进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务
函数之前一定要进行一次任务切换。
返回值:
pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有
更新成功就返回 pdFAIL。
pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。
函数 xTaskNotifyGive()
发送任务通知,相对于函数 xTaskNotify(),此函数发送任务通知的时候不带有通知值。此
函数只是将任务通知值简单的加一,此函数是个宏,真正执行的是函数 xTaskGenericNotify(),
此函数原型如下:
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
返回值:
pdPASS: 此函数只会返回 pdPASS。
函数 vTaskNotifyGiveFromISR()
此函数为 xTaskNotifyGive()的中断版本,用在中断服务函数中,函数原型如下:
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t * pxHigherPriorityTaskWoken );
参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动
设置的,用户不用进行设置,用户只需要提供一个变量来保存这
个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之
前一定要进行一次任务切换。
返回值:
无。
函数 xTaskNotifyAndQuery()
此函数和 xTaskNotify()很类似,此函数比 xTaskNotify()多一个参数,此参数用来保存更新
前的通知值。此函数是个宏,真正执行的是函数 xTaskGenericNotify(),此函数原型如下:
BaseType_t xTaskNotifyAndQuery ( TaskHandle_t xTaskToNotify,
uint32_t ulValue,
eNotifyAction eAction
uint32_t * pulPreviousNotificationValue);
参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
ulValue: 任务通知值。
eAction: 任务通知更新的方法。
pulPreviousNotificationValue:用来保存更新前的任务通知值。
返回值:
pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有
更新成功就返回 pdFAIL。
pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。
函数 xTaskNotifyAndQueryFromISR()
此函数为 xTaskNorityAndQuery()的中断版本,用在中断服务函数中。此函数同样为宏,真
正执行的是函数 xTaskGenericNotifyFromISR(),此函数的原型如下:
BaseType_t xTaskNotifyAndQueryFromISR ( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t * pulPreviousNotificationValueBaseType_t * pxHigherPriorityTaskWoken );
参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
ulValue: 任务通知值。
eAction: 任务通知更新的方法。
pulPreviousNotificationValue:用来保存更新前的任务通知值。
pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动设置的,
用户不用进行设置,用户只需要提供一个变量来保存这个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之
前一定要进行一次任务切换。
返回值:
pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有
更新成功就返回 pdFAIL。
pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。
任务通知通用发送函数源码
任务级任务通知通用发送函数在上面中有3 个任务级任务通知发送函数:xTaskNotify()、xTaskNotifyGive()
和 xTaskNotifyAndQuery(),这三个函数最终调用的都是函数 xTaskGenericNotify()!此函数在文件 tasks.c 中有如下定义,缩减后的函数如下:
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 ) (1){//如果提供了一个非NULL指针来接收更新前的通知值,则将当前的通知值保存到该位置*pulPreviousNotificationValue = pxTCB->ulNotifiedValue; (2)}//保存任务通知状态,因为下面会修改这个状态,//后面我们要根据这个状态来确定是否将任务从阻塞态解除。ucOriginalNotifyState = pxTCB->ucNotifyState; (3)pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; (4)switch( eAction ){case eSetBits : (5)pxTCB->ulNotifiedValue |= ulValue;break;case eIncrement : (6)( pxTCB->ulNotifiedValue )++;break;case eSetValueWithOverwrite: (7)pxTCB->ulNotifiedValue = ulValue;break;case eSetValueWithoutOverwrite : (8)if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ){pxTCB->ulNotifiedValue = ulValue;}else{xReturn = pdFAIL;}break;case eNoAction:break;}traceTASK_NOTIFY();//如果任务因为等待任务通知而进入阻塞态的话就需要解除阻塞if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) (9){( void ) uxListRemove( &( pxTCB->xStateListItem ) ); (10)prvAddTaskToReadyList( pxTCB ); (11)/******************************************************************//********************省略相关的条件编译代码************************//******************************************************************/if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) (12){//解除阻塞的任务优先级比当前运行的任务优先级高,所以需要进行//任务切换。taskYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();return xReturn; (13)
}
中断级任务通知发送函数
中 断 级 任 务 通 知 发 送 函 数 也 有 三 个 , 分 别 为 : xTaskNotifyFromISR() 、
xTaskNotifyAndQueryFromISR()和 vTaskNotifyGiveFromISR()。其中函数 xTaskNotifyFromISR()
和 xTaskNotifyAndQueryFromISR()最终调用的都是函数 xTaskGenericNotifyFromISR(),此函数
的原型如下:
BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t * pulPreviousNotificationValue, BaseType_t * pxHigherPriorityTaskWoken )参数:
xTaskToNotify: 任务句柄,指定任务通知是发送给哪个任务的。
ulValue: 任务通知值。
eAction: 任务通知更新的方法。
pulPreviousNotificationValue:用来保存更新前的任务通知值。
pxHigherPriorityTaskWoken: 记退出此函数以后是否进行任务切换,这个变量的值函数会自动
设置的,用户不用进行设置,用户只需要提供一个变量来保存这
个值就行了。当此值为 pdTRUE 的时候在退出中断服务函数之
前一定要进行一次任务切换。
返回值:
pdFAIL: 当参数 eAction 设置为 eSetValueWithoutOverwrite 的时候,如果任务通知值没有
更新成功就返回 pdFAIL。
pdPASS: eAction 设置为其他选项的时候统一返回 pdPASS。
获取任务通知
获取任务通知的函数有两个:
ulTaskNotifyTake()获取任务通知,可以设置在退出此函数的时候将任务通知值清零或者减一。
当任务通知用作二值信号量或者计数信号量的时候使用此函数来获取信号量。
xTaskNotifyWait()等待任务通知,比 ulTaskNotifyTak()更为强大,全功能版任务通
知获取函数。
函数 ulTaskNotifyTake()
此函数为获取任务通知函数,当任务通知用作二值信号量或者计数型信号量的时候可以使
用此函数来获取信号量,函数原型如下:
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
参数:
xClearCountOnExit: 参数为 pdFALSE 的话在退出函数 ulTaskNotifyTake()的时候任务通知值
减一,类似计数型信号量。当此参数为 pdTRUE 的话在退出函数的时候
任务任务通知值清零,类似二值信号量。
xTickToWait: 阻塞时间。
返回值:
任何值 : 任务通知值减少或者清零之前的值。
函数 xTaskNotifyWait()
此函数也是用来获取任务通知的,不过此函数比 ulTaskNotifyTake()更为强大,不管任务通知用作二值信号量、计数型信号量、队列和事件标志组中的哪一种,都可以使用此函数来获取任务通知。但是当任务通知用作位置信号量和计数型信号量的时候推荐使用函数ulTaskNotifyTake()。此函数原型如下:
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,uint32_t ulBitsToClearOnExit, uint32_t * pulNotificationValue, TickType_t xTicksToWait );参数:
ulBitsToClearOnEntry:当没有接收到任务通知的时候将任务通知值与此参数的取反值进行按
位与运算,当此参数为 0xffffffff 或者 ULONG_MAX 的时候就会将任务
通知值清零。
ulBitsToClearOnExit:如果接收到了任务通知,在做完相应的处理退出函数之前将任务通知值
与此参数的取反值进行按位与运算,当此参数为 0xffffffff 或者ULONG_MAX 的时候就会将任务通知值清零。
pulNotificationValue:此参数用来保存任务通知值。
xTickToWait: 阻塞时间。
返回值:
pdTRUE: 获取到了任务通知。
pdFALSE: 任务通知获取失败。