ESP32 FreeRTOS-任务通知(11)

提示:好记性不如烂笔头。本博客作为学习笔记,有错误的地方希望指正

前言:

  参考资料:FreeRTOS API参考
  这里主要是任务通知,任务通知可以实现我们任务与任务之间同时多种信号的一种信号的发生,或者多个任务同时等待一种任务的发生。

一、xTaskNotifyGive()、xTaskNotifyGiveIndexed()

API原型:

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

  每个任务都有一组“任务通知” (或仅“通知” ) ,每个 通知都包含状态和一个 32 位值。 直达任务通知是直接发送至任务的事件, 可以取消接收任务的阻塞状态,还 可以选择通过多种方式更新接收任务的某个通知值。 例如,通知可覆盖接收任务的通知值中的一个,或仅设置 更多位。
  当任务通知 用作轻量级且更快的二进制或计数信号量 替代方案时,可以使用宏 xTaskNotifyGive ()。 FreeRTOS 信号用通过使用 xSemaphoreGive () API 函数给出,而 xTaskNotifyGive () 与其等效,使用接收 RTOS 任务的一个通知值代替信号量。
  xTaskNotifyGive () 和 xTaskNotifyGiveIndexed () 是等效宏——唯一的区别是 xTaskNotifyGiveIndexed () 可以在始终在数组内任何任务通知上操作 ,而 xTaskNotifyGive () 始终在数组索引 0 处的任务通知上运行。
  当任务通知值用作二进制或等效计数信号量时, 则被通知的任务应等待 使用 ulTaskNotifyTake() API 函数的通知,而不是 xTaskNotifyWait() API 函数。
  注意: 数组中每个通知都独立运行——任务一次只能阻止数组中的一个通知,并且不会被发送到任何其他数组索引的通知取消阻止。
  xTaskNotifyGive () 不能从中断服务例程调用。 使用 vTaskNotifyGiveFromISR() 代替。
  configUSE_TASK_NOTIFICATIONS 必须在 FreeRTOSConfig.h 中设置为 1(或保留为未定义)才能使用这些宏。 常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 设置 任务通知的每个任务数组中的索引数量。
向后兼容性信息:
  在 FreeRTOS V10.4.0 之前,每个任务有一个单一的“通知值”,且 所有任务通知 API 函数都在该值上运行。用通知值的数组 更换单个通知值需要 新的 API 函数集,该函数集应能在数组内处理 。 xTaskNotifyGive () 是原始 API 函数,并且 通过始终在数组内索引 0 处的通知值上运行 来保持向后兼容。调用 xTaskNotifyGive () 等于调用 xTaskNotifyGiveIndexed (),其中 uxIndexToNotify 参数设置为 0。
参数:
  xTaskToNotify 正被通知的 RTOS 任务的句柄,且该任务的通知值 正在递增。
要获取任务句柄,请使用 xTaskCreate() 创建任务并使用 pxCreatedTask 参数,或使用返回值创建任务 xTaskCreateStatic() 并存储该值,或在 xTaskGetHandle() 调用中使用任务的名称。
  当前执行的 RTOS 任务的句柄通过以下方式 由 xTaskGetCurrentTaskHandle() API 函数返回。
  uxIndexToNotify 目标任务通知值数组中的索引, 通知值将发送给该索引。
uxIndexToNotify 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。 xTaskNotifyGive () 没有此参数,并且总是向索引 0 发送通知。
返回值:
  xTaskNotifyGiveIndexed () 是用 eAction 参数调用 xTaskNotifyIndexed() 的宏, eAction 参数设置为 eIncrement,因此所有调用都返回 pdPASS。
用法示例:

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

/* 由main()创建的任务的处理程序。*/
static TaskHandle_t xTask1 = NULL, xTask2 = NULL;

/* 创建两个任务,相互之间发送通知。
然后启动实时操作系统的调度程序。*/
void main( void )
{
    
    
    xTaskCreate( prvTask1, "Task1", 200, NULL, tskIDLE_PRIORITY, &xTask1 );
    xTaskCreate( prvTask2, "Task2", 200, NULL, tskIDLE_PRIORITY, &xTask2 );
    vTaskStartScheduler()}
/*-----------------------------------------------------------*/

/* prvTask1()使用的是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()

API原型:

 void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify,
                              BaseType_t *pxHigherPriorityTaskWoken );

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

  可在中断服务程序 (ISR) 中使用的 xTaskNotifyGive() 和 xTaskNotifyGiveIndexed() 的版本。 请参阅文档页面,以获取关于 API 函数 xTaskNotifyGive() 的 操作描述和必要的配置参数, 以及向后兼容性信息。
参数:
  xTaskToNotify 正被通知的 RTOS 任务的句柄,且该任务的通知值 正在递增。
要获取任务句柄,请使用 xTaskCreate() 创建任务并使用 pxCreatedTask 参数,或使用返回值创建任务 xTaskCreateStatic() 并存储该值,或在 xTaskGetHandle() 调用中使用任务的名称。
  当前执行的 RTOS 任务的句柄通过以下方式 由 xTaskGetCurrentTaskHandle() API 函数返回。
  uxIndexToNotify 目标任务通知值数组中的索引, 通知值将发送给该索引。
uxIndexToNotify 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。
  xTaskNotifyGiveFromISR() 不具有此参数,并且始终 将通知发送到索引 0。
  pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken 必须初始化为 0。
如果发送通知导致任务解除阻塞,并且被阻塞任务的优先级高于 当前运行的任务,vTaskNotifyGiveFromISR() 会将 *pxHigherPriorityTaskWoken 设 xTaskNotifyFromISR() 会将 *pxHigherPriorityTaskWoken 。
  如果 vTaskNotifyGiveFromISR() 将此值设置为 pdTRUE , 则应在中断退出之前请求上下文切换 。 请参阅以下示例。
  pxHigherPriorityTaskWoken 为可选参数,且可 设置为 NULL。
用法示例:

/* 这是一个通用外设驱动程序中传输函数的例子。 一个RTOS任务调用传输函数,然后在阻塞状态下等待(所以不需要等待)。RTOS任务调用传输函数,然后在阻塞状态下等待(所以不使用CPU时间),直到被通知传输完成。
使用CPU时间),直到它被通知传输完成。 传输传输是由一个DMA执行的,DMA结束中断被用来通知任务。*/

static TaskHandle_t xTaskToNotify = NULL;

