注:本文介绍任务通知的基础知识,具体源代码分析见《》
每一个RTOS任务都有一个32位的通知值,任务创建时,这个值被初始化为0。RTOS任务通知相当于直接向任务发送一个事件,接收到通知的任务能够解除堵塞状态,前提是这个堵塞事件是因等待通知而引起的。
发送通知的同一时候。也能够可选的改变接收任务的通知值。
能够通过下列方法向接收任务更新通知:
- 不覆盖接收任务的通知值
- 覆盖接收任务的通知值
- 设置接收任务通知值的某些位
- 添加接收任务的通知值
相对于用前必须分别创建队列、二进制信号量、计数信号量或事件组的情况。使用任务通知显然更灵活。更好的是,相比于使用信号量解除任务堵塞。使用任务通知能够快45%、使用更少的RAM(使用GCC编译器,-o2优化级别)。
使用API函数xTaskNotify()和xTaskNotifyGive()(中断保护等价函数为xTaskNotifyFromISR()和vTaskNotifyGiveFromISR())发送通知,在接收RTOS任务调用API函数xTaskNotifyWait()或ulTaskNotifyTake()之前。这个通知都被保持着。假设接收RTOS任务已经由于等待通知而进入堵塞状态。则接收到通知后任务解除堵塞并清除通知。
RTOS任务通知功能默认是使能的。能够通过在文件FreeRTOSConfig.h中设置宏configUSE_TASK_NOTIFICATIONS为0来禁止这个功能。禁止后每一个任务节省8字节内存。
尽管RTOS任务通知速度更快而且占用内存更少。但它也有一些限制:
- 仅仅能有一个任务接收通知事件。
- 接收通知的任务能够由于等待通知而进入堵塞状态,可是发送通知的任务即便不能马上完毕通知发送也不能进入堵塞状态。
1.发送通知-方法1
1.1函数描写叙述
BaseType_t xTaskNotify( TaskHandle_txTaskToNotify, uint32_t ulValue, eNotifyAction eAction);
向指定任务发送指定的通知值。假设打算使用RTOS任务通知实现轻量级的二进制或计数信号量,推荐使用API函数xTaskNotifyGive()来取代本函数。
此函数不能够在中断服务例程中调用,中断保护等价函数为xTaskNotifyFromISR()。
1.2參数描写叙述
- xTaskToNotify:被通知的任务句柄。
- ulValue:通知更新值
- eAction:枚举类型,指明更新通知值的方法
枚举变量成员以及作用例如以下表所看到的。
1.3返回值
eAction为eSetValueWithoutOverwrite时,假设被通知任务还没取走上一个通知。又接收到了一个通知,则这次通知值未能更新并返回pdFALSE,否则返回pdPASS。
2.发送通知-方法2
2.1函数描写叙述
BaseType_t xTaskNotifyGive(TaskHandle_t xTaskToNotify );
事实上这是一个宏。本质上相当于xTaskNotify( ( xTaskToNotify ), ( 0 ), eIncrement )。能够使用该API函数取代二进制或计数信号量,但速度更快。在这样的情况下。应该使用API函数ulTaskNotifyTake()来等待通知。而不应该使用API函数xTaskNotifyWait()。
此函数不能够在中断服务例程中调用,中断保护等价函数为vTaskNotifyGiveFromISR()。
2.2參数描写叙述
- xTaskToNotify:被通知的任务句柄。
2.3使用方法举例
staticvoid prvTask1( void *pvParameters );staticvoid prvTask2( void *pvParameters ); /* 保存任务句柄 */staticTaskHandle_t xTask1 = NULL, xTask2 = NULL; /* 创建两个任务,它们之间重复发送通知,启动RTOS调度器*/voidmain( void ){ xTaskCreate( prvTask1, "Task1",200, NULL, tskIDLE_PRIORITY, &xTask1 ); xTaskCreate( prvTask2, "Task2",200, NULL, tskIDLE_PRIORITY, &xTask2 ); vTaskStartScheduler();}/*-----------------------------------------------------------*/ staticvoid prvTask1( void *pvParameters ){ for( ;; ) { /*向prvTask2(),发送通知,使其解除堵塞状态 */ xTaskNotifyGive( xTask2 ); /* 等待prvTask2()的通知,进入堵塞 */ ulTaskNotifyTake( pdTRUE, portMAX_DELAY); }}/*-----------------------------------------------------------*/ staticvoid prvTask2( void *pvParameters ){ for( ;; ) { /* 等待prvTask1()的通知。进入堵塞 */ ulTaskNotifyTake( pdTRUE, portMAX_DELAY); /*向prvTask1(),发送通知,使其解除堵塞状态 */ xTaskNotifyGive( xTask1 ); }}
3.获取通知
3.1函数描写叙述
uint32_t ulTaskNotifyTake( BaseType_txClearCountOnExit, TickType_txTicksToWait );
ulTaskNotifyTake()是专门为使用更轻量级更快的方法来取代二进制或计数信号量而量身打造的。
FreeRTOS获取信号量的API函数为xSemaphoreTake(),能够使用ulTaskNotifyTake()函数等价取代。
当一个任务使用通知值来实现二进制或计数信号量时,其他任务或者中断要使用API函数xTaskNotifyGive()或者使用參数eAction为eIncrement的API函数xTaskNotify()。推荐使用xTaskNotifyGive()函数(事实上是个宏,我们这里把它看作一个API函数)。另外须要注意的是,假设在中断中使用。要使用它们的中断保护等价函数:vTaskNotifyGiveFromISR()和xTaskNotifyFromISR()。
API函数xTaskNotifyTake()有两种方法处理任务的通知值。一种方法是在函数退出时将通知值清零,这样的方法适用于实现二进制信号量;第二种方法是在函数退出时将通知值减1。这样的方法适用于实现计数信号量。
假设RTOS任务的通知值为0,使用xTaskNotifyTake()能够可选的使任务进入堵塞状态,直到该任务的通知值不为0。进入堵塞的任务不消耗CPU时间。
3.2參数描写叙述
- xClearCountOnExit:假设该參数设置为pdFALSE,则API函数xTaskNotifyTake()退出前,将任务的通知值减1;假设该參数设置为pdTRUE。则API函数xTaskNotifyTake()退出前,将任务通知值清零。
- xTicksToWait:因等待通知而进入堵塞状态的最大时间。时间单位为系统节拍周期。
宏pdMS_TO_TICKS用于将指定的毫秒时间转化为对应的系统节拍数。
3.3返回值
返回任务的当前通知值。为0或者为调用API函数xTaskNotifyTake()之前的通知值减1。
3.4使用方法举例
/* 中断处理程序。*/voidvANInterruptHandler( void ){BaseType_txHigherPriorityTaskWoken; prvClearInterruptSource(); /* xHigherPriorityTaskWoken必须被初始化为pdFALSE。假设调用vTaskNotifyGiveFromISR()会解除vHandlingTask任务的堵塞状态,而且vHandlingTask任务的优先级高于当前处于执行状态的任务,则xHigherPriorityTaskWoken将会自己主动被设置为pdTRUE。*/ xHigherPriorityTaskWoken = pdFALSE; /*向一个任务发送通知,xHandlingTask是该任务的句柄。
*/ vTaskNotifyGiveFromISR( xHandlingTask,&xHigherPriorityTaskWoken ); /* 假设xHigherPriorityTaskWoken为pdTRUE。则强制上下文切换。这个宏的实现取决于移植层。可能会调用portEND_SWITCHING_ISR */ portYIELD_FROM_ISR(xHigherPriorityTaskWoken ); } /*---------------------------------------------------------------------------------------------------*/ /* 一个由于等待通知而堵塞的任务。
*/ voidvHandlingTask( void *pvParameters ) { BaseType_txEvent; for( ;; ) { /*等待通知。无限期堵塞。
參数pdTRUE表示函数退出前会清零通知值。这显然是用于替代二进制信号量的使用方法。
须要注意的是,真实的程序一般不会无限期堵塞。*/ ulTaskNotifyTake( pdTRUE, portMAX_DELAY); /* 当处理全然部事件后,仍然等待下一个通知*/ do { xEvent = xQueryPeripheral(); if( xEvent != NO_MORE_EVENTS ) { vProcessPeripheralEvent( xEvent); } } while( xEvent != NO_MORE_EVENTS ); } }
4.等待通知
4.1函数描写叙述
BaseType_t xTaskNotifyWait( uint32_tulBitsToClearOnEntry, uint32_tulBitsToClearOnExit, uint32_t*pulNotificationValue, TickType_txTicksToWait );
假设打算使用RTOS任务通知实现轻量级的二进制或计数信号量,推荐使用API函数ulTaskNotifyTake()来取代本函数。
4.2參数描写叙述
- ulBitsToClearOnEntry:在使用通知之前。先将任务的通知值与參数ulBitsToClearOnEntry的按位取反值按位与操作。
设置參数ulBitsToClearOnEntry为0xFFFFFFFF(ULONG_MAX),表示清零任务通知值。
- ulBitsToClearOnExit:在函数xTaskNotifyWait()退出前,将任务的通知值与參数ulBitsToClearOn的按位取反值按位与操作。
设置參数ulBitsToClearOnExit为0xFFFFFFFF(ULONG_MAX),表示清零任务通知值。
- pulNotificationValue:用于向外回传任务的通知值。这个通知值在參数ulBitsToClearOnExit起作用前将通知值复制到*pulNotificationValue中。
假设不须要返回任务的通知值。这里设置成NULL。
- xTicksToWait:因等待通知而进入堵塞状态的最大时间。
时间单位为系统节拍周期。
宏pdMS_TO_TICKS用于将指定的毫秒时间转化为对应的系统节拍数。
4.3返回值
假设接收到通知,返回pdTRUE,假设API函数xTaskNotifyWait()等待超时。返回pdFALSE。
4.4使用方法举例
/*这个任务使用任务通知值的位来传递不同的事件,这在某些情况下能够取代事件组。*/voidvAnEventProcessingTask( void *pvParameters ){uint32_tulNotifiedValue; for( ;; ) { /*等待通知,无限期堵塞(没有超时,所以不用检查函数返回值)。其他任务或者中断设置的通知值中的不同位表示不同的事件。參数0x00表示使用通知前不清除任务的通知值位,參数ULONG_MAX 表示函数xTaskNotifyWait()退出前将任务通知值设置为0*/ xTaskNotifyWait( 0x00, ULONG_MAX,&ulNotifiedValue, portMAX_DELAY ); /*依据通知值处理事件*/ if( ( ulNotifiedValue & 0x01 ) != 0) { prvProcessBit0Event(); } if( ( ulNotifiedValue & 0x02 ) != 0) { prvProcessBit1Event(); } if( ( ulNotifiedValue & 0x04 ) != 0) { prvProcessBit2Event(); } /* ……*/ }}
5.任务通知并查询
5.1函数描写叙述
BaseType_t xTaskNotifyAndQuery(TaskHandle_t xTaskToNotify, uint32_tulValue, eNotifyActioneAction, uint32_t*pulPreviousNotifyValue );
此函数与任务通知API函数xTaskNotify()很像。仅仅只是此函数具有一个附加參数,用来回传任务当前的通知值,然后依据參数ulValue和eAction更新任务的通知值。
此函数不能在中断服务例程中使用,在中断服务例程中使用xTaskNotifyAndQueryFromISR()函数。
5.2參数描写叙述
- xTaskToNotify:被通知的任务句柄。
- ulValue:通知更新值
- eAction:枚举类型,指明更新通知值的方法,枚举变量成员以及作用见xTaskNotify()一节。
- pulPreviousNotifyValue:回传未被更新的任务通知值。
假设不须要回传未被更新的任务通知值,这里设置为NULL,这样就等价于调用xTaskNotify()函数。
5.3返回值
參数eAction为eSetValueWithoutOverwrite时。假设被通知任务还没取走上一个通知。又接收到了一个通知,则这次通知值未能更新并返回pdFALSE,否则返回pdPASS。