任务通知的简介(了解)
任务通知是 FreeRTOS 中一种用于任务间通信的机制,它允许一个任务向其他任务发送简单的通知或信号,以实现任务间的同步和协作。任务通知通常用于替代二值信号量或事件标志组,提供了更轻量级的任务间通信方式。
大多数任务间通信方法通过中间对象,如队列、信号量或事件组。发送任务写入通信对象,接收任务从通信对象读取。当使用直接任务通知时,顾名思义,发送任务直接向接收任务发送通知,而无需中间对象。




每个 RTOS 任务都有一个任务通知组,每条通知均独立运行,都有“挂起”或“非挂起”的通知状态,以及一个 32 位通知值。常量 configTASK_NOTIFICATION_ARRAY_ENTRIES 可设置任务通知组中的索引数量。在 FreeRTOS V10.4.0 版本前,任务只有单条任务通知(即只能一对一),没有任务通知组。
向任务发送“任务通知” 会将目标任务通知设为“挂起”状态。 正如任务可以阻塞中间对象 (如等待信号量可用的信号量),任务也可以阻塞任务通知, 以等待通知状态变为“挂起”。向任务发送“任务通知”也可以更新目标通知的值(可选),可使用下列任一方法:
- 覆盖原值,无论接收任务是否已读取被覆盖的值。
- 覆盖原值(仅当接收任务已读取被覆盖的值时)。
- 在值中设置一个或多个位。
- 对值进行增量(添加 1)。
RTOS 任务通知功能默认为启用状态,将configUSE_TASK_NOTIFICATIONS 设为0可以禁用。
任务通知相关API函数介绍(熟悉)
任务通知相关函数如下:
| 函数 | 描述 |
| xTaskNotify() | 发送通知,带有通知值 |
| xTaskNotifyAndQuery() | 发送通知,带有通知值并且保留接收任务的原通知值 |
| xTaskNotifyGive() | 发送通知,不带通知值 |
| xTaskNotifyFromISR() | 在中断中发送任务通知 |
| xTaskNotifyAndQueryFromISR() | |
| vTaskNotifyGiveFromISR() | |
| ulTaskNotifyTake() | 获取任务通知,可选退出函数时对通知值清零或减1 |
| xTaskNotifyWait() | 获取任务通知,可获取通知值和清除通知值的指定位 |
注意:发送通知有相关ISR函数,接收通知没有ISR函数,不能在ISR中接收任务通知。
任务通知模拟信号量实验(掌握)实验目标
学习将任务通知用作轻量级二进制信号量:
- start_task:用来创建其他2个任务。
- task1:用于按键扫描,当检测到按键KEY1被按下时,将发送任务通知。
- task2:用于接收任务通知,并打印相关提示信息。
freertos_demo.c代码清单
#include "freertos_demo.h"
/* freertos相关的头文件,必须的 */
#include "FreeRTOS.h"
#include "task.h"
/* 需要用到的其他头文件 */
#include "LED.h"
#include "Key.h"
/* 启动任务的配置 */
#define START_TASK_STACK 128
#define START_TASK_PRIORITY 1
TaskHandle_t start_task_handle;
void start_task(void *pvParameters);
/* 任务1的配置 */
#define TASK1_STACK 128
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
void task1(void *pvParameters);
/* 任务2的配置 */
#define TASK2_STACK 128
#define TASK2_PRIORITY 3
TaskHandle_t task2_handle;
void task2(void *pvParameters);
/**
* @description: 启动FreeRTOS
* @return {*}
*/
void freertos_start(void)
{
/* 1.创建一个启动任务 */
xTaskCreate((TaskFunction_t)start_task, // 任务函数的地址
(char *)"start_task", // 任务名字符串
(configSTACK_DEPTH_TYPE)START_TASK_STACK, // 任务栈大小,默认最小128,单位4字节
(void *)NULL, // 传递给任务的参数
(UBaseType_t)START_TASK_PRIORITY, // 任务的优先级
(TaskHandle_t *)&start_task_handle); // 任务句柄的地址
/* 2.启动调度器:会自动创建空闲任务 */
vTaskStartScheduler();
}
/**
* @description: 启动任务:用来创建其他Task
* @param {void} *pvParameters
* @return {*}
*/
void start_task(void *pvParameters)
{
/* 进入临界区:保护临界区里的代码不会被打断 */
taskENTER_CRITICAL();
/* 创建3个任务 */
xTaskCreate((TaskFunction_t)task1,
(char *)"task1",
(configSTACK_DEPTH_TYPE)TASK1_STACK,
(void *)NULL,
(UBaseType_t)TASK1_PRIORITY,
(TaskHandle_t *)&task1_handle);
xTaskCreate((TaskFunction_t)task2,
(char *)"task2",
(configSTACK_DEPTH_TYPE)TASK2_STACK,
(void *)NULL,
(UBaseType_t)TASK2_PRIORITY,
(TaskHandle_t *)&task2_handle);
/* 启动任务只需要执行一次即可,用完就删除自己 */
vTaskDelete(NULL);
/* 退出临界区 */
taskEXIT_CRITICAL();
}
/**
* @description: 任务一:用于按键扫描,当检测到按键KEY1被按下时,将发送任务通知。
* @param {void} *pvParameters
* @return {*}
*/
void task1(void *pvParameters)
{
uint8_t key = 0;
BaseType_t res = 0;
while (1)
{
key = Key_Detect();
if(key==KEY1_PRESS)
{
/* 发送任务通知 */
res = xTaskNotifyGive(task2_handle);
if(res == pdPASS)
{
printf("task1向task2发送任务通知成功...\r\n");
}
}
vTaskDelay(500);
}
}
/**
* @description: 任务二:用于接收任务通知,并打印相关提示信息。
* @param {void} *pvParameters
* @return {*}
*/
void task2(void *pvParameters)
{
uint32_t notify_value = 0;
while (1)
{
printf("task2运行...\r\n");
notify_value = ulTaskNotifyTake(
// pdTRUE, // 接受完通知后,是否对通知置清零: pdTRUE 清零, pdFALSE 不清零,通知值-1
pdFALSE, // 接受完通知后,是否对通知置清零: pdTRUE 清零, pdFALSE 不清零,通知值-1
portMAX_DELAY // 等待任务通知的最大阻塞时间
);
printf("Task2接收到通知值=%d\r\n",notify_value);
// vTaskDelay(500);
vTaskDelay(5000);
}
}
任务通知模拟消息邮箱实验(掌握)实验目标
学习将任务通知用作轻量级邮箱:
- start_task:用来创建其他2个任务。
- task1:用于按键扫描,将按下的按键键值通过任务通知发送给指定任务。
- task2:用于接收任务通知,并根据接收到的数据做相应动作。
freertos_demo.c代码清单
#include "freertos_demo.h"
/* freertos相关的头文件,必须的 */
#include "FreeRTOS.h"
#include "task.h"
/* 需要用到的其他头文件 */
#include "LED.h"
#include "Key.h"
/* 启动任务的配置 */
#define START_TASK_STACK 128
#define START_TASK_PRIORITY 1
TaskHandle_t start_task_handle;
void start_task(void *pvParameters);
/* 任务1的配置 */
#define TASK1_STACK 128
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
void task1(void *pvParameters);
/* 任务2的配置 */
#define TASK2_STACK 128
#define TASK2_PRIORITY 3
TaskHandle_t task2_handle;
void task2(void *pvParameters);
/**
* @description: 启动FreeRTOS
* @return {*}
*/
void freertos_start(void)
{
/* 1.创建一个启动任务 */
xTaskCreate((TaskFunction_t)start_task, // 任务函数的地址
(char *)"start_task", // 任务名字符串
(configSTACK_DEPTH_TYPE)START_TASK_STACK, // 任务栈大小,默认最小128,单位4字节
(void *)NULL, // 传递给任务的参数
(UBaseType_t)START_TASK_PRIORITY, // 任务的优先级
(TaskHandle_t *)&start_task_handle); // 任务句柄的地址
/* 2.启动调度器:会自动创建空闲任务 */
vTaskStartScheduler();
}
/**
* @description: 启动任务:用来创建其他Task
* @param {void} *pvParameters
* @return {*}
*/
void start_task(void *pvParameters)
{
/* 进入临界区:保护临界区里的代码不会被打断 */
taskENTER_CRITICAL();
/* 创建3个任务 */
xTaskCreate((TaskFunction_t)task1,
(char *)"task1",
(configSTACK_DEPTH_TYPE)TASK1_STACK,
(void *)NULL,
(UBaseType_t)TASK1_PRIORITY,
(TaskHandle_t *)&task1_handle);
xTaskCreate((TaskFunction_t)task2,
(char *)"task2",
(configSTACK_DEPTH_TYPE)TASK2_STACK,
(void *)NULL,
(UBaseType_t)TASK2_PRIORITY,
(TaskHandle_t *)&task2_handle);
/* 启动任务只需要执行一次即可,用完就删除自己 */
vTaskDelete(NULL);
/* 退出临界区 */
taskEXIT_CRITICAL();
}
/**
* @description: 任务一:用于按键扫描,当检测到按键KEY1被按下时,将发送任务通知。
* @param {void} *pvParameters
* @return {*}
*/
void task1(void *pvParameters)
{
uint8_t key = 0;
BaseType_t res = 0;
while (1)
{
key = Key_Detect();
if (key == KEY1_PRESS || key == KEY2_PRESS)
{
/* 发送任务通知 */
res = xTaskNotify(
task2_handle, // 接收方的任务句柄
key, // 要发送的通知值
eSetValueWithOverwrite // 写入的行为:强行覆盖
);
if (res == pdPASS)
{
printf("task1向task2发送任务通知[%d]成功...\r\n", key);
}
}
vTaskDelay(500);
}
}
/**
* @description: 任务二:用于接收任务通知,并打印相关提示信息。
* @param {void} *pvParameters
* @return {*}
*/
void task2(void *pvParameters)
{
uint32_t notify_value = 0;
BaseType_t res = 0;
while (1)
{
printf("task2运行...\r\n");
res = xTaskNotifyWait(
0x00000000, // 接收通知前是否清理通知值,全0,表示32bit的都是0,都不清理
0xffffffff, // 接收到通知值后,是否清理通知值, 全1,表示32bit都是1,都要清零
¬ify_value, // 用来保存读取到的通知值
portMAX_DELAY);
if (res == pdTRUE)
{
printf("Task2接收到通知值=%d\r\n", notify_value);
}
}
}
任务通知模拟事件标志组实验(掌握)实验目标
学习将任务通知用作轻量级事件标志组:
- start_task:用来创建其他2个任务。
- task1:用于按键扫描,当检测到按键按下时,发送任务通知设置不同标志位。
- task2:用于接收任务通知,并打印相关提示信息。
freertos_demo.c代码清单
#include "freertos_demo.h"
/* freertos相关的头文件,必须的 */
#include "FreeRTOS.h"
#include "task.h"
/* 需要用到的其他头文件 */
#include "LED.h"
#include "Key.h"
/* 启动任务的配置 */
#define START_TASK_STACK 128
#define START_TASK_PRIORITY 1
TaskHandle_t start_task_handle;
void start_task(void *pvParameters);
/* 任务1的配置 */
#define TASK1_STACK 128
#define TASK1_PRIORITY 2
TaskHandle_t task1_handle;
void task1(void *pvParameters);
/* 任务2的配置 */
#define TASK2_STACK 128
#define TASK2_PRIORITY 3
TaskHandle_t task2_handle;
void task2(void *pvParameters);
/**
* @description: 启动FreeRTOS
* @return {*}
*/
void freertos_start(void)
{
/* 1.创建一个启动任务 */
xTaskCreate((TaskFunction_t)start_task, // 任务函数的地址
(char *)"start_task", // 任务名字符串
(configSTACK_DEPTH_TYPE)START_TASK_STACK, // 任务栈大小,默认最小128,单位4字节
(void *)NULL, // 传递给任务的参数
(UBaseType_t)START_TASK_PRIORITY, // 任务的优先级
(TaskHandle_t *)&start_task_handle); // 任务句柄的地址
/* 2.启动调度器:会自动创建空闲任务 */
vTaskStartScheduler();
}
/**
* @description: 启动任务:用来创建其他Task
* @param {void} *pvParameters
* @return {*}
*/
void start_task(void *pvParameters)
{
/* 进入临界区:保护临界区里的代码不会被打断 */
taskENTER_CRITICAL();
/* 创建3个任务 */
xTaskCreate((TaskFunction_t)task1,
(char *)"task1",
(configSTACK_DEPTH_TYPE)TASK1_STACK,
(void *)NULL,
(UBaseType_t)TASK1_PRIORITY,
(TaskHandle_t *)&task1_handle);
xTaskCreate((TaskFunction_t)task2,
(char *)"task2",
(configSTACK_DEPTH_TYPE)TASK2_STACK,
(void *)NULL,
(UBaseType_t)TASK2_PRIORITY,
(TaskHandle_t *)&task2_handle);
/* 启动任务只需要执行一次即可,用完就删除自己 */
vTaskDelete(NULL);
/* 退出临界区 */
taskEXIT_CRITICAL();
}
/**
* @description: 任务一:用于按键扫描,当检测到按键KEY1被按下时,将发送任务通知。
* @param {void} *pvParameters
* @return {*}
*/
#define EVENTBIT_0 (1 << 0)
#define EVENTBIT_1 (1 << 1)
void task1(void *pvParameters)
{
uint8_t key = 0;
BaseType_t res = 0;
while (1)
{
key = Key_Detect();
if (key == KEY1_PRESS)
{
/* 发送任务通知 */
res = xTaskNotify(
task2_handle, // 接收方的任务句柄
EVENTBIT_0, // 要发送的通知值: 需要置位的bit置1
eSetBits // 写入的行为:设置bit位
);
if (res == pdPASS)
{
printf("KEY1按下,设置bit0为1..\r\n");
}
}
else if (key == KEY2_PRESS)
{
/* 发送任务通知 */
res = xTaskNotify(
task2_handle, // 接收方的任务句柄
EVENTBIT_1, // 要发送的通知值: 需要置位的bit置1
eSetBits // 写入的行为:设置bit位
);
if (res == pdPASS)
{
printf("KEY2按下,设置bit1为1..\r\n");
}
}
vTaskDelay(500);
}
}
/**
* @description: 任务二:用于接收任务通知,并打印相关提示信息。
* @param {void} *pvParameters
* @return {*}
*/
void task2(void *pvParameters)
{
uint32_t notify_value = 0;
uint32_t expect_value = 0;
BaseType_t res = 0;
while (1)
{
printf("task2运行...\r\n");
res = xTaskNotifyWait(
0x00000000, // 接收通知前是否清理通知值,全0,表示32bit的都是0,都不清理
0xffffffff, // 接收到通知值后,是否清理通知值, 全1,表示32bit都是1,都要清零
¬ify_value, // 用来保存读取到的通知值
portMAX_DELAY);
if (notify_value & EVENTBIT_0)
{
/* bit0 = 1 */
printf("接收到的bit0=1\r\n");
expect_value |= EVENTBIT_0;
}
if (notify_value & EVENTBIT_1)
{
/* bit1 = 1 */
printf("接收到的bit1=1\r\n");
expect_value |= EVENTBIT_1;
}
/* 判断,是否希望的bit位都是1,如果是,则进行***处理 */
if(expect_value == 3)
{
printf("期望条件满足,值=%d\r\n",expect_value);
}
vTaskDelay(500);
}
}

Comments NOTHING