第10章 消息队列
约 1273 字大约 4 分钟
2026-01-19
10.1 核心知识点
队列是任务间通信的主要形式。 它们可以用于在任务之间以及中断和任务之间发送消息。

相关类型:
QueueHandle_t 队列句柄类型相关函数
xQueueCreate() 动态方式创建队列
xQueueCreateStatic() 静态方式创建队列
xQueueSend() 往队列的尾部写入消息
xQueueSendToBack() 同 xQueueSend()
xQueueSendToFront() 往队列的头部写入消息
xQueueOverwrite() 覆写队列消息(只用于队列长度为 1 的情况)
xQueueSendFromISR() 在中断服务函数中往队列的尾部写入消息
xQueueSendToBackFromISR() 同 xQueueSendFromISR()
xQueueSendToFrontFromISR() 在中断服务函数中往队列的头部写入消息
xQueueOverwriteFromISR() 在中断服务函数中覆写队列消息(只用于队列长度为 1 的情况)
xQueueReceive() 从队列头部读取消息,并删除消息
xQueuePeek() 从队列头部读取消息
xQueueReceiveFromISR() 在中断服务函数中从队列头部读取消息,并删除消息
xQueuePeekFromISR() 在中断服务函数中从队列头部读取消息10.2 案例
① 需求
task1:
当按键SW3或SW4按下,将按键的值添加到队列 queue1(入队);
当按键SW5按下,将传输较大数据,这里添加大数据的地址到队列 queue2 中。
task2:
读取队列 queue1 中的消息(出队),打印出接收到的键值。
task3:
从队列 queue2 读取大数据地址,通过地址访问大数据。② 代码
App_Task.h
添加头文件包含:
#include "queue.h"App_Task.c
#include "App_Task.h"
// 任务1 ------------------------------------
// 任务1函数的原型
void task1_callback(void *pvParameters);
// 任务1名称
#define TASK1_NAME "task1"
// 任务1堆栈大小
#define TASK1_STACK_SIZE 128
// 任务1的优先级
#define TASK1_PRIORITY 1
// 任务1的句柄
TaskHandle_t task1_handle;
// 任务2 ------------------------------------
// 任务2函数的原型
void task2_callback(void *pvParameters);
// 任务2名称
#define TASK2_NAME "task2"
// 任务2堆栈大小
#define TASK2_STACK_SIZE 128
// 任务2的优先级
#define TASK2_PRIORITY 2
// 任务2的句柄
TaskHandle_t task2_handle;
// 任务3 ------------------------------------
// 任务3函数的原型
void task3_callback(void *pvParameters);
// 任务3名称
#define TASK3_NAME "task3"
// 任务3堆栈大小
#define TASK3_STACK_SIZE 128
// 任务3的优先级
#define TASK3_PRIORITY 3
// 任务3的句柄
TaskHandle_t task3_handle;
// 定义队列1的句柄
QueueHandle_t queue1_handle = NULL;
// 定义队列2的句柄
QueueHandle_t queue2_handle = NULL;
// 定义全局字符串,用于任务1中向队列中发送
uint8_t *tx_msg = "How Are You ...";
/**
* @brief 启动 FreeRTOS 任务管理
*
*/
void App_Task_Start(void)
{
// 进入临界区
taskENTER_CRITICAL();
// 创建任务1
xTaskCreate(task1_callback, TASK1_NAME, TASK1_STACK_SIZE, NULL, TASK1_PRIORITY, &task1_handle) == pdPASS ? printf("任务1创建成功! \n") : printf("任务1穿件失败! \n");
// 创建任务2
xTaskCreate(task2_callback, TASK2_NAME, TASK2_STACK_SIZE, NULL, TASK2_PRIORITY, &task2_handle) == pdPASS ? printf("任务2创建成功! \n") : printf("任务2穿件失败! \n");
// 创建任务3
xTaskCreate(task3_callback, TASK3_NAME, TASK3_STACK_SIZE, NULL, TASK3_PRIORITY, &task3_handle) == pdPASS ? printf("任务3创建成功! \n") : printf("任务3穿件失败! \n");
// 创建队列1,长度是2,队列项目大小是1字节
queue1_handle = xQueueCreate(2, sizeof(uint8_t));
queue1_handle != NULL ? printf("队列1创建成功! \n") : printf("队列1创建失败! \n");
// 创建队列2,长度是1,项目大小是4字节(存地址)
queue2_handle = xQueueCreate(1, 4);
queue2_handle != NULL ? printf("队列2创建成功! \n") : printf("队列2创建失败! \n");
// 退出临界区
taskEXIT_CRITICAL();
// 启动任务调度器 ( vTaskStartScheduler() 后面的代码不会被执行)
printf("任务调度器启动... \n");
vTaskStartScheduler();
}
// 任务1函数的实现
void task1_callback(void *pvParameters)
{
printf("任务1启动... \n");
// 定义变量,用于保存按键值(Int_Key_IsDetect() 返回值)
uint8_t key = 0;
while (1)
{
// 检测按键是否按下
key = Int_Key_IsDetect();
switch (key)
{
case 3:
// 按键SW3被触发,向队列1中添加数据 3
printf("Task1: SW3被触发, 向队列1中添加数据 3 ... \n");
xQueueSend(queue1_handle, &key, 0);
break;
case 4:
// 按键SW4被触发,向队列1中添加数据 4
printf("Task1: SW4被触发, 向队列1中添加数据 4 ... \n");
xQueueSend(queue1_handle, &key, 0);
break;
case 5:
// 按键SW5被触发,向队列2中添加较大数据的地址 5
printf("Task1: SW5被触发, 向队列2中添加较大数据的地址 ... \n");
xQueueSend(queue2_handle, &tx_msg, 0);
break;
default:
break;
}
}
}
// 任务2函数的实现
void task2_callback(void *pvParameters)
{
printf("任务2启动... \n");
// 定义变量,用于接收队列1中的消息
uint8_t rx_queue1_msg = 0;
while (1)
{
// 从队列1的头部中接收消息,如果队列为空,就一直阻塞
printf("Task2: 等待从队列1中接收数据... \n");
xQueueReceive(queue1_handle, &rx_queue1_msg, portMAX_DELAY);
// 后面代码,接收到队列消息才会执行,打印接收到的数据
printf("Task2: 从队列1中接收到数据: %d \n", rx_queue1_msg);
}
}
// 任务3函数的实现
void task3_callback(void *pvParameters)
{
printf("任务3启动... \n");
uint8_t *rx_queue2_msg = NULL;
while (1)
{
// 从队列2的头部中接收消息,如果队列为空,就一直阻塞
printf("Task3: 等待从队列2中接收数据... \n");
xQueueReceive(queue2_handle, &rx_queue2_msg, portMAX_DELAY);
// 后面代码,接收到队列消息才会执行,打印接收到的数据
printf("Task3: 从队列2中接收到数据: %s \n", rx_queue2_msg);
}
}