[转]freeRTOS 任务通知 - 为民除害 - 博客园
一、任务通知(Task Notifictions)
可以代替信号量、消息队列、事件标志组等这些东西。使用任务通知的话效率会更高!
配置宏 configUSE_TASK_NOTIFICATIONS 打开任务通知。FreeRTOS 的每个任务都有一个 32 位的通知值,TCB中的成员变量 ulNotifiedValue就是这个通知值。
#if( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue;
volatile uint8_t ucNotifyState;
#endif
任务通知的状态:
/* Values that can be assigned to the ucNotifyState member of the TCB. */
#define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 )
#define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 )
#define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 )
任务通知虽然可以提高速度,并且减少 RAM 的使用,但是任务通知也是有使用限制的:
● FreeRTOS 的任务通知只能有一个接收任务,其实大多数的应用都是这种情况。
● 接收任务可以因为接收任务通知而进入阻塞态,但是发送任务不会因为任务通知发送失败而阻塞。
二、发生任务通知
任务通知更新方法 eNotifyAction:
eNoAction = 0,
eSetBits, //更新指定的 bit
eIncrement, //通知值加一
eSetValueWithOverwrite, //覆写的方式更新通知值
eSetValueWithoutOverwrite //不覆写通知值
参数:任务通知值Value,任务通知新方法eAction。
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction )BaseType_t xTaskNotifyFromISR( ... , BaseType_t * pxHigherPriorityTaskWoken );
参数:没有任务通知值Value,只是简单的把任务通知值加一。BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t * pxHigherPriorityTaskWoken );参数:保存更新之前的任务通知值PreNotifyValueBaseType_t xTaskNotifyAndQuery ( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t * pulPreviousNotificationValue);BaseType_t xTaskNotifyAndQueryFromISR ( ... , BaseType_t * pxHigherPriorityTaskWoken );返回值在使用SetValueWithoutOverwrite写入方式时,可能返回fail
其他时候都是pass(true).
以上API真正实现的函数:xTaskGenericNotify()
xTaskGenericNotifyFromISR()
任务级通知发送:
1 #if( configUSE_TASK_NOTIFICATIONS == 1 ) 2 3 BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) 4 { 5 TCB_t * pxTCB; 6 BaseType_t xReturn = pdPASS; 7 uint8_t ucOriginalNotifyState; 8 9 configASSERT( xTaskToNotify ); 10 pxTCB = ( TCB_t * ) xTaskToNotify; 11 12 taskENTER_CRITICAL(); 13 { 14 if( pulPreviousNotificationValue != NULL ) 15 { 16 *pulPreviousNotificationValue = pxTCB->ulNotifiedValue; 保存原来的任务通知值。 17 } 18
保存任务通知值状态,函数会修改这个状态,也会根据这个状态来确定是否将任务从阻塞状态解除。 19 ucOriginalNotifyState = pxTCB->ucNotifyState; 20 21 pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; 更新任务通知状态 22 23 switch( eAction ) 24 { 25 case eSetBits : 26 pxTCB->ulNotifiedValue |= ulValue; 27 break; 28 29 case eIncrement : 30 ( pxTCB->ulNotifiedValue )++; 31 break; 32 33 case eSetValueWithOverwrite : 34 pxTCB->ulNotifiedValue = ulValue; 35 break; 36 37 case eSetValueWithoutOverwrite :
这种情况需要判断原来的任务通知值是否被处理,如已被处理就可以更新,未被处理返回错误。 38 if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) 39 { 40 pxTCB->ulNotifiedValue = ulValue; 41 } 42 else 43 { 44 /* The value could not be written to the task. */ 45 xReturn = pdFAIL; 46 } 47 break; 48 49 case eNoAction: 50 /* The task is being notified without its notify value being 51 updated. */ 52 break; 53 } 54 55 traceTASK_NOTIFY(); 56 57 /* If the task is in the blocked state specifically to wait for a 58 notification then unblock it now. */
如果任务因为等待任务通知阻塞了的话,就需要解除阻塞。(这个状态是任务通知获取里修改的!见下边) 59 if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) 60 { 61 ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); 移除阻塞到的那个列表 62 prvAddTaskToReadyList( pxTCB ); 加入就绪列表 63 64 /* The task should not have been on an event list. */ 65 configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); 66 67 #if( configUSE_TICKLESS_IDLE != 0 ) 68 { 69 /* If a task is blocked waiting for a notification then 70 xNextTaskUnblockTime might be set to the blocked task's time 71 out time. If the task is unblocked for a reason other than 72 a timeout xNextTaskUnblockTime is normally left unchanged, 73 because it will automatically get reset to a new value when 74 the tick count equals xNextTaskUnblockTime. However if 75 tickless idling is used it might be more important to enter 76 sleep mode at the earliest possible time - so reset 77 xNextTaskUnblockTime here to ensure it is updated at the 78 earliest possible time. */ 79 prvResetNextTaskUnblockTime(); 80 } 81 #endif 82 83 if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) 新解除阻塞的任务优先级高 84 { 85 /* The notified task has a priority above the currently 86 executing task so a yield is required. */ 87 taskYIELD_IF_USING_PREEMPTION(); 切换任务 88 } 89 else 90 { 91 mtCOVERAGE_TEST_MARKER(); 92 } 93 } 94 else 95 { 96 mtCOVERAGE_TEST_MARKER(); 97 } 98 } 99 taskEXIT_CRITICAL();100 101 return xReturn;102 }103 104 #endif /* configUSE_TASK_NOTIFICATIONS */
中断级通知发送:(只看与任务级的区别即可)
1 BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ) 2 { 3 TCB_t * pxTCB; 4 uint8_t ucOriginalNotifyState; 5 BaseType_t xReturn = pdPASS; 6 UBaseType_t uxSavedInterruptStatus; 7 8 configASSERT( xTaskToNotify ); 9 10 /* RTOS ports that support interrupt nesting have the concept of a 11 maximum system call (or maximum API call) interrupt priority. 12 Interrupts that are above the maximum system call priority are keep 13 permanently enabled, even when the RTOS kernel is in a critical section, 14 but cannot make any calls to FreeRTOS API functions. If configASSERT() 15 is defined in FreeRTOSConfig.h then 16 portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion 17 failure if a FreeRTOS API function is called from an interrupt that has 18 been assigned a priority above the configured maximum system call 19 priority. Only FreeRTOS functions that end in FromISR can be called 20 from interrupts that have been assigned a priority at or (logically) 21 below the maximum system call interrupt priority. FreeRTOS maintains a 22 separate interrupt safe API to ensure interrupt entry is as fast and as 23 simple as possible. More information (albeit Cortex-M specific) is 24 provided on the following link: 25 http://www.freertos.org/RTOS-Cortex-M3-M4.html */ 26 portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); 27 28 pxTCB = ( TCB_t * ) xTaskToNotify; 29 30 uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); 31 { 32 if( pulPreviousNotificationValue != NULL ) 33 { 34 *pulPreviousNotificationValue = pxTCB->ulNotifiedValue; 35 } 36 37 ucOriginalNotifyState = pxTCB->ucNotifyState; 38 pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; 39 40 switch( eAction ) 41 { 42 case eSetBits : 43 pxTCB->ulNotifiedValue |= ulValue; 44 break; 45 46 case eIncrement : 47 ( pxTCB->ulNotifiedValue )++; 48 break; 49 50 case eSetValueWithOverwrite : 51 pxTCB->ulNotifiedValue = ulValue; 52 break; 53 54 case eSetValueWithoutOverwrite : 55 if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) 56 { 57 pxTCB->ulNotifiedValue = ulValue; 58 } 59 else 60 { 61 /* The value could not be written to the task. */ 62 xReturn = pdFAIL; 63 } 64 break; 65 66 case eNoAction : 67 /* The task is being notified without its notify value being 68 updated. */ 69 break; 70 } 71 72 traceTASK_NOTIFY_FROM_ISR(); 73
从这里开始,中断级和任务级不一样了。 74 /* If the task is in the blocked state specifically to wait for a 75 notification then unblock it now. */ 76 if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) 77 { 78 /* The task should not have been on an event list. */ 79 configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); 80 81 if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) 调度器没上锁 82 { 83 ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); 84 prvAddTaskToReadyList( pxTCB ); 85 } 86 else 调度器上锁了,加入到PendingReadyList中!!! 87 { 88 /* The delayed and ready lists cannot be accessed, so hold 89 this task pending until the scheduler is resumed. */ 90 vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); 91 } 92 93 if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) 解除阻塞的任务优先级高 94 { 95 /* The notified task has a priority above the currently 96 executing task so a yield is required. */ 97 if( pxHigherPriorityTaskWoken != NULL ) 只能先标记一下,中断退出之后,切换任务。 98 { 99 *pxHigherPriorityTaskWoken = pdTRUE;100 }101 else102 {103 /* Mark that a yield is pending in case the user is not104 using the "xHigherPriorityTaskWoken" parameter to an ISR105 safe FreeRTOS function. */106 xYieldPending = pdTRUE;107 }108 }109 else110 {111 mtCOVERAGE_TEST_MARKER();112 }113 }114 }115 portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );116 117 return xReturn;118 }
三、获取任务通知
TaskNotifyTake,可以在获取之后对任务通知值清零,或减一。
TaskNotifyWait,功能更强!
1 xClearCountOnExit: 2 为true时,退出函数任务通知值被清零,类似二值信号量! 3 为false时,只是减一,类似计数型信号量! 4 TicksToWait:阻塞时间。
返回值,原来的任务通知值! 5 uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) 6 { 7 uint32_t ulReturn; 8 9 taskENTER_CRITICAL();10 {11 /* Only block if the notification count is not already non-zero. */12 if( pxCurrentTCB->ulNotifiedValue == 0UL ) 如果任务通知值为0,说明还没获取到(Value的修改在任务通知发送里处理的!见上边)13 {14 /* Mark this task as waiting for a notification. */15 pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION; 先改一次状态为“等待通知”16 17 if( xTicksToWait > ( TickType_t ) 0 ) 阻塞时间不为零18 {19 prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); 加入到延时列表,并调度任务!20 traceTASK_NOTIFY_TAKE_BLOCK();21 22 /* All ports are written to allow a yield in a critical23 section (some will yield immediately, others wait until the24 critical section exits) - but it is not something that25 application code should ever do. */26 portYIELD_WITHIN_API();27 }28 else29 {30 mtCOVERAGE_TEST_MARKER();31 }32 }33 else34 {35 mtCOVERAGE_TEST_MARKER();36 }37 }38 taskEXIT_CRITICAL();39 40 taskENTER_CRITICAL();41 {42 traceTASK_NOTIFY_TAKE();43 ulReturn = pxCurrentTCB->ulNotifiedValue;44 45 if( ulReturn != 0UL ) 任务通知值不为零,清零,或减一。46 {47 if( xClearCountOnExit != pdFALSE )48 {49 pxCurrentTCB->ulNotifiedValue = 0UL;50 }51 else52 {53 pxCurrentTCB->ulNotifiedValue = ulReturn - 1;54 }55 }56 else57 {58 mtCOVERAGE_TEST_MARKER();59 }60 61 pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; 更新状态为“不再等待任务通知”62 }63 taskEXIT_CRITICAL();64 65 return ulReturn;66 }
更强大的:
OnEntry:当没有接收到任务通知时,任务通知值 &= ~(OnEntry)
OnExit :当接收到了任务通知时,任务通知值 &= ~(OnExit)
NotifyValue: 保存原来的任务通知值
TickToWait: 阻塞时间
返回值true表示获取到了。
1 BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit,
uint32_t *pulNotificationValue, TickType_t xTicksToWait ) 2 { 3 BaseType_t xReturn; 4 5 taskENTER_CRITICAL(); 6 { 7 /* Only block if a notification is not already pending. */ 不是Recved,只能是Waiting, NotWaiting. 8 if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED ) 没有阻塞的任务才能阻塞,废话嘛! 9 {10 /* Clear bits in the task's notification value as bits may get11 set by the notifying task or interrupt. This can be used to12 clear the value to zero. */13 pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry; 呃呃14 15 /* Mark this task as waiting for a notification. */16 pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION; 修改状态17 18 if( xTicksToWait > ( TickType_t ) 0 ) 阻塞时间大于0,将任务添加到延时列表,并切换任务。19 {20 prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE );21 traceTASK_NOTIFY_WAIT_BLOCK();22 23 /* All ports are written to allow a yield in a critical24 section (some will yield immediately, others wait until the25 critical section exits) - but it is not something that26 application code should ever do. */27 portYIELD_WITHIN_API();28 }29 else30 {31 mtCOVERAGE_TEST_MARKER();32 }33 }34 else35 {36 mtCOVERAGE_TEST_MARKER();37 }38 }39 taskEXIT_CRITICAL();40 41 taskENTER_CRITICAL(); 运行到这里说明,任务通知状态是“received”,原来没有在等待任务通知。42 {43 traceTASK_NOTIFY_WAIT();44 45 if( pulNotificationValue != NULL )46 {47 /* Output the current notification value, which may or may not48 have changed. */49 *pulNotificationValue = pxCurrentTCB->ulNotifiedValue; 保存任务通知值50 }51 52 /* If ucNotifyValue is set then either the task never entered the53 blocked state (because a notification was already pending) or the54 task unblocked because of a notification. Otherwise the task55 unblocked because of a timeout. */56 if( pxCurrentTCB->ucNotifyState == taskWAITING_NOTIFICATION ) “在等待”57 {58 /* A notification was not received. */59 xReturn = pdFALSE;60 }61 else62 {63 /* A notification was already pending or a notification was64 received while the task was waiting. */65 pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit; 表示获取成功,呃呃66 xReturn = pdTRUE;67 }68 69 pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; 重新标记状态“不再等待”70 }71 taskEXIT_CRITICAL();72 73 return xReturn;74 }
别整蒙圈了,把那三个状态搞清楚就行了!
待整理。
留白
---------------------
作者:为民除害
来源:CNBLOGS
原文:https://www.cnblogs.com/WMCH/articles/7915200.html
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件