FreeRTOS使用任务通知替代信号量

使用任务通知在任务间通信

        当使用通信对象时,事件和数据不会直接发送给接收任务或接收的中断服务程序(ISR),而是发送给通信对象。同样,任务和ISR从通信对象接收事件和数据,而不是直接从发送事件或数据的任务或ISR接收。下图描述了使用通信对象的方式。

        “任务通知”允许任务与其他任务进行交互,并与中断服务程序(ISR)进行同步,而无需单独的通信对象。通过使用任务通知,任务或ISR 可以直接将事件发送给接收任务。

        任务通知功能是可选的。要在FreeRTOS中包含任务通知功能,需要在FreeRTOSConfig.h中将configUSE_TASK_NOTIFICATIONS常量设置为1。当configUSE_TASK_NOTIFICATIONS被设置为1时,每个任务至少有一个“通知状态”,可以是“待定”或“未定”,以及一个32位无符号整数的“通知值”。当一个任务接收到通知时,其通知状态被设置为待定。当一个任务读取其通知值时,其通知状态被设置为未定。

        如果configTASK_NOTIFICATION_ARRAY_ENTRIES被设置为大于1的值,则通过索引可以标识多个通知状态和值。

任务通知的优势点

        使用任务通知向任务发送事件或数据所需的RAM显著少于使用队列、信号量或事件组执行等效操作所需的RAM。这是因为每个通信对象(队列、信号量或事件组)在使用之前都必须被创建,而启用任务通知功能的开销是固定的。

        任务通知的RAM大小是是由配置常量configTASK_NOTIFICATION_ARRAY_ENTRIES * 5 字节进行设置。configTASK_NOTIFICATION_ARRAY_ENTRIES的默认值是1,因此任务通知的默认大小是每个任务5字节。

任务通知的局限性

任务通知比通信对象更快且使用更少的RAM,但任务通知并非在所有场景中都能使用。无法使用任务通知的场景:

  • 发送事件或数据到ISR(中断服务程序) 通信对象可用于从ISR向任务发送事件和数据,以及从任务向ISR发送事件和数据。 任务通知可用于从ISR向任务发送事件和数据,但不能用于从任务向ISR发送事件或数据。

  • 启用多个接收任务 任何知道通信对象句柄(可能是队列句柄、信号量句柄或事件组句柄)的任务或ISR都可以访问通信对象。任意数量的任务和ISR都可以处理发送到给定通信对象的事件或数据。 任务通知直接发送到接收任务,因此只能由接收通知的任务处理。然而,在实际情况中这很少是限制,因为虽然多个任务和ISR向同一个通信对象发送数据是很常见的,但很少会有多个任务和ISR从同一个通信对象接收数据。

  • 缓冲多个数据项 队列是一种可以同时容纳多个数据项的通信对象。已经发送到队列但尚未从队列中接收的数据在队列对象内部进行缓冲。 任务通知通过更新接收任务的通知值来向任务发送数据。任务的通知值一次只能保持一个值。

  • 向多个任务广播 事件组是一种通信对象,可以用来同时向多个任务发送事件。 任务通知直接发送到接收任务,因此只能由接收任务处理。

  • 在阻塞状态下等待发送完成 如果通信对象暂时处于无法再向其写入数据或事件的状态(例如,当队列已满时无法再向队列发送数据),那么尝试写入该对象的任务可以选择进入阻塞状态以等待其写入操作完成。 如果任务尝试向已经有待定通知的任务发送任务通知,那么发送任务无法在阻塞状态下等待接收任务重置其通知状态。正如将看到的那样,使用任务通知的实际情况时,这很少是一个限制。

