tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第10章 消息队列" author: "尚硅谷研究院" date: 2026-07-17
用生活理解:消息队列就像快递柜。寄件人(发送任务)把包裹放进柜子,收件人(接收任务)从柜子里取走。柜子有固定数量的格子(队列长度),格子满了寄件人就得排队等空位,格子空了收件人就得等新包裹。每个格子只放固定尺寸的物品(数据拷贝),而不是放一把钥匙让你自己去取(传指针)。
队列(Queue)是 FreeRTOS 中任务间通信的主要形式,用于在任务之间以及中断和任务之间发送消息。
队列内部结构包含:
当队列有空间时,最高优先级的等待发送任务将解除阻塞;当队列有数据时,最高优先级的等待接收任务将解除阻塞。
| Function | Params | Description |
|---|---|---|
xQueueCreate() |
uxQueueLength, uxItemSize |
动态创建队列 |
xQueueSend() |
xQueue, pvItemToQueue, xTicksToWait |
尾部写入消息 |
xQueueSendToBack() |
同上 | 同 xQueueSend() |
xQueueSendToFront() |
同上 | 头部写入消息 |
xQueueReceive() |
xQueue, pvBuffer, xTicksToWait |
头部读取并删除消息 |
xQueuePeek() |
同上 | 头部读取消息(不删除) |
xQueueOverwrite() |
xQueue, pvItemToQueue |
覆写(仅队列长度=1时可用) |
学习使用 FreeRTOS 队列相关函数,包括创建队列、入队和出队操作:
queue1(小数据队列);按键 KEY3 按下时,将大数据的地址发送到 big_queue(指针队列)queue1 读取消息并打印键值big_queue 读取大数据地址,通过地址访问数据File: P09/09_消息队列/Core/Src/freertos_demo.c
#include "queue.h" /* 队列操作头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* 任务配置 */
#define START_TASK_PRIORITY 1
#define START_TASK_STACK_DEPTH 128
TaskHandle_t start_task_handler;
void Start_Task(void *pvParameters);
#define TASK1_PRIORITY 2
#define TASK1_STACK_DEPTH 128
TaskHandle_t task1_handler;
void Task1(void *pvParameters);
#define TASK2_PRIORITY 3
#define TASK2_STACK_DEPTH 128
TaskHandle_t task2_handler;
void Task2(void *pvParameters);
#define TASK3_PRIORITY 4
#define TASK3_STACK_DEPTH 128
TaskHandle_t task3_handler;
void Task3(void *pvParameters);
QueueHandle_t queue1; /* 小数据队列句柄 */
QueueHandle_t big_queue; /* 大数据队列句柄 */
char buff[100] = {"大大大fdahjk324hjkhfjksdahjk#$@!@#jfaskdfhjka"};
/* FreeRTOS入口函数 */
void FreeRTOS_Start(void)
{
/* 创建queue1队列:长度2,每个元素1字节 */
queue1 = xQueueCreate(2, sizeof(uint8_t));
if (queue1 != NULL) {
printf("queue1队列创建成功\r\n");
} else {
printf("queue1队列创建失败\r\n");
}
/* 创建big_queue队列:长度1,每个元素为指针(char*) */
big_queue = xQueueCreate(1, sizeof(char *));
if (big_queue != NULL) {
printf("big_queue队列创建成功\r\n");
} else {
printf("big_queue队列创建失败\r\n");
}
xTaskCreate((TaskFunction_t)Start_Task,
(char *)"Start_Task",
(configSTACK_DEPTH_TYPE)START_TASK_STACK_DEPTH,
(void *)NULL,
(UBaseType_t)START_TASK_PRIORITY,
(TaskHandle_t *)&start_task_handler);
vTaskStartScheduler();
}
/* 启动任务:创建三个工作任务 */
void Start_Task(void *pvParameters)
{
taskENTER_CRITICAL(); /* 进入临界区 */
xTaskCreate((TaskFunction_t)Task1,
(char *)"Task1",
(configSTACK_DEPTH_TYPE)TASK1_STACK_DEPTH,
(void *)NULL,
(UBaseType_t)TASK1_PRIORITY,
(TaskHandle_t *)&task1_handler);
xTaskCreate((TaskFunction_t)Task2,
(char *)"Task2",
(configSTACK_DEPTH_TYPE)TASK2_STACK_DEPTH,
(void *)NULL,
(UBaseType_t)TASK2_PRIORITY,
(TaskHandle_t *)&task2_handler);
xTaskCreate((TaskFunction_t)Task3,
(char *)"Task3",
(configSTACK_DEPTH_TYPE)TASK3_STACK_DEPTH,
(void *)NULL,
(UBaseType_t)TASK3_PRIORITY,
(TaskHandle_t *)&task3_handler);
vTaskDelete(NULL); /* 删除启动任务自身 */
taskEXIT_CRITICAL(); /* 退出临界区 */
}
/* task1:扫描按键,入队 */
void Task1(void *pvParameters)
{
uint8_t key = 0;
char *buf;
BaseType_t err = 0;
buf = &buff[0]; /* 获取大数据缓冲区地址 */
while (1) {
key = Key_Detect();
if (key == KEY1_PRESS || key == KEY2_PRESS) {
/* 发送键值到queue1队列,永久等待直到成功 */
err = xQueueSend(queue1, &key, portMAX_DELAY);
if (err != pdTRUE) {
printf("queue1队列发送失败\r\n");
}
} else if (key == KEY3_PRESS) {
/* 发送大数据地址到big_queue队列 */
err = xQueueSend(big_queue, &buf, portMAX_DELAY);
if (err != pdTRUE) {
printf("big_queue队列发送失败\r\n");
}
}
vTaskDelay(10);
}
}
/* task2:从queue1出队(小数据) */
void Task2(void *pvParameters)
{
uint8_t key = 0;
BaseType_t err = 0;
while (1) {
/* 从queue1读取键值,永久等待 */
err = xQueueReceive(queue1, &key, portMAX_DELAY);
if (err != pdTRUE) {
printf("queue1队列读取失败\r\n");
} else {
printf("queue1读取队列成功,数据:%d\r\n", key);
}
}
}
/* task3:从big_queue出队(大数据) */
void Task3(void *pvParameters)
{
char *buf;
BaseType_t err = 0;
while (1) {
/* 从big_queue读取指针,永久等待 */
err = xQueueReceive(big_queue, &buf, portMAX_DELAY);
if (err != pdTRUE) {
printf("big_queue队列读取失败\r\n");
} else {
printf("数据:%s\r\n", buf); /* 通过指针访问大数据 */
}
}
}
/* FreeRTOSConfig.h */
#define configQUEUE_REGISTRY_SIZE 8 /* 可注册的队列/信号量个数 */
| Function | Parameters | Return | Description |
|---|---|---|---|
QueueHandle_t xQueueCreate(uxQueueLength, uxItemSize) |
队列长度, 每项字节数 | NULL=失败, 非NULL=句柄 | 动态创建队列 |
BaseType_t xQueueSend(xQueue, pvItemToQueue, xTicksToWait) |
队列句柄, 数据指针, 超时ticks | pdPASS/errQUEUE_FULL | 尾部写入 |
BaseType_t xQueueSendToFront(xQueue, pvItemToQueue, xTicksToWait) |
同上 | 同上 | 头部写入 |
BaseType_t xQueueReceive(xQueue, pvBuffer, xTicksToWait) |
队列句柄, 接收缓冲区指针, 超时ticks | pdPASS/errQUEUE_EMPTY | 读取并删除 |
BaseType_t xQueuePeek(xQueue, pvBuffer, xTicksToWait) |
同上 | 同上 | 读取不删除 |
BaseType_t xQueueOverwrite(xQueue, pvItemToQueue) |
队列句柄, 数据指针 | pdPASS | 覆写(长度须为1) |
UBaseType_t uxQueueMessagesWaiting(xQueue) |
队列句柄 | 当前消息数 | 查询队列中消息数量 |
xQueueCreate 的 uxItemSize 必须与发送数据大小一致。传结构体时特别注意大小。xTicksToWait = portMAX_DELAY 且队列始终无空位,任务将永久阻塞。建议设计时确保接收方及时取走数据。xQueueCreate(1, sizeof(char*))),但需确保指针指向的内存生命周期有效。xQueueSendFromISR() 而非 xQueueSend(),且末尾调用 portYIELD_FROM_ISR() 触发切换。