欢迎光临
我们一直在努力

FreeRTOS(13):任务通知模拟(二值信号量+消息邮箱

模拟二值信号量

实验步骤

实验步骤:

实验现象:按下按键后串口助手输出如下所示的内容。

工程创建

复制Freertos_事件标志组实验文件,粘贴并且重命名为Freertos_任务通知模拟二值信号量的实验,打开keil,删除FreeRTOS_Task.c文件中的有关事件标志组和task1,task2的全部内容,删除后的文件代码如下所示:

#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "led.h"
#include "key.h"
#include "serial.h"
#include "semphr.h"
#include "event_groups.h"

/* 任务参数 */
#define START_TASK_SIZE 128
#define START_TASK_PRIO 1
#define TASK1_SIZE 256
#define TASK1_PRIO 3
#define TASK2_SIZE 256
#define TASK2_PRIO 2

/* 任务句柄 */
TaskHandle_t start_task_handle;
TaskHandle_t task1_handle;
TaskHandle_t task2_handle;

void Start_task(void *pvParameters);
void task_1(void *pvParameters);
void task_2(void *pvParameters);

/**
* @brief 系统入口,创建启动任务并开启调度器
*/
void FreeRTOS_Task(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Key_Init();
LED_Init();
Serial_Init();

xTaskCreate((TaskFunction_t)Start_task, "Start_task",
START_TASK_SIZE, NULL, START_TASK_PRIO, &start_task_handle);
vTaskStartScheduler();
}

/**
* @brief 启动任务:创建事件组和两个应用任务,然后删除自身
*/
void Start_task(void *pvParameters)
{
taskENTER_CRITICAL();

xTaskCreate((TaskFunction_t)task_1, "task_1",
TASK1_SIZE, NULL, TASK1_PRIO, &task1_handle);
xTaskCreate((TaskFunction_t)task_2, "task_2",
TASK2_SIZE, NULL, TASK2_PRIO, &task2_handle);

taskEXIT_CRITICAL();
vTaskDelete(NULL);
}

void task_1(void *pvParameters)
{
while (1)
{

}
}

void task_2(void *pvParameters)
{

while (1)
{

}
}

而后在task1上操作,当按键按下后串口输出Task notification given (key pressed),提示按键按下和发送任务通知唤醒task2,下方是完整代码:

/**
* @brief 任务1:按键扫描,消抖后通过任务通知唤醒 task2
* – 按下 KEY1 (keynum == 1) 时,向 task2 发送一个通知
* – 包含上升沿消抖逻辑,确保单次按键只发送一次通知
* – 每 10ms 扫描一次,释放 CPU
*/
void task_1(void *pvParameters)
{
uint8_t keynum;
uint8_t last_key = 0; // 记录上一次按键状态,用于检测上升沿

while (1)
{
keynum = Key_GetNum(); // 读取当前按键值

/* 按键消抖:仅当按键从未按下变为按下时(上升沿)才处理 */
if (keynum != 0 && last_key == 0)
{
vTaskDelay(30); // 消抖延时 30 ms
if (Key_GetNum() == keynum) // 再次读取确认按键仍按下
{
if (keynum == 1) // KEY1 按下
{
/* 向 task2 发送一个任务通知(模拟二值信号量释放) */
xTaskNotifyGive(task2_handle);
Serial_Sendstring("Task notification given (key pressed)\\r\\n");
}
// 可扩展其他按键
}
}

last_key = keynum; // 更新上一次状态
vTaskDelay(10); // 10ms 扫描一次,降低 CPU 占用
}
}

task2负责接受任务通知,大家可以在接受任务通知后加上自己的操作,比如点亮LED灯等,下方是task2的代码:

/**
* @brief 任务2:等待任务通知,收到后执行相应操作
* – 永久阻塞等待 task1 发来的通知
* – 收到通知后打印提示信息并清零通知值
*/
void task_2(void *pvParameters)
{
uint32_t rev;

while (1)
{
/*
* ulTaskNotifyTake(pdTRUE, portMAX_DELAY) 说明:
* – 第一个参数 pdTRUE:函数返回前将通知值清零
* – 第二个参数 portMAX_DELAY:永久阻塞直到收到通知
* 返回值是清零前的通知值(此处至少为1)
*/
rev = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (rev != 0)
{
Serial_Sendstring("Task2 received notification, running…\\r\\n");
/* 在这里执行被唤醒后的工作,例如翻转LED等 */
}
}
}

下方是整个FreeRTOS_Task.c的完整代码:

#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "led.h"
#include "key.h"
#include "serial.h"
#include "semphr.h"
#include "event_groups.h" // 保留以备后用,当前未使用

/* 任务参数 */
#define START_TASK_SIZE 128
#define START_TASK_PRIO 1
#define TASK1_SIZE 256
#define TASK1_PRIO 3
#define TASK2_SIZE 256
#define TASK2_PRIO 2

/* 任务句柄 */
TaskHandle_t start_task_handle;
TaskHandle_t task1_handle;
TaskHandle_t task2_handle;

void Start_task(void *pvParameters);
void task_1(void *pvParameters);
void task_2(void *pvParameters);

/**
* @brief 系统入口,创建启动任务并开启调度器
*/
void FreeRTOS_Task(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Key_Init();
LED_Init();
Serial_Init();

xTaskCreate((TaskFunction_t)Start_task, "Start_task",
START_TASK_SIZE, NULL, START_TASK_PRIO, &start_task_handle);
vTaskStartScheduler();
}

/**
* @brief 启动任务:创建两个应用任务后删除自身
*/
void Start_task(void *pvParameters)
{
taskENTER_CRITICAL();

xTaskCreate((TaskFunction_t)task_1, "task_1",
TASK1_SIZE, NULL, TASK1_PRIO, &task1_handle);
xTaskCreate((TaskFunction_t)task_2, "task_2",
TASK2_SIZE, NULL, TASK2_PRIO, &task2_handle);

taskEXIT_CRITICAL();
vTaskDelete(NULL);
}

/**
* @brief 任务1:按键扫描,消抖后通过任务通知唤醒 task2
* – 按下 KEY1 (keynum == 1) 时,向 task2 发送一个通知
* – 包含上升沿消抖逻辑,确保单次按键只发送一次通知
* – 每 10ms 扫描一次,释放 CPU
*/
void task_1(void *pvParameters)
{
uint8_t keynum;
uint8_t last_key = 0; // 记录上一次按键状态,用于检测上升沿

while (1)
{
keynum = Key_GetNum(); // 读取当前按键值

/* 按键消抖:仅当按键从未按下变为按下时(上升沿)才处理 */
if (keynum != 0 && last_key == 0)
{
vTaskDelay(30); // 消抖延时 30 ms
if (Key_GetNum() == keynum) // 再次读取确认按键仍按下
{
if (keynum == 1) // KEY1 按下
{
/* 向 task2 发送一个任务通知(模拟二值信号量释放) */
xTaskNotifyGive(task2_handle);
Serial_Sendstring("Task notification given (key pressed)\\r\\n");
}
// 可扩展其他按键
}
}

last_key = keynum; // 更新上一次状态
vTaskDelay(10); // 10ms 扫描一次,降低 CPU 占用
}
}

/**
* @brief 任务2:等待任务通知,收到后执行相应操作
* – 永久阻塞等待 task1 发来的通知
* – 收到通知后打印提示信息并清零通知值
*/
void task_2(void *pvParameters)
{
uint32_t rev;

while (1)
{
/*
* ulTaskNotifyTake(pdTRUE, portMAX_DELAY) 说明:
* – 第一个参数 pdTRUE:函数返回前将通知值清零
* – 第二个参数 portMAX_DELAY:永久阻塞直到收到通知
* 返回值是清零前的通知值(此处至少为1)
*/
rev = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (rev != 0)
{
Serial_Sendstring("Task2 received notification, running…\\r\\n");
/* 在这里执行被唤醒后的工作,例如翻转LED等 */
}
}
}

模拟消息邮箱

实验步骤

实验步骤:

实验现象:按键按下后,串口输出对应的按键和PB引脚

工程创建

 复制Freertos_事件标志组实验文件,粘贴并且重命名为Freertos_任务通知模拟消息邮箱的实验,打开keil,删除FreeRTOS_Task.c文件中的有关事件标志组和task1,task2的全部内容,删除后的文件代码如下所示:

#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "led.h"
#include "key.h"
#include "serial.h"
#include "semphr.h"
#include "event_groups.h"

/* 任务参数 */
#define START_TASK_SIZE 128
#define START_TASK_PRIO 1
#define TASK1_SIZE 256
#define TASK1_PRIO 3
#define TASK2_SIZE 256
#define TASK2_PRIO 2

/* 任务句柄 */
TaskHandle_t start_task_handle;
TaskHandle_t task1_handle;
TaskHandle_t task2_handle;

void Start_task(void *pvParameters);
void task_1(void *pvParameters);
void task_2(void *pvParameters);

/**
* @brief 系统入口,创建启动任务并开启调度器
*/
void FreeRTOS_Task(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Key_Init();
LED_Init();
Serial_Init();

xTaskCreate((TaskFunction_t)Start_task, "Start_task",
START_TASK_SIZE, NULL, START_TASK_PRIO, &start_task_handle);
vTaskStartScheduler();
}

/**
* @brief 启动任务:创建事件组和两个应用任务,然后删除自身
*/
void Start_task(void *pvParameters)
{
taskENTER_CRITICAL();

xTaskCreate((TaskFunction_t)task_1, "task_1",
TASK1_SIZE, NULL, TASK1_PRIO, &task1_handle);
xTaskCreate((TaskFunction_t)task_2, "task_2",
TASK2_SIZE, NULL, TASK2_PRIO, &task2_handle);

taskEXIT_CRITICAL();
vTaskDelete(NULL);
}

void task_1(void *pvParameters)
{
while (1)
{

}
}

void task_2(void *pvParameters)
{

while (1)
{

}
}

随后来到task1,按键扫描,消抖后通过任务通知发送按键值给 task2,KEY1(GPIOB1) → 发送值 1,task2 中识别为 "key1(pb1) pressed",KEY2(GPIOB11) → 发送值 2,task2 中识别为 "key2(pb11) pressed",下方是task1的代码:

/**
* @brief 任务1:按键扫描,消抖后通过任务通知发送按键值给 task2
* – KEY1(GPIOB1) → 发送值 1,task2 中识别为 "key1(pb1) pressed"
* – KEY2(GPIOB11) → 发送值 2,task2 中识别为 "key2(pb11) pressed"
* – 使用上升沿消抖(检测按下瞬间),每 10ms 扫描一次,释放 CPU
*/
void task_1(void *pvParameters)
{
uint8_t keynum;
uint8_t last_key = 0; // 上一次按键状态,用于检测上升沿

while (1)
{
keynum = Key_GetNum(); // 读取当前按键值

/* 消抖:仅当按键从未按下变为按下时(上升沿)才处理 */
if (keynum != 0 && last_key == 0)
{
vTaskDelay(30); // 消抖延时 30ms
if (Key_GetNum() == keynum) // 再次读取,确认按键仍按下
{
/* 只对已知按键 KEY1/KEY2 发送通知 */
if (keynum == 1 || keynum == 2)
{
/*
* 向 task2 发送通知,通知值为按键编号
* eSetValueWithOverwrite:覆盖旧通知值(即使上次值未被读取)
*/
xTaskNotify(task2_handle,
keynum,
eSetValueWithOverwrite);
Serial_Sendstring("Task notification sent (key=");
Serial_SendNum(keynum, 1);
Serial_Sendstring(")\\r\\n");
}
}
}

last_key = keynum; // 记录本次状态
vTaskDelay(10); // 10ms 扫描周期,降低 CPU 占用
}
}

随后来到task2,等待任务通知,接收按键值并打印对应信息,永久阻塞等待 task1 发来的通知,收到通知后根据数值区分按键,并清空通知,下方是task2的代码:

/**
* @brief 任务2:等待任务通知,接收按键值并打印对应信息
* – 永久阻塞等待 task1 发来的通知
* – 收到通知后根据数值区分按键,并清空通知值
*/
void task_2(void *pvParameters)
{
uint32_t notify_val = 0;

while (1)
{
/*
* xTaskNotifyWait:
* – 第一个参数 0:进入等待时不清除任何位
* – 第二个参数 0xFFFFFFFF:退出时将通知值全部清除(清零)
* – 第三个参数 &notify_val:保存接收到的通知值
* – 第四个参数 portMAX_DELAY:永久阻塞直到收到通知
*/
xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);

if (notify_val == 1)
{
Serial_Sendstring("Key1 (PB1) pressed\\r\\n");
}
else if (notify_val == 2)
{
Serial_Sendstring("Key2 (PB11) pressed\\r\\n");
}
else
{
Serial_Sendstring("Unknown key value\\r\\n");
}
}
}

