【CI130x 离在线】音频传输的数据结构——FreeRTOS的消息队列

一句话概括

xQueueCreate 是 FreeRTOS 实时操作系统中用于动态创建消息队列的函数。它负责分配内存并返回一个可以用于发送和接收数据的队列句柄。


详细解释

1. 什么是消息队列?

在深入函数本身之前,理解“队列”的概念至关重要。你可以把它想象成一个管道传送带

  • 生产者:任务或中断服务程序将数据(消息)放入队列的一端(发送)。

  • 消费者:任务从队列的另一端取出数据(接收)。

  • 先进先出:数据按照被送入队列的顺序被取出,就像在超市排队结账一样。

队列是 FreeRTOS 中最重要的任务间通信机制之一,它允许任务安全、有序地传递信息,而不用担心并发访问导致的数据损坏。

2. xQueueCreate 的作用

xQueueCreate 就是用来创建这样一个“管道”的工具。它主要做两件事:

  1. 分配内存:从 FreeRTOS 的堆中分配两块内存。

    • 一块用于存储队列的结构体,其中包含了管理队列所需的所有信息(如头指针、尾指针、队列长度、项目大小等)。

    • 另一块用于作为队列的存储区,这是一个字节数组,实际的数据就存储在这里。

  2. 初始化队列:将队列结构体中的各个字段设置为初始状态(例如,队列为空)。

  3. 返回句柄:如果创建成功,它返回一个指向这个队列的句柄。后续所有对队列的操作(如发送、接收)都需要使用这个句柄来指定操作哪个队列。


函数原型

QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength, // 队列长度
                            UBaseType_t uxItemSize     // 每个队列项目的大小(字节)
                          );

参数说明

  • uxQueueLength

    • 队列能够存储的最大项目数量

    • 例如,如果你设置为 5,那么这个队列最多可以同时存放 5 条消息。

  • uxItemSize

    • 队列中每个项目所占的字节数

    • 如果你要发送一个整数,在 32 位系统上就是 sizeof(int) = 4 字节。

    • 如果你要发送一个结构体,就是 sizeof(my_struct_t)

返回值

  • 成功:返回一个非 NULL 的 QueueHandle_t 类型的句柄。这个句柄需要被你保存起来,供后续使用。

  • 失败:返回 NULL。失败通常是因为没有足够的堆内存来创建队列。


工作流程示意图

假设我们创建一个队列:xQueueCreate(3, sizeof(int))

这表示一个能存放 3 个整数的队列。

  1. 创建后(初始状态)

     
    队列存储区: [ 空 | 空 | 空 ]
                 ^
                写指针 & 读指针

    队列为空,读写指针都指向起始位置。

  2. 发送一个数据(例如 10)

     
    队列存储区: [ 10 | 空 | 空 ]
                 ^     ^
                读指针 写指针
  3. 再发送一个数据(例如 20)

     
    队列存储区: [ 10 | 20 | 空 ]
                 ^         ^
                读指针     写指针
  4. 接收一个数据

    • 从读指针位置取出数据 10

    • 读指针移动到下一个位置。

    text
     
     
    队列存储区: [ 空 | 20 | 空 ]
                      ^    ^
                     读指针 写指针

    现在队列里只有一个数据 20


代码示例

下面是一个简单的示例,演示如何创建和使用队列。

#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>

// 1. 定义队列句柄变量
QueueHandle_t xIntegerQueue;

// 发送任务
void vSenderTask(void *pvParameters) {
    int value_to_send = 0;
    for(;;) {
        // 2. 发送数据到队列
        if(xQueueSend(xIntegerQueue, &value_to_send, portMAX_DELAY) == pdPASS) {
            // 发送成功
        }
        value_to_send++;
        vTaskDelay(pdMS_TO_TICKS(1000)); // 每隔1秒发送一次
    }
}

// 接收任务
void vReceiverTask(void *pvParameters) {
    int received_value = 0;
    for(;;) {
        // 3. 从队列接收数据
        if(xQueueReceive(xIntegerQueue, &received_value, portMAX_DELAY) == pdPASS) {
            // 成功接收到数据,可以处理 received_value
            // 例如,打印出来
        }
    }
}

void main(void) {
    // 4. 创建队列:长度为5,每个项目是一个int
    xIntegerQueue = xQueueCreate(5, sizeof(int));

    // 检查队列是否创建成功
    if(xIntegerQueue != NULL) {
        // 创建任务...
        xTaskCreate(vSenderTask, "Sender", 1000, NULL, 1, NULL);
        xTaskCreate(vReceiverTask, "Receiver", 1000, NULL, 1, NULL);

        // 启动调度器
        vTaskStartScheduler();
    } else {
        // 队列创建失败!可能是内存不足
        // 处理错误
    }

    // 程序不应该运行到这里
    for(;;);
}

重要注意事项

  1. 动态内存xQueueCreate 使用 pvPortMalloc 动态分配内存,因此你需要确保 FreeRTOS 的堆配置得足够大。

  2. 替代函数xQueueCreateStatic。这是一个静态创建版本,它允许你提供存储队列结构体和存储区的内存缓冲区,而不是由函数内部动态分配。这在内存受限或不允许动态分配的系统中非常有用。

  3. 线程安全:队列的发送和接收函数是原子的,可以被多个任务或中断安全地调用,无需额外的保护。

  4. 阻塞:当队列已满时尝试发送,或者队列为空时尝试接收,任务可以进入阻塞状态等待,直到条件满足。这由 xQueueSend 和 xQueueReceive 的第三个参数(阻塞时间)控制。

总结

xQueueCreate 是 FreeRTOS 中构建任务间通信桥梁的基石函数。通过指定队列的容量和每个消息的大小,它为你创建了一个安全、可靠的数据通道,是多任务程序设计中不可或缺的工具。

posted @ 2025-10-26 21:34  FBshark  阅读(11)  评论(0)    收藏  举报