函数xTaskNotifyGive()

        每个任务都有一组“任务通知” (或仅“通知” ) ,每个通知都包含状态和一个 32 位值。直达任务通知是直接发送至任务的事件,可以取消阻塞接收任务,并可以选择通过多种方式更新接收任务的某个通知值。例如,通知可覆盖接收任务的通知值中的一个或仅设置更多位。

        当任务通知用作轻量级且更快的二进制或计数信号量替代方案时,可以使用宏 xTaskNotifyGive ()。 FreeRTOS 信号量通过使用 xSemaphoreGive () API 函数给出,而 xTaskNotifyGive () 与其等效,使用接收 FreeRTOS任务的一个通知值代替信号量。

        xTaskNotifyGive () 和 xTaskNotifyGiveIndexed () 是等效宏,唯一的区别是 xTaskNotifyGiveIndexed () 可以在始终在数组内任何任务通知上操作 ,而 xTaskNotifyGive () 始终在数组索引 0 处的任务通知上运行。

        当任务通知值用作二进制或等效计数信号量时, 则被通知的任务应等待使用 ulTaskNotifyTake() API 函数的通知,而不是 xTaskNotifyWait() API 函数。

        xTaskNotifyGive() API函数直接向任务发送通知,并将接收任务的通知值递增(即加1)。如果接收任务的通知状态尚未处于待定状态,则调用 xTaskNotifyGive() 会将其设置为待定状态。

xTaskNotifyGive () 和xTaskNotifyGiveIndexed () API 函数的原型如下所示:

BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
BaseType_t xTaskNotifyGiveIndexed( TaskHandle_t xTaskToNotify, 
                                   UBaseType_t uxIndexToNotify );

xTaskNotifyGive () 和xTaskNotifyGiveIndexed () API 函数的参数和返回值说明如下所示:

参数名称 参数说明
xTaskToNotify 接收通知的 RTOS 任务的句柄,通知值会递增。
uxIndexToNotify 目标任务通知值数组中要发送通知的索引。xTaskNotifyGive() 没有该参数,并总将通知发送到索引 0。uxIndexToNotify 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。
返回值 xTaskNotifyGive() 实际上是调用xTaskNotify() API函数的宏。该宏传递给 xTaskNotify() 的参数被设置为只能返回 pdPASS 的返回值。

        注意,数组中每个通知都独立运行,任务一次只能阻塞在数组中的一个通知,并且不会被发送到任何其他数组索引的通知取消阻塞。

        xTaskNotifyGive () API函数不能从中断服务例程调用,可以使用 vTaskNotifyGiveFromISR() A函数代替。

        configUSE_TASK_NOTIFICATIONS 必须在 FreeRTOSConfig.h 中被设置为 1(或保留为未定义)才能使用这些宏。常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 设置任务通知的每个任务数组中的索引数量。

xTaskNotifyGive () 和xTaskNotifyGiveIndexed () API 函数的使用示例如下所示:

/* 由 main() 创建的两个任务的原型。 */
static void prvTask1( void *pvParameters );
static void prvTask2( void *pvParameters );

/* 由 main() 创建的任务的句柄。 */
static TaskHandle_t xTask1 = NULL, xTask2 = NULL;

/* 创建两个任务,这两个任务相互发送通知,然后启动RTOS调度器。*/
void main( void )
{
  xTaskCreate( prvTask1, "Task1", 200, NULL, tskIDLE_PRIORITY, &xTask1 );
  xTaskCreate( prvTask2, "Task2", 200, NULL, tskIDLE_PRIORITY, &xTask2 );
  vTaskStartScheduler();
}
/*-----------------------------------------------------------*/
/* prvTask1() 使用 'indexed' 版本的 API。*/
static void prvTask1( void *pvParameters )
{
  for( ;; )
  {
    /* 向 prvTask2() 发送通知,使其从阻塞状态变为就绪状态。 */
    xTaskNotifyGiveIndexed( xTask2, 0 );

    /* 阻塞等待 prvTask2() 向此任务发送通知。*/
    ulTaskNotifyTakeIndexed( 0, pdTRUE, portMAX_DELAY );
  }
}
/*-----------------------------------------------------------*/

