@(嵌入式)
简述
FreeRTOS 从版本 V8.2.0
开始提供任务通知这个功能,每个任务都有一个32位的通知值。按照 FreeRTOS 官方的说法,使用消息通知比通过二进制信号量方式解除阻塞任务快 45%, 并且更加省内存(无需创建队列)。
FreeRTOS 提供以下几种方式发送通知给任务 :
* 发送消息给任务, 如果有通知未读, 不覆盖通知值
* 发送消息给任务,直接覆盖通知值
* 发送消息给任务,设置通知值的一个或者多个位
* 发送消息给任务,递增通知值
通过对以上方式的合理使用,可以在一定场合下替代原本的信号量,队列等。
当然,消息通知也有其限制 :
* 通知只能一对一,因为通知必须指定任务
* 等待通知的任务可以被阻塞, 但是发送消息的任务,任何情况下都不会被阻塞等待
分析的源码版本是 v9.0.0
通知 API
FreeRTOS 关于任务通知的 API 如下
API | 功能 |
---|---|
xTaskNotifyGive() | 发送通知,没有通知值 (信号量类型) |
ulTaskNotifyTake() | 获取通知,(对应 Give) |
xTaskNotify() | 发送通知, 带通知值 |
xTaskNotifyAndQuery() | 发送通知,带通知值,并且返回原通知值 |
xTaskNotifyWait() | 等待通知 |
vTaskNotifyGiveFromISR() | xTaskNotifyGive 的中断版本 |
xTaskNotifyAndQueryFromISR() | xTaskNotifyAndQuery 的中断版本 |
xTaskNotifyFromISR() | ulTaskNotifyTake 的中断版本 |
xTaskNotifyStateClear() | 清除所有未读消息 |
可能你会想,消息通知就一个发送一个接收 API 不就好了,为什么要搞出这么多个 API ?
实际上, 以上的 API,有的是宏定义,而如此实现是方便特定情况下使用,比如用通知去实现轻量化的二进制信号量,计数信号量,队列等。
数据结构
方便下文叙述,先介绍下实现的相关变量定义。
为了实现任务通知,任务控制块 TCB_t 结构体中有两个任务通知的相关变量, 默认情况下, 任务通知这个功能是打开的,也就是宏 configUSE_TASK_NOTIFICATIONS
设置为 1
源码
#if( configUSE_TASK_NOTIFICATIONS == 1 )
volatile uint32_t ulNotifiedValue;
volatile uint8_t ucNotifyState;
#endif
- 变量
ulNotifiedValue
存储任务通知的数值, 初始化为 0。 - 变量
ucNotifyState
存储当前任务通知的状态,对应存在以下三种状态
// 1 没有未读通知,任务没有等待通知
#define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 )
// 2 任务等待通知
#define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 )
// 3 通知等待任务读取
#define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 )
该变量初始化为 taskNOT_WAITING_NOTIFICATION
。
文章开头提到发送任务通知的几种方式,对应系统源码中定义了如下 5 种命令类型 :
typedef enum
{
// 1 发送通知,但是没有更新通知值
eNoAction = 0,
// 2 发送通知,将新通知值与原通知值或操作(置位)
eSetBits,
// 3 发送通知,原通知值加 1
eIncrement,
// 4 发送通知,直接修改通知值(不过上次通知是否已经读取)
eSetValueWithOverwrite,
// 5 发送通知,如果没有未读消息,设置通知值
eSetValueWithoutOverwrite
} eNotifyAction;
轻量级二进制信号量
我在 << FreeRTOS 信号量 >> 一文中举过一个例子,用二进制信号量进行同步。
这里,我们使用任务通知来实现同样的任务同步功能。
先看例子源码 :
// 等待通知的任务句柄
static TaskHandle_t xTaskToNotify = NULL;
void vATask( void * pvParameters )
{
uint32_t ulNotificationValue;
// 设置等待通知阻塞时间
const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 200 );
// 获取任务句柄
xTask