

任务通知的优势
使用任务通知向任务发送事件或数据比使用队列、事件标志组或信号量快得多;并且使用 任务通知代替队列、事件标志组或信号量,可以节省大量的内存,这是因为每个通讯对象在使 用之前都需要被创建,而任务通知功能中的每个通知只需要在每个任务中占用固定的 5 字节内存。
FreeRTOS 任务通知相关 API 函数
FreeRTOS 提供了任务通知的一些相关操作函数,其中任务通知相关 API 函数,如下两表所示:
以上两表列出了 FreeRTOS 提供的几个任务通知相关的操作函数,从第 17.1 小节《FreeRTOS 任务通知简介》中,可以知道任务的任务控制块中,与任务通知功能相关的两个成员变量,任 务通知值和任务通知状态,是两个数组,也就是说,一个任务可以有多个任务通知,多个通知 就通过数组的下标进行索引。
表 17.2.1 所列出的 API 函数都是对任务通知相关数组中下标为 0 的元素进行操作,而表 17.2.2 中列出的 API 函数可以指定对任务通知相关数组中的元素进行操作。两表中对应的 API 函数原理上是一样的,只是表 17.2.1 中的 API 是固定对任务的任务通知 0 进行操作,而表 17.2.2 中的 API 函数可以对任务的指定任务通知进行操作,本文以表 17.2.1 中的函数为例进行讲解。
发送任务通知
三个用于在任务中发送任务通知的函数,实际上都是调用了函 数 xTaskGenericNotify()来发送任务通知的,只是传入了不同的参数。函数 xTaskGenericNotify() 的函数原型如下所示:
函数 xTaskGenericNotify()的形参描述,如下表所示:
结合函数 xTaskNotify()、函数 xTaskNotifyAndQuery()、函数 xTaskNotifyGive()的定义和以 上代码,可以知道函数 xTaskNotify()、函数 xTaskNotifyAndQuery()、函数 xTaskNotifyGive()的 作用如下所示:
函数 xTaskNotify(): 此函数用于往指定任务发送任务通知,通知方式可以自由指定,并且不获取发送任务通知 前任务通知的通知值。
函数 xTaskNotifyAndQuery(): 此函数用于往指定任务发送任务通知,通知方式可以自由指定,并且获取发送任务通知前 任务通知的通知值。
函数 xTaskNotifyGive(): 此函数用于往指定任务发送任务通知,通知方式为将通知值加 1,并且不获取发送任务通 知前任务通知的通知值。

函数 xTaskGenericNotifyFromISR()的形参描述,如下表所示:
函数 xTaskGenericNotifyFromISR()于函数 xTaskNotify()是很相 似的,只是多了对中断做了一些相应的处理。
函数 vTaskGenericNotifyGiveFromISR()的函数原型如下所示:
结合以上函数 xTaskGenericNotifyFromISR()和函数 vTaskGenericNotifyGiveFromISR()的源 代码和函数 xTaskNotifyFromISR() 和函数 xTaskNotifyAndQueryFromISR() 和函数 vTaskNotifyGiveFromISR()的定义,表 17.2.1 中列出的三个在中断中发送任务通知的 API 函数作 用如下:
函数 xTaskNotifyFromISR()
此函数用于在中断中往指定任务发送任务通知,通知方式可以自由指定,并且不获取发送 任务通知前任务通知的通知值,但获取发送通知后是否需要进行任务切换的标志。
函数 xTaskNotifyAndQueryFromISR()
此函数用于在中断中往指定任务发送任务通知,通知方式可以自由指定,并且获取发送任 务通知前任务通知的通知值,和发送通知后是否需要进行任务切换的标志。
函数 vTaskNotifyGiveFromISR()
此函数用于在中断中往指定任务发送任务通知,通知方式为将通知值加 1,并且不获取发 送任务通知前任务通知的通知值,但获取发送通知后是否需要进行任务切换的标志。