下方是完整代码:
 

#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "led.h"
#include "key.h"
#include "serial.h"
#include "semphr.h"
#include "event_groups.h" // 当前未使用,可保留

/* 任务参数 */
#define START_TASK_SIZE 128
#define START_TASK_PRIO 1
#define TASK1_SIZE 256
#define TASK1_PRIO 3
#define TASK2_SIZE 256
#define TASK2_PRIO 2

/* 任务句柄 */
TaskHandle_t start_task_handle;
TaskHandle_t task1_handle;
TaskHandle_t task2_handle;

void Start_task(void *pvParameters);
void task_1(void *pvParameters);
void task_2(void *pvParameters);

/**
* @brief 系统入口,创建启动任务并开启调度器
*/
void FreeRTOS_Task(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Key_Init();
LED_Init();
Serial_Init();

xTaskCreate((TaskFunction_t)Start_task, "Start_task",
START_TASK_SIZE, NULL, START_TASK_PRIO, &start_task_handle);
vTaskStartScheduler();
}

/**
* @brief 启动任务:创建两个应用任务后删除自身
*/
void Start_task(void *pvParameters)
{
taskENTER_CRITICAL();

xTaskCreate((TaskFunction_t)task_1, "task_1",
TASK1_SIZE, NULL, TASK1_PRIO, &task1_handle);
xTaskCreate((TaskFunction_t)task_2, "task_2",
TASK2_SIZE, NULL, TASK2_PRIO, &task2_handle);

taskEXIT_CRITICAL();
vTaskDelete(NULL);
}

/**
* @brief 任务1:按键扫描,消抖后通过任务通知发送按键值给 task2
* – KEY1(GPIOB1) → 发送值 1,task2 中识别为 "key1(pb1) pressed"
* – KEY2(GPIOB11) → 发送值 2,task2 中识别为 "key2(pb11) pressed"
* – 使用上升沿消抖(检测按下瞬间),每 10ms 扫描一次,释放 CPU
*/
void task_1(void *pvParameters)
{
uint8_t keynum;
uint8_t last_key = 0; // 上一次按键状态,用于检测上升沿

while (1)
{
keynum = Key_GetNum(); // 读取当前按键值

/* 消抖:仅当按键从未按下变为按下时(上升沿)才处理 */
if (keynum != 0 && last_key == 0)
{
vTaskDelay(30); // 消抖延时 30ms
if (Key_GetNum() == keynum) // 再次读取,确认按键仍按下
{
/* 只对已知按键 KEY1/KEY2 发送通知 */
if (keynum == 1 || keynum == 2)
{
/*
* 向 task2 发送通知,通知值为按键编号
* eSetValueWithOverwrite:覆盖旧通知值(即使上次值未被读取)
*/
xTaskNotify(task2_handle,
keynum,
eSetValueWithOverwrite);
Serial_Sendstring("Task notification sent (key=");
Serial_SendNum(keynum, 1);
Serial_Sendstring(")\\r\\n");
}
}
}

last_key = keynum; // 记录本次状态
vTaskDelay(10); // 10ms 扫描周期,降低 CPU 占用
}
}