/* prvTask2() 使用 API 的原始版本(不带 'Indexed')。*/
static void prvTask2( void *pvParameters )
{
  for( ;; )
  {
    /* 阻塞等待 prvTask1() 向此任务发送通知。 */
    ulTaskNotifyTake( pdTRUE, portMAX_DELAY );

    /* 向 prvTask1() 发送一个通知,将其从阻塞状态中转出。*/
    xTaskNotifyGive( xTask1 );
  }
}

函数vTaskNotifyGiveFromISR()和函数vTaskNotifyGiveIndexedFromISR()

        vTaskNotifyGiveFromISR() API函数是 xTaskNotifyGive() API函数的另外一个版本,它可以在中断服务程序(Interrupt Service Routine,简称 ISR)中使用。

xTaskNotifyGive () 和xTaskNotifyGiveIndexed () API 函数的原型如下所示:

void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify,
                             BaseType_t *pxHigherPriorityTaskWoken );

 void vTaskNotifyGiveIndexedFromISR( TaskHandle_t xTaskHandle, 
                                     UBaseType_t uxIndexToNotify, 
                                     BaseType_t *pxHigherPriorityTaskWoken );

vTaskNotifyGiveFromISR() 和vTaskNotifyGiveIndexedFromISR() 函数的参数说明如下所示:

参数名称 参数说明
xTaskToNotify 接收通知的 RTOS 任务的句柄,通知值会递增。
uxIndexToNotify 目标任务通知值数组中要发送通知的索引。xTaskNotifyGive() 没有该参数,并总将通知发送到索引 0。uxIndexToNotify 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。
pxHigherPriorityTaskWoken 如果调用 vTaskNotifyGiveFromISR() 函数导致任务离开阻塞状态,并且解除阻塞的任务的优先级高于当前正在执行的任务(即被中断的任务)的优先级,那么,vTaskNotifyGiveFromISR() 会将 *pxHigherPriorityTaskWoken 设置为 pdTRUE,那么在退出中断之前应该执行上下文切换。

vTaskNotifyGiveFromISR() 和vTaskNotifyGiveIndexedFromISR() API 函数的使用示例如下所示:

/* 这是一个通用外设驱动程序中的发送函数的示例。一个RTOS任务调用发送函数,然后在阻塞状态(因此
不使用CPU时间)中等待,直到它被通知传输已完成。传输是通过DMA(直接内存访问)进行的,而DMA结束
中断用于通知任务。*/

static TaskHandle_t xTaskToNotify = NULL;

/* 外设驱动程序的发送函数。*/
void StartTransmission( uint8_t *pcData, size_t xDataLength )
{
  /* 在这一点上,xTaskToNotify 应该是 NULL,因为没有传输正在进行。如果需要的话,可以使用
  互斥锁(mutex)来保护对外设的访问。*/
  configASSERT( xTaskToNotify == NULL );

  /* 存储调用任务的句柄。 */
  xTaskToNotify = xTaskGetCurrentTaskHandle();

  /* 开始传输 - 当传输完成时,将生成一个中断。*/
  vStartTransmit( pcData, xDatalength );
}
/*-----------------------------------------------------------*/

/* 发送端中断。 */
void vTransmitEndISR( void )
{
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;

  /* 在这一点上,xTaskToNotify 不应该为空,因为传输正在进行中。 */
  configASSERT( xTaskToNotify != NULL );

  /* 通知任务传输已完成。 */
  vTaskNotifyGiveIndexedFromISR( xTaskToNotify, 0, &xHigherPriorityTaskWoken );

  /* 当前没有正在进行的传输,因此没有任务需要通知。 */
  xTaskToNotify = NULL;

  /* 如果现在 xHigherPriorityTaskWoken 被设置为 pdTRUE,那么应该执行上下文切换,以确保
  中断直接返回到最高优先级的任务。用于此目的的宏取决于所使用的移植。*/
  portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/*-----------------------------------------------------------*/

/* 发起传输的任务然后进入阻塞状态(因此不消耗任何CPU时间)以等待其完成。 */
void vAFunctionCalledFromATask( uint8_t ucDataToTransmit, size_t xDataLength )
{
  uint32_t ulNotificationValue;
  const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 200 );

  /* 通过调用上面展示的函数来启动传输。 */
  StartTransmission( ucDataToTransmit, xDataLength );

  /* 等待传输完成。 */
  ulNotificationValue = ulTaskNotifyTakeIndexed( 0, pdFALSE, xMaxBlockTime );

  if( ulNotificationValue == 1 )
  {
    /* 传输已按预期结束。 */
  }
  else
  {
    /* 调用 ulTaskNotifyTake() 函数时发生了超时。 */
  }
}

