建立一个购物网站平台费用,注册岩土工程师,怎么做网站的rss,北京4a广告公司有哪些目录
一、FreeRTOS 直达任务通知#xff1a;
#xff08;1#xff09;直达任务通知基本介绍#xff1a;
#xff08;2#xff09;更新目标通知的值#xff1a;
#xff08;3#xff09;性能优势和使用限制#xff1a;
二、直达任务通知 API#xff1a;
#…目录
一、FreeRTOS 直达任务通知
1直达任务通知基本介绍
2更新目标通知的值
3性能优势和使用限制
二、直达任务通知 API
1向任务发送通知
2向任务发送通知ISR
3唤醒阻塞中任务
4直接向任务发送通知
5向任务发送通知并查询任务的通知值
6向任务发送通知并查询任务的通知值ISR
7在中断上下文中发送通知给任务
8使任务等待接收通知
9清除指定任务的通知状态
10 清除任务通知值中指定的位
三、用作轻量级二进制信号量
1优点
2实现
3用法示例
四、用作轻量级计数信号量
1优点
2实现
3用法示例
五、用作轻量级事件组
1优点
2实现
3用法示例
六、用作轻量级邮箱
1限制
2实现
七、任务间通信 八、FreeRTOS教程示例代码下载 一、FreeRTOS 直达任务通知
从 FreeRTOS V8.2.0 开始可用直达任务通知。自 V10.4.0 起支持单任务多条通知。
1直达任务通知基本介绍
可以参考通过 FreeRTOS 通知减少 RAM 占用空间并加速执行。
每个 RTOS 任务都有一个任务通知数组。每个任务都有“挂起”或“非挂起”的通知状态和一个 32 位通知值。常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 设置任务通知数组中的索引数量。在 FreeRTOS V10.4.0 版本前任务只有单条任务通知而无通知数组。
直达任务通知是直接发送到任务的事件而不是通过中间对象如队列、事件组或信号量间接发送至任务的事件。向任务发送“直达任务通知”会将目标任务通知设为“挂起”状态。正如任务可以阻塞中间对象如等待信号量可用的信号量任务也可以阻塞任务通知以等待通知状态变为“挂起”。
2更新目标通知的值
向任务发送“直达任务通知”也可以使用下列任一方法更新目标通知的值可选 覆盖原值无论接收任务是否读取被覆盖的值。 覆盖原值但前提是接收任务已读取被覆盖的值。 在值中设置一个或多个位。 对值进行增量添加 1。
调用 xTaskNotifyWait()或xTaskNotifyWaitIndexed() 读取通知值会将该通知的状态清除为“非挂起”。此外也可以通过调用 xTaskNotifyStateClear()或xTaskNotifyStateClearIndexed() 将通知状态明确设置为“未挂起”。
注意“数组中的每条通知均独立运行 —— 任务一次只能阻塞数组中的一个通知并且不会被发送到任何其他数组索引的通知解除阻塞。
默认情况下RTOS 任务通知功能处于启用状态并且可以通过将 configUSE_TASK_NOTIFICATIONS 在 FreeRTOSConfig.h 中设置为 0 从构建中排除每个任务每个数组索引节省 8 个字节。
注意事项FreeRTOS 流和消息缓冲区在数值索引为 0 时使用任务通知。如需在调用流或消息缓冲区 API 函数时保持任务通知的状态请使用数组索引大于 0 的任务通知。
3性能优势和使用限制
任务通知具有高度灵活性使得它们可以在必须要创建单独队列、二进制信号量、计数信号量或事件组的情况下进行使用。与通过诸如二进制信号量等中间对象来解除任务阻塞状态相比通过直接通知解除 RTOS 任务阻塞状态的速度快 45%使用的 RAM 也更少不过这些性能优势也有一些意料之内的使用限制
RTOS 任务通知仅可在只有一个任务可以接收事件时使用。不过这个条件在大多数真实世界情况下是满足的。比如中断解除了一个任务的阻塞状态该任务将处理由中断接收的数据。仅可在使用 RTOS 任务通知代替队列的情况下当某个接收任务可在阻塞状态下等待通知因而不花费任何 CPU 时间时发送任务不能在阻塞状态下等待发送完成在发送不能立刻完成的情况下。
二、直达任务通知 API
1向任务发送通知
函数原型 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() 代替。必须在 FreeRTOSConfig.h 中将 configUSE_TASK_NOTIFICATIONS 设置为 1或保留为未定义状态才可使用这些宏。常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 决定了每项任务的任务通知数组中的索引数。向后兼容性信息在 FreeRTOS V10.4.0 之前每项任务只有一个“通知值”所有任务通知 API 函数都只能操作这一个值。用通知值数组替代单个通知值需要一组新的 API 函数以处理数组中的特定通知。xTaskNotifyGive() 是原始 API 函数为保持向后兼容始终操作数组中索引为 0 的通知值。调用 xTaskNotifyGive() 等同于调用 xTaskNotifyGiveIndexed()其中 uxIndexToNotify 参数设置为 0。
参数/返回值说明xTaskToNotify接收通知的 RTOS 任务的句柄通知值会递增。可通过以下方法获取任务句柄br- 使用 xTaskCreate() 创建任务并通过 pxCreatedTask 参数获取句柄br- 使用 xTaskCreateStatic() 创建任务并存储返回值作为句柄br- 调用 xTaskGetHandle()通过任务名称获取句柄。当前正在执行的 RTOS 任务的句柄由 xTaskGetCurrentTaskHandle() API 函数返回。uxIndexToNotify目标任务的通知值数组中要向其发送通知的索引。uxIndexToNotify 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。xTaskNotifyGive() 没有此参数并且总是将通知发送到索引 0。返回值xTaskNotifyGiveIndexed() 是一个宏调用 xTaskNotifyIndexed()并将 eAction 参数设置为 eIncrement因此所有调用都返回 pdPASS。
用法示例
/* 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 ); // 创建任务1xTaskCreate( prvTask2, Task2, 200, NULL, tskIDLE_PRIORITY, xTask2 ); // 创建任务2vTaskStartScheduler(); // 启动 RTOS 调度器
} /*-----------------------------------------------------------*/ /* 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 ); }
}
2向任务发送通知ISR
函数原型
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify,BaseType_t *pxHigherPriorityTaskWoken );void vTaskNotifyGiveIndexedFromISR( TaskHandle_t xTaskHandle, UBaseType_t uxIndexToNotify, BaseType_t *pxHigherPriorityTaskWoken );可在中断服务程序 (ISR) 中使用的 xTaskNotifyGive() 和 xTaskNotifyGiveIndexed() 版本 。
参数说明xTaskToNotify 接收通知的 RTOS 任务的句柄通知值会递增。可通过以下方法获取任务句柄使用 xTaskCreate() 创建任务并通过 pxCreatedTask 参数获取句柄使用 xTaskCreateStatic() 创建任务并存储返回值作为句柄调用 xTaskGetHandle()通过任务名称获取句柄。当前正在执行的 RTOS 任务的句柄由 xTaskGetCurrentTaskHandle() API 函数返回。 uxIndexToNotify目标任务的通知值数组中要向其发送通知的索引。uxIndexToNotify 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。xTaskNotifyGiveFromISR() 没有此参数并且总是将通知发送到索引 0。*pxHigherPriorityTaskWoken必须初始化为 0。如果发送通知导致任务解除阻塞并且解除阻塞的任务的优先级高于当前正在运行的任务则 vTaskNotifyGiveFromISR() 会将 *pxHigherPriorityTaskWoken 设置为 pdTRUE。如果 vTaskNotifyGiveFromISR() 将此值设置为 pdTRUE则应在中断退出前请求上下文切换。pxHigherPriorityTaskWoken 是可选参数可设置为 NULL。
用法示例
/* 这是一个通用外设驱动程序中的发送函数示例。RTOS 任务调用发送函数然后在阻塞状态因此不使用 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() 超时。 */}
}
3唤醒阻塞中任务
函数原型
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit,TickType_t xTicksToWait );uint32_t ulTaskNotifyTakeIndexed( UBaseType_t uxIndexToWaitOn, BaseType_t xClearCountOnExit, TickType_t xTicksToWait );每个任务都有一组“任务通知”或仅“通知”每个通知都包含状态和一个 32 位值。直达任务通知直接发送给任务的事件可以取消接收任务的阻塞状态还可以选择通过多种方式更新接收任务的某个通知值。例如通知可覆盖接收任务的通知值中的一个或仅设置接收任务的通知值中的一个或多个比特位。ulTaskNotifyTake() 是一个宏用于将任务通知作为一种速度更快、重量更轻的二进制或计数信号量替代品。FreeRTOS 信号量是使用 xSemaphoreTake() API 函数提取的是使用通知值代替信号量的等效宏。ulTaskNotifyTake() 和 ulTaskNotifyTakeIndexed() 是等效的宏——唯一的区别是 ulTaskNotifyTakeIndexed() 可以在数组内的任何任务通知上运行而 ulTaskNotifyTake() 始终在数组索引 0 处的任务通知上运行。当任务使用通知值作为二进制或计数信号量时其他任务和中断应使用 xTaskNotifyGive() 宏或 xTaskNotify() 函数将数据发给任务其中函数的 eAction 参数设置为 eIncrement这两者是等效的。ulTaskNotifyTake() 可以在退出时清除任务的通知值为 0在这种情况下通知值起到二进制信号量的作用或在退出时递减任务的通知值在这种情况下通知值更像是计数信号量。RTOS 任务可以使用 ulTaskNotifyTake() [可选] 进入阻塞状态以等待任务通知指。任务处于“阻塞”状态时不会占用任何 CPU 时间。注意 数组中的每条通知均独立运行——任务一次只能阻塞数组中的一个通知并且不会被发送到任何其他数组索引的通知解除阻塞。然而当通知被挂起时xTaskNotifyWait() 将返回ulTaskNotifyTake() 将在任务的通知值不为零时返回并在返回之前递减任务通知值。必须在 FreeRTOSConfig.h 中将 configUSE_TASK_NOTIFICATIONS 设置为 1或保留为未定义这些宏才能可用。常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 设置每个任务的任务通知数组中的索引数。
参数/返回值说明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 滴答周期为单位。pdMS_TO_TICKS() 宏可用于将以毫秒为单位的时间转换为以滴答为单位的时间。返回值被递减或清除之前的任务通知值的值。
用法示例
/* 一个中断处理程序。中断处理程序不执行任何处理而是解除一个高优先级任务的阻塞在该任务中处理产生中断的事件。如果任务的优先级足够高那么中断将直接返回到任务因此它将中断一个任务但返回到一个不同的任务这样处理就会在时间上连续发生——就像所有的处理都是在中断处理程序本身中完成的一样。*/
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 );}
}
4直接向任务发送通知
函数原型 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() 代替。必须在 FreeRTOSConfig.h 中将 configUSE_TASK_NOTIFICATIONS 设置为 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一种枚举类型可以取下列任一值以执行相关操作eNoAction目标任务接收事件但其通知值不会更新。在这种情况下不会使用 ulValue。eSetBits目标任务的通知值将与 ulValue 进行按位“或”操作。例如如果 ulValue 设置为 0x01则目标任务通知值中的第 0 位将被设置。同样如果 ulValue 设置为 0x04则目标任务通知值中的第 2 位将被设置。通过这种方式RTOS 任务通知机制可以作为事件组的轻量级替代方案。eIncrement目标任务的通知值将增加 1这样调用 xTaskNotify() 相当于调用 xTaskNotifyGive()。在这种情况下不会使用 ulValue。eSetValueWithOverwrite目标任务的通知值无条件设置为 ulValue。通过这种方式RTOS 任务通知机制可以作为 xQueueOverwrite() 的轻量级替代方案。eSetValueWithoutOverwrite如果目标任务当前没有挂起的通知则其通知值将设置为 ulValue。如果目标任务已有挂起的通知则其通知值不会更新以免之前的值在使用前被覆盖。在这种情况下调用 xTaskNotify() 会失败返回 pdFALSE。通过这种方式RTOS 任务通知机制可以在长度为 1 的队列上作为 xQueueSend() 的轻量级替代方案。返回值除了 eAction 设置为 eSetValueWithoutOverwrite 且目标任务的通知值无法更新因为目标任务已有挂起的通知时其他情况下均返回 pdPASS。
用法示例
/* 在引用 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
{/* 任务的通知值未更新。 */
}
5向任务发送通知并查询任务的通知值
函数原型 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() 的轻量级替代方案。eSetValueWithoutOverwrite如果目标任务当前没有挂起的通知则其通知值将设置为 ulValue。如果目标任务已有挂起的通知则其通知值不会更新以免之前的值在使用前被覆盖。在这种情况下调用 xTaskNotify() 会失败返回 pdFALSE。通过这种方式RTOS 任务通知机制可以在长度为 1 的队列上作为 xQueueSend() 的轻量级替代方案。返回值除了 eAction 设置为 eSetValueWithoutOverwrite 且目标任务的通知值无法更新因为目标任务已有挂起的通知时其他情况下均返回 pdPASS。
用法示例
uint32_t ulPreviousValue;/* 在引用 xTask1Handle 的任务的第 0 个通知值中设置第 8 位。将任务的先前第 0 个通知值在设置第 8 位之前存储在 ulPreviousValue 中。 */
xTaskNotifyAndQueryIndexed( xTask1Handle,0,( 1UL 8UL ),eSetBits,ulPreviousValue );/* 向引用 xTask2Handle 的任务发送通知可能会将任务从阻塞状态移除但不更新任务的通知值。将任务的通知值存储在 ulPreviousValue 中。 */
xTaskNotifyAndQuery( xTask2Handle, 0, eNoAction, ulPreviousValue );/* 将引用 xTask3Handle 的任务的通知值设置为 0x50即使任务尚未读取其先前的通知值。任务的先前通知值没有兴趣因此最后一个参数设置为 NULL。 */
xTaskNotifyAndQuery( xTask3Handle, 0x50, eSetValueWithOverwrite, NULL );/* 将引用 xTask4Handle 的任务的通知值设置为 0xfff但仅当这样做不会在任务获取该值之前通过调用 xTaskNotifyWait() 或 ulTaskNotifyTake()覆盖任务的现有通知值时。任务的先前通知值保存在 ulPreviousValue 中。 */
if( xTaskNotifyAndQuery( xTask4Handle,0xfff,eSetValueWithoutOverwrite,ulPreviousValue ) pdPASS )
{/* 任务的通知值已更新。 */
}
else
{/* 任务的通知值未更新。 */
}
6向任务发送通知并查询任务的通知值ISR
函数原型 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 uxIndexToNotifyuint32_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可用于在 xTaskNotifyAndQueryFromISR() 修改任何位之前传出目标任务的通知值。pulPreviousNotifyValue 是可选参数如果不需要可设置为 NULL。如果不使用 pulPreviousNotifyValue可以考虑使用 xTaskNotify() 替代 xTaskNotifyAndQueryFromISR()。pxHigherPriorityTaskWoken*pxHigherPriorityTaskWoken 必须初始化为 pdFALSE (0)。如果发送通知导致任务解除阻塞并且解除阻塞的任务的优先级高于当前正在运行的任务则 xTaskNotifyAndQueryFromISR() 会将 *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() 的轻量级替代方案。eSetValueWithoutOverwrite如果目标任务当前没有挂起的通知则其通知值将设置为 ulValue。如果目标任务已有挂起的通知则其通知值不会更新以免之前的值在使用前被覆盖。在这种情况下调用 xTaskNotify() 会失败返回 pdFALSE。通过这种方式RTOS 任务通知机制可以在长度为 1 的队列上作为 xQueueSend() 的轻量级替代方案。返回值除了 eAction 设置为 eSetValueWithoutOverwrite 且目标任务的通知值无法更新因为目标任务已有挂起的通知时其他情况下均返回 pdPASS。
用法示例
void vAnISR( void )
{/* 必须初始化为 pdFALSE */BaseType_t xHigherPriorityTaskWoken pdFALSE;uint32_t ulPreviousValue;/* 在引用 xTask1Handle 的任务的第 0 个通知值中设置第 8 位。将任务的先前第 0 个通知值在设置第 8 位之前存储在 ulPreviousValue 中。 */xTaskNotifyAndQueryIndexedFromISR( xTask1Handle,0,( 1UL 8UL ),eSetBits,ulPreviousValue,xHigherPriorityTaskWoken );/* 任务的先前通知值保存在 ulPreviousValue 中。 *//* 如果引用 xTask1Handle 的任务处于阻塞状态等待通知那么它现在将从阻塞状态转移到就绪状态。如果其优先级高于当前执行任务中断的这个任务的优先级那么 xHigherPriorityTaskWoken 将被设置为 pdTRUE将变量传递到 portYIELD_FROM_ISR() 调用中将导致中断直接返回到未阻塞的任务。如果 xHigherPriorityTaskWoken 仍然是 pdFALSE那么将其传递到 portYIELD_FROM_ISR() 将没有效果。 */portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
7在中断上下文中发送通知给任务
函数原型 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() 版本 。参数/返回值同上。
用法示例
/* 中断处理程序本身不执行任何处理。相反它解锁一个高优先级任务在该任务中处理产生中断的事件。如果任务的优先级足够高则中断将直接返回到任务因此它将中断一个任务但返回到不同的任务这样处理将在时间上连续发生 -就像所有处理都是在中断处理程序本身中完成的一样。通过 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 ); /* 无限期阻塞。 *//* 处理接收到的通知值中设置的任何位。这假设外设为 Rx 中断设置位 1为 Tx 中断设置位 2为缓冲区溢出中断设置位 3。 */if( ( ulInterruptStatus 0x01 ) ! 0x00 ){prvProcessRxInterrupt();}if( ( ulInterruptStatus 0x02 ) ! 0x00 ){prvProcessTxInterrupt();}if( ( ulInterruptStatus 0x04 ) ! 0x00 ){prvClearBufferOverrun();}}
}
8使任务等待接收通知
函数原型 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 );如果使用 RTOS 任务通知来实现二进制或计数信号量行为则应使用更简单的 ulTaskNotifyTake() API 函数而不是 xTaskNotifyWait()。xTaskNotifyWait() 用于使调用任务等待接收通知可以为其设置一个可选的超时时间。如果接收 RTOS 任务在等待通知时已经处于阻塞状态则在等待的通知到达时接收 RTOS 任务将解除阻塞状态通知也将清除。
参数/返回值描述uxIndexToWaitOn调用任务的通知值数组中用于等待接收通知的索引。uxIndexToWaitOn 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。xTaskNotifyWait() 没有此参数总是在索引 0 的位置等待通知。ulBitsToClearOnEntry调用 xTaskNotifyWait() 时如果通知已挂起则在进入 xTaskNotifyWait() 函数即任务等待新通知之前时ulBitsToClearOnEntry 中设置的任何位都会在调用 RTOS 任务的通知值中被清除。例如如果 ulBitsToClearOnEntry 设为 0x01则任务通知值中的第 0 位将在进入函数时被清除。如果 ulBitsToClearOnEntry 设为 0xffffffffULONG_MAX则将清除任务通知值中的所有位相当于将值清零。ulBitsToClearOnExit如果在调用 xTaskNotifyWait() 函数时收到了通知则在 xTaskNotifyWait() 函数退出之前ulBitsToClearOnExit 中设置的任何位都会在调用 RTOS 任务的通知值中被清除。RTOS 任务的通知值保存到 *pulNotificationValue 之后这些位即被清除。例如如果 ulBitsToClearOnExit 设为 0x03则在函数退出之前任务通知值中的第 0 位和第 1 位将被清除。如果 ulBitsToClearOnExit 设为 0xffffffffULONG_MAX则将清除任务通知值中的所有位相当于将值清零。pulNotificationValue用于传出 RTOS 任务的通知值。复制到 *pulNotificationValue 的值是 RTOS 任务的通知值该值是在应用 ulBitsToClearOnExit 设置清除任何位之前的值。如果无需通知值可以将 pulNotificationValue 设置为 NULL。xTicksToWait调用 xTaskNotifyWait() 时没有挂起通知的情况下在阻塞状态下等待接收通知的最长时间。RTOS 任务在阻塞状态下不会消耗 CPU 时间。时间以 RTOS 滴答周期为单位。可以使用 pdMS_TO_TICKS() 宏将以毫秒为单位的时间转换为以滴答为单位的时间。返回值如果收到了通知或者在调用 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();}/* 等等。 */}
}
9清除指定任务的通知状态
函数原型
BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask );BaseType_t xTaskNotifyStateClearIndexed( TaskHandle_t xTask, UBaseType_t uxIndexToClear );每项RTOS任务都有一个任务通知数组。每条任务通知都有通知状态可以是“挂起”或“非挂起”以及一个32位通知值。如果通知被发送到通知数组中的索引那么该索引处的通知被称为“待定”直到任务读取其通知值或通过调用xTaskNotifyStateClear()将通知状态明确清除为“非挂起”为止。xTaskNotifyStateclear()和xTaskNotityStateClearlndexed(是等效宏一一唯一的区别是xTaskNotifyStateClearlndexed可以在数组内任何任务通知上运行而xTaskNotifyStateClear始终在数组索引0处的任务通知上运行。ConfigUSETASK_NOTIFICATIONS必须在FreeRTOSConfig.h中设置为1或保留为未定义才能使用这些宏。常量 COnfigTASK NOTIFICATION ARRAY_ENTRIES 设置每个任务的任务通知数组中的索引数。
向后兼容性信息
在FreeRTOSV10.4.0之前每个任务有一个单一的“通知值且所有任务通知API函数都在该值上运行。用通知值的数组更换单个通知值需要新的API函数集该函数集应能在数组内处理具体通知。xTaskNotifyStateClear()是原始API函数并且通过始终在数组内索引 0 处的通知值上运行来保持向后兼容性。调用xTaskNotifyStateClear等于调用xTaskNotifyStateClearlndexed()其中uxlndexToNotify参数设置为 0。
参数描述xTask将清除其通知状态的 RTOS 任务的句柄。将 xTask 设置为 NULL 以清除调用任务的通知状态。要获取任务句柄请使用 xTaskCreate() 创建任务并使用 pxCreatedTask 参数或使用 xTaskCreateStatic() 创建任务并存储返回值或在调用 xTaskGetHandle() 时使用任务名称。当前执行的 RTOS 任务的句柄由 xTaskGetCurrentTaskHandle() API 函数返回。uxIndexToClear目标任务数组中要执行的通知值的索引。例如将 uxIndexToClear 设置为 1 将清除数组内索引为 1 时的通知状态。uxIndexToClear 必须小于 configTASK_NOTIFICATION_ARRAY_ENTRIES。ulTaskNotifyStateClear() 没有此参数并且始终作用于索引 0 的通知上。返回值如果 xTask 引用的任务有挂起的通知则通知已清除然后返回 pdTRUE。如果 xTask 引用的任务有待处理的通知那么返回 pdFALSE。
用法示例
/* 一个示例 UART 发送函数。该函数启动 UART 传输然后等待通知传输完成。传输完成通知是从 UART 中断发送的。在开始传输之前清除调用任务的通知状态以确保在任务尝试在其通知状态上阻塞之前不会偶然已经挂起。 */void vSerialPutString( const 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 发送第 0 个通知给 xSendingTask以通知并解锁任务传输完成。 */ ulTaskNotifyTake( 0, pdTRUE, xMaxBlockTime ); }
}
10 清除任务通知值中指定的位
函数原型
uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear );uint32_t ulTaskNotifyValueClearIndexed( TaskHandle_t xTask, UBaseType_t uxIndexToClear,uint32_t ulBitsToClear );每项 RTOS 任务都有一个任务通知数组。每条任务通知都有通知状态可以是“挂起”或“非挂起”以及一个 32 位通知值。ulTaskNotifyValueClearIndexed() 清除 ulBitsToClear 位掩码指定的位该掩码位于 xTask 所引用任务的数组索引 uxIndexToClear 的通知值中。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 设置为 0xffffffff32 位架构上的 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 ); /* 如果没有超时则唯一可能的事件是已接收到消息。*/ if( ulNotification 0u ) { /* 处理响应超时。*/ process_response_timeout(); } else if( ulNotification ulMessageReceivedMask ) { /* 处理响应事件。*/ process_response(); ulTaskNotifyValueClear( ulMessageReceivedMask ); } else { /* 示例任务应该只接收 MESSAGE_RECEIVED_EVENTS。*/ process_error(); }
三、用作轻量级二进制信号量
1优点
与通过二进制信号量解除任务阻塞状态不同通过直接通知解除 RTOS 任务阻塞状态的速度提高 45%而且使用的 RAM 减少。
2实现
二进制信号量是一种最大计数为 1 的信号量因此称为“二进制”。只有在信号量可用的情况下任务才能“获取”信号量而只有在其计数为 1 的情况下信号量才可用。当使用任务通知代替二进制信号量时接收任务的通知值会用于替代二进制信号量的计数值而且 ulTaskNotifyTake()或 ulTaskNotifyTakeIndexed()API 函数会用于代替信号量的 xSemaphoreTake() API 函数。ulTaskNotifyTake() 函数的 xClearOnExit 参数设置为 pdTRUE这样每次获取通知时计数值均归零——模拟二进制信号量。同样xTaskNotifyGive()或 xTaskNotifyGiveIndexed()或者 vTaskNotifyGiveFromISR()或 vTaskNotifyGiveIndexedFromISR()函数用于代替信号量的 xSemaphoreGive() 和 xSemaphoreGiveFromISR() 函数。
3用法示例
/* 这是一个通用外设驱动程序中的发送函数示例。一个RTOS任务调用发送函数然后在阻塞状态因此不使用CPU时间等待传输完成的通知。传输由DMA执行DMA结束中断用于通知任务。 *//* 存储将在传输完成时被通知的任务的句柄。 */
static TaskHandle_t xTaskToNotify NULL;/* 在目标任务的任务通知数组中使用的索引。 */
const UBaseType_t xArrayIndex 1;/* 外设驱动程序的发送函数。 */
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,xArrayIndex,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 );/* 等待传输完成的通知。注意第一个参数是 pdTRUE这将任务的通知值清零使得通知值表现得像一个二进制而不是计数信号量。 */ulNotificationValue ulTaskNotifyTakeIndexed( xArrayIndex,pdTRUE,xMaxBlockTime );if( ulNotificationValue 1 ){/* 传输按预期结束。 */}else{/* 调用 ulTaskNotifyTake() 超时。 */}
}
四、用作轻量级计数信号量
1优点
与通过信号量解除任务阻塞状态不同通过直接通知解除RTOS任务阻塞状态的速度提高45%而且使用的RAM更少。
2实现
计数信号量指的是计数值范围从0到信号量创建时所设最高值的一种信号量。只有在信号量可用的情况下任务才能“获取”信号量而只有在计数大于零的情况下信号量才可用。当使用任务通知代替计数信号量时接收任务的通知值会用于替代计数信号量的计数值而且 ulTaskNotifyTake()或 ulTaskNotifyTakeIndexed()API 函数会用于代替信号量的 xSemaphoreTake() API 函数。ulTaskNotifyTake() 函数的 xClearOnExit 参数设置为 pdFALSE因此每次接收通知时计数值只会递减而不是清除模拟计数信号量。同样xTaskNotifyGive()或 xTaskNotifyGiveIndexed()或者 vTaskNotifyGiveFromISR()或 vTaskNotifyGiveIndexedFromISR()函数用于代替信号量的 xSemaphoreGive() 和 xSemaphoreGiveFromISR() 函数。
3用法示例
示例一使用接收任务的通知值作为计数信号量。
/* 一个中断处理程序它不直接处理中断而是将处理推迟到一个高优先级的RTOS任务。ISR使用RTOS任务通知来解除RTOS任务的阻塞并增加RTOS任务的通知值。 */
void vANInterruptHandler( void )
{BaseType_t xHigherPriorityTaskWoken;/* 清除中断。 */prvClearInterruptSource();/* xHigherPriorityTaskWoken必须初始化为pdFALSE。如果调用vTaskNotifyGiveFromISR()解除了处理任务的阻塞并且处理任务的优先级高于当前运行任务的优先级那么xHigherPriorityTaskWoken将自动设置为pdTRUE。 */xHigherPriorityTaskWoken pdFALSE;/* 解除处理任务的阻塞以便任务可以执行由中断引起的任何处理。xHandlingTask是任务的句柄它是在任务创建时获得的。vTaskNotifyGiveFromISR()也增加了接收任务的通知值。 */vTaskNotifyGiveFromISR( xHandlingTask, xHigherPriorityTaskWoken );/* 如果xHigherPriorityTaskWoken现在设置为pdTRUE则强制上下文切换。用于此目的的宏取决于端口可能称为portEND_SWITCHING_ISR。 */portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/*-----------------------------------------------------------*//* 一个阻塞等待通知的任务该通知表明外设需要服务。 */
void vHandlingTask( void *pvParameters )
{BaseType_t xEvent;const TickType_t xBlockTime pdMS_TO_TICKS( 500 );uint32_t ulNotifiedValue;for( ;; ){/* 阻塞等待通知。在这里RTOS任务通知被用作计数信号量。每次ISR调用vTaskNotifyGiveFromISR()时任务的通知值都会增加每次RTOS任务调用ulTaskNotifyTake()时通知值都会减少因此实际上保存了未处理中断的数量。第一个参数设置为pdFALSE因此通知值只会减少而不会清零一次处理一个延迟的中断事件。参见下面的例子2了解更实用的方法。 */ulNotifiedValue ulTaskNotifyTake( pdFALSE,xBlockTime );if( ulNotifiedValue 0 ){/* 执行由中断引起的任何处理。 */xEvent xQueryPeripheral();if( xEvent ! NO_MORE_EVENTS ){vProcessPeripheralEvent( xEvent );}}else{/* 在预期的时间内没有收到通知。 */vCheckForErrorConditions();}}
}
示例二提供更加实用和有效的实现。
在此实现中从 ulTaskNotifyTake() 返回的值用于了解必须处理多少未完成的 ISR 事件从而允许在每次调用 ulTaskNotifyTake() 时将RTOS任务的通知计数清零。假设中断服务程序 (ISR) 如上面的示例 1 所示。
/* 在目标任务的任务通知数组中使用的索引。 */
const UBaseType_t xArrayIndex 0;/* 一个阻塞等待通知的任务该通知表明外设需要服务。 */
void vHandlingTask( void *pvParameters )
{BaseType_t xEvent;const TickType_t xBlockTime pdMS_TO_TICKS( 500 ); // 最大阻塞时间转换为系统滴答计数uint32_t ulNotifiedValue; // 用于存储接收到的通知值for( ;; ) // 无限循环持续运行任务{/* 与之前类似阻塞等待来自ISR的通知。这次第一个参数设置为pdTRUE清除任务的通知值到0意味着每个未处理的延迟中断事件必须在再次调用ulTaskNotifyTake()之前处理。 */ulNotifiedValue ulTaskNotifyTakeIndexed( xArrayIndex,pdTRUE, // 清除通知值xBlockTime );if( ulNotifiedValue 0 ){/* 在预期的时间内没有收到通知。 */vCheckForErrorConditions(); // 检查错误条件}else{/* ulNotifiedValue 保存了未处理中断的数量。逐个处理每个中断。 */while( ulNotifiedValue 0 ){xEvent xQueryPeripheral(); // 查询外设状态if( xEvent ! NO_MORE_EVENTS ) // 如果有事件需要处理{vProcessPeripheralEvent( xEvent ); // 处理外设事件ulNotifiedValue--; // 减少未处理中断计数}else{break; // 如果没有更多事件跳出循环}}}}
}
五、用作轻量级事件组
1优点
事件组是一组二进制标志或位应用程序编写者可以对其中的每一个指定含义。RTOS 任务可以进入“阻塞”状态以等待组内的一个或多个标志激活。待验证的 RTOS 任务处于“阻塞”状态时不会占用任何 CPU 时间。
2实现
当使用任务通知代替事件组时使用接收任务的通知值代替事件组接收任务通知值中的位被用作事件标志xTaskNotifyWait() API 函数被用于代替事件组的 xEventGroupWaitBits() API 函数。同样使用 xTaskNotify() 和 xTaskNotifyFromISR() API 函数其 eAction 参数设置为 eSetBits分别代替 xEventGroupSetBits() 和 xEventGroupSetBitsFromISR() 函数来设置位。与 xEventGroupSetBitsFromISR() 相比xTaskNotifyFromISR() 具有显著的性能优势这是因为 xTaskNotifyFromISR() 完全在 ISR 中执行而 xEventGroupSetBitsFromISR() 必须将某些处理推迟到 RTOS 守护程序任务。与使用事件组时的情况不同的是接收任务无法指定它只想在位组合同时处于活动状态时解除阻塞状态。相反任务在任何位变为活动状态时结束阻塞并且必须测试位组合本身。
3用法示例
/* 此示例演示了单个RTOS任务如何被用来处理来自两个不同中断服务例程的事件——发送中断和接收中断。许多外设将使用相同的处理程序在这种情况下外设的中断状态寄存器可以简单地与接收任务的通知值进行位或运算。首先定义一些位来表示每个中断源。 */
#define TX_BIT 0x01 // 表示发送中断的位
#define RX_BIT 0x02 // 表示接收中断的位/* 将从中断接收通知的任务的句柄。该句柄是在任务创建时获得的。 */
static TaskHandle_t xHandlingTask;/*-----------------------------------------------------------*//* 发送中断服务例程的实现。 */
void vTxISR( void )
{BaseType_t xHigherPriorityTaskWoken pdFALSE;/* 清除中断源。 */prvClearInterrupt();/* 通过在任务的通知值中设置TX_BIT来通知任务传输已完成。 */xTaskNotifyFromISR( xHandlingTask,TX_BIT,eSetBits,xHigherPriorityTaskWoken );/* 如果xHigherPriorityTaskWoken现在设置为pdTRUE则应执行上下文切换以确保中断直接返回到最高优先级任务。用于此目的的宏取决于使用的端口可能称为portEND_SWITCHING_ISR()。 */portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/*-----------------------------------------------------------*//* 接收中断服务例程的实现与上述相同除了在接收任务的通知值中设置的位不同。 */
void vRxISR( void )
{BaseType_t xHigherPriorityTaskWoken pdFALSE;/* 清除中断源。 */prvClearInterrupt();/* 通过在任务的通知值中设置RX_BIT来通知任务接收已完成。 */xTaskNotifyFromISR( xHandlingTask,RX_BIT,eSetBits,xHigherPriorityTaskWoken );/* 如果xHigherPriorityTaskWoken现在设置为pdTRUE则应执行上下文切换以确保中断直接返回到最高优先级任务。用于此目的的宏取决于使用的端口可能称为portEND_SWITCHING_ISR()。 */portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/*-----------------------------------------------------------*//* 被中断服务例程通知的任务的实现。 */
static void prvHandlingTask( void *pvParameter )
{const TickType_t xMaxBlockTime pdMS_TO_TICKS( 500 );BaseType_t xResult;uint32_t ulNotifiedValue; // 用于存储接收到的通知值for( ;; ){/* 等待中断的通知。 */xResult xTaskNotifyWait( pdFALSE, /* 进入时不清除位。 */ULONG_MAX, /* 退出时清除所有位。 */ulNotifiedValue, /* 存储通知的值。 */xMaxBlockTime );if( xResult pdPASS ){/* 收到通知。查看哪些位被设置。 */if( ( ulNotifiedValue TX_BIT ) ! 0 ){/* 发送ISR已设置一个位。 */prvProcessTx();}if( ( ulNotifiedValue RX_BIT ) ! 0 ){/* 接收ISR已设置一个位。 */prvProcessRx();}}else{/* 在预期的时间内没有收到通知。 */prvCheckForErrors();}}
}
六、用作轻量级邮箱
1限制
RTOS 任务通知可用于向任务发送数据但相比使用 RTOS 队列实现有一些限制因为
只能发送 32 位值。该值保存为接收任务的通知值然后在任何时间只能有一个通知值
2实现
因此使用“轻量级邮箱”这个短语代替“轻量级队列”。任务的通知值就是邮箱值。
使用 xTaskNotify()或 xTaskNotifyIndexed()和 xTaskNotifyFromISR()或 xTaskNotifyIndexedFromISR()API 函数将数据发给任务其中函数的 eAction 参数设置为 eSetValueWithOverwrite 或 eSetValueWithoutOverwrite。如果 eAction 设置为 eSetValueWithOverwrite则即使接收任务已有挂起的通知也会更新接收任务的通知值。如果 eAction 设置为 eSetValueWithoutOverwrite则只有在接收任务没有挂起通知时才会更新接收任务的通知值因为更新通知值会在接收任务处理之前覆盖以前的值。任务可以使用 xTaskNotifyWait()或 xTaskNotifyWaitIndexed()读取自己的通知值。
七、任务间通信 【FreeRTOS 教程 一】任务结构体及其基础创建使用
【FreeRTOS 教程 二】任务优先级与任务控制
【FreeRTOS 教程 三】协程状态、优先级、实现及调度
【FreeRTOS 教程 四】队列创建与发布项目到队列
【FreeRTOS 教程 五】FreeRTOS 内存管理细致讲解
【FreeRTOS 教程 六】二进制信号量与计数信号量
【FreeRTOS 教程 七】互斥锁与递归互斥锁 八、FreeRTOS教程示例代码下载
通过网盘分享的文件FreeRTOS教程示例代码