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 ); 参数:保存更新之前的任务通知值PreNotifyValue BaseType_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 else 102 { 103 /* Mark that a yield is pending in case the user is not 104 using the "xHigherPriorityTaskWoken" parameter to an ISR 105 safe FreeRTOS function. */ 106 xYieldPending = pdTRUE; 107 } 108 } 109 else 110 { 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 critical 23 section (some will yield immediately, others wait until the 24 critical section exits) - but it is not something that 25 application code should ever do. */ 26 portYIELD_WITHIN_API(); 27 } 28 else 29 { 30 mtCOVERAGE_TEST_MARKER(); 31 } 32 } 33 else 34 { 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 else 52 { 53 pxCurrentTCB->ulNotifiedValue = ulReturn - 1; 54 } 55 } 56 else 57 { 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 get 11 set by the notifying task or interrupt. This can be used to 12 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 critical 24 section (some will yield immediately, others wait until the 25 critical section exits) - but it is not something that 26 application code should ever do. */ 27 portYIELD_WITHIN_API(); 28 } 29 else 30 { 31 mtCOVERAGE_TEST_MARKER(); 32 } 33 } 34 else 35 { 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 not 48 have changed. */ 49 *pulNotificationValue = pxCurrentTCB->ulNotifiedValue; 保存任务通知值 50 } 51 52 /* If ucNotifyValue is set then either the task never entered the 53 blocked state (because a notification was already pending) or the 54 task unblocked because of a notification. Otherwise the task 55 unblocked because of a timeout. */ 56 if( pxCurrentTCB->ucNotifyState == taskWAITING_NOTIFICATION ) “在等待” 57 { 58 /* A notification was not received. */ 59 xReturn = pdFALSE; 60 } 61 else 62 { 63 /* A notification was already pending or a notification was 64 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 }

 

 

别整蒙圈了,把那三个状态搞清楚就行了!

待整理。

 

 

 

 

 

 

留白

posted @ 2017-11-29 02:09  为民除害  阅读(1956)  评论(0编辑  收藏  举报