函数ulTaskNotifyTake()

        ulTaskNotifyTake() API函数允许任务在阻塞状态下等待,直到其通知值(notification value)大于0。在返回之前,该函数可以将任务的通知值递减(减1)或者清除。

        提供ulTaskNotifyTake() API函数的目的,是为了允许任务通知作为二进制信号量(binary semaphore)或计数信号量(counting semaphore)的更轻量级、更快的替代方案。使用任务通知可以避免信号量可能带来的某些开销,特别是在高频率的同步和通知场景中。

        简而言之,ulTaskNotifyTake() API函数允许任务基于其通知值来决定是否继续执行,并提供了一个快速、轻量级的同步机制。

        ulTaskNotifyTake()和ulTaskNotifyTakeIndexed() 是等效宏,唯一的区别是 ulTaskNotifyTakeIndexed() 可以在数组内的任何任务通知上运行, 而 ulTaskNotifyTake() 始终在数组索引 0 处的任务通知上运行。

        当任务使用通知值作为二进制或计数信号量时, 其他任务和中断应使用 xTaskNotifyGive() 宏,或将 eAction 参数设置为 eIncrement 的 xTaskNotify() 函数 (二者等效)。        

        ulTaskNotifyTake() 可以在退出时清除任务的通知值为 0,在这种情况下,通知值起到二进制信号量的作用;或在退出时递减任务的通知值,在这种情况下, 通知值更像是计数信号量。

        FreeRTOS 任务可以使用 ulTaskNotifyTake()进入阻塞状态以等待任务的通知值。任务处于阻塞状态时 不消耗任何 CPU 时间。

ulTaskNotifyTake() 和ulTaskNotifyTakeIndexed() API 函数的原型如下所示:

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,
                           TickType_t xTicksToWait );
 
uint32_t ulTaskNotifyTakeIndexed( UBaseType_t uxIndexToWaitOn, 
                                  BaseType_t xClearCountOnExit, 
                                  TickType_t xTicksToWait );

ulTaskNotifyTake() 和ulTaskNotifyTakeIndexed() API 函数的参数和返回值说明如下所示:

参数名称 参数说明
xClearCountOnExit 如果 xClearCountOnExit 被设置为 pdTRUE,在调用 ulTaskNotifyTake() 返回之前,调用任务的通知值将被清零。如果 xClearCountOnExit 被设置为 pdFALSE,并且调用任务的通知值大于0,在调用 ulTaskNotifyTake() 返回之前,调用任务的通知值将被递减(递减)。
xTicksToWait 调用任务在阻塞状态下等待其通知值大于0的最长时间。
uxIndexToWaitOn 调用任务将在其通知值数组中的哪个索引上等待通知变为非0。xTaskNotifyTake() 没有该参数,总是在索引 0 处等待通知。uxIndexToWaitOn 必须小于configTASK_NOTIFICATION_ARRAY_ENTRIES。
返回值 返回值是调用任务的通知值在清零或递减之前的值,由xClearCountOnExi参数的值设置。

ulTaskNotifyTake() 和ulTaskNotifyTakeIndexed() API 函数的使用示例如下所示:

