目录
一、特点
- 无需创建独立句柄:通过任务句柄直接发送 / 接收通知,省内存;
- 32 位通知内容:可存储命令、参数、状态等(灵活传递少量数据);
- 支持多模式:覆盖、叠加、置位等,适配不同场景;
- 替代简单队列 / 信号量:适合 “任务→任务” 的一对一 / 一对多通信。
二、使用流程(无 “创建” 步骤,直接用任务句柄)
1. 前置准备(已经有一个可以run的任务,下面这些得有)
包含头文件
#include "FreeRTOS.h"
#include "task.h" // 任务通知API在task.h中声明
定义接收任务的句柄
TaskHandle_t xRecvTaskHandle = NULL; // 接收通知的任务句柄
2. 创建发送 / 接收任务
通过xTaskCreate()创建两个任务(发送方和接收方),并保存接收任务的句柄:
// 创建接收任务(保存句柄)
xTaskCreate(RecvTask, "RecvTask", 128, NULL, 1, &xRecvTaskHandle);
// 创建发送任务
xTaskCreate(SendTask, "SendTask", 128, NULL, 2, NULL);
三、基本使用(核心 API 与操作)
1. 发送通知(发送任务调用)
在任务中:用xTaskNotify()发送 32 位通知内容,关键是指定 “接收任务句柄” 和 “通知模式”:
uint32_t ulNotifyValue = 0x12345678; // 32位通知内容(可存命令+参数)
// 发送通知(核心函数)
xTaskNotify(
xRecvTaskHandle, // 接收通知的任务句柄(必须有效)
ulNotifyValue, // 要发送的32位内容
eSetValueWithOverwrite // 通知模式(常用3种,见下表)
);
常用的通知模式:
| 模式 | 作用 | 适用场景 |
|---|---|---|
eSetValueWithOverwrite | 新通知覆盖旧通知(仅保留最新值) | 传递实时参数(如闪烁频率) |
eSetValueWithoutOverwrite | 旧通知未处理则不覆盖(防止丢失) | 传递事件(如按键触发) |
eSetBits | 按位叠加(仅置 1 对应位,不清除其他位) | 多事件标记(如多个按键) |
2. 接收通知(接收任务调用)
在任务中:用xTaskNotifyWait()等待并接收通知,可设置 “阻塞超时” 和 “位操作”:
参数定义放任务起始:
uint32_t ulRecvValue; // 存储接收的32位通知内容
BaseType_t xResult; // 接收结果(pdPASS=成功)
接收的代码:
// 接收通知(核心函数)
xResult = xTaskNotifyWait(
0xFFFFFFFFU, // 接收后要清除的位(0xFFFFFFFF=清除所有位)
0xFFFFFFFFU, // 接收时要保留的位(0xFFFFFFFF=保留所有位)
&ulRecvValue, // 存储接收内容的指针
portMAX_DELAY // 阻塞超时时间(0=非阻塞,portMAX_DELAY=永久等待)
);
// 处理通知
if(xResult == pdPASS) {
// 解析ulRecvValue(如命令+参数分离)
uint16_t cmd = (ulRecvValue >> 16) & 0xFFFF; // 高16位为命令
uint16_t param = ulRecvValue & 0xFFFF; // 低16位为参数
}
四、接收和发送加一个简单的校验,这里以LED为例:
通过位运算检查高 16 位的 “命令标识”:
为什么用高 16 位做命令?
避免参数值和命令冲突(比如参数最大是 65535,不会影响高 16 位);
为什么用固定命令值?
唯一标识通知的用途(比如后续扩展CMD_SET_COLOR控制 LED 颜色,也用高 16 位区分)。
1、main外的:
// 任务通知内容定义(用32位值传递:高16位标识命令,低16位传递参数)
#define CMD_SET_DELAY 0x00010000U // 命令:设置LED延时
#define GET_DELAY_PARAM(x) (x & 0xFFFFU) // 提取低16位的延时参数
2、我有创建的两个任务:
//外:
osThreadId_t LED_TaskHandle;
const osThreadAttr_t LED_Task_attributes = {
.name = "LED_Task",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityBelowNormal7,
};
osThreadId_t KEY_TaskHandle;
const osThreadAttr_t KEY_Task_attributes = {
.name = "KEY_Task",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
void Start_LED_Task(void *argument);
void Start_KEY_Task(void *argument);
//内:
LED_TaskHandle = osThreadNew(Start_LED_Task, NULL, &LED_Task_attributes);
KEY_TaskHandle = osThreadNew(Start_KEY_Task, NULL, &KEY_Task_attributes);
任务1、
void Start_LED_Task(void *argument)
{
uint32_t ulNotificationValue; // 接收的通知内容
uint16_t current_delay = 500U; // 默认500ms闪烁
BaseType_t xResult;
// 初始状态:确保LED是熄灭的(可选,根据硬件调整)
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
for(;;)
{ //HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_10);
/* 核心修改:用 xTaskNotifyWait 替代 xTaskNotifyWaitIndexed(基础API) */
xResult = xTaskNotifyWait(
0xFFFFFFFFU, // 接收后清除所有位(清空通知)
0xFFFFFFFFU, // 保留所有位(完整接收32位内容)
&ulNotificationValue, // 存储通知内容
0 //portMAX_DELAY // 永久等待通知
);
/* 收到通知后解析处理 */
if(xResult == pdPASS)
{
// 判断是否是“设置延时”命令
if((ulNotificationValue & 0xFFFF0000U) == CMD_SET_DELAY)
{
current_delay = GET_DELAY_PARAM(ulNotificationValue); // 更新延时
}
}
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
vTaskDelay(current_delay); // ѓʱ500ms
}
}
任务2、这个函数读取不同的按键来发不同的任务通知
case 1:
// 构造通知:命令 + 延时参数
ulNotificationValue = CMD_SET_DELAY | 200U;
// 核心修改:用 xTaskNotify 替代 xTaskNotifyGiveIndexed(基础API,无索引)
// 接收通知的任务句柄、通知内容、覆盖模式:新通知替换旧通知
xTaskNotify(LED_TaskHandle, ulNotificationValue,eSetValueWithOverwrite);
);
break;
case 2:
ulNotificationValue = CMD_SET_DELAY | 800U;
xTaskNotify(LED_TaskHandle, ulNotificationValue, eSetValueWithOverwrite);
break;
247

被折叠的 条评论
为什么被折叠?



