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

FREERTOS CMSIS-RTOS v2 队列完整指南:核心函数 + 指针传递 + 队列集

CMSIS-RTOS v2 是 ARM 官方对 FreeRTOS 的标准封装提供了统一的 RTOS API 接口完全兼容 STM32CubeMX 自动生成的工程。它在原生 FreeRTOS 队列基础上增加了消息优先级特性函数命名和参数风格更统一错误处理更规范。一、CMSIS-RTOS v2 队列核心函数与基础用例1. 队列创建与删除1.1osMessageQueueNew()- 动态创建队列最常用功能从 RTOS 堆中分配内存创建消息队列原型osMessageQueueId_t osMessageQueueNew( uint32_t msg_count, // 队列最大消息数 uint32_t msg_size, // 单个消息的字节大小 const osMessageQueueAttr_t *attr // 队列属性NULL为默认 );返回值成功返回队列 ID失败返回NULL用例#include cmsis_os2.h // 全局队列ID osMessageQueueId_t g_intQueue; // 存储int类型的队列 osMessageQueueId_t g_structQueue; // 存储结构体的队列 // 自定义传感器数据结构体 typedef struct { uint8_t sensorId; float value; uint32_t timestamp; } SensorData_t; void Queue_Init(void) { // 1. 创建能容纳10个int的队列 g_intQueue osMessageQueueNew(10, sizeof(int), NULL); if (g_intQueue NULL) { Error_Handler(); // 内存不足创建失败 } // 2. 创建能容纳5个SensorData_t结构体的队列 g_structQueue osMessageQueueNew(5, sizeof(SensorData_t), NULL); if (g_structQueue NULL) { Error_Handler(); } }1.2osMessageQueueNewStatic()- 静态创建队列功能使用用户提供的静态内存创建队列不依赖堆无内存碎片风险原型osMessageQueueId_t osMessageQueueNewStatic( uint32_t msg_count, uint32_t msg_size, void *msg_buffer, // 存储消息数据的缓冲区 StaticQueue_t *queue_cb, // 存储队列控制块的内存 const osMessageQueueAttr_t *attr );用例#define STATIC_QUEUE_LEN 5 #define STATIC_MSG_SIZE sizeof(int) // 静态内存必须全局或static不能是栈变量 static uint8_t s_msgBuffer[STATIC_QUEUE_LEN * STATIC_MSG_SIZE]; static StaticQueue_t s_queueCb; osMessageQueueId_t g_staticIntQueue; void StaticQueue_Init(void) { g_staticIntQueue osMessageQueueNewStatic( STATIC_QUEUE_LEN, STATIC_MSG_SIZE, s_msgBuffer, s_queueCb, NULL ); // 静态创建永远不会失败无需检查NULL }1.3osMessageQueueDelete()- 删除队列功能删除队列并释放动态内存静态队列仅释放控制块关联原型osStatus_t osMessageQueueDelete(osMessageQueueId_t mq_id);用例if (g_intQueue ! NULL) { osMessageQueueDelete(g_intQueue); g_intQueue NULL; // 防止野指针 }2. 任务级消息发送函数osMessageQueuePut()- 发送消息支持优先级功能将消息复制到队列支持指定消息优先级高优先级消息排在前面 CMSIS-RTOS v2 独有特性原生 FreeRTOS 队列只有 FIFOCMSIS 增加了消息优先级原型osStatus_t osMessageQueuePut( osMessageQueueId_t mq_id, const void *msg_ptr, // 指向要发送的消息 uint8_t msg_prio, // 消息优先级0(最低)~255(最高) uint32_t timeout // 队列满时的阻塞时间osWaitForever永久等待 );返回值osOK成功osErrorTimeout超时osErrorResource队列满用例void ProducerTask(void *argument) { int counter 0; SensorData_t sensorData; uint32_t timestamp 0; while (1) { // 1. 发送普通int消息优先级0 counter; osStatus_t ret osMessageQueuePut(g_intQueue, counter, 0, osWaitForever); if (ret osOK) { printf(发送普通消息: %d\n, counter); } // 2. 发送紧急消息优先级255会排在队列最前面 if (counter % 10 0) { int emergencyCode 0xFF; osMessageQueuePut(g_intQueue, emergencyCode, 255, osWaitForever); printf(发送紧急消息!\n); } // 3. 发送结构体消息 sensorData.sensorId 1; sensorData.value 25.0f (rand() % 100) / 10.0f; sensorData.timestamp timestamp; osMessageQueuePut(g_structQueue, sensorData, 0, osWaitForever); osDelay(1000); // 每秒发送一次 } }注意CMSIS-RTOS v2 没有单独的SendToFront函数通过设置最高优先级 (255) 即可实现紧急消息插队3. 中断级消息发送函数osMessageQueuePutFromISR()- 中断中发送消息功能中断服务例程 (ISR) 专用的消息发送函数无阻塞原型osStatus_t osMessageQueuePutFromISR( osMessageQueueId_t mq_id, const void *msg_ptr, uint8_t msg_prio, int32_t *pxHigherPriorityTaskWoken // 输出参数是否需要任务切换 );用例STM32 外部中断osMessageQueueId_t g_buttonQueue; // 按键事件队列 void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); int32_t xHigherPriorityTaskWoken 0; uint8_t buttonEvent 1; // 1表示按键按下 // 中断中发送按键事件 osMessageQueuePutFromISR(g_buttonQueue, buttonEvent, 0, xHigherPriorityTaskWoken); // 如果有更高优先级任务被唤醒立即进行任务切换 portYIELD_FROM_ISR(xHigherPriorityTaskWoken); }4. 任务级消息接收函数4.1osMessageQueueGet()- 接收并删除消息功能从队列头部接收消息并将消息从队列中删除原型osStatus_t osMessageQueueGet( osMessageQueueId_t mq_id, void *msg_ptr, // 接收消息的缓冲区 uint8_t *msg_prio, // 输出参数获取消息优先级不需要传NULL uint32_t timeout // 队列空时的阻塞时间 );用例void ConsumerTask(void *argument) { int recvInt; uint8_t msgPrio; SensorData_t recvSensor; while (1) { // 1. 接收int消息同时获取消息优先级 osStatus_t ret osMessageQueueGet(g_intQueue, recvInt, msgPrio, osWaitForever); if (ret osOK) { printf(接收消息: %d, 优先级: %d\n, recvInt, msgPrio); // 处理紧急消息 if (recvInt 0xFF) { printf(处理紧急事件!\n); // 紧急事件处理逻辑 } } // 2. 接收结构体消息不关心优先级传NULL osMessageQueueGet(g_structQueue, recvSensor, NULL, osWaitForever); printf(传感器数据: ID%d, 值%.1f, 时间戳%lu\n, recvSensor.sensorId, recvSensor.value, recvSensor.timestamp); } }4.2osMessageQueuePeek()- 查看但不删除消息功能查看队列头部消息但不将其从队列中移除原型与osMessageQueueGet()完全相同用例void PeekTask(void *argument) { int data; while (1) { // 查看队列头部消息不删除 if (osMessageQueuePeek(g_intQueue, data, NULL, 100) osOK) { if (data 0xFF) { // 是紧急消息立即接收处理 osMessageQueueGet(g_intQueue, data, NULL, 0); printf(紧急处理: %d\n, data); } else { // 普通消息稍后处理 osDelay(500); } } } }5. 队列状态查询函数函数功能原型osMessageQueueGetCount()查询队列中当前消息数uint32_t osMessageQueueGetCount(osMessageQueueId_t mq_id)osMessageQueueGetSpace()查询队列剩余可用空间uint32_t osMessageQueueGetSpace(osMessageQueueId_t mq_id)osMessageQueueGetCapacity()查询队列最大容量uint32_t osMessageQueueGetCapacity(osMessageQueueId_t mq_id)osMessageQueueGetMsgSize()查询单个消息大小uint32_t osMessageQueueGetMsgSize(osMessageQueueId_t mq_id)用例非阻塞接收// 先检查是否有消息再非阻塞接收 if (osMessageQueueGetCount(g_intQueue) 0) { int data; osMessageQueueGet(g_intQueue, data, NULL, 0); // 立即返回不阻塞 printf(非阻塞接收: %d\n, data); }二、高级用法 1队列传递指针当需要传递大数据块如图片、音频、大结构体时使用值传递会导致大量内存拷贝降低系统性能。此时推荐使用指针传递只传递数据的内存地址。⚠️ 指针传递核心注意事项绝对不能传递栈变量指针栈内存会在函数返回时被释放接收方会访问到无效内存内存所有权转移发送方发送指针后就不能再修改该内存直到接收方处理完成避免内存泄漏动态分配的内存必须在接收方处理完成后释放推荐方案使用静态内存池循环分配最安全高效方案 1静态内存池指针传递推荐预先分配一块静态内存作为内存池生产者从池中获取空闲内存填充数据后发送指针消费者处理完成后将内存归还池中。完整用例#define MEM_POOL_SIZE 5 // 内存池大小 typedef struct { uint8_t data[128]; // 128字节大数据块 uint32_t len; } BigData_t; // 静态内存池和空闲队列 static BigData_t s_memPool[MEM_POOL_SIZE]; osMessageQueueId_t g_freeBlockQueue; // 空闲内存块队列 // 初始化内存池 void MemPool_Init(void) { g_freeBlockQueue osMessageQueueNew(MEM_POOL_SIZE, sizeof(BigData_t*), NULL); // 将所有内存块加入空闲队列 for (int i 0; i MEM_POOL_SIZE; i) { BigData_t *ptr s_memPool[i]; osMessageQueuePut(g_freeBlockQueue, ptr, 0, osWaitForever); } } // 生产者任务获取空闲内存填充数据发送指针 void BigDataProducerTask(void *argument) { BigData_t *dataPtr; uint32_t counter 0; while (1) { // 1. 从空闲队列获取一个内存块 osMessageQueueGet(g_freeBlockQueue, dataPtr, NULL, osWaitForever); // 2. 填充数据 dataPtr-len sprintf((char*)dataPtr-data, 大数据块_%lu, counter); printf(生产数据: %s\n, dataPtr-data); // 3. 发送指针到数据队列 osMessageQueuePut(g_bigDataQueue, dataPtr, 0, osWaitForever); osDelay(500); } } // 消费者任务接收指针处理数据归还内存 void BigDataConsumerTask(void *argument) { BigData_t *dataPtr; while (1) { // 1. 接收数据指针 osMessageQueueGet(g_bigDataQueue, dataPtr, NULL, osWaitForever); // 2. 处理数据 printf(处理数据: %s, 长度: %lu\n, dataPtr-data, dataPtr-len); // 3. 归还内存块到空闲队列 osMessageQueuePut(g_freeBlockQueue, dataPtr, 0, osWaitForever); } }方案 2动态内存指针传递使用pvPortMalloc()动态分配内存发送指针消费者处理完成后用vPortFree()释放。用例// 生产者 void DynamicProducerTask(void *argument) { while (1) { // 动态分配内存 BigData_t *dataPtr pvPortMalloc(sizeof(BigData_t)); if (dataPtr NULL) { printf(内存分配失败!\n); osDelay(100); continue; } // 填充数据 sprintf((char*)dataPtr-data, 动态数据); dataPtr-len strlen((char*)dataPtr-data); // 发送指针 osMessageQueuePut(g_bigDataQueue, dataPtr, 0, osWaitForever); osDelay(1000); } } // 消费者 void DynamicConsumerTask(void *argument) { BigData_t *dataPtr; while (1) { osMessageQueueGet(g_bigDataQueue, dataPtr, NULL, osWaitForever); // 处理数据 printf(处理动态数据: %s\n, dataPtr-data); // 释放内存 vPortFree(dataPtr); } }注意动态内存分配可能产生内存碎片长时间运行可能导致分配失败优先使用静态内存池方案。三、高级用法 2队列集Message Queue Set队列集用于一个任务同时等待多个队列中的任意一个有消息的场景。例如一个任务需要同时处理按键事件、传感器数据和串口命令。队列集核心函数函数功能osMessageQueueSetNew()创建队列集osMessageQueueSetAdd()将队列添加到队列集osMessageQueueSetRemove()从队列集移除队列osMessageQueueSetWait()等待队列集中任意队列有消息完整用例多队列统一等待// 三个独立的队列 osMessageQueueId_t g_buttonQueue; // 按键事件队列 osMessageQueueId_t g_sensorQueue; // 传感器数据队列 osMessageQueueId_t g_cmdQueue; // 串口命令队列 // 队列集ID osMessageQueueSetId_t g_queueSet; void QueueSet_Init(void) { // 1. 创建三个业务队列 g_buttonQueue osMessageQueueNew(5, sizeof(uint8_t), NULL); g_sensorQueue osMessageQueueNew(10, sizeof(SensorData_t), NULL); g_cmdQueue osMessageQueueNew(5, sizeof(uint32_t), NULL); // 2. 创建队列集最大事件数所有队列长度之和 uint32_t maxEvents osMessageQueueGetCapacity(g_buttonQueue) osMessageQueueGetCapacity(g_sensorQueue) osMessageQueueGetCapacity(g_cmdQueue); g_queueSet osMessageQueueSetNew(maxEvents, NULL); // 3. 将所有队列添加到队列集 osMessageQueueSetAdd(g_queueSet, g_buttonQueue, 0); osMessageQueueSetAdd(g_queueSet, g_sensorQueue, 0); osMessageQueueSetAdd(g_queueSet, g_cmdQueue, 0); } // 统一处理任务等待队列集处理所有类型的消息 void UnifiedProcessTask(void *argument) { osMessageQueueId_t activeQueue; uint8_t buttonEvent; SensorData_t sensorData; uint32_t cmd; while (1) { // 1. 等待队列集中任意队列有消息永久等待 activeQueue osMessageQueueSetWait(g_queueSet, osWaitForever, NULL); // 2. 根据返回的队列ID处理对应消息 if (activeQueue g_buttonQueue) { // 处理按键事件 osMessageQueueGet(g_buttonQueue, buttonEvent, NULL, 0); printf(按键事件: %d\n, buttonEvent); } else if (activeQueue g_sensorQueue) { // 处理传感器数据 osMessageQueueGet(g_sensorQueue, sensorData, NULL, 0); printf(传感器数据: %.1f\n, sensorData.value); } else if (activeQueue g_cmdQueue) { // 处理串口命令 osMessageQueueGet(g_cmdQueue, cmd, NULL, 0); printf(收到命令: %lu\n, cmd); } } }队列集重要注意事项队列集大小必须≥所有加入队列的长度之和否则可能丢失事件不能嵌套一个队列不能同时加入多个队列集中断限制不能在中断中调用osMessageQueueSetWait()消息处理osMessageQueueSetWait()只返回有消息的队列 ID必须手动调用osMessageQueueGet()接收消息四、CMSIS-RTOS v2 队列使用最佳实践优先使用静态创建避免内存碎片和创建失败适合资源受限的嵌入式系统消息优先级合理使用仅对真正紧急的消息使用高优先级避免优先级反转大数据用指针传递配合静态内存池既高效又安全多队列用队列集避免创建多个等待任务简化系统设计中断中仅使用 FromISR 函数绝对不能在中断中调用普通任务级函数错误处理所有 RTOS 函数都要检查返回值特别是内存分配和队列操作
http://www.zskr.cn/news/1407831.html

相关文章:

  • asnumpy:NumPy 语义在 NPU 上的零拷贝实现与算子映射机制
  • CANN catlass:MLA 模板如何实现多级归约
  • Explore with Long-term Memory:基于多模态大语言模型与强化学习的具身探索框架
  • 如何快速掌握围棋AI分析:LizzieYzy从入门到精通的完整指南
  • 河南沃德智能科技集团水文水资源物联网监测设备技术合集
  • 终极百度网盘下载加速方案:Python命令行工具突破限速瓶颈
  • 当边缘AI遇上光网建设:预测式熔接控制如何挑战传统算法?
  • Harness工程全方面拆解教程
  • 保姆级横评!如何下载视频号的视频到手机相册?2026年这7个方法实测告诉你哪个最靠谱 - 科技热点发布
  • 151、运动控制中的固件开发:在线升级(OTA)
  • 2026年iherb最新折扣码618大促优惠码 - 李先生sir
  • 从双流网络到时序金字塔:5个关键模型带你读懂视频分类的十年演进(保姆级图解)
  • 百考通开题报告智能生成,事半功倍,让研究起点更坚实
  • 我用3天做了一款旅行规划APP,上线第一天爆了!当天就有11个全5星好评!
  • Django 从 0 到 1 打造完整电商平台:系列总结 + 项目演示与后续扩展
  • AI写论文大揭秘!4款AI论文写作工具,助你快速完成职称论文
  • 严恭敏老师PSINS工具箱探秘——glvf函数:导航算法的地球基准构建
  • 4款降AI软件实测红黑榜:2026年5月哪个能真的去AI痕迹 - 我要发一区
  • 解耦异构算力与多协议接入:基于Docker与源码交付的开源企业级GB28181/RTSP边缘计算AI视频管理平台架构深度解析
  • 解密千万级安防架构:基于 Docker 与 边缘计算 的 AI 视频平台,如何实现 GB28181/RTSP 统一接入与源码交付?
  • DBSCAN-Leak:基于动态密度聚类的智能水务泄漏检测算法详解
  • 浩卡联盟推广手机卡真的靠谱吗?2026佣金置顶全网最高结算率98%以上 - 流量卡代理招商
  • 关于贪心算法的一些自我总结【力扣45.跳跃游戏II】【灵感来源:代码随想录】
  • 2026年全国对讲机优选厂家榜单:从“能用”到“耐用”,为何驰尔达成为3000+客户的首选? - 资讯纵览
  • P15366 [IOI 2013] Cave
  • 从零构建植物大战僵尸C++重制版:掌握游戏开发核心架构的实战指南
  • Windows TrustedInstaller 权限深度解析:RunAsTI 完全掌握指南
  • 前缀树 C++实现
  • 网易云音乐无损下载工具:三步获取专业级音质音乐
  • 嵌入式 - 数据结构与算法:(1-14)排序算法 - 冒泡/选择/快速/希尔排序对比