一.任务通知(Task Notifications)介绍
FreeRTOS 每个已经创建的任务都有一个任务控制块( task control block),任务控制块就是一个结构体变量, 用于记录任务的相关信息。 结构体变量中有一个 32 位的变量成员 ulNotifiedValue 是专门用于任务通知的。通过任务通知方式可以实现计数信号量, 二值信号量,事件标志组和消息邮箱(消息邮箱就是消息队列长度为 1 的情况)。
任务通知方式实现的计数信号量, 二值信号量,事件标志组和消息邮箱是通过修改变量ulNotifiedValue 实现的:
设置接收任务控制块中的变量 ulNotifiedValue 可以实现消息邮箱。
如果接收任务控制块中的变量 ulNotifiedValue 还没有被其接收到, 也可以用新数据覆盖原有数据, 这就是覆盖方式的消息邮箱。
设置接收任务控制块中的变量 ulNotifiedValue 的 bit0-bit31 数值可以实现事件标志组。
设置接收任务控制块中的变量 ulNotifiedValue 数值进行加一或者减一操作可以实现计数信号量和二值信号量。
二、通知任务计数信号量
1.任务计数信号量概念及其作用
FreeRTOS 计数信号量的另一种实现方式--基于任务通知( Task Notifications)的计数信号量,这里我们将这种方式实现的计数信号量称之为任务计数信号量。 任务计数信号量效率更高,需要的 RAM 空间更小。实际项目中,如果使用计数信号量和任务计数信号量都能实现相应功能,强烈建议使用任务计数信号量。
2.任务计数信号量 API 函数
1)函数 xTaskNotifyGive 用于任务中释放信号量( 含任务二值信号量, 任务计数信号量)
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify ); /* 任务句柄 */
返回 :pdPASS
注意:此函数用于任务代码中调用,不可在中断服务程序中调用!!!
2)函数 vTaskNotifyGiveFromISR用于中断中释放信号量(含任务二值信号量, 任务计数信号量)
void vTaskNotifyGiveFromISR(
TaskHandle_t xTaskToNotify, /* 任务句柄 */
BaseType_t *pxHigherPriorityTaskWoken ); /* 高优先级任务是否被唤醒的状态保存 */
3)函数 ulTaskNotifyTake 从调用任务的任务控制块中检索通知值,并可选地清除该值
uint32_t ulTaskNotifyTake(
BaseType_t xClearCountOnExit, /* 选择是否清零用于任务通知的 ulNotifiedValue */
TickType_t xTicksToWait ); /* 等待信号量可用的最大等待时间 */
参数:
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 为单位的时间。 |
返回:被递减或清除之前的任务通知值的值
三、通知任务二值信号量
1、二值信号量的概念及其作用
多次调用函数 xTaskNotifyGive ()难免会出现计数值大于 1 的情况,用作任务二值信号量时,我们可以将所有大于 1 的计数理解为一种情况,即二值信号量管理的资源可用。因此, 不管当前的计数是多少,大于 0 的计数在通过函数 ulTaskNotifyTake()获取二值信号量的时候统一清零,这样就实现了二值信号量的功能。实际项目中,如果使用二值信号量和任务二值信号量都能实现相应功能,强烈建议使用任务二值信号量。
2.任务二值信号量 API 函数
函数以及用法与任务计数信号量基本相同。
四、任务事件标志组
1、任务事件标志组的概念及其作用
任务事件标志组与事件标志组要实现的功能是一样的, 不同的是调用的函数和支持的事件标志个数, 任务事件标志组支持 32 个事件标志设置, 事件标志组,每创建一个支持 24 个事件标志设置。实际项目中,如果使用事件标志组和任务事件标志组都能实现相应功能,强烈建议使用任务事件标志组。
2、任务事件标志组 API 函数
1)函数 xTaskNotify
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, /* 任务句柄 */uint32_t ulValue, /* 更新任务控制块中的变量 ulNotifiedValue */eNotifyAction eAction ); /* 任务通知模式设置 */
参数:
xTaskToNotify | 正在通知的 RTOS 任务的句柄。 |
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。
函数 xTaskNotify 通过设置任务控制块中的变量 ulNotifiedValue 可以在任务代码中实现任务事件标志组,任务计数信号量, 任务消息邮箱和任务二值信号量四种方式的消息通知。
2)函数 xTaskNotifyFromISR
BaseType_t xTaskNotifyFromISR(
TaskHandle_t xTaskToNotify, /* 任务句柄 */
uint32_t ulValue, /* 更新任务控制块中的变量 ulNotifiedValue */
eNotifyAction eAction, /* 任务通知模式设置 */
BaseType_t *pxHigherPriorityTaskWoken ); /* 高优先级任务是否被唤醒的状态保存 */
函数 xTaskNotifyFromISR 通过设置任务控制块中的变量 ulNotifiedValue 可以在中断服务程序中实现任务事件标志组,任务计数信号量, 任务消息邮箱和任务二值信号量四种方式的消息通知。
3)函数 xTaskNotifyWait
函数 xTaskNotifyWait 可以在任务代码中实现任务事件标志组,任务计数信号量, 任务消息邮箱和任务二值信号量四种方式的消息获取。
BaseType_t xTaskNotifyWait(
/* 设置函数执行前清零任务控制块中变量 ulNotifiedValue 那些位 */
uint32_t ulBitsToClearOnEntry,
/*设置函数退出前清零任务控制块中变量 ulNotifiedValue 那些位 */
uint32_t ulBitsToClearOnExit,
/* 保存任务控制块中的变量 ulNotifiedValue 到指针变量 pulNotifiedValue 所指向的存储单元 */
uint32_t *pulNotificationValue,
/* 等待消息通知的最大等待时间 */
TickType_t xTicksToWait );
参数:
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。
五、任务消息邮箱
1、任务消息邮箱的概念及其作用
我们将消息队列( 消息队列长度固定为 1) 称之为任务消息邮箱。 这种方式实现的消息队列效率更高,需要的 RAM 空间更小。任务消息邮箱是通过任务控制块中的一个 32 位变量 ulNotifiedValue 对数据进行存取;实际项目中,如果使用任务消息邮箱和消息队列都能实现相应功能,强烈建议使用任务消息邮箱。
2、任务消息邮箱使用的API与上面相同,参数设置用法不同
1)覆盖方式 xTaskNotify 第三个参数使用 eSetValueWithOverwrite
2)非覆盖方式 xTaskNotify 第三个参数使用 eSetValueWithoutOverwrite
六、测试例程
https://gitee.com/hutaooooooo/git_keil/tree/master/freetos_xTaskNotifyCreate