/* 中断处理程序。该中断处理程序不执行任何处理,而是解除一个高优先级任务的阻塞,该任务将处理产生
中断的事件。如果任务的优先级足够高,那么中断将直接返回到该任务,因此处理将在时间上连续发生,就好
像所有的处理都在中断处理程序本身中完成一样。 */
void vANInterruptHandler( void )
{
  BaseType_t xHigherPriorityTaskWoken;
  /* 清除中断。 */
  prvClearInterruptSource();
  /* xHigherPriorityTaskWoken必须初始化为 pdFALSE。
  如果调用vTaskNotifyGiveFromISR()解除处理任务的阻塞,并且处理任务的优先级高于当前正在运
  行的任务的优先级,xHigherPriorityTaskWoken将自动设置为 pdTRUE。*/
  xHigherPriorityTaskWoken = pdFALSE;
  /* 解锁处理任务,以便该任务可以执行中断所需的任何处理。xHandlingTask 是任务的句柄,该句
  柄在任务创建时获得。 */
  vTaskNotifyGiveIndexedFromISR( xHandlingTask, 0, &xHigherPriorityTaskWoken );
  /* 如果 xHigherPriorityTaskWoken 现在被设置为 pdTRUE,则强制进行上下文切换。*/
  portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/* 一个任务,该任务会阻塞等待被通知外设需要服务,每次被通知要处理时,它都会处理外设中待处理的所
有事件。 */
void vHandlingTask( void *pvParameters )
{
  BaseType_t xEvent;
  for( ;; )
  {
    /* 无限期阻塞(没有超时,因此无需检查函数的返回值)以等待通知。在这里,任务通知被用作二
    进制信号量,因此在退出时通知值被清零。注意!真实的应用程序不应该无限期阻塞,而应该设置超
    时,以便处理可能阻止中断发送更多通知的错误情况。*/
    ulTaskNotifyTakeIndexed( 0,               /* 使用第0个通知 */
                             pdTRUE,          /* 在退出之前清除通知值。 */
                             portMAX_DELAY ); /* 无限期阻塞。 */

    /* RTOS任务通知被用作二进制信号量(与计数信号量相反),因此只有当外设中所有待处理的事
    件都被处理后,才回到等待更多通知的状态。 */
     do
    {
      xEvent = xQueryPeripheral();

      if( xEvent != NO_MORE_EVENTS )
      {
        vProcessPeripheralEvent( xEvent );
      }

    } while( xEvent != NO_MORE_EVENTS );
  }
}

函数xTaskNotify()和函数xTaskNotifyFromISR()

xTaskNotify() 是 xTaskNotifyGive() API函数的更强大的版本,可以通过以下任意方式更新接收任务的通知值:

  • 递增(加1)接收任务的通知值,在这种情况下,xTaskNotify() 相当于 xTaskNotifyGive()。
  • 在接收任务的通知值中设置一个或多个位。这允许任务的通知值用作事件组的更轻量且更快的替代方案。
  • 将一个全新的数字写入接收任务的通知值,但仅在接收任务自上次更新以来已经读取了其通知值的情况下才进行。这允许任务的通知值提供与长度为1的队列类似的功能。
  • 将一个全新的数字写入接收任务的通知值,即使接收任务自上次更新以来尚未读取其通知值也进行。这允许任务的通知值提供与 xQueueOverwrite() API 函数类似的功能。产生的行为有时被称为“邮箱”。 xTaskNotify()API函数比 xTaskNotifyGive() API函数更加灵活和强大,也正因为这种额外的灵活性和强大功能,它的使用也相对复杂一些。

        xTaskNotifyFromISR() 是 xTaskNotify() API函数可以在中断服务程序中使用的版本,因此有一个额外的 pxHigherPriorityTaskWoken 参数。如果通知状态尚未待定,调用 xTaskNotify() API函数将始终设置接收任务的通知状态为待定状态。

xTaskNotify() 和xTaskNotifyFromISR() API 函数的原型如下所示:

BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify,
                        uint32_t ulValue,
                        eNotifyAction eAction );
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify,
                               uint32_t ulValue,
                               eNotifyAction eAction,
                               BaseType_t *pxHigherPriorityTaskWoken );

xTaskNotify() 和xTaskNotifyFromISR() API 函数的参数和返回值说明如下所示:

参数名称 参数说明
xTaskToNotify 任务句柄,将通知发往该句柄引用的任务。
ulValue ulValue 的使用方式取决于eNotifyAction 的值。
eNotifyAction 枚举类型,用于指定如何更新接收任务的通知值。
返回值 如xTaskNotify() 将返回 pdPASS,但以下列出的一个例外情况除外。

xTaskNotify() API函数有效的eNotifyAction 参数值及其对接收任务通知值的影响如下所示:

eNotifyAction参数值 对接收任务通知值的影响
eNoAction 将接收任务的通知状态设置为待定,任务通知值不会被更新。xTaskNotify() API函数的 ulValue 参数不会被使用。
eSetBits 接收任务的通知值将与 xTaskNotify() API函数的 ulValue 参数中传递的值进行按位或( OR)运算。例如,如果 ulValue 被设置为 0x01,那么接收任务的通知值的第 0 位将被设置。再举一个例子,如果 ulValue 是 0x06(二进制 0110),那么接收任务的通知值的第 1 位和第 2 位将被设置。
eIncrement 接收任务的通知值将被增加。xTaskNotify()API函数的 ulValue 参数在这里不会被使用。
eSetValueWithoutOverwrite 如果在调用 xTaskNotify() API函数之前,接收任务已经有待处理的通知,那么将不采取任何行动,并且 xTaskNotify() API函数将返回 pdFAIL。如果在调用 xTaskNotify() API函数之前,接收任务没有待处理的通知,那么接收任务的通知值将被设置为 xTaskNotify() API函数的 ulValue 参数中传递的值。
eSetValueWithOverwrite 无论接收任务在调用 xTaskNotify() API函数之前是否已有待处理的通知,接收任务的通知值都将被设置为 xTaskNotify() API函数的 ulValue 参数传递的值。

xTaskNotify() 和xTaskNotifyFromISR() API 函数的使用示例如下所示:

/* 在由 xTask1Handle 引用的任务的第 0 个通知值中设置第 8 位。*/
xTaskNotifyIndexed( xTask1Handle, 0, ( 1UL << 8UL ), eSetBits );

/* 向由 xTask2Handle 引用的任务发送通知,可能会使该任务从阻塞状态变为非阻塞状态,但不更新任
务的通知值。*/
xTaskNotify( xTask2Handle, 0, eNoAction );

/* 将由 xTask3Handle 引用的任务的通知值设置为 0x50,即使该任务尚未读取其之前的通知值。*/
xTaskNotify( xTask3Handle, 0x50, eSetValueWithOverwrite );

/* 将由 xTask4Handle 引用的任务的通知值设置为 0xfff,仅当这样做不会覆盖任务在获取该值之前
(通过调用 xTaskNotifyWait() 或 ulTaskNotifyTake())已经存在的通知值时。*/
if( xTaskNotify( xTask4Handle, 0xfff, eSetValueWithoutOverwrite ) == pdPASS )
{
  /* 任务的通知值已更新。 */
}
else
{
  /* 任务的通知值未被更新。 */
}

 函数xTaskNotifyWait()和函数xTaskNotifyWaitIndexed()

xTaskNotifyWait()和xTaskNotifyWaitIndexed()是等效的宏,唯一的区别是 xTaskNotifyWaitIndexed() 可以在数组内的任何任务通知上运行, 而 xTaskNotifyWait() 始终在数组索引 0 处的任务通知上运行。

xTaskNotifyWait()是 ulTaskNotifyTake()API函数的一个更强大的版本。它允许一个任务等待(可选超时),直到调用任务的通知状态变为待定(如果当前通知状态还不是待定)。xTaskNotifyWait() API函数提供了在函数进入时和退出时清除调用任务的通知值中特定位的选项。

xTaskNotifyWait() 和xTaskNotifyWaitIndexed() API 函数的原型如下所示:

BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry,
                             uint32_t ulBitsToClearOnExit,
                             uint32_t *pulNotificationValue,
                             TickType_t xTicksToWait );

 BaseType_t xTaskNotifyWaitIndexed( UBaseType_t uxIndexToWaitOn, 
                                    uint32_t ulBitsToClearOnEntry, 
                                    uint32_t ulBitsToClearOnExit, 
                                    uint32_t *pulNotificationValue, 
                                    TickType_t xTicksToWait );