函数 xTaskNotifyWait()
此函数用于等待任务通知通知值中的指定比特位被置一,此函数可以在等待前和成功等待 到任务通知通知值中的指定比特位被置一后清零指定比特位,并且还能获取等待超时后任务通 知的通知值。此函数实际上是一个宏定义,具体的代码如下所示:
从 上 面 的 代 码 中 可 以 看 出 , 函 数 xTaskNotifyWait() 实 际 上 是 调 用 了 函 数 xTaskGenericNotifyWait(),函数 xTaskGenericNotifyWait()的函数原型如下所示:
void led_green_task(void *pvParameters)
{
unsigned char led_green_on_flag = 0;
while(1)
{
if(led_green_on_flag)
{
LED_GREEN_ON;
if(ApiTask_Handler != NULL)
{
xTaskNotifyGive((TaskHandle_t)ApiTask_Handler);
}
}else
{
LED_GREEN_OFF;
}
led_green_on_flag = ~led_green_on_flag;
vTaskDelay(50);
}
}
void api_task(void *pvParameters)
{
uint32_t notify_val = 0;
while(1)
{
notify_val = ulTaskNotifyTake((BaseType_t)pdTRUE, (TickType_t)portMAX_DELAY);
if(notify_val != 0)
{
printf("\r\n接收到LED RED任务通知:%d.\r\n", notify_val);
}
vTaskDelay(20);
}
}
下载验证:
FreeRTOS 任务通知模拟计数型信号量实验
计数型信号量的模拟也是相对比较简单的,程序设计验证思想还是以LED_GREEN任务来发送任务,API任务负责接收任务同时来进行显示。
NOTE: notify_val = ulTaskNotifyTake((BaseType_t)pdFALSE, (TickType_t)portMAX_DELAY);
因为是计数型信号量,所以需要将此参数设置为pdFALSE,每次任务通知获取成功后就将该信号通知数量-1。
也需要特别注意一下两个任务的延时,为了验证计数型信号量,所以任务发出函数的运行频率要远大于任务通知接收函数,以此可以达到任务通知计数的目的,为了防止溢出,限定了任务通知次数最大为10。
void led_green_task(void *pvParameters)
{
unsigned char led_green_on_flag = 0;
unsigned char task_give_counter = 0;
while(1)
{
if(led_green_on_flag)
{
LED_GREEN_ON;
if(ApiTask_Handler != NULL && task_give_counter <= 10)
{
task_give_counter++;
xTaskNotifyGive((TaskHandle_t)ApiTask_Handler);
}
}else
{
LED_GREEN_OFF;
}
led_green_on_flag = ~led_green_on_flag;
vTaskDelay(50);
}
}
void api_task(void *pvParameters)
{
uint32_t notify_val = 0;
while(1)
{
notify_val = ulTaskNotifyTake((BaseType_t)pdFALSE, (TickType_t)portMAX_DELAY);
if(notify_val != 0)
{
printf("\r\n接收到LED RED任务通知:%d.\r\n", notify_val);
}
vTaskDelay(1000);
}
}
下载验证:可以看到还是符合程序设计预期的,任务通知会积累到10,任务接收函数会按照自己的频率一个一个清除。
void led_red_task(void *pvParameters)
{
unsigned char led_red_on_flag = 0;
unsigned char led_red_task_num = 0X0A;
while(1)
{
if(led_red_on_flag)
{
LED_RED_ON;
if(CounterTask_Handler != NULL)
{
xTaskNotify((TaskHandle_t)CounterTask_Handler,
(uint32_t)led_red_task_num,
(eNotifyAction)eSetValueWithoutOverwrite);
}
}else
{
LED_RED_OFF;
}
led_red_on_flag = ~led_red_on_flag;
vTaskDelay(2000);
}
}
void ws2812b_task(void *pvParameters)
{
unsigned char ws2812b_change_flag = 0;
unsigned char ws2812b_task_num = 0X0B;
while(1)
{
if(ws2812b_change_flag)
{
ws2812b_write_rgb(0, 20, 0);
if(CounterTask_Handler != NULL)
{
xTaskNotify((TaskHandle_t)CounterTask_Handler,
(uint32_t)ws2812b_task_num,
(eNotifyAction)eSetValueWithoutOverwrite);
}
}else
{
ws2812b_write_rgb(0, 0, 0);
}
ws2812b_change_flag = ~ws2812b_change_flag;
vTaskDelay(3000);
}
}
COUNTER任务负责处理任务消息:
void counter_task(void *pvParameters)
{
uint32_t notify_val = 0;
while(1)
{
xTaskNotifyWait( (uint32_t )0X00000000,
(uint32_t )0XFFFFFFFF,
(uint32_t*)¬ify_val,
(TickType_t)portMAX_DELAY);
switch(notify_val)
{
case 0X0A:
printf("\r\n接收到LED GREEN任务的任务通知。\r\n");
break;
case 0X0B:
printf("\r\n接收到WS2812B任务的任务通知。\r\n");
break;
default:
break;
}
vTaskDelay(20);
}
}
下载验证:一把成功,还是很符合设计预期的。
FreeRTOS 任务通知模拟事件标志组实验
程序设计验证思想是以LED_RED任务以及WS2812B任务来发送任务通知的消息邮箱,COUNTER任务负责接收任务同时来进行显示。
其实和上述消息邮箱的机制基本是类似的,无非就是将传递的具体数据转变为标志位,同时任务处理端也是靠获取相应的标志位来进行处理。
#define EVENTBIT_0 (1<<0)
#define EVENTBIT_1 (1<<1)
#define EVENTBIT_ALL (EVENTBIT_0|EVENTBIT_1)
void led_red_task(void *pvParameters)
{
unsigned char led_red_on_flag = 0;
unsigned char led_red_task_num = 0X0A;
while(1)
{
if(led_red_on_flag)
{
LED_RED_ON;
if(CounterTask_Handler != NULL)
{
xTaskNotify((TaskHandle_t)CounterTask_Handler,
(uint32_t)EVENTBIT_0,
(eNotifyAction)eSetBits); //在实际工程调试时,发现了这个bug!需要修改为eSetBits
}
}else
{
LED_RED_OFF;
}
led_red_on_flag = ~led_red_on_flag;
vTaskDelay(2000);
}
}
void ws2812b_task(void *pvParameters)
{
unsigned char ws2812b_change_flag = 0;
unsigned char ws2812b_task_num = 0X0B;
while(1)
{
if(ws2812b_change_flag)
{
ws2812b_write_rgb(0, 20, 0);
if(CounterTask_Handler != NULL)
{
xTaskNotify((TaskHandle_t)CounterTask_Handler,
(uint32_t)EVENTBIT_1,
(eNotifyAction)eSetBits);//在实际工程调试时,发现了这个bug!需要修改为eSetBits
}
}else
{
ws2812b_write_rgb(0, 0, 0);
}
ws2812b_change_flag = ~ws2812b_change_flag;
vTaskDelay(3000);
}
}
void counter_task(void *pvParameters)
{
uint32_t notify_val = 0;
uint32_t event_val = 0;
while(1)
{
xTaskNotifyWait( (uint32_t )0X00000000,
(uint32_t )0XFFFFFFFF,
(uint32_t*)¬ify_val,
(TickType_t)portMAX_DELAY);
if(notify_val & EVENTBIT_0)
{
printf("\r\n接收到LED GREEN任务的任务通知。\r\n");
event_val |= EVENTBIT_0;
}else if(notify_val & EVENTBIT_1)
{
printf("\r\n接收到WS2812B任务的任务通知。\r\n");
event_val |= EVENTBIT_1;
}
if(event_val == EVENTBIT_ALL)
{
printf("\r\n接收到所有任务的任务通知。\r\n");
event_val = 0;
}
vTaskDelay(20);
}
}
下载验证: