从队列中接收数据API函数

在 FreeRTOS 中,队列的读取函数用于从队列中获取数据,根据是否需要移除数据项或仅查看数据,分为以下两种核心函数:


1. 从队列中移除并读取数据:xQueueReceive

函数原型
BaseType_t xQueueReceive(
    QueueHandle_t xQueue,       // 队列句柄(目标队列)
    void *pvBuffer,             // 接收数据的缓冲区指针
    TickType_t xTicksToWait     // 最大阻塞时间(单位:Tick)
);
功能
  • 从队列头部(队首)读取一个数据项,并移除该数据
  • 如果队列为空,任务会进入阻塞状态,直到队列中有数据或超时。
参数说明
  • xQueue:目标队列的句柄,由 xQueueCreate() 创建。
  • pvBuffer:指向接收数据的缓冲区,需确保缓冲区大小与队列数据项一致。
  • xTicksToWait:阻塞超时时间:
    • 0:立即返回,不等待。
    • portMAX_DELAY:无限阻塞,直到队列中有数据。
    • 其他值:阻塞指定的 Tick 数(例如 pdMS_TO_TICKS(100) 表示阻塞 100ms)。
返回值
  • pdPASS:成功读取数据。
  • pdFAIL:超时或队列句柄无效。
示例
// 定义数据格式
typedef struct {
    uint8_t sensor_id;
    float value;
} SensorData;

// 任务中读取队列
SensorData data;
if (xQueueReceive(xSensorQueue, &data, portMAX_DELAY) == pdPASS) {
    process_sensor_data(&data);  // 处理数据
}

2. 查看队列数据但不移除:xQueuePeek

函数原型
BaseType_t xQueuePeek(
    QueueHandle_t xQueue,       // 队列句柄
    void *pvBuffer,             // 接收数据的缓冲区指针
    TickType_t xTicksToWait     // 最大阻塞时间
);
功能
  • 从队列头部读取数据项,但不将其从队列中移除
  • 行为与 xQueueReceive 类似,但数据保留在队列中供其他任务读取。
使用场景
  • 需要多个任务查看同一数据(例如广播监控)。
  • 数据需被多次处理(例如日志记录和实时显示同时进行)。
示例
SensorData data;
if (xQueuePeek(xSensorQueue, &data, 0) == pdPASS) {
    display_data(&data);  // 显示数据(数据仍保留在队列中)
}

3. 中断安全版本

在中断服务程序(ISR)中,需使用以下函数以避免阻塞:

3.1 xQueueReceiveFromISR
BaseType_t xQueueReceiveFromISR(
    QueueHandle_t xQueue,
    void *pvBuffer,
    BaseType_t *pxHigherPriorityTaskWoken
);
3.2 xQueuePeekFromISR
BaseType_t xQueuePeekFromISR(
    QueueHandle_t xQueue,
    void *pvBuffer
);
参数说明
  • pxHigherPriorityTaskWoken:标记是否有高优先级任务被唤醒,退出中断后需调用 portYIELD_FROM_ISR() 触发任务切换。
示例(在 ISR 中读取队列)
void vSensorISR() {
    SensorData data;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    if (xQueueReceiveFromISR(xSensorQueue, &data, &xHigherPriorityTaskWoken) == pdPASS) {
        process_in_isr(&data);  // 中断中快速处理
    }
    
    // 如果有任务被唤醒,触发上下文切换
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

4. 多任务竞争与调度策略

  • 优先级决定顺序
    如果多个任务阻塞在同一个队列上,当数据到达时,最高优先级任务优先接收数据。
  • 同优先级任务
    若多个任务优先级相同,则等待时间最长的任务(按 FIFO)获得数据。

5. 设计注意事项

  1. 数据所有权

    • 使用 xQueueReceive 后,数据所有权转移到接收任务,需确保发送方不再修改数据。
    • 使用 xQueuePeek 时,数据仍属于队列,可能被后续的 xQueueReceive 移除。
  2. 队列深度与性能

    • 队列深度不足可能导致数据丢失(发送方阻塞或覆盖)。
    • 频繁的小数据传递建议使用任务通知(更高效)。
  3. 超时处理

    • 避免在关键任务中使用 portMAX_DELAY,防止系统死锁。
    • 超时后应检查返回值并处理异常(例如重试或错误日志)。

6. 典型应用场景

场景 1:生产者-消费者模型
// 生产者任务(发送数据)
void vProducerTask(void *pvParameters) {
    SensorData data;
    while (1) {
        read_sensor(&data);
        xQueueSend(xSensorQueue, &data, portMAX_DELAY);  // 阻塞直到发送成功
    }
}

// 消费者任务(接收数据)
void vConsumerTask(void *pvParameters) {
    SensorData data;
    while (1) {
        if (xQueueReceive(xSensorQueue, &data, pdMS_TO_TICKS(100)) == pdPASS) {
            process_data(&data);
        } else {
            log_error("Queue timeout!");  // 处理超时
        }
    }
}
场景 2:数据广播(多任务查看)
// 任务1:实时显示数据
void vDisplayTask(void *pvParameters) {
    SensorData data;
    while (1) {
        xQueuePeek(xSensorQueue, &data, portMAX_DELAY);
        update_display(&data);  // 不移除数据,供其他任务使用
    }
}

// 任务2:记录数据到存储
void vLoggerTask(void *pvParameters) {
    SensorData data;
    while (1) {
        xQueueReceive(xSensorQueue, &data, portMAX_DELAY);
        save_to_flash(&data);  // 移除数据,确保只记录一次
    }
}

7. 常见问题

Q1:队列为空时,xQueueReceivexQueuePeek 的行为?
  • 若指定 xTicksToWait > 0,任务进入阻塞状态,直到数据到达或超时。
  • xTicksToWait = 0,立即返回 pdFAIL
Q2:如何在中断中安全读取队列?
  • 必须使用 xQueueReceiveFromISRxQueuePeekFromISR,并处理任务切换标记。
Q3:多个任务等待同一队列时,如何保证特定任务接收数据?
  • 无法直接指定接收者,需通过设计解决:
    • 为每个任务分配独立队列(点对点通信)。
    • 在消息中添加目标标识符,非目标任务重新放回队列(需谨慎处理竞争)。

总结

  • xQueueReceive 用于消费数据(移除队列项),xQueuePeek 用于查看数据(保留队列项)。
  • 中断中必须使用 FromISR 后缀的函数,并处理任务切换。
  • 通过合理设计队列所有权和超时机制,可以构建高效可靠的多任务通信系统。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九层指针

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值