xTaskNotifyWait() 和xTaskNotifyWaitIndexed() API 函数的参数和返回值说明如下所示:

参数名称 参数说明
uxIndexToWaitOn 调用任务的通知值数组中的索引,调用任务将在该通知值上等待接收通知。
ulBitsToClearOnEntry 如果调用任务在调用 xTaskNotifyWait() 函数之前没有待处理的通知,那么在进入该API函数时 ulBitsToClearOnEntry 参数设置的位所对应的任务通知值中的位将被清除。例如,如果 ulBitsToClearOnEntry 是 0x01,那么该任务的通知值的第 0 位将被清除。再举一个例子,将 ulBitsToClearOnEntry 设置为 0xffffffff(即 ULONG_MAX)将清除该任务通知值中的所有位,实际上将该值清零。
ulBitsToClearOnExit 如果调用任务因为收到通知而退出 xTaskNotifyWait()函数,或者因为在调用 xTaskNotifyWait() 函数时已经有待定通知,那么在任务退出 xTaskNotifyWait() 函数之前,ulBitsToClearOnExit 中设置的位所对应的任务通知值中的位将被清除。任务的通知值保存到 *pulNotificationValue(见下面对 pulNotificationValue 的描述)后,对应位被清除。例如,如果 ulBitsToClearOnExit 参数是 0x03,那么在函数退出之前,任务的通知值的第 0 位和第 1 位将被清除。将 ulBitsToClearOnExit 设置为 0xffffffff(即 ULONG_MAX)将清除任务通知值中的所有位,实际上将该值清零。
pulNotificationValue 用于传递任务的通知值。复制到 *pulNotificationValue 的值是任务在因 ulBitsToClearOnExit 参数设置,而引起对应位被清除之前的任务通知值的值。pulNotificationValue 是可选参数,如果不需要可以设置为 NULL。
xTicksToWait 调用任务在阻塞状态下等待其通知状态变为待定状态的最大时间。
返回值 pdTRUE:这表示xTaskNotifyWait() 函数因为收到通知而返回,或者在调用 xTaskNotifyWait() API函数时,调用任务已经有了待定通知。pdFALSE:这表示 xTaskNotifyWait()函数在调用任务没有收到任务通知的情况下返回。

xTaskNotifyWait() 和xTaskNotifyWaitIndexed() API 函数的使用示例如下所示:

/* 该任务展示了任务通知值中使用的位,这些位被用来以与事件组中标志相同的
方式向任务传递不同的事件。 */
void vAnEventProcessingTask( void *pvParameters )
{
  uint32_t ulNotifiedValue;
  for( ;; )
  {
  /* 无限期阻塞(没有超时,因此无需检查函数的返回值)以等待通知。此任务
  的通知值中的位由通知任务和中断设置,以指示哪些事件已经发生。*/
  xTaskNotifyWaitIndexed( 0,         /* 等待第0个通知。 */
                          0x00,         /* 在进入时不清除任何通知位。 */
                          ULONG_MAX,    /* 退出时将通知值重置为0。 */
                          &ulNotifiedValue, /* 通过ulNotifiedValue传出通知值。*/
                          portMAX_DELAY );  /* 无限期阻塞。 */
  /* 处理已在通知值中记录的事件。*/

  if( ( ulNotifiedValue & 0x01 ) != 0 )
  {
    /* 位0被设置 - 处理由位0表示的任何事件。 */
    prvProcessBit0Event();
  }
  if( ( ulNotifiedValue & 0x02 ) != 0 )
  {
    /* 位1被设置 - 处理由位1表示的任何事件。 */
    prvProcessBit1Event();
  }
  if( ( ulNotifiedValue & 0x04 ) != 0 )
  {
    /* 位2被设置 - 处理由位2表示的任何事件。 */
    prvProcessBit2Event();
  }
  /* Etc. */
  }
}
Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