/**
* @brief 任务2:等待任务通知,接收按键值并打印对应信息
* – 永久阻塞等待 task1 发来的通知
* – 收到通知后根据数值区分按键,并清空通知值
*/
void task_2(void *pvParameters)
{
uint32_t notify_val = 0;

while (1)
{
/*
* xTaskNotifyWait:
* – 第一个参数 0:进入等待时不清除任何位
* – 第二个参数 0xFFFFFFFF:退出时将通知值全部清除(清零)
* – 第三个参数 &notify_val:保存接收到的通知值
* – 第四个参数 portMAX_DELAY:永久阻塞直到收到通知
*/
xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);

if (notify_val == 1)
{
Serial_Sendstring("Key1 (PB1) pressed\\r\\n");
}
else if (notify_val == 2)
{
Serial_Sendstring("Key2 (PB11) pressed\\r\\n");
}
else
{
Serial_Sendstring("Unknown key value\\r\\n");
}
}
}

模拟事件标志组

实验步骤

实验步骤:

实验现象:只有当两个按键都按下后,串口才输出Both keys pressed (task successful):

工程创建

 复制Freertos_事件标志组实验文件,粘贴并且重命名为Freertos_任务通知模拟事件标志组的实验,打开keil,删除FreeRTOS_Task.c文件中的有关事件标志组和task1,task2的全部内容,删除后的文件代码如下所示:

#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "led.h"
#include "key.h"
#include "serial.h"
#include "semphr.h"
#include "event_groups.h"

/* 任务参数 */
#define START_TASK_SIZE 128
#define START_TASK_PRIO 1
#define TASK1_SIZE 256
#define TASK1_PRIO 3
#define TASK2_SIZE 256
#define TASK2_PRIO 2

/* 任务句柄 */
TaskHandle_t start_task_handle;
TaskHandle_t task1_handle;
TaskHandle_t task2_handle;

void Start_task(void *pvParameters);
void task_1(void *pvParameters);
void task_2(void *pvParameters);

/**
* @brief 系统入口,创建启动任务并开启调度器
*/
void FreeRTOS_Task(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Key_Init();
LED_Init();
Serial_Init();

xTaskCreate((TaskFunction_t)Start_task, "Start_task",
START_TASK_SIZE, NULL, START_TASK_PRIO, &start_task_handle);
vTaskStartScheduler();
}

/**
* @brief 启动任务:创建事件组和两个应用任务,然后删除自身
*/
void Start_task(void *pvParameters)
{
taskENTER_CRITICAL();

xTaskCreate((TaskFunction_t)task_1, "task_1",
TASK1_SIZE, NULL, TASK1_PRIO, &task1_handle);
xTaskCreate((TaskFunction_t)task_2, "task_2",
TASK2_SIZE, NULL, TASK2_PRIO, &task2_handle);

taskEXIT_CRITICAL();
vTaskDelete(NULL);
}

void task_1(void *pvParameters)
{
while (1)
{

}
}

void task_2(void *pvParameters)
{

while (1)
{

}
}

来到task1,按键扫描与任务通知发送,检测两个按键(KEY1: PB1, KEY2: PB11),消抖后向 task2 发送特定位,按 KEY1 发送 0x01 (即位0),按 KEY2 发送 0x02 (即位1),通知采用 eSetBits 方式,task2 会累积这些位直到两个位都置位,下方是task1的代码:

