FreeRTOS队列用法

lcysts
15
2025-10-19

生活中排队的现象天天都能看见,比如你去海底捞吃饭,高峰期就需要排队,这里排队要解决的问题就是商家由于座位有限,无法同时满足所有消费者的用餐需求,所以就需要排队,依次服务。

而我们FreeRTOS中要使用的队列,本质上和上面的情况也是一样的,对于队列,首先介绍几个概念:

  • 队列:存储数据的一个缓冲区域,类似海底捞排位等号区的坐席位数,如果坐席满了,就不再接待排位了;

  • 生产者:往队列里面发送数据的一方称之为生产者,类似海底捞的门迎服务台;

  • 消费者:从队列里面取数据并处理的一方称之为消费者,类似海底捞的用餐引导员

对于ESP32中的队列,其主要用途有以下一些场景:

  • 多任务之间消息通信:A任务处理的一些数据,需要同步给B任务,就可以通过队列实现,A作为生产者往队列里面发送消息,B作为消费者从队列里面取消息,就实现了A与B的消息通信。

  • 削峰填谷:当短时间内需要处理的消息太多,并不能立马全部处理时,可以将消息暂存到队列,慢慢消费处理,起到一个消峰的效果。

创建队列

QueueHandle_t xQueueCreate( const UBaseType_t uxQueueLength,
                                       const UBaseType_t uxItemSize)

该函数用于创建一个队列,一共有两个参数:

  • uxQueueLength:这个队列的最大长度;

  • uxItemSize:队列中每一个元素所占的内存空间;

返回值为创建的队列的句柄

往队列发布消息

BaseType_t xQueueSend( QueueHandle_t xQueue,
                              const void * const pvItemToQueue,
                              TickType_t xTicksToWait)

该函数用于往一个指定的队列中发送一个数据,数据将被放在队列的队尾,一共有三个参数:

  • xQueue:队列句柄

  • pvItemToQueue:需要发送的消息的指针(内存地址);

  • xTicksToWait:如果队列当前已满,最长等待多长时间,单位是tick

返回值为pdTRUE时,表示消息发布成功,返回值为pdFALSE时,表示消息发布失败。

从队列消费消息

BaseType_t xQueueReceive( QueueHandle_t xQueue,
                          void * const pvBuffer,
                          TickType_t xTicksToWait )

该函数用于从队列中取出一个消息,默认是从队列头取出消息,一共有三个参数:

  • xQueue:需要取消息的队列的句柄

  • pvBuffer:取出的消息需要存放的指针(内存地址)

  • xTicksToWait:如果队列为空,最长等待时间,单位为tick

返回值为pdTRUE时,表示成功从队列取出消息,返回值为pdFALSE时,表示当前队列为空,暂时取不到消息。

获取队列当前可用空间

UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue )

该函数用于获取一个队列当前可用空间,只有一个参数:

  • xQueue:要获取的队列的句柄

返回值为当前队列可用空间

实战演练

实现如下需求:

模拟海底捞接客流程,一个客户去用餐,首先去门迎服务台签到,门迎服务台的服务员会给客户一个号牌,并要求客户在叫号区等待;当有用餐座位时,餐饮服务员会从叫号区按顺序叫客户去用餐,叫号区最大可容纳10人。

#include <Arduino.h>

struct Consumer
{
  uint16_t number; // 客户的编号
  char type[10];   // 坐席的类型
};

QueueHandle_t queue;
uint16_t number = 1;

void send(void *ptr)
{
  while (1)
  {
    Consumer consumer;
    consumer.number = number++;
    strcpy(consumer.type, "大桌");
    if (xQueueSend(queue, &consumer, 0) == pdTRUE)
    {
      Serial.printf("欢迎客户: %d(%s), 当前空余位置: %d\n", consumer.number, consumer.type, uxQueueSpacesAvailable(queue));
    }
    else
    {
      Serial.println("叫号区已经没有空位");
    }
    vTaskDelay(random(1000));
  }
}

void receive(void *ptr)
{
  while (1)
  {
    Consumer consumer;
    if (xQueueReceive(queue, &consumer, 0) == pdTRUE)
    {
      Serial.printf("有请客户用餐: %d(%s)\n", consumer.number, consumer.type);
      vTaskDelay(1000 + random(1000));
    }
  }
}

void setup()
{
  Serial.begin(115200);
  queue = xQueueCreate(10, sizeof(Consumer));              // 创建一个队列
  xTaskCreate(send, "send", 2048, nullptr, 1, NULL);       // 模拟门迎服务台服务员的任务
  xTaskCreate(receive, "receive", 2048, nullptr, 1, NULL); // 模拟用餐服务员的任务
}

void loop()
{
}

运行结果如下:

动物装饰