一、任务通知的概念
FreeRTOS 从 V8.2.0 版本开始提供任务通知功能,每个任务都有一个 32 位的通知值,在多数情况下,任务通可以替代二值信号量、计数信号量、事件组,也可以替代长度为 1 的队列 (可以保存一个 32 位整数或指针值)。
相对于以前使用 FreeRTOS 内核通信的资源时必须创建队列、二进制信号量、计数信号量或事件组的情况,使用任务通知显然更灵活。按照 FreeRTOS 官方的说法,使用任务通知比通过信号量等 ICP 通信方式解除阻塞的任务速度快 45%,并且更加省 RAM 内存空间,任务通知的使用无须创建队列。要想使用任务通知,必须将 FreeRTOSConfig.h 中的宏定义 configUSE_TASK_NOTIFICATIONS 设置为 1。其实 FreeRTOS 默认是为 1 的,所以任务通知是默认可用的。
FreeRTOS 提供以下几种方式发送通知给任务:
- 发送通知给任务,如果有通知未读,则不覆盖通知值。
- 发送通知给任务,直接覆盖通知值。
- 发送通知给任务, 设置通知值的一个或者多个位,可以当作事件组来使用。
- 发送通知给任务,递增通知值,可以当作信号量使用。
通过对以上任务通知方式的合理使用,可以在一定场合下替代 FreeRTOS 的信号量、队列、事件组等。
当然,凡事都有利弊,消息通知虽然处理更快,RAM 开销更小,但也有以下限制:
- 只能有一个任务接收通知消息,因为必须指定接收通知的任务。
- 只有等待通知的任务可以被阻塞,发送通知的任务在任何情况下都不会因为发送失败
而进入阻塞态。
二、任务通知的运行机制
任务通知属于任务附带的资源,所以在任务被创建时,任务通知也被初始化,我们知道在使用队列、信号量前,必须先创建队列和信号量,目的是创建队列数据结构,比如使用 xQueueCreate() 函数创建队列,用 xSemaphoreCreateBinary() 函数创建二值信号量等。再来看任务通知,由于任务通知的数据结构包含在任务控制块中,只要任务存在,任务通知数据结构就已经创建完毕,可以直接使用,所以使用时很方便
任务通知可以在任务中向指定任务发送通知,也可以在中断中向指定任务发送通知。FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue 就是这个通知值。只有在任务中可以等待通知,而不允许在中断中等待通知。如果任务在等待的通知暂时无效,任务会根据用户指定的阻塞超时时间进入阻塞状态,我们可以将等待通知的任务看作消费者; 其他任务和中断可以向等待通知的任务发送通知,发送通知的任务和中断服务函数可以看作生产者,当其他任务或者中断向这个任务发送任务通知,且任务获得通知以后,该任务就会从阻塞态中解除,这与 FreeRTOS 中内核的其他通信机制一致。
三、任务通知的 API 函数
3.1、任务通知的数据结构
任务通知是任务控制块的资源,属于任务控制块中的成员变量,具体如下:
ulNotifiedValue: 任务通知值,用于保存一个 32 位整数或指针值
ucNotifyState: 任务通知状态,用于标识任务的通知状态
3.2、常用的 API 函数
① 替代二值信号量与计数信号量
发送任务通知:
xTaskNotifyGive()
vTaskNotifyGiveFromISR()
获取任务通知:
ulTaskNotifyTake()
② 替代长为 1 的队列与事件组
发送任务通知:
xTaskNotify()
vTaskNotifyFromISR()
获取任务通知:
ulTaskNotifyWait()
3.3、函数 xTaskNotifyGive()
①函数原型
功能如下:在任务中向指定任务发送通知,使任务通知值加 1,这种方式用于信号量
② 形参说明
形参 1:任务句柄
形参 2: 任务通知值,取 0
形参 3: 指示任务控制块中的变量 ulNotifiedValue 实现加一操作
形参 4:空
③返回值
没有超时机制,永远返回 pdPASS
3.4、函数 ulTaskNotifyTake()
① 函数原型
功能如下:在任务中获取通知,与 xTaskNotifyGive() 配套使用,用于替代二值信号量与计数信号量
②形参说明
形参 1:退出时是否清零计数。pdTRUE-> 清零, pdFALSE -> 减 1
形参 2: 超时时间,单位为系统节拍
③返回值
返回任务之前的任务通知值
3.5、函数 xTaskNotify()
① 函数原型
功能如下:在任务中向指定任务发送通知,根据 eAction 的值完成不同操作
②形参说明
形参 1:任务句柄
形参 2: 任务通知值
形参 3: 如下图片
形参 4:空
③返回值
根据上面第 3 个形参的说明,将其设置为 eSetValueWithoutOverwrite,有可能返回 pdFALSE,其余所有情况都返回值 pdPASS
3.6、函数 ulTaskNotifyWait()
① 函数原型
功能如下:在任务中获取通知,与 xTaskNotify() 配套使用,用于替代长度位 1 的队列与事件组
②形参说明
形参 1:进入函数时,清除哪些位 -> 0:无变化 1:清除相应位
形参 2:退出函数时,清除哪些位 -> 0:无变化 1:清除相应位
形参 3:保存通知值
形参 4: 超时时间,单位为系统节拍
③返回值
pdTRUE: 获取任务通知成功
pdFALSE:获取任务通知失败