/**
* @brief 任务1:按键扫描与任务通知发送
* – 检测两个按键(KEY1: PB1, KEY2: PB11),消抖后向 task2 发送特定位
* – 按 KEY1 发送 0x01 (即位0)
* – 按 KEY2 发送 0x02 (即位1)
* – 通知采用 eSetBits 方式,task2 会累积这些位直到两个位都置位
* – 每 10ms 扫描一次,降低 CPU 占用
*/
void task_1(void *pvParameters)
{
uint8_t keynum;
uint8_t last_key = 0; /* 上一次按键状态,用于检测上升沿 */

while (1)
{
keynum = Key_GetNum(); /* 读取当前按键值 */

/* 消抖:仅当按键从未按下变为按下时(上升沿)才处理 */
if (keynum != 0 && last_key == 0)
{
vTaskDelay(30); /* 消抖延时 30ms */
if (Key_GetNum() == keynum) /* 再次确认按键仍按下 */
{
if (keynum == 1) /* KEY1 按下 */
{
xTaskNotify(task2_handle, 0x01, eSetBits);
Serial_Sendstring("Update bit0\\r\\n");
}
else if (keynum == 2) /* KEY2 按下 */
{
xTaskNotify(task2_handle, 0x02, eSetBits);
Serial_Sendstring("Update bit1\\r\\n");
}
}
}

last_key = keynum; /* 记录本次状态,为下次上升沿检测用 */
vTaskDelay(10); /* 10ms 扫描周期 */
}
}

来到task2,等待任务通知并累积事件位,阻塞等待 task1 发来的通知,将通知值中的特定位累积到本地变量 combined_bits,当 combined_bits 同时包含 0x01 和 0x02 时,打印成功信息并清零重新累积,下方是task2的代码:

/**
* @brief 任务2:等待任务通知并累积事件位
* – 阻塞等待 task1 发来的通知
* – 将通知值中的特定位累积到本地变量 combined_bits
* – 当 combined_bits 同时包含 0x01 和 0x02 时,打印成功信息并清零重新累积
*
* 注意:每次 xTaskNotifyWait 返回后通知值会被清零(因为使用了 0xFFFFFFFF 掩码)
* 所以需要通过本地变量 combined_bits 实现跨次通知的“位累积”效果。
*/
void task_2(void *pvParameters)
{
uint32_t notify_val; /* 单次收到的通知值 */
uint32_t combined_bits = 0; /* 累积的事件位 */

while (1)
{
/*
* 等待通知,退出时清除整个通知值(掩码 = 0xFFFFFFFF)
* 将当前通知值存入 notify_val
*/
xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);

/* 记录本次触发的事件位 */
if (notify_val & 0x01)
{
combined_bits |= 0x01;
}
if (notify_val & 0x02)
{
combined_bits |= 0x02;
}

/* 检查是否两个位都已置位 */
if ((combined_bits & 0x03) == 0x03)
{
Serial_Sendstring("Both keys pressed (task successful)\\r\\n");
combined_bits = 0; /* 清零,准备下一次累积 */
}
}
}

下方是完整代码:

#include "stm32f10x.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "led.h"
#include "key.h"
#include "serial.h"
#include "semphr.h"
#include "event_groups.h" /* 保留以备后用 */

/* 任务参数 */
#define START_TASK_SIZE 128
#define START_TASK_PRIO 1
#define TASK1_SIZE 256
#define TASK1_PRIO 3
#define TASK2_SIZE 256
#define TASK2_PRIO 2

/* 任务句柄 */
TaskHandle_t start_task_handle;
TaskHandle_t task1_handle;
TaskHandle_t task2_handle;

void Start_task(void *pvParameters);
void task_1(void *pvParameters);
void task_2(void *pvParameters);

/**
* @brief 系统入口,创建启动任务并开启调度器
*/
void FreeRTOS_Task(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
Key_Init();
LED_Init();
Serial_Init();

xTaskCreate((TaskFunction_t)Start_task, "Start_task",
START_TASK_SIZE, NULL, START_TASK_PRIO, &start_task_handle);
vTaskStartScheduler();
}

/**
* @brief 启动任务:创建两个应用任务后删除自身
*/
void Start_task(void *pvParameters)
{
taskENTER_CRITICAL();

xTaskCreate((TaskFunction_t)task_1, "task_1",
TASK1_SIZE, NULL, TASK1_PRIO, &task1_handle);
xTaskCreate((TaskFunction_t)task_2, "task_2",
TASK2_SIZE, NULL, TASK2_PRIO, &task2_handle);

taskEXIT_CRITICAL();
vTaskDelete(NULL);
}

/**
* @brief 任务1:按键扫描与任务通知发送
* – 检测两个按键(KEY1: PB1, KEY2: PB11),消抖后向 task2 发送特定位
* – 按 KEY1 发送 0x01 (即位0)
* – 按 KEY2 发送 0x02 (即位1)
* – 通知采用 eSetBits 方式,task2 会累积这些位直到两个位都置位
* – 每 10ms 扫描一次,降低 CPU 占用
*/
void task_1(void *pvParameters)
{
uint8_t keynum;
uint8_t last_key = 0; /* 上一次按键状态,用于检测上升沿 */

while (1)
{
keynum = Key_GetNum(); /* 读取当前按键值 */

/* 消抖:仅当按键从未按下变为按下时(上升沿)才处理 */
if (keynum != 0 && last_key == 0)
{
vTaskDelay(30); /* 消抖延时 30ms */
if (Key_GetNum() == keynum) /* 再次确认按键仍按下 */
{
if (keynum == 1) /* KEY1 按下 */
{
xTaskNotify(task2_handle, 0x01, eSetBits);
Serial_Sendstring("Update bit0\\r\\n");
}
else if (keynum == 2) /* KEY2 按下 */
{
xTaskNotify(task2_handle, 0x02, eSetBits);
Serial_Sendstring("Update bit1\\r\\n");
}
}
}

last_key = keynum; /* 记录本次状态,为下次上升沿检测用 */
vTaskDelay(10); /* 10ms 扫描周期 */
}
}

/**
* @brief 任务2:等待任务通知并累积事件位
* – 阻塞等待 task1 发来的通知
* – 将通知值中的特定位累积到本地变量 combined_bits
* – 当 combined_bits 同时包含 0x01 和 0x02 时,打印成功信息并清零重新累积
*
* 注意:每次 xTaskNotifyWait 返回后通知值会被清零(因为使用了 0xFFFFFFFF 掩码)
* 所以需要通过本地变量 combined_bits 实现跨次通知的“位累积”效果。
*/
void task_2(void *pvParameters)
{
uint32_t notify_val; /* 单次收到的通知值 */
uint32_t combined_bits = 0; /* 累积的事件位 */

while (1)
{
/*
* 等待通知,退出时清除整个通知值(掩码 = 0xFFFFFFFF)
* 将当前通知值存入 notify_val
*/
xTaskNotifyWait(0, 0xFFFFFFFF, &notify_val, portMAX_DELAY);

/* 记录本次触发的事件位 */
if (notify_val & 0x01)
{
combined_bits |= 0x01;
}
if (notify_val & 0x02)
{
combined_bits |= 0x02;
}

/* 检查是否两个位都已置位 */
if ((combined_bits & 0x03) == 0x03)
{
Serial_Sendstring("Both keys pressed (task successful)\\r\\n");
combined_bits = 0; /* 清零,准备下一次累积 */
}
}
}

若有错误,还望海涵。

赞(0)
未经允许不得转载:171主机测评 » FreeRTOS(13):任务通知模拟(二值信号量+消息邮箱
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址