FreeRTOS队列:入队与出队详解
2026/7/17 8:07:06 网站建设 项目流程

一、基本概念

入队(Enqueue):向队列中添加数据(发送消息)出队(Dequeue):从队列中获取数据(接收消息)

FreeRTOS队列采用FIFO(先进先出)机制,是任务间通信的核心方式。队列存储的是数据的拷贝,而非指针,确保了数据安全。

二、队列数据结构

队列的核心结构体Queue_t包含以下关键成员:

typedef struct QueueDefinition { int8_t *pcHead; // 指向队列存储区开始地址 int8_t *pcWriteTo; // 指向存储区中下一个空闲位置 union { QueuePointers_t xQueue; // 队列相关数据 SemaphoreData_t xSemaphore; // 信号量相关数据 } u; List_t xTasksWaitingToSend; // 等待发送任务列表(按优先级排序) List_t xTasksWaitingToReceive; // 等待接收任务列表(按优先级排序) volatile UBaseType_t uxMessagesWaiting; // 当前队列中消息数量 UBaseType_t uxLength; // 队列长度(最大可存储消息数) UBaseType_t uxItemSize; // 每个消息的大小 volatile int8_t cRxLock; // 接收锁 } Queue_t;

队列空/满判断

  • 队列为空pcWriteTo == pcReadFrom
  • 队列已满pcWriteTo + itemSize == pcReadFrom(考虑回绕)

三、入队操作

1. 入队函数

// 向队列尾部入队(默认方式) BaseType_t xQueueSendToBack(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait); // 向队列头部入队 BaseType_t xQueueSendToFront(QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait); // 覆写入队(仅当队列长度为1时有效) BaseType_t xQueueOverwrite(QueueHandle_t xQueue, const void *pvItemToQueue);

2. 入队操作原理

  1. 检查队列状态

    • 如果队列未满,继续入队
    • 如果队列已满,根据等待时间决定处理方式
  2. 数据拷贝

    • 调用prvCopyDataToQueue将数据从源地址拷贝到队列存储区
    • uxMessagesWaiting(消息数量)+1
  3. 任务唤醒

    • 如果有任务在等待接收(xTasksWaitingToReceive非空)
    • 将等待接收任务从阻塞态唤醒,加入就绪队列
    • 调用vTaskMissedYield()进行任务切换
  4. 队列满处理

    • 如果等待时间不为0,将当前任务加入等待发送列表
    • 如果等待时间=0,直接返回errQUEUE_FULL

3. 入队阻塞机制

阻塞时间行为
0立即返回,不等待
0 ~ portMAX_DELAY等待指定时间,超时后返回
portMAX_DELAY无限等待,直到队列有空闲位置

四、出队操作

1. 出队函数

// 从队列接收数据(出队并删除数据) BaseType_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait); // 从队列读取数据(出队但不删除数据) BaseType_t xQueuePeek(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait);

2. 出队操作原理

  1. 检查队列状态

    • 如果队列非空,继续出队
    • 如果队列为空,根据等待时间决定处理方式
  2. 数据拷贝

    • 将队列中的数据拷贝到接收缓冲区
    • uxMessagesWaiting(消息数量)-1
  3. 任务唤醒

    • 如果有任务在等待发送(xTasksWaitingToSend非空)
    • 将等待发送任务从阻塞态唤醒,加入就绪队列
    • 调用vTaskMissedYield()进行任务切换
  4. 队列空处理

    • 如果等待时间不为0,将当前任务加入等待接收列表
    • 如果等待时间=0,直接返回errQUEUE_EMPTY

3. 出队阻塞机制

阻塞时间行为
0立即返回,不等待
0 ~ portMAX_DELAY等待指定时间,超时后返回
portMAX_DELAY无限等待,直到队列有消息

五、入队与出队的对比

特性入队出队
数据传递方式值传递(拷贝数据)值传递(拷贝数据)
队列满处理阻塞等待或返回错误队列满不影响入队
队列空处理队列空不影响出队阻塞等待或返回错误
阻塞优先级优先级最高的任务先被唤醒优先级最高的任务先被唤醒
消息顺序FIFO(先进先出)FIFO(先进先出)

六、实际使用示例

入队示例(发送消息)

// 创建队列 QueueHandle_t xQueue = xQueueCreate(5, sizeof(uint32_t)); // 任务中发送消息 void SenderTask(void *pvParameters) { uint32_t data = 0; while(1) { data++; // 向队列尾部发送数据,阻塞等待10个tick if(xQueueSendToBack(xQueue, &data, 10) != pdPASS) { // 队列满,处理错误 } } }

出队示例(接收消息)

// 任务中接收消息 void ReceiverTask(void *pvParameters) { uint32_t data; while(1) { // 从队列头部接收数据,永久阻塞 if(xQueueReceive(xQueue, &data, portMAX_DELAY) == pdPASS) { // 处理接收到的数据 } } }

七、关键注意事项

  1. 数据拷贝:队列存储的是数据的拷贝,不是指针。传递大结构体时,建议使用指针传递(传递结构体地址),但需确保指针指向的数据在队列处理期间有效。

  2. 队列长度:合理设置队列长度,过小会导致频繁阻塞,过大则浪费内存。

  3. 阻塞时间:根据应用需求设置合适的阻塞时间,避免任务长时间阻塞。

  4. 中断安全:在中断中操作队列,必须使用xQueueSendFromISRxQueueReceiveFromISR

  5. 队列空/满检测:使用uxQueueMessagesWaiting()uxQueueSpacesAvailable()查询队列状态,避免数据丢失。

八、队列工作流程图

任务A(发送) 队列 任务B(接收) | | | | 入队操作 | | |----------------->| | | | | | | | | | 入队成功 | | |--------------->| | | | | | | | | | | | 出队操作 | | |<---------------| | | | | | |

入队和出队操作确保了任务间的解耦,使系统设计更加清晰、灵活,是FreeRTOS中实现任务间通信的关键机制。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询