当前位置: 首页 > news >正文

ESP32学习-FreeRTOS队列使用指南与实战

FreeRTOS 缓存队列关键概念

FreeRTOS 的缓存队列(Queue)是一种用于任务间通信的机制,允许任务或中断服务例程(ISR)以 FIFO(先进先出)的方式发送和接收数据。缓存队列可以传输固定大小的数据块,支持多任务并发操作,并通过阻塞机制实现同步。

关键特性:

  • 线程安全:队列操作是原子的,无需额外保护。
  • 阻塞与非阻塞:任务可以阻塞等待队列空间或数据,或立即返回。
  • 多任务支持:多个任务可以同时读写队列。
  • 中断安全:提供带 FromISR 后缀的 API 供中断上下文使用。

缓存队列使用模板

创建队列

使用 xQueueCreate()xQueueCreateStatic() 创建队列。动态创建示例:

QueueHandle_t xQueue = xQueueCreate(uxQueueLength,     // 队列长度(最大可存数据项数)uxItemSize         // 每个数据项的大小(字节)
);

参数说明:

  • uxQueueLength:队列容量,即队列可存储的最大数据项数量。
  • uxItemSize:每个数据项的大小(如传输 int 则设为 sizeof(int))。
发送数据到队列

使用 xQueueSend()xQueueSendFromISR()(中断中发送):

BaseType_t xStatus = xQueueSend(xQueue,           // 队列句柄pvItemToQueue,    // 指向待发送数据的指针xTicksToWait      // 阻塞超时时间(portMAX_DELAY 表示无限等待)
);

参数说明:

  • pvItemToQueue:指向待发送数据的指针(数据会被复制到队列)。
  • xTicksToWait:若队列满时,任务阻塞的 tick 数。0 表示不阻塞,立即返回。
从队列接收数据

使用 xQueueReceive()xQueueReceiveFromISR()(中断中接收):

BaseType_t xStatus = xQueueReceive(xQueue,           // 队列句柄pvBuffer,         // 接收数据的缓冲区指针xTicksToWait      // 阻塞超时时间
);

参数说明:

  • pvBuffer:指向接收缓冲区的指针(队列数据会被复制到此)。
  • xTicksToWait:若队列为空时,任务阻塞的 tick 数。
其他常用函数
  • uxQueueMessagesWaiting():获取队列中当前数据项数量。
  • xQueueReset():重置队列为空状态。
  • vQueueDelete():删除动态创建的队列。

完整示例代码

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include <stdio.h>// 定义队列参数
#define QUEUE_LENGTH 5             // 队列最多容纳 5 个元素
#define ITEM_SIZE sizeof(int)      // 每个元素大小为一个 int// 发送任务:每秒向队列发送一个整数
void vSenderTask(void *pvParameters) {QueueHandle_t xQueue = (QueueHandle_t)pvParameters; // 获取队列句柄int valueToSend = 42;while (1) {// 向队列发送数据,如果队列已满则阻塞等待xQueueSend(xQueue, &valueToSend, portMAX_DELAY);printf("Sent: %d\n", valueToSend);// 延时 1 秒(1000 毫秒)vTaskDelay(pdMS_TO_TICKS(1000));}
}// 接收任务:阻塞等待从队列接收数据
void vReceiverTask(void *pvParameters) {QueueHandle_t xQueue = (QueueHandle_t)pvParameters; // 获取队列句柄int receivedValue;while (1) {// 从队列接收数据(如果队列为空,则阻塞等待)if (xQueueReceive(xQueue, &receivedValue, portMAX_DELAY) == pdPASS) {// 成功接收到数据,打印处理printf("Received: %d\n", receivedValue);}}
}// 系统主入口函数
void main() {// 创建队列:最大容纳 QUEUE_LENGTH 个 int 类型数据QueueHandle_t xQueue = xQueueCreate(QUEUE_LENGTH, ITEM_SIZE);// 创建发送任务,优先级为 1,传入队列句柄xTaskCreate(vSenderTask, "Sender", configMINIMAL_STACK_SIZE, xQueue, 1, NULL);// 创建接收任务,优先级也为 1,传入队列句柄xTaskCreate(vReceiverTask, "Receiver", configMINIMAL_STACK_SIZE, xQueue, 1, NULL);// 启动 FreeRTOS 调度器(之后永不返回)vTaskStartScheduler();
}

关键注意事项

  • 数据复制:队列通过内存拷贝传输数据,需确保发送的数据指针有效。
  • 中断上下文:在中断中必须使用 FromISR 函数,且不可阻塞。
  • 优先级反转:高优先级任务长时间阻塞可能导致低优先级任务无法释放队列资源。

问题

1.为什么main函数不会直接退出吗?
vTaskStartScheduler()这个函数会启动 FreeRTOS 的调度器,从此进入多任务运行状态:

  • 控制权会交由调度器管理;
  • main() 不会继续执行;
  • 实际上 vTaskStartScheduler() 永不返回(除非发生错误);
    所以,只要调度器启动成功,main() 的最后一行之后的代码永远不会执行
http://www.dtcms.com/a/300457.html

相关文章:

  • 【多模态】天池AFAC赛道四-智能体赋能的金融多模态报告自动化生成part2-报告输出
  • Java面试实战:企业级性能优化与JVM调优全解析
  • 小白成长之路-Ansible自动化(一)
  • 将远程 main 分支同步到 develop 分支的完整指南
  • 【硬件】嵌入式软件开发(2)
  • STM32-USART串口实现接收数据三种方法(1.根据\r\n标志符、2.空闲帧中断、3.根据定时器辅助接收)
  • Pytest 参数化进阶:掌握 parametrize 的多种用法
  • HCIP---MGRE实验
  • 嵌入式硬件篇---ESP32稳压板
  • OpenLayers 综合案例-轨迹回放
  • LeetCode|Day27|70. 爬楼梯|Python刷题笔记
  • catkin_make与catkin build的关系与区别(使用catkin build的好处)
  • MGRE实验
  • 深入解析 Vue 3 中 v-model 与表单元素的绑定机制
  • 多租户Kubernetes集群架构设计实践——隔离、安全与弹性扩缩容
  • Spring Boot自动配置原理深度解析
  • 昇思学习营-模型推理和性能优化
  • Keepalived + LVS-DR 高可用与负载均衡实验
  • 【Python系列】使用 memory_profiler 诊断 Flask 应用内存问题
  • 结构化文本文档的内容抽取与版本重构策略
  • 8. 状态模式
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 微博舆情分析实现
  • vLLM 的“投机取巧”:Speculative Decoding 如何加速大语言模型推理
  • Spring Boot2错误处理
  • 负载均衡 LoadBalance
  • Spring Boot音乐服务器项目-查询音乐模块
  • 《Foundation 面板:设计、功能与最佳实践解析》
  • Java学习-------序列化与反序列化
  • UV: 下一代 Python 包管理工具
  • golang--虚拟地址空间