/* 外围驱动器的传输功能。*/
void StartTransmission( uint8_t *pcData, size_t xDataLength )
{
    
    
    /* 在这一点上,xTaskToNotify应该是NULL,因为没有传输正在进行。
    进展。 如果有必要的话,可以使用一个突变器来保护对外围的访问。
    如果有必要的话,可以使用一个突变器来保护对外设的访问。*/
    configASSERT( xTaskToNotify == NULL );

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

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

/* 发送结束的中断。*/
void vTransmitEndISR( void )
{
    
    
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    /* 此时xTaskToNotify不应该是NULL,因为有一个传输正在进行。
    正在进行中。*/
    configASSERT( xTaskToNotify != NULL );

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

    /* 没有正在进行的传输,所以没有任务需要通知。*/
    xTaskToNotify = NULL;

    /* 如果xHigherPriorityTaskWoken现在被设置为pdTRUE,那么就应该进行上下文切换,以确保中断者的工作。
    应该进行上下文切换,以确保中断直接返回到最高优先级的
    优先级的任务。 用于此目的的宏取决于所使用的端口。
    使用,可以称为portend_SWITCHING_ISR()。*/
    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()、ulTaskNotifyTakeIndexed()

API原型:

uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,
                           TickType_t xTicksToWait );

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

  每个任务都有一组“任务通知” (或仅“通知” ) ,每个 通知都包含状态和一个 32 位值。 直达任务通知是直接发送至任务的事件, 可以取消接收任务的阻塞状态,还 可以选择通过多种方式更新接收任务的某个通知值。 例如,通知可覆盖接收任务的通知值中的一个,或仅设置 接收任务的通知值中的一个或多个比特位。
  ulTaskNotifyTake() 是一个宏,在任务通知 被用作更快、更轻的二进制 或计数信号量替代时使用。 使用 xSemaphoreTake() API 函数获取 FreeRTOS 信号量,ulTaskNotifyTake() 是使用通知值代替信号量的等效宏。
  ulTaskNotifyTake() 和 ulTaskNotifyTakeIndexed() 是等效的宏,它们唯一的区别 是 ulTaskNotifyTakeIndexed() 可以在数组内的任何任务通知上运行, 而 ulTaskNotifyTake() 始终在数组索引 0 处的任务通知上运行。
  当任务使用通知值作为二进制或计数信号量时, 其他任务和中断应使用 xTaskNotifyGive() 宏,或 eAction 参数设置为 eIncrement 的 xTaskNotify() 函数 (二者等效)。
  ulTaskNotifyTake() 可以在退出时清除任务的通知值为 0, 在这种情况下,通知值起到二进制信号量的作用; 或在退出时递减任务的通知值,在这种情况下, 通知值更像是计数信号量。
  RTOS 任务可以使用 ulTaskNotifyTake() [可选]进入阻塞状态以等待 任务的通知值。 任务处于阻塞状态时 不消耗任何 CPU 时间。
  注意:数组中每个通知都独立运行——在数组中的一个通知上一次只能阻塞一个任务,并且该任务不会被发送到任何其他数组索引处的通知取消阻塞。
  然而当通知被挂起时,xTaskNotifyWait() 将返回, 当任务的通知值不为 0 时,ulTaskNotifyTake() 将返回, 并在返回之前递减任务的通知值。
  configUSE_TASK_NOTIFICATIONS 必须在 FreeRTOSConfig.h 中设置为 1(或保留为未定义)才能使用这些宏。 常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 设置 任务通知的每个任务数组中的索引数量。
  向后兼容性信息:
  在 FreeRTOS V10.4.0 之前,每个任务有一个单一的“通知值”,且 所有任务通知 API 函数都在该值上运行。用通知值的数组 更换单个通知值需要 新的 API 函数集,该函数集应能在数组内处理 特定通知。 ulTaskNotifyTake() 是原始 API 函数,并且 通过始终在数组内索引 0 处的通知值上运行保持 向后兼容。调用 ulTaskNotifyTake() 等于调用 ulTaskNotifyTakeIndexed(),其中 uxIndexToWaitOn 参数设置为 0。
参数:
  uxIndexToWaitOn 调用任务的数组中通知值的索引, 调用任务将在该通知值上等待通知 变成非零。
uxIndexToWaitOn 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。
xTaskNotifyTake() 没有此参数,总是在索引 0 处等待通知。
  xClearCountOnExit 如果收到 RTOS 任务通知且 xClearCountOnExit 设置为 pdFALSE,则 RTOS 任务的通知值 在 ulTaskNotifyTake() 退出之前递减。 这 等同于 通过成功调用 xSemaphoreTake() 而递减计数信号量的值。
  如果收到 RTOS 任务通知且 xClearCountOnExit 设置为 pdTRUE,则 RTOS 任务的通知值 在 ulTaskNotifyTake() 退出之前重设为 0。 这 等同于在成功调用 xSemaphoreTake() 后 将二进制信号量的值保留为 0 (或为空,或为“不可用” )。
  xTicksToWait 在阻塞状态下等待接收通知的最长时间, 如果通知在 ulTaskNotifyTake() 被调用时 尚未挂起。
  处于阻塞状态的 RTOS 任务不会消耗 任何 CPU 时间。
  时间以 RTOS tick 周期为单位。 宏 pdMS_TO_TICKS() 可以 将以毫秒为单位的时间 转换成以 tick 为单位的时间。
返回值:
被递减或清楚之前的任务通知值的值 (请参阅 xClearCountOnExit 的说明)。
用法示例:

/* 一个中断处理程序。 中断处理程序不进行任何处理。
相反,它解除了一个高优先级的任务,在这个任务中,产生中断的事件被处理。
中断的事件被处理。 如果该任务的优先级足够高,那么
中断将直接返回到该任务(因此它将中断一个任务,但是
返回到一个不同的任务),所以处理过程将在时间上连续发生--就像所有的处理过程都是连续的一样。
就像所有的处理都是在中断处理程序本身中完成的一样。*/
void vANInterruptHandler( void )
{
    
    
	BaseType_t xHigherPriorityTaskWoken;
    /* 清除中断。*/
    prvClearInterruptSource();

    /* xHigherPriorityTaskWoken必须被初始化为pdFALSE。 如果调用
    vTaskNotifyGiveFromISR()解除对处理任务的封锁,并且处理任务的优先级高于
    处理任务的优先级高于当前运行任务的优先级。
    那么xHigherPriorityTaskWoken将自动被设置为pdTRUE。*/
    xHigherPriorityTaskWoken = pdFALSE;

    /* 解除对处理任务的封锁,以便该任务可以执行任何由中断引起的必要处理。
    xHandlingTask是任务的句柄,它是在创建任务时获得的。
    任务的句柄,该句柄是在创建该任务时获得的。*/
    vTaskNotifyGiveIndexedFromISR( xHandlingTask, 0, &xHigherPriorityTaskWoken );

    /* 如果xHigherPriorityTaskWoken现在被设置为pdTRUE,则强制进行上下文切换。
    用来做这件事的宏取决于端口,可以称为
    portEND_SWITCHING_ISR。*/
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/*-----------------------------------------------------------*/

/* 一个等待通知外设需要服务的任务阻塞。
每次接到通知时,它都会处理外设中所有待处理的事件。
的所有事件。*/
void vHandlingTask( void *pvParameters )
{
    
    
	BaseType_t xEvent;
    for( ; ; )
    {
    
    
        /* 无限地阻塞(没有超时,所以不需要检查函数的
        返回值)来等待一个通知。 这里RTOS的任务通知
        被用作二进制信号,所以通知值在退出时被清零。
        为零。 注意!真正的应用程序不应该无限期地阻塞。
        而是偶尔超时,以便处理错误情况
        这可能会阻止中断发送任何更多的通知。*/
        ulTaskNotifyTakeIndexed( 0, /*使用第0个通知 */
                                 pdTRUE, /*在退出前清除通知值。
                                                     在退出之前。*/
                                 portMAX_DELAY ); /*无限期地阻塞。*/

        /* RTOS的任务通知是作为二进制(而不是计数)信号使用的。
        计时)信号,所以只有当外设中所有待处理的事件都完成后,才会返回去等待进一步的通知。
        当外设中所有待处理的事件都被处理后,才回去等待进一步的通知。*/
        do
        {
    
    
            xEvent = xQueryPeripheral();
            if( xEvent != NO_MORE_EVENTS )
            {
    
    
                vProcessPeripheralEvent( xEvent );
            }
        } while( xEvent != NO_MORE_EVENTS );
    }
}

四、xTaskNotify()、xTaskNotifyIndexed()

API原型:

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

BaseType_t xTaskNotifyIndexed( TaskHandle_t xTaskToNotify, 
                               UBaseType_t uxIndexToNotify, 
                               uint32_t ulValue, 
                               eNotifyAction eAction );

  [如果你使用 RTOS 任务通知来实现二进制或计数 信号量类型行为,请使用更简单的 xTaskNotifyGive() API 函数,而不是 xTaskNotify()]
  每个任务都有一个“任务通知”数组(或简称为“通知”),每个 通知都包含状态和一个 32 位值。 直达任务通知是直接发送至任务的事件, 可以取消接收任务的阻塞状态,还 可以选择通过多种方式更新接收任务的某个通知值。 例如,通知可覆写接收任务的某个通知值,或仅设置 接收任务的通知值中的一个或多个比特位。
  xTaskNotify() 用于将事件 直接发送到 RTOS 任务并可能取消该任务的阻塞状态,还可选择 以下列方式更新接收任务的通知值:

  • 将 32 位数字写入通知值
  • 添加一个(递增)通知值
  • 设置通知值中的一个或多个位
  • 保持通知值不变
      xTaskNotify() 和 xTaskNotifyIndexed() 是等效的函数 - 唯一的区别 是 xTaskNotifyIndexed() 可以对数组内的任何任务通知进行操作, 而 xTaskNotify() 始终对数组索引 0 处的任务通知进行操作。
      不得从中断服务例程 (ISR) 调用此函数。 使用 xTaskNotifyFromISR() 代替。
      configUSE_TASK_NOTIFICATIONS 必须在 FreeRTOSConfig.h 中设置为 1(或保留为未定义)以使这些函数可用。 常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 设置 任务通知的每个任务数组中的索引数量。
    向后兼容性信息:
      在 FreeRTOS V10.4.0 之前,每个任务有一个单一的“通知值”,且 所有任务通知 API 函数都在该值上运行。用通知值的数组 更换单个通知值需要 新的 API 函数集,该函数集应能在数组内处理 数组中的特定通知。 xTaskNotify() 是原始 API 函数,并且 通过始终对数组中索引 0 处的通知值进行操作来保持向后兼容 。调用 xTaskNotify() 相当于调用 xTaskNotifyIndexed(), 其 uxIndexToNotify 参数设置为 0。
    参数:
      xTaskToNotify 正在通知的 RTOS 任务的句柄。 这是目标任务。
    要获取任务句柄, xTaskCreate() 创建任务并使用 pxCreatedTask 参数,或使用返回值创建任务 xTaskCreateStatic() 并存储该值,或在 xTaskGetHandle() 调用中使用任务的名称。
      当前执行的 RTOS 任务的句柄通过以下方式 由 xTaskGetCurrentTaskHandle() API 函数返回。
      uxIndexToNotify 目标任务通知值数组中的索引, 通知值将发送给该索引。
      uxIndexToNotify 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。
      xTaskNotify() 没有此参数,并且始终向索引 0 发送通知。
      ulValue 用于更新目标任务的通知值。 请参阅下面 eAction 参数的说明。
      eAction 一种枚举类型,可以采用 下表中记录的值之一来执行相关操作。
      eAction 设置 已执行的操作
      eNoAction 目标任务接收事件,但其 通知值未更新。 在这种情况下,不使用 ulValue。
      eSetBits 目标任务的通知值 使用 ulValue 按位或运算。 例如, 如果 ulValue 设置为 0x01,则将在 目标任务的通知值中设置位 0。 同样,如果 ulValue 为 0x04,则将在 目标任务的通知值中设置位 2。 通过这种方式,RTOS 任务通知机制 可以用作 事件组的轻量级替代方案。
      eIncrement 目标任务的通知值 自增 1,使得调用 xTaskNotify() 相当于调用 xTaskNotifyGive()。 在这种情况下,不使用 ulValue。
      eSetValueWithOverwrite 目标任务的通知值 无条件设置为 ulValue。 因此, RTOS 任务通知机制可用作 xQueueOverwrite() 的轻量级替代方案。
      eSetValueWithoutOrwrite 如果目标任务没有 挂起的通知,则其通知值 将设置为 ulValue。
    如果目标任务已经有 挂起的通知,则不会更新其通知值, 因为这样做会覆盖 之前使用的值。 在这种情况下, 调用 xTaskNotify() 失败,返回 pdFALSE。
    因此,RTOS 任务通知机制可 在长度为 1 的队列上用作 xQueueSend() 在长度为 的轻量级替代方案。
    返回值:
    除 eAction 设置为 eSetValueWithoutOverwrite 且目标任务的通知值 因目标任务已有挂起的通知而无法更新之外, 在所有情况下均返回 pdPASS。
    用法示例:
/* 设置由xTask1Handle引用的任务的第0个通知值中的第8位。
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
{
    
    
    /* 该任务的通知值没有被更新。*/
}

五、xTaskNotifyAndQuery()、xTaskNotifyAndQueryIndexed()

API原型:

BaseType_t xTaskNotifyAndQuery( TaskHandle_t xTaskToNotify,
                                uint32_t ulValue,
                                eNotifyAction eAction,
                                uint32_t *pulPreviousNotifyValue );

BaseType_t xTaskNotifyAndQueryIndexed( TaskHandle_t xTaskToNotify, 
                                       UBaseType_t uxIndexToNotify, 
                                       uint32_t ulValue, 
                                       eNotifyAction eAction, 
                                       uint32_t *pulPreviousNotifyValue );

  xTaskNotifyAndQueryIndexed() 执行的 操作与 xTaskNotifyIndexed() 相同, 还会在附加的 pulPreviousNotifyValue 参数中 返回目标任务的上一个通知值 (调用函数时的通知值,而不是函数返回时的通知值)。
  xTaskNotifyAndQuery() 执行的 操作与 xTaskNotify() 相同, 同样,还会在附加的 pulPreviousNotifyValue 参数中 返回目标任务的上一个通知值 (调用函数时的通知值, 而不是函数返回时的通知值)。
  不得从中断服务程序 (ISR) 调用此函数。 使用 xTaskNotifyAndQueryFromISR() 代替。
参数:
  xTaskToNotify 正在通知的 RTOS 任务的句柄。 这是目标任务。
要获取任务句柄, xTaskCreate() 创建任务并使用 pxCreatedTask 参数,或使用返回值创建任务 xTaskCreateStatic() 并存储该值,或在 xTaskGetHandle() 调用中使用任务的名称。
  当前执行的 RTOS 任务的句柄通过以下方式 由 xTaskGetCurrentTaskHandle() API 函数返回。
  uxIndexToNotify 目标任务通知值数组中的索引, 通知值将发送给该索引。
uxIndexToNotify 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。
  ulValue 用于更新目标任务的通知值。 请参阅下面 eAction 参数的说明。
  eAction 一种枚举类型,可以采用 下表中记录的值之一来执行相关操作。
  pulPreviousNotifyValue 可用于在 xTaskNotifyAndQuery() 操作修改任何位之前 传递目标任务的通知值。pulPreviousNotifyValue 是一个可选参数, 如不需要,可设置为 NULL。 如果未使用 pulPreviousNotifyValue, 则考虑使用 xTaskNotify() 来 代替 xTaskNotifyAndQuery()。
  eNoAction 目标任务接收事件,但其 通知值未更新。 在这种情况下,不使用 ulValue。
  eSetBits 目标任务的通知值 使用 ulValue 按位或运算。 例如, 如果 ulValue 设置为 0x01,则将在 目标任务的通知值中设置位 0。 同样,如果 ulValue 为 0x04,则将在 目标任务的通知值中设置位 2。 通过这种方式,RTOS 任务通知机制 可以用作 事件组的轻量级替代方案。
  eIncrement 目标任务的通知值 按 1 递增,使调用 xTaskNotify() 相当于调用 xTaskNotifyGive()。 在这种情况下,不使用 ulValue。
  eSetValueWithOverwrite 目标任务的通知值 无条件设置为 ulValue。 因此, RTOS 任务通知机制可用作 xQueueOverwrite() 的轻量级替代方案。
  eSetValueWithoutOrwrite 如果目标任务没有 挂起的通知,则其通知值 将设置为 ulValue。
  如果目标任务已经有 挂起的通知,则不会更新其通知值, 因为这样做会覆盖 之前使用的值。 在这种情况下, 调用 xTaskNotify() 失败,返回 pdFALSE。
  因此,RTOS 任务通知机制可 在长度为 1 的队列上用作 xQueueSend() 在长度为 的轻量级替代方案。
返回值:
  除 eAction 设置为 eSetValueWithoutOverwrite 且目标任务的通知值 因目标任务已有挂起的通知而无法更新之外, 在所有情况下均返回 pdPASS。
用法示例:

uint32_t ulPreviousValue;

/* 设置由xTask1Handle引用的任务的第0个通知值中的第8位。
xTask1Handle所引用的任务的第0个通知值的第8位。将该任务的前一个0号通知值 
存储在ulPreviousValue中(在第8位被设置之前)。*/
xTaskNotifyAndQueryIndexed( xTask1Handle, 
                            0, 
                            ( 1ul << 8ul ), 
                            eSetBits, 
                            &ulPreviousValue);

/* 向xTask2Handle所引用的任务发送通知。
潜在地将该任务从阻塞状态中移除,但不更新该任务的通知值。
更新该任务的通知值。 存储任务的通知 
值存储在 ulPreviousValue 中。*/
xTaskNotifyAndQuery( xTask2Handle, 0, eNoAction, &ulPreviousValue );

/* 将xTask3Handle所引用的任务的通知值设置为0x50,即使该任务是由xTask3Handle引用的。
的任务的通知值设置为0x50,即使该任务没有读取其先前的通知值。
任务的前一个通知值是没有意义的,所以最后一个参数被设置为NULL。
参数被设置为NULL。*/
xTaskNotifyAndQuery( xTask3Handle, 0x50, eSetValueWithOverwrite, NULL ); 

/* 设置由xTask4Handle引用的任务的通知值 
到0xfff。
但是,只有在这样做不会覆盖该任务现有的通知值的情况下,才能这样做。
值之前(通过调用xTaskNotifyWait()
或 ulTaskNotifyTake()的调用)。 任务的前一个通知值被保存在
ulPreviousValue中。*/
if( xTaskNotifyAndQuery( xTask4Handle,
                         0xfff,
                         eSetValueWithoutOverwrite,
                         &ulPreviousValue ) == pdPASS )
{
    
    
    /* 该任务的通知值被更新。*/
}
else
{
    
    
    /* 该任务的通知值没有被更新。*/
}

六、xTaskNotifyAndQueryFromISR()、xTaskNotifyAndQueryIndexedFromISR()

API原型:

BaseType_t xTaskNotifyAndQueryFromISR( 
                     TaskHandle_t xTaskToNotify,
                     uint32_t ulValue,
                     eNotifyAction eAction,
                     uint32_t *pulPreviousNotifyValue,
                     BaseType_t *pxHigherPriorityTaskWoken );

BaseType_t xTaskNotifyAndQueryIndexedFromISR( 
                     TaskHandle_t xTaskToNotify,
                     UBaseType_t uxIndexToNotify
                     uint32_t ulValue,
                     eNotifyAction eAction,
                     uint32_t *pulPreviousNotifyValue,
                     BaseType_t *pxHigherPriorityTaskWoken );

  xTaskNotifyAndQueryIndexedFromISR() 执行的操作 与xTaskNotifyIndexedFromISR() 执行的操作相同,此外, 还会在附加的 pulPreviousNotifyValue 参数中 返回目标任务的先前通知值 (函数被调用时的通知值,而不是函数返回时的通知值)。
  xTaskNotifyAndQueryFromISR() 执行的操作与 xTaskNotifyFromISR() 执行的操作相同,此外 还会在附加的 pulPreviousNotifyValue 参数中 返回目标任务的先前通知值 (函数被调用时的通知值,而不是函数返回时的通知值)。
参数:
  xTaskToNotify 正在通知的 RTOS 任务的句柄。 这是目标任务。
要获取任务句柄, xTaskCreate() 创建任务并使用 pxCreatedTask 参数,或使用返回值创建任务 xTaskCreateStatic() 并存储该值,或在 xTaskGetHandle() 调用中使用任务的名称。
  当前执行的 RTOS 任务的句柄通过以下方式 由 xTaskGetCurrentTaskHandle() API 函数返回。
  uxIndexToNotify 目标任务通知值数组中的索引, 通知值将发送给该索引。
  uxIndexToNotify 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。
  ulValue 用于更新目标任务的通知值。 请参阅下面 eAction 参数的说明。
  eAction 一种枚举类型,可以采用 下表中记录的值之一来执行相关操作。
  pulPreviousNotifyValue 可用于在xTaskNotifyAndQuery()操作修改任何位之前 传递目标任务的通知值。pulPreviousNotifyValue是一个可选参数, 如不需要,可设置为 NULL。 如果未使用 pulPreviousNotifyValue, 则考虑使用 xTaskNotify() 来代替xTaskNotifyAndQuery()。
  pxHigherPriorityTaskWoken * pxHigherPriorityTaskWoken必须初始化为pdFALSE (0)。
xTaskNotifyAndQueryFromISR ()将设置* pxHigherPriorityTaskWoken 如果发送通知导致一个任务解除阻塞,且解除阻塞的任务的优先级高于当前正在运行的任务, 那么xTaskNotifyFromISR()将把*pxHigherPriorityTaskWoken设置 为pdTRUE。
  如果xTaskNotifyAndQueryFromISR()将该值设置为pdTRUE 那么应该在中断退出之前请求 切换上下文。请参阅以下示例。
  pxHigherPriorityTaskWoken 为可选参数,且可 设置为 NULL。
  eNoAction 目标任务接收事件,但其 通知值未更新。 在这种情况下,不使用 ulValue。
  eSetBits 目标任务的通知值 使用 ulValue 按位或运算。 例如, 如果 ulValue 设置为 0x01,则将在 目标任务的通知值中设置位 0。 同样,如果 ulValue 为 0x04,则将在 目标任务的通知值中设置位 2。 通过这种方式,RTOS 任务通知机制 可以用作 事件组的轻量级替代方案。
eIncrement 目标任务的通知值 按 1 递增,使调用 xTaskNotify() 相当于调用 xTaskNotifyGive()。 在这种情况下,不使用 ulValue。
  eSetValueWithOverwrite 目标任务的通知值 无条件设置为 ulValue。 因此, RTOS 任务通知机制可用作 xQueueOverwrite() 的轻量级替代方案。
  eSetValueWithoutOrwrite 如果目标任务没有 挂起的通知,则其通知值 将设置为 ulValue。
  如果目标任务已经有 挂起的通知,则不会更新其通知值, 因为这样做会覆盖 之前使用的值。 在这种情况下, 调用 xTaskNotify() 失败,返回 pdFALSE。
  因此,RTOS 任务通知机制可 在长度为 1 的队列上用作 xQueueSend() 在长度为 的轻量级替代方案。
返回值:
  除 eAction 设置为 eSetValueWithoutOverwrite 且目标任务的通知值 因目标任务已有挂起的通知而无法更新之外, 在所有情况下均返回 pdPASS。
用法示例:

vAnISR( void )
{
    
    
	/* 必须初始化为pdFALSE! */
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
	uint32_t ulPreviousValue;

    /* 设置由xTask1Handle引用的任务的第0个通知值中的第8位。
    xTask1Handle所引用的任务的第0个通知值的第8位。将该任务的前一个0号通知值 
    (在第8位被设置之前)存储在ulPreviousValue中。*/
    xTaskNotifyAndQueryIndexedFromISR( xTask1Handle,
                                       0,
                                       ( 1ul << 8ul ),
                                       eSetBits,
                                       &ulPreviousValue,
                                       &xHigherPriorityTaskWoken);

    /* 该任务的前一个通知值被保存在 
    ulPreviousValue。*/

    /* 如果xTask1Handle所引用的任务处于阻塞状态,正在等待通知,那么它将会成为一个新的任务。
    处于阻塞状态,等待通知,那么它现在将被从阻塞状态移到 
    从阻塞状态移到就绪状态。 如果它的优先级 
    高于当前正在执行的任务(即被中断的任务)的优先级。
    任务),那么xHigherPriorityTaskWoken将被设置为pdTRUE。
    已经被设置为pdTRUE,并将该变量传入对 
    的调用中传递这个变量,会导致中断直接返回到 
    到未受阻的任务。 如果xHigherPriorityTaskWoken仍为pdFALSE 
    那么将其传入portYIELD_FROM_ISR()将不会有任何影响。*/

    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

七、xTaskNotifyFromISR()、xTaskNotifyIndexedFromISR()

API原型:

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

BaseType_t xTaskNotifyIndexedFromISR( TaskHandle_t xTaskToNotify, 
                                      UBaseType_t uxIndexToNotify, 
                                      uint32_t ulValue, 
                                      eNotifyAction eAction, 
                                      BaseType_t *pxHigherPriorityTaskWoken );

  可从中断服务程序 (ISR) 中使用的 xTaskNotify() 和 xTaskNotifyIndexed() 版本。请参阅 xTaskNotify() API 函数的文档页面,了解有关其操作的描述和必要的配置参数,以及向后兼容性信息。
参数:
  xTaskToNotify 正在通知的 RTOS 任务的句柄。 这是目标任务。
要获取任务句柄, xTaskCreate() 创建任务并使用 pxCreatedTask 参数,或使用返回值创建任务 xTaskCreateStatic() 并存储该值,或在 调用 xTaskGetHandle() 中使用任务的名称。
  当前执行的 RTOS 任务的句柄通过以下方式 由 xTaskGetCurrentTaskHandle() API 函数返回。
  uxIndexToNotify 目标任务通知值数组中的索引, 通知值将发送给该索引。
  uxIndexToNotify 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。
  xTaskNotifyFromISR() 中没有此参数,总是向索引 0 发送通知。
  ulValue 用于更新目标任务的通知值。 请参阅下面 eAction 参数的说明。
  eAction 一种枚举类型,可以采用 下表中记录的值之一来执行相关操作。
  pxHigherPriorityTaskWoken *pxHigherPriorityTaskWoken 必须初始化为 0。
如果发送通知导致某个任务解除阻塞, 且被解除阻塞的任务的优先级高于当前运行的任务, xTaskNotifyFromISR() 会将 *pxHigherPriorityTaskWoken 设置为 pdTRUE。
  如果 xTaskNotifyFromISR() 将此值设置为 pdTRUE, 则应在中断退出之前请求上下文切换 。 请参阅以下示例。
  pxHigherPriorityTaskWoken 为可选参数,且可 设置为 NULL。
  eNoAction 目标任务接收事件,但其 通知值未更新。 在这种情况下,不使用 ulValue。
  eSetBits 目标任务的通知值 使用 ulValue 按位或运算。 例如, 如果 ulValue 设置为 0x01,则将在 目标任务的通知值中设置位 0。 同样,如果 ulValue 为 0x04,则将在 目标任务的通知值中设置位 2。 通过这种方式,RTOS 任务通知机制 可以用作 事件组的轻量级替代方案。
  eIncrement 目标任务的通知值将 按 1 递增,使调用 xTaskNotifyFromISR() 相当于调用 vTaskNotifyGiveFromISR()。 在这种情况下,不使用 ulValue。
  eSetValueWithOverwrite 目标任务的通知值 无条件设置为 ulValue。 因此, RTOS 任务通知机制可用作 xQueueOverwrite() 的轻量级替代方案。
  eSetValueWithoutOrwrite 如果目标任务没有 挂起的通知,则其通知值 将设置为 ulValue。
  如果目标任务已经有 挂起的通知,则不会更新其通知值, 因为这样做会覆盖 之前使用的值。 在这种情况下,调用 xTaskNotify() 会失败。
  通过这种方式,RTOS 任务通知机制 在长度为 1 的队列上用作 xQueueSend() 的轻量级替代方案。
返回值:
  除了 eAction 被设置为 eSetValueWithoutOverwrite 且目标任务的通知值 因目标任务已有挂起的通知而无法更新之外, 在所有情况下均返回 pdPASS。
用法示例:
  此示例演示了如何结合使用 xTaskNotifyFromISR() 和 eSetBits 操作。 请参阅 xTaskNotify() API 文档页面, 查看有关如何使用 eNoAction、eSetValueWithOverwrite 和 eSetValueWithoutOverwrite 行为的示例。

/* 中断处理程序本身并不执行任何处理。 相反,它
它解锁一个高优先级的任务,在这个任务中,产生中断的事件被处理。
中断的事件被处理。 如果该任务的优先级足够高,那么中断将直接返回到该任务。
中断将直接返回到该任务(所以它将中断一个任务,但是
返回到一个不同的任务),所以处理过程将在时间上连续发生--就像所有的处理过程都是连续的一样。
就像所有的处理都是在中断处理程序本身中完成的一样。
中断外设的状态是通过RTOS的任务
通知。*/
void vANInterruptHandler( void )
{
    
    
	BaseType_t xHigherPriorityTaskWoken;
	uint32_t ulStatusRegister;

    /* 读取中断状态寄存器,每个中断源有一个位。
    源(例如,可能是一个Rx位,一个Tx位,一个缓冲区超限位,等等。*/
    ulStatusRegister = ulReadPeripheralInterruptStatus();

    /* 清除中断。*/
    vClearPeripheralInterruptStatus( ulStatusRegister );

    /* xHigherPriorityTaskWoken必须被初始化为pdFALSE。 如果调用
    xTaskNotifyFromISR()解除了处理任务的封锁,并且处理任务的优先级高于
    处理任务的优先级高于当前运行任务的优先级。
    那么xHigherPriorityTaskWoken将自动被设置为pdTRUE。*/
    xHigherPriorityTaskWoken = pdFALSE;

    /* 解除对处理任务的封锁,以便该任务可以执行任何由中断引起的必要处理。
    xHandlingTask是任务的句柄,它是在创建任务时获得的。
    任务的句柄,该句柄是在创建该任务时获得的。 处理任务的第0个通知值
    与中断状态进行比特运算 - 确保已经设置的位不会被覆盖。
    的位不会被覆盖。*/
    xTaskNotifyIndexedFromISR( xHandlingTask,
                               0,
                               ulStatusRegister,
                               eSetBits,
                               xHigherPriorityTaskWoken);

    /* 如果xHigherPriorityTaskWoken现在被设置为pdTRUE,则强制进行上下文切换。
    用来做这件事的宏取决于端口,可以称为
    portEND_SWITCHING_ISR。*/
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

/* ----------------------------------------------------------- */

/* 一个等待通知外设需要服务的任务阻塞。
每当它被通知处理外设中所有待处理的事件时
处理外设中所有待处理的事件。*/
void vHandlingTask( void *pvParameters )
{
    
    
	uint32_t ulInterruptStatus;

    for( ; ; )
    {
    
    
        /* 无限地阻塞(没有超时,所以不需要检查函数的
        返回值)来等待一个通知。 注意!真正的应用程序
        不应无限期阻塞,而应偶尔超时,以便
        来处理错误情况,这些错误情况可能会阻止中断发送
        任何更多的通知。*/
        xTaskNotifyWaitIndexed( 0, /*等待第0次通知 */
                                0x00, /*在进入时不要清除任何位。*/
                                ULONG_MAX, /* 退出时清除所有位。*/
                                &ulInterruptStatus, /*接收通知值。*/
                                portMAX_DELAY ); /*无限期地阻塞。*/

        /* 处理收到的通知值中设置的任何位。 这假定
        外设设置位1为Rx中断,位2为Tx中断。
        第3位是缓冲区超限中断。*/
        if( ( ulInterruptStatus & 0x01 ) != 0x00 )
        {
    
    
            prvProcessRxInterrupt();
        }

        if( ( ulInterruptStatus & 0x02 ) != 0x00 )
        {
    
    
            prvProcessTxInterrupt();
        }

        if( ( ulInterruptStatus & 0x04 ) != 0x00 )
        {
    
    
            prvClearBufferOverrun();
        }
    }
}

八、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 );

  每个任务都有一组“任务通知” (或仅“通知” ),每个 通知都包含状态和一个 32 位值。 直达任务通知是直接发送至任务的事件, 可以取消阻塞接收任务,并 可以选择通过多种方式更新接收任务的某个通知值。 例如,通知可覆盖接收任务的一个通知值,或仅设置 接收任务的一个通知值中的一位或多位。
  xTaskNotifyWait() 会等待调用任务接收通知,并有可选的超时。 如果等待的通知到达时,相关的接收 RTOS 任务已被阻塞且正在等待通知,则 接收 RTOS 任务将解除阻塞,通知也将清除。
  注意:数组中每个通知都独立运行——在数组中的一个通知上一次只能阻塞一个任务,并且该任务不会被发送到任何其他数组索引的通知取消阻塞。
  xTaskNotifyWait() 和 xTaskNotifyWaitIndexed() 是等效的宏,它们唯一的区别 是 xTaskNotifyWaitIndexed() 可以在数组内的任何任务通知上运行, 而 xTaskNotifyWait() 始终在数组索引 0 处的任务通知上运行。
  xTaskNotifyGive() 不能从中断服务程序调用。 使用 vTaskNotifyGiveFromISR() 代替。
  configUSE_TASK_NOTIFICATIONS 必须在 FreeRTOSConfig.h 中设置为 1(或保留为未定义)才能使用这些宏。 常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 设置 任务通知的每个任务数组中的索引数量。
  向后兼容性信息:
  在 FreeRTOS V10.4.0 之前,每个任务有一个单一的“通知值”,且 所有任务通知 API 函数都在该值上运行。用通知值的数组 更换单个通知值需要 新的 API 函数集,该函数集应能在数组内处理 特定通知。 xTaskNotifyWait() 是原始 API 函数,并 通过始终在数组内索引 0 处的通知值上运行来保持 向后兼容。调用 xTaskNotifyWait() 等于调用 xTaskNotifyWaitIndexed(),其中 uxIndexToWaitOn 参数设置为 0。
参数:
  uxIndexToWaitOn 调用任务的数组中通知值的索引, 调用任务将在该通知值上等待接收 通知。
  uxIndexToWaitOn 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。
  xTaskNotifyWait() 没有此参数,总是在索引 0 处等待通知。
  ulBitsToClearOnEntry 在 xTaskNotifyWait() 函数入口, ulBitsToClearOnEntry 中设置的任何位都将在调用 RTOS 任务通知值中 被清除(在任务等待新通知之前), 前提是在调用 xTaskNotifyWait() 时通知尚未 挂起。
例如,如果 ulBitsToClearOnEntry 为 0x01, 则任务通知值的位 0 将在函数入口 被清除。
  将 ulBitsToClearOnEntry 设置为 0xffffffff (ULONG_MAX) 将 清除任务通知值中的所有位,有效 将值清除为 0。
  ulBitsToClearOnExit 在 xTaskNotifyWait() 函数退出之前, ulBitsToClearOnExit 中设置的任何位都将在调用 RTOS 任务通知值中 被清除,前提是接收到通知。
在 RTOS 任务通知值保存到 *pulNotificationValue 中 之后清除位 (请参阅下面的 pulNotificationValue 的说明)。
  例如,如果 ulBitsToClearOnExit 为 0x03,则位 0 和 位 1 将在函数退出之前 清除。
  将 ulBitsToClearOnExit 设置为 0xffffffff (ULONG_MAX) 将 清除任务通知值中的所有位,有效 将值清除为 0。
  pulNotificationValue 用于传出 RTOS 任务的通知值。 在由于 ulBitsToClearOnExit 的设置清除任何位之前,复制到 *pulNotificationValue 的值是 RTOS 任务的 通知值 。
如果不需要通知值,则设置 pulNotificationValue 为 NULL。
  xTicksToWait 在阻塞状态下等待接收通知的最长时间, 前提是在调用 xTaskNotifyWait() 时通知尚未 挂起。
处于阻塞状态的 RTOS 任务不会消耗 任何 CPU 时间。
  时间以 RTOS tick 周期为单位。 宏 pdMS_TO_TICKS() 可以 将以毫秒为单位的时间 转换成以 tick 为单位的时间。
返回值:
  在调用 xTaskNotifyWait() 时,如果收到通知, 或通知已经挂起,则返回 pdTRUE。
  如果调用 xTaskNotifyWait() 在收到通知之前超时, 则返回 pdFALSE。
用法示例:

/* 该任务显示了RTOS任务通知值中的位,被用来将不同的事件传递给任务,其方式与事件组中的标记可能是一样的。
的方式将不同的事件传递给任务,就像事件组中的标志可能被用于相同的目的一样。
的方式将不同的事件传递给任务,就像事件组中的标志可能被用于相同的目的一样。*/
void vAnEventProcessingTask( void *pvParameters )
{
    
    
	uint32_t ulNotifiedValue;
    for( ; ; )
    {
    
    
        /*无限期地阻塞(没有超时,所以不需要检查函数的
        返回值)来等待一个通知。

        这个RTOS任务的通知值中的位被通知的任务和中断所设置,以指示哪些任务和中断。
        任务和中断设置,以表明哪些事件已经发生。*/
        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();
        }

        /* 等等。*/
    }
}

九、xTaskNotifyStateClear()、xTaskNotifyStateClearIndexed()

API原型:

BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask );

BaseType_t xTaskNotifyStateClearIndexed( TaskHandle_t xTask, 
                                         UBaseType_t uxIndexToClear );

  每个 RTOS 任务都有一个任务通知数组。 每条任务通知 都有通知状态 ,可以是“挂起”或“非挂起” , 以及一个 32 位通知值。
  如果通知被发送到通知数组中的索引,那么 该索引处的通知被称为“待定” ,直到任务读取 其通知值,或通过调用 xTaskNotifyStateClear () 将通知状态明确清除为“非挂起”为止 。
  xTaskNotifyStateClear () 和 xTaskNotifyStateClearIndexed () 是等效宏——唯一的区别 是 xTaskNotifyStateClearIndexed () 可以在数组内任何任务通知上运行,而 xTaskNotifyStateClear () 始终在数组索引 0 处的任务通知上运行。
  configUSE_TASK_NOTIFICATIONS 必须在 FreeRTOSConfig.h 中设置为 1(或保留为未定义)才能使用这些宏。 常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 设置 任务通知的每个任务数组中的索引数量。
向后兼容性信息:
  在 FreeRTOS V10.4.0 之前,每个任务有一个单一的“通知值”,且 所有任务通知 API 函数都在该值上运行。用通知值的数组 更换单个通知值需要 新的 API 函数集,该函数集应能在数组内处理 。 xTaskNotifyStateClear () 是原始 API 函数,并且 通过始终在数组内索引 0 处的通知值上操作来保留向后兼容性 。调用 xTaskNotifyStateClear () 等于调用 xTaskNotifyStateClearIndexed (),其中 uxIndexToNotify 参数设置为 0。
参数:
  xTask 将收到此通知的 RTOS 任务的句柄 状态已清除。 将 xTask 设置为 NULL 以清除通知 调用任务的状态。
要获取任务句柄,请使用 xTaskCreate() 创建任务并使用 pxCreatedTask 参数,或使用返回值创建任务 xTaskCreateStatic() 并存储该值,或在 调用 xTaskGetHandle() 中使用任务的名称。
  当前执行的 RTOS 任务的句柄通过以下方式 由 xTaskGetCurrentTaskHandle() API 函数返回。
  uxIndexToClear 待采取行动通知值索引内的目标任务数组 。 例如,将   uxIndexToClear 设置为 1 将清除数组内索引为 1 时的通知状态。
  uxIndexToClear must be less than configTASK_NOTIFICATION_ARRAY_ENTRIES。
ulTaskNotifyStateClear() 没有此参数,并且始终作用于 索引 0 的通知上。
返回值:
  如果 xTask 引用的任务有挂起的通知,则通知 已清除,然后返回 pdTRUE。 如果 xTask 引用的任务 有待处理的通知,那么返回 pdFALSE。
用法示例:

/* 一个UART发送函数的例子。 该函数启动一个UART传输,然后
等待传输完成的通知。 传输
完成的通知是由UART中断发送的。 调用任务的
通知状态在传输开始前被清空,以确保在任务尝试传输前,它不会被同时挂起。
在任务试图阻止它的通知状态之前,它不可能恰好已经挂起。
通知状态。*/
void vSerialPutString( const signed char * const pcStringToSend,unsigned short usStringLength )
{
    
    
	const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 5000 );

    /* xSendingTask持有等待传输完成的任务的句柄。
    完成。 如果xSendingTask为NULL,那么传输就没有在进行。
    除非前一个字符串的传输完成,否则不要开始发送新的字符串。
    完成后才开始发送新的字符串。*/
    if( ( xSendingTask == NULL ) && ( usStringLength > 0 ) )
    {
    
    
        /* 确保调用任务的第0个通知状态不已经是
        挂起。*/
        xTaskNotifyStateClearIndexed( NULL, 0 );

        /* 存储发送任务的句柄。 这将用于在传输完成后解除对
        传输完成后,用于解除对该任务的封锁。*/
        xSendingTask = xTaskGetCurrentTaskHandle();

        /* 开始发送字符串--然后由一个中断来控制传输。
        中断控制。*/
        UARTSendString( pcStringToSend, usStringLength );

        /* 在阻塞状态下等待(所以不使用任何CPU时间),直到UART
        ISR向xSendingTask发送第0个通知,以便在传输完成后通知(和解除封锁)该任务。
        任务(并解除封锁)。*/
        ulTaskNotifyTake( 0, pdTRUE, xMaxBlockTime );
    }
}

