FreeRTOS 任务通知浅析

FreeRTOS 任务通知浅析

概述

FreeRTOS 提供了任务间传递信息的机制。任务可以等待一个通知信息进入阻塞状态,并在通知信息到来时自动解除阻塞,进入运行的状态。

任务通知是实现任务之间消息传递的机制之一。通常可以让一个A任务进入等待通知的状态,待B任务通知 A 任务后,A 任务继续执行:
在这里插入图片描述
如前所述,任务创建时将分配一块任务控制块(TCB),用于记录该任务的状态、优先级等信息。在该任务控制块中,还记录了与任务通知相关的信息:

volatile uint32_t ulNotifiedValue;// 用来表示通知值
volatile uint8_t ucNotifyState;// 用来表示该任务的通知状态

因此每个任务在创建时都自动地包含一个用来记录当前通知值的 uint32_t 类型的 NotifiedValue,以及一个 记录当前任务通知状态的 uint8_t 类型的 NotifyState。任务之间可以通过更改、查询 NotifiedValue 传递消息。

其中 NotifyState 的取值为:

#define taskNOT_WAITING_NOTIFICATION              ( ( uint8_t ) 0 )  /* 初始状态,不使用任务通知 */
#define taskWAITING_NOTIFICATION                  ( ( uint8_t ) 1 )  /* 使用任务通知,且当前任务处于等待通知的阻塞状态 */
#define taskNOTIFICATION_RECEIVED                 ( ( uint8_t ) 2 )  /* 使用任务通知,且当前任务处于接收到通知的解除阻塞状态,也称为 "pending"(即解除阻塞,有数据未读) 状态*/

任务通知主要有两组 API 组成:

加减1版本操作任意值版本
发送通知xTaskNotifyGivexTaskNotify
接收通知xTaskNotifyTakexTaskNotifyWait

两组 API 的主要区别在于 Give\Take 版本一次只能对 NotifiedValue 加\减1,而Notify\Wait 版本可以对 NotifiedValue 执行更多的运算。

这些 API 的简介如下:

1)

/ *
该函数调用后的行为:
1)使得通知值加一
2)将对应 Task 由阻塞状态解除为非阻塞状态
*/
xTaskNotifyGive(xTaskToNotify // 要通知的任务的句柄
				)

2)

/ *
该函数调用后的行为:
1)进入阻塞,直到接收到通知或者时间超时
2)返回值:函数返回之前,在清零或减一之前的通知值。
*/
ulTaskNotifyTake(xClearCountOnExit, // 退出该函数时如何处理 NotifiedValue,pdTRUE:把通知值清零;pdFALSE:如果通知值大于0,则把通知值减一
					xTicksToWait) // 等待通知的时间

3)

/ *
该函数调用后的行为:
1)使得通知值加一
2)将对应 Task 由阻塞状态解除为非阻塞状态
*/
xTaskNotify(xTaskToNotify, // 要通知的任务的句柄
			ulValue,       // 要通知的任务的NotifiedValue的值
			eAction)       // 对 NotifiedValue 的处理,详细取值见下

其中 eAction 的行为定义如下:

eNoAction	// 仅仅是更新通知状态为"pending"(即解除阻塞),未使用ulValue。这个选项相当于轻量级的、更高效的二进制信号量。
eSetBits	// 通知值 = 原来的通知值 | ulValue,按位或。相当于轻量级的、更高效的事件组。
eIncrement	// 通知值 = 原来的通知值 + 1,未使用ulValue。相当于轻量级的、更高效的二进制信号量、计数型信号量。相当于xTaskNotifyGive()函数。
eSetValueWithoutOverwrite	// 不覆盖。如果通知状态为"pending"(表示有数据未读),则此次调用xTaskNotify不做任何事,返回pdFAIL。如果通知状态不是"pending"(表示没有新数据),则通知值 = ulValue。
eSetValueWithOverwrite	// 覆盖。无论如何,不管通知状态是否为"pendng",通知值 = ulValue.

4)

/ *
该函数调用后的行为:
1)使得通知值加一
2)将对应 Task 由阻塞状态解除为非阻塞状态
*/
xTaskNotifyWait(ulBitsToClearOnEntry, /* 进入后,要清除哪些位,通知值 = 通知值 & ~(ulBitsToClearOnEntry)。
比如传入0x01,表示清除通知值的bit0;传入0xffffffff 即ULONG_MAX,表示清除所有位,即把值设置为0 */
				ulBitsToClearOnExit,  /* 退出时,要清除哪些位,通知值 = 通知值 & ~(ulBitsToClearOnExit)。
在清除某些位之前,通知值先被赋给"*pulNotificationValue"。比如入0x03,表示清除通知值的bit0、bit1;传入0xffffffff即ULONG_MAX,表示清除所有位,即把值设置为0。*/
				pulNotificationValue, /* 通知值 = 通知值 & ~(ulBitsToClearOnExit)。
在清除某些位之前,通知值先被赋给"*pulNotificationValue"。比如入0x03,表示清除通知值的bit0、bit1;传入0xffffffff即ULONG_MAX,表示清除所有位,即把值设置为0 */
				xTicksToWait)         /* 任务进入阻塞态的超时时间 */

需求及功能解析

示例演示了两组任务通知 API 的使用方法。其中 Task1 与 Task3 之间使用 Give\Tick 机制,Task2 和 Task1 之间使用 Notify\Wait 机制。

示例解析

示例输出:

This is esp32 chip with 2 CPU core(s), WiFi/BT/BLE, Minimum free heap size: 295348 bytes
TASK2: task2_flag = 0
TASK1: notify num: 1
TASK3: task3_flag = 0, ret = 1
TASK2: task2_flag = 1
TASK1: notify num: 2
TASK3: task3_flag = 1, ret = 1
TASK2: task2_flag = 2

示例中,三个任务的执行均是 Task2 驱动,只有 Task2 通知 Task1,Task1 才能解除阻塞,运行下面的代码。同理,只有 Task1 通知 Task3 ,Task3 才能解除阻塞,得到执行。因此整体的执行流为 Task2->Task1->Task3。Task1、Task3 完全是“事件”驱动的,并且在任务代码中不含任何延时,只有对应的事件触发后,才能继续向下执行。

讨论

Task Notify,即任务通知的使用方法和 API 还有很多,本小节作为入门篇简介其基本原理和用法。重要的是,读者可以理解这种事件驱动的编程思想,体会任务通知的方法和特点。对于已经熟悉 FreeRTOS 的读者还可能想了解任务通知与其他通信组件的优缺点。

Task Notifications 的优缺点

优点

  • 消息传递的更快,效率更高。
  • 每个任务创建时会自动创建该通信机制,内存消耗比其它IPC通信相比,明显少很多。

缺点

  • 只能从ISR 发送event或者data 到task, 而不能从task发送至ISR.
  • 发出的消息只能被一个task接收,但这个限制很少,因为很少有多个task从同一个通信对象获取消息。
  • 不能缓冲消息,只能保存一个值。而queue可以
  • 不能向多个task广播消息,而event group可以
  • 发送方无等待,即task Notification 不会因为taks的状态为正在阻塞而去等待发送完成后,再发送。而是会直接覆盖掉。

总结

1)FreeRTOS 提供了任务间传递信息的机制。任务可以等待一个通知信息进入阻塞状态,并在通知信息到来时自动解除阻塞,进入就绪的状态。

2)任务通知是实现任务之间消息传递的机制之一,每个任务在创建时都自动地包含一个用来记录当前通知值的 uint32_t 类型的 NotifiedValue,以及一个 记录当前任务通知状态的 uint8_t 类型的 NotifyState。任务之间可以通过更改、查询 NotifiedValue 传递消息。

3)任务通知机制主要是两组 API:加减1版本的 xTaskNotifyGive\xTaskNotifyTake,操作任意值版本的 xTaskNotify\xTaskNotifyWait。

资源链接

1)Learning-FreeRTOS-with-esp32 系列博客介绍
2)对应示例的 code 链接 (点击直达代码仓库)

3)下一篇:获取 FreeRTOS 栈空间大小及其高水位线

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

物联网老王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值