tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — 第12章 队列集" author: "尚硅谷研究院" date: 2026-07-17
用生活理解:队列集就像医院的叫号系统。你去挂号(注册队列),然后在候诊区等待。不管是哪个科室(队列/信号量)叫号,你都能听到(统一通知)。不用每个科室门口都去排队看。
队列集(Queue Set)是 FreeRTOS 中用于管理多个队列/信号量的数据结构。它解决了"一个任务需要等待多个队列或信号量"的问题。
xQueueSelectFromSet() 阻塞等待任一注册成员有数据xQueueAddToSet() 注册成员xQueueSelectFromSet() 阻塞等待限制:
| Function | Params | Description |
|---|---|---|
xQueueCreateSet() |
uxEventQueueLength |
创建队列集 |
xQueueAddToSet() |
xQueueOrSemaphore, xQueueSet |
注册成员到队列集 |
xQueueRemoveFromSet() |
xQueueOrSemaphore, xQueueSet |
从队列集中移除成员 |
xQueueSelectFromSet() |
xQueueSet, xTicksToWait |
等待队列集中任一成员有数据 |
xQueueSelectFromSetFromISR() |
xQueueSet |
中断中查询就绪成员 |
学习使用 FreeRTOS 队列集:
File: P14/14_队列集/Core/Src/freertos_demo.c
#include "queue.h" /* 队列操作头文件 */
#include "semphr.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);
QueueSetHandle_t queueset_handle; /* 队列集句柄 */
QueueHandle_t queue_handle; /* 普通队列句柄 */
QueueHandle_t semphr_handle; /* 二值信号量句柄 */
/* FreeRTOS入口函数 */
void FreeRTOS_Start(void)
{
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();
/* 创建队列集,可容纳2个成员 */
queueset_handle = xQueueCreateSet(2);
if (queueset_handle != NULL) {
printf("队列集创建成功\r\n");
}
/* 创建普通队列:长度1,元素1字节 */
queue_handle = xQueueCreate(1, sizeof(uint8_t));
/* 创建二值信号量 */
semphr_handle = xSemaphoreCreateBinary();
/* 将队列和信号量注册到队列集 */
xQueueAddToSet(queue_handle, queueset_handle);
xQueueAddToSet(semphr_handle, queueset_handle);
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);
vTaskDelete(NULL);
taskEXIT_CRITICAL();
}
/* task1:KEY1发队列,KEY2释放信号量 */
void Task1(void *pvParameters)
{
uint8_t key = 0;
BaseType_t err = 0;
while (1) {
key = Key_Detect();
if (key == KEY1_PRESS) {
/* 往队列写入数据 */
err = xQueueSend(queue_handle, &key, portMAX_DELAY);
if (err == pdPASS) {
printf("往队列queue_handle写入数据成功\r\n");
}
} else if (key == KEY2_PRESS) {
/* 释放二值信号量 */
err = xSemaphoreGive(semphr_handle);
if (err == pdPASS) {
printf("释放信号量成功\r\n");
}
}
vTaskDelay(10);
}
}
/* task2:从队列集中等待任一成员就绪 */
void Task2(void *pvParameters)
{
QueueSetMemberHandle_t member_handle;
uint8_t key;
while (1) {
/* 阻塞等待队列集中任一成员有数据,永久等待 */
member_handle = xQueueSelectFromSet(queueset_handle, portMAX_DELAY);
if (member_handle == queue_handle) {
/* 是队列成员就绪 */
xQueueReceive(member_handle, &key, portMAX_DELAY);
printf("获取到的队列数据=%d\r\n", key);
} else if (member_handle == semphr_handle) {
/* 是信号量成员就绪 */
xSemaphoreTake(member_handle, portMAX_DELAY);
printf("获取信号量成功\r\n");
}
}
}
/* FreeRTOSConfig.h */
#define configUSE_QUEUE_SETS 1 /* 使能队列集功能 */
| Function | Parameters | Return | Description |
|---|---|---|---|
QueueSetHandle_t xQueueCreateSet(uxEventQueueLength) |
事件队列长度 | NULL=失败 | 创建队列集 |
BaseType_t xQueueAddToSet(xQueueOrSemaphore, xQueueSet) |
成员句柄, 队列集句柄 | pdPASS/pdFAIL | 注册成员 |
BaseType_t xQueueRemoveFromSet(xQueueOrSemaphore, xQueueSet) |
同上 | pdPASS/pdFAIL | 移除成员 |
QueueSetMemberHandle_t xQueueSelectFromSet(xQueueSet, xTicksToWait) |
队列集句柄, 超时 | NULL=超时 | 等待就绪成员 |
QueueSetMemberHandle_t xQueueSelectFromSetFromISR(xQueueSet) |
队列集句柄 | NULL=无就绪 | ISR中查询就绪成员 |
xQueueCreateSet(N) 的 N 必须 ≥ 注册的成员总数,否则注册可能失败。xQueueRemoveFromSet() 再删除,否则行为未定义。xQueueSelectFromSet() 返回后,该成员已从等待列表中移除,若不及时读取可能导致其他任务抢先读取。