十、ulTaskNotifyValueClear()、ulTaskNotifyValueClearIndexed()

API原型:

uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, 
                                 uint32_t ulBitsToClear );

uint32_t ulTaskNotifyValueClearIndexed( TaskHandle_t xTask, 
                                        UBaseType_t uxIndexToClear,
                                        uint32_t ulBitsToClear );

  每个 RTOS 任务都有一个任务通知数组。 每条任务通知 都有通知状态 ,可以是“挂起”或“非挂起” , 以及一个 32 位通知值。
  ulTaskNotifyValueClearIndexed() 清除 xTask 引用的任务的数组索引 uxIndexToClear 的通知值中 ulBitsToClear 位掩码指定的位。
  ulTaskNotifyValueClear() 和 ulTaskNotifyValueClearIndexed() 是等效的宏 - 唯一的区别 是 ulTaskNotifyValueClearIndexed() 可以对 数组中的任何任务通知进行操作,而 ulTaskNotifyValueClear() 始终在数组索引 0 处对任务通知进行操作。
  configUSE_TASK_NOTIFICATIONS 必须在 FreeRTOSConfig.h 中设置为 1(或保留为未定义)才能使用这些宏。 常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 设置 每个任务的任务通知数组中的索引数。
参数:
  xTask 将清除其通知值中的位的 RTOS 任务的句柄。将 xTask 设置为 NULL 以清除调用任务的通知值中的位。
  若要获取任务句柄,请使用 xTaskCreate() 创建任务并使用 pxCreatedTask 参数,或使用 xTaskCreateStatic() 创建任务并存储返回值,或在调用 xTaskGetHandle() 时使用任务名称。
  当前执行的 RTOS 任务的句柄由 xTaskGetCurrentTaskHandle() API 函数返回。
  uxIndexToClear 目标任务的通知值数组中的索引, 用于清除其中的位。 uxIndexToClear 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。 ulTaskNotifyValueClear() 没有此参数,并且始终清除 索引 0 处的通知值中的位。
  ulBitsToClear 要在 xTask 的通知值中清除的位的位掩码 。将位设置为 1 可清除任务的通知值中的相应位 。 将 ulBitsToClear 设置为 0xffffffff(32 位架构上的 UINT_MAX)以 将通知值清除为 0。 将 ulBitsToClear 设置为 0 以查询任务的 通知值,而不清除任何位。
返回值:
  清除 ulBitsToClear 指定的位之前的目标任务的通知值。
用法示例:

#define MESSAGE_RECEIVED_BIT 8
#define TICKS_UNTIL_TIMEOUT 100

unsigned long ulNotification, ulMessageReceivedMask;

/* 清除任何信息接收事件。*/
ulMessageReceivedMask = 1u << MESSAGE_RECEIVED_BIT;
ulTaskNotifyValueClear( ulMessageReceivedMask );

/* 发送一个期望得到响应的消息。*/
send_message();

/* 阻止这个任务,直到它有另一个待定通知。在这个例子中。
该任务只使用其通知值的MESSAGE_RECEIVED_BIT。
所以下一个事件只能是收到的消息。*/
xTaskNotifyWait( 0u, /*进入时不要清除任何通知位。*/
                    0u, /*退出时不清除任何通知位。 */
                    &ulNotification,
                    ticks_until_timeout );

/* 如果没有超时,那么就收到了唯一可能的事件。
在这个例子中,这就是MESSAGE_RECEIVED_EVENT。*/
if( ulNotification == 0u ) 
{
    
    
    /* 处理响应超时的问题。*/
    process_response_timeout();
} 
else if( ulNotification == ulMessageReceivedMask )
{
    
    
    /* 处理响应事件。*/
    process_response();
    ulTaskNotifyValueClear( ulMessageReceivedMask );
} 
else
{
    
    
    /* 该示例任务应该只接收MESSAGE_RECEIVED_EVENTS。*/
    process_error();
}

十一、综合示例

任务通知

/**
 * @file 17_NotificationSync.c
 * @author WSP
 * @brief 任务通知同步
 * @version 0.1
 * @date 2022-10-23
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_NotificationSync";

/**
 * @brief   Task_One
 * @param   arg
 * @return  NULL
*/
void Task_One(void *arg)
{
    
    
    while (1) {
    
    
        ESP_LOGI(TAG, "Task_One Notify wait");
        ulTaskNotifyTake(pdTRUE,portMAX_DELAY);
        ESP_LOGI(TAG, "Task_One Notify got");
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
/**
 * @brief   Task_Two
 * @param   arg
 * @return  NULL
*/
void Task_Two(void *arg)
{
    
    
    TaskHandle_t Task_One_Handle = (TaskHandle_t)arg;
    while (1) {
    
    
        vTaskDelay(5000/portTICK_PERIOD_MS);
        xTaskNotifyGive(Task_One_Handle);
        ESP_LOGI(TAG, "Task Two Notify Give");
    }
}

/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void NotificationSync_Init(void)
{
    
    
    TaskHandle_t Task_One_Handle;
    // 创建任务一
    xTaskCreate(Task_One,
                "Task_One",
                2048,
                NULL,
                1,
                &Task_One_Handle);
    if(Task_One_Handle != NULL) {
    
    
        // 创建任务二
        xTaskCreate(Task_Two,
                    "Task_Two",
                    2048,
                    (void *)&Task_One_Handle,
                    2,
                    NULL);
    }
    ESP_LOGI(TAG,"task Create ok");
    vTaskDelay(2000/portTICK_PERIOD_MS);        // 延时等待
}

任务通知同步值

/**
 * @file 19_NotificationValue.c
 * @author WSP
 * @brief 任务通知同步值 
 * @version 0.1
 * @date 2022-10-23
 *
 * @copyright Copyright (c) 2022
 *
 */
#include "FreeRTOS_Include.h"

const static char *TAG = "APP_NotificationValue";

/**
 * @brief   Task_One
 * @param   arg
 * @return  NULL
*/
void Task_One(void *arg)
{
    
    
    uint32_t ulNotifiedValue = 0;
    while (1) {
    
    
        /*此 RTOS 任务的通知值中的位由通知任务和中断设置,以指示发生了哪些事件。*/ 
        xTaskNotifyWaitIndexed( 0,                  /* 等待第 0 个通知。*/
                                0x00,               /* 进入时不清除任何通知位。*/ 
                                ULONG_MAX,          /* 退出时将通知值重置为 0。*/ 
                                &ulNotifiedValue,   /* 通知值在ulNotifiedValue 中传递。*/
                                portMAX_DELAY);     /* 无限期阻塞。*/ 

        /* 处理已锁定在通知值中的任何事件。*/
 
        if( ( ulNotifiedValue & 0x01 )) {
    
     
            /* 位 0 已设置 - 处理位 0 表示的任何事件。 */ 
            ESP_LOGI(TAG, "Task_One Notify  get 0x01");
        }

        if( ( ulNotifiedValue & 0x02 )) {
    
     
            /* 位 1 已设置 - 处理位 1 表示的任何事件。 */ 
            ESP_LOGI(TAG, "Task_One Notify  get 0x02");
        } 

        if( ( ulNotifiedValue & 0x04 )) {
    
     
            /* 位 2 已设置 - 处理位 2 表示的任何事件。 */ 
            ESP_LOGI(TAG, "Task_One Notify get 0x04");
            printf("\n\r");
        } 
        ESP_LOGI(TAG, "Task_One Notify get value:%d",ulNotifiedValue);
        vTaskDelay(1000/portTICK_PERIOD_MS);
    }
}
/**
 * @brief   Task_Two
 * @param   arg
 * @return  NULL
*/
void Task_Two(void *arg)
{
    
    
    TaskHandle_t Task_One_Handle = (TaskHandle_t)arg;
    while (1) {
    
    
        vTaskDelay(5000/portTICK_PERIOD_MS);
        /* 向 xTask2Handle 引用的任务发送通知,可能会将任务从阻塞状态中移除,但不会更新任务的通知值。*/ 
        xTaskNotify(Task_One_Handle, 0x01, eSetValueWithOverwrite);
        ESP_LOGI(TAG, "Task Two Notify Give 0x01");
        xTaskNotify(Task_One_Handle, 0x02, eSetValueWithOverwrite);
        ESP_LOGI(TAG, "Task Two Notify Give 0x02");
        xTaskNotify(Task_One_Handle, 0x04, eSetValueWithOverwrite);
        ESP_LOGI(TAG, "Task Two Notify Give 0x04");
        printf("\n\r");
    }
}

/**
 * @brief   创建函数初始化
 * @param   NULL
 * @return  NULL
 */
void NotificationValue_Init(void)
{
    
    
    TaskHandle_t Task_One_Handle;
    // 创建任务一
    xTaskCreate(Task_One,
                "Task_One",
                2048,
                NULL,
                1,
                &Task_One_Handle);
    if(Task_One_Handle != NULL) {
    
    
        // 创建任务二
        xTaskCreate(Task_Two,
                    "Task_Two",
                    2048,
                    (void *)&Task_One_Handle,
                    2,
                    NULL);
    }
    ESP_LOGI(TAG,"task Create ok");
    vTaskDelay(2000/portTICK_PERIOD_MS);        // 延时等待
}

猜你喜欢

转载自blog.csdn.net/believe666/article/details/127205659
今日推荐