reeRTOS队列提供:

  • 任务间数据传递;
  • 按值复制数据;
  • 多条消息缓存;
  • 队列为空时阻塞接收任务;
  • 队列满时阻塞发送任务;
  • 自动处理并发访问。

创建

static QueueHandle_t xSensorQueue = null;
xSensorQueue = xQueueCreate(3, sizeof(SensorMessage_t));

生产的消息类型

typedef struct
{
    uint32_t ulSequence;
    int32_t lValue;
} SensorMessage_t;

生产者

static void vProducerTask(void *pvParameters)
{
    SensorMessage_t xMessage;

    (void)pvParameters;
    xMessage.ulSequence = 0;
    xMessage.lValue = 100;

    for (;;)
    {
        if (xQueueSend(xSensorQueue, &xMessage, portMAX_DELAY) == pdPASS) //消息发送成功返回
        {
            printf("生产消息\n");
        }
        xMessage.ulSequence++;
        xMessage.lValue += 10;
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

消费者

static void vConsumerTask(void *pvParameters)
{
    SensorMessage_t xReceived; //接收消息的对象

    (void)pvParameters;

    for (;;)
    {
    if (xQueueReceive(xSensorQueue, &xReceived, portMAX_DELAY) == pdPASS) //队列是否为空判断
    {
        printf("处理消息\n");
    }
    }
}

创建两个任务

xTaskCreate(vConsumerTask,"Consumer",configMINIMAL_STACK_SIZE,NULL,2,NULL); //高优先级

xTaskCreate(vProducerTask,"Producer",configMINIMAL_STACK_SIZE,NULL,1,NULL);

启动之后流程

  1. 调度器首先运行Consumer。但此时队列为空。

    Consumer:Running → Blocked
    
  2. portMAX_DELAY在当前配置下表示持续等待,直到有消息到达。

  3. Consumer阻塞以后,Producer才开始运行。

  4. 队列会把消息内容复制到自己的内部存储区:

    Producer的xMessage
            ↓ 复制
    Queue内部消息槽
    
    队列保存的不是:&xMessage指针,而是整个:SensorMessage_t结构体的副本。
    
  5. Producer把消息写入队列

  6. Consumer等待的条件满足:队列不再为空

    Consumer:Blocked → Ready
    Consumer优先级2 > Producer优先级1
    所以:
    Producer:Running → Ready
    Consumer:Ready → Running
    
  7. 消费信息,队列又空了,consumer再次阻塞。

  8. Producer才恢复执行。

Producer进入xQueueSend()
→ 消息复制进队列
→ Consumer被唤醒
→ Consumer抢占Producer
→ Consumer取走并打印消息
→ Consumer再次阻塞
→ Producer从xQueueSend()中恢复
→ Producer打印sent
  1. Queue用于在任务之间安全传递数据。
  2. xQueueSend默认复制消息内容。
  3. 队列为空时,接收任务可以进入Blocked。
  4. 消息到来后,接收任务自动恢复Ready。
  5. 如果被唤醒任务优先级更高,发送API内部就可能发生抢占。
  6. 队列让任务等待数据,而不是持续轮询数据。