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

Freertos中队列头尾指针及读写指针工作机制

1.数据结构定义


// 简化版队列结构体(仅保留指针相关成员)
typedef struct QueueDefinition
{
int8_t *pcHead; // 队列存储区起始地址
int8_t *pcWriteTo; // 写指针,指向下一个可写入的位置
union
{
struct
{
int8_t *pcTail; // 队列存储区结束地址(最后一个字节的下一个位置)
int8_t *pcReadFrom; // 读指针,指向最后一次读取的末尾(经典实现)
} xQueue;
// 其他成员(用于互斥量等)省略
} u;
UBaseType_t uxLength; // 队列长度(消息个数)
UBaseType_t uxItemSize; // 每个消息的字节数
UBaseType_t uxMessagesWaiting; // 当前消息数量
} Queue_t;

2. 队列创建与初始化

假设我们创建一个能存放3个uint32_t类型数据的队列(每个消息 4 字节)。

QueueHandle_t xQueue = xQueueCreate(3, sizeof(uint32_t));
QueueHandle_t xQueue = xQueueCreate(3, sizeof(uint32_t)) { xQueueGenericCreate() { pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); pucQueueStorage = ( uint8_t * ) pxNewQueue;//消息头部地址 pucQueueStorage += sizeof( Queue_t ); //消息内容地址 prvInitialiseNewQueue() { // 1. 设置存储区边界 pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage; xQueueGenericReset() { pxQueue->u.xQueue.pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); // 2. 写指针指向头部 pxQueue->pcWriteTo = pxQueue->pcHead; // 3. 读指针指向存储区的最后一个消息的起始位置(关键!) pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - 1U ) * pxQueue->uxItemSize ); } } } }

初始化后内存示意图如下(假设起始地址 0x1000):

地址: 0x1000 0x1004 0x1008 0x100C |-------------|-------------|-------------| 存储区: | 消息槽 1 | 消息槽 2 | 消息槽 3 | |-------------|-------------|-------------| ^ ^ pcHead ^ pcTail pcwriteTo pcReadFrom
消息边界头:pcHead = 0x1000 消息边界尾:pcTail = 0x100C (0x1000 + 12) 写指针:pcWriteTo = 0x1000 读指针:pcReadFrom = 0x1008 (0x100C - 4) 消息计数器:uxMessagesWaiting = 0,队列为空。

3. 发送一个消息到队列尾部(写入)

调用xQueueSend()写入一个值,比如0x12345678

xQueueSend() { xQueueGenericSend() { prvCopyDataToQueue() { else if( xPosition == queueSEND_TO_BACK ) { //将数据拷贝到 pcWriteTo 指向的位置 memcpy( pxQueue->pcWriteTo, pvItemToQueue, pxQueue->uxItemSize ); // 移动写指针 pxQueue->pcWriteTo += pxQueue->uxItemSize; // 如果写指针到达尾部,回绕到头部 if( pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail ) { pxQueue->pcWriteTo = pxQueue->pcHead; } } // 消息计数增加 pxQueue->uxMessagesWaiting++; } } }
地址: 0x1000 0x1004 0x1008 0x100C |-------------|-------------|-------------| 存储区: |0x12345678 | 消息槽 2 | 消息槽 3 | |-------------|-------------|-------------| ^ ^ pcHead ^ ^ pcTail pcwriteTo pcReadFrom 写入前: pcWriteTo = 0x1000 写入后: pcWriteTo = 0x1004 (指向槽2),先拷贝数据再移动写指针读指针不变: pcReadFrom = 0x1008 (仍指向槽3) 消息计数器:uxMessagesWaiting = 1

4. 接收一个消息到队列(读取)

调用xQueueReceive()读取数据。

xQueueReceive() { prvCopyDataFromQueue() { // 1. 先移动读指针(经典实现) pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; // 2. 如果移动后到达或超过尾部,回绕到头部 if (pxQueue->u.xQueue.pcReadFrom >= pxQueue->pcTail) { pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; } // 3. 从新的 pcReadFrom 位置拷贝数据 memcpy(pvBuffer,pxQueue->u.xQueue.pcReadFrom, pxQueue->uxItemSize ); } // 4. 消息计数减1 pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1; }
地址: 0x1000 0x1004 0x1008 0x100C |-------------|-------------|-------------| 存储区: |0x12345678 | 消息槽 2 | 消息槽 3 | |-------------|-------------|-------------| ^ ^ pcHead ^ pcTail pcReadFrom pcwriteTo 读取前: pcReadFrom= 0x1008 读取后: pcReadFrom= 0x1000 (移动指针后到达尾部,回绕到头部),先移动读指针后再拷贝数据,读出0x12345678 写指针不变: pcWriteTo = 0x1008 (仍指向槽2) 消息计数器:uxMessagesWaiting = 0

5.发送一个消息到队列头部(在序列3基础上写入

调用 xQueueSendToFront()写入一个值,比如0x87654321

xQueueSendToFront() { xQueueGenericSend() { prvCopyDataToQueue() { else if( xPosition == queueSEND_TO_BACK ) { ... } else { //将数据拷贝到 pcReadFrom指向的位置 memcpy( pxQueue->u.xQueue.pcReadFrom, pvItemToQueue,pxQueue->uxItemSize ) // 移动读指针 pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize; //如果超过头部,回绕到尾部的最后一个消息槽起始地址 if( pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead ) { pxQueue->u.xQueue.pcReadFrom = ( pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize ); } } // 消息计数增加 pxQueue->uxMessagesWaiting++; } } }
地址: 0x1000 0x1004 0x1008 0x100C |-------------|-------------|-------------| 存储区: |0x12345678 | 消息槽 2 | 0x87654321 | |-------------|-------------|-------------| ^ ^ pcHead ^ pcTail pcwriteTo pcReadFrom 写入前: pcReadFrom = 0x1008,先拷贝数据再移动读指针写入后: pcReadFrom = 0x1004 写指针不变: pcWriteTo = 0x1004 (仍指向槽1) 消息计数器:uxMessagesWaiting = 2 此时若读取一条消息,则读指针+4后拷贝数据,读出头部消息0x87654321

以上例程基于队列未满时写入、队列有消息时读出。其他情况可对照源码查看,本文旨在说明一般情况下队列读写时读指针与写指针的工作机制。

核心设计思想

  • pcWriteTo永远指向下一个可写入的空槽

  • pcReadFrom永远指向下一个将要读取的消息的前一个位置(经典实现中先移动再读取),使得 FIFO 顺序和环形缓冲区完美结合。

  • 通过回绕机制,实现存储区的循环利用,无需移动数据。

这种设计使得队列操作非常高效,只需要简单的指针加减和比较,无需复杂的内存搬移。

http://www.jsqmd.com/news/636436/

相关文章:

  • fMRI(4-1)统计分析报告生成器说明
  • D11 15. 三数之和 18. 四数之和
  • 2026贵阳车牌识别系统与无人值守停车场完全指南:5大本土品牌深度横评+官方直达联系方式 - 精选优质企业推荐榜
  • EtherCAT:工业自动化中的实时通信引擎
  • 别再乱用配合了!SolidWorks装配体设计中‘重合’、‘同轴’、‘距离’三大核心关系的深度解析与实战技巧
  • ESPS USB MSC 调试全过程记录范
  • 璀璨星河Starry Night应用场景:儿童绘本AI辅助创作落地案例
  • 深度解析猫抓扩展:从资源嗅探到流媒体下载的全面实战指南
  • 零基础快速上手:CodeFormer AI人脸修复开源工具完全指南
  • 别再数据线了!用FastAPI 分钟搭个局域网文件+剪贴板神器刭
  • 5分钟掌握模糊PID控制器:让机器人控制像人脑一样智能思考
  • C语言_数组_题3
  • 从CTF赛题到实战:利用phar伪协议绕过上传限制的攻防演练
  • CSS如何保证移动端顶部Fixed头部的安全区域
  • 打通智能体孤岛:用 AgentRun 构建生产级 AA 多 Agent 管理协作系统僦
  • 别再迷信仿真!实测STM32的3.3V PWM也能驱动IR2104(附完整代码与波形分析)
  • PubTest_1775973795700
  • 大学思政课高分通关秘籍:我用思维导图搞定马原期末考试(附全套复习资料)
  • 大模型平台选型指南:从Xinference的分布式架构到Ollama的轻量哲学
  • RK3576摄像头MIPI-CSI拆分与DTS解析
  • 二维核密度估计图 (KDE Plot) 实战:用 Seaborn 解锁双变量数据分布的深层洞察
  • 告别手动配置烦恼:OpCore-Simplify智能黑苹果配置助手终极指南
  • **反编译防护新思路:基于混淆+加密的C++程序加固实战**在软件安全领域,**反编译防护**始终是开发者绕不开
  • SpaceClaim旋风分离器建模实战:从粗到细的精准设计
  • 从赛季数据到模板图库:深入解析 tft_fetch_assets.py和TFT 截图识别的资源构建链路
  • 猫抓浏览器扩展:3分钟掌握网页视频音频资源一键下载完整指南
  • 低成本DIY家庭监控:基于ESP32-CAM和OV2640的无线视频流方案实战
  • 在jupyter里面画图,并且显示中文字体
  • 别再弯腰插拔了!用闲置MicroUSB线和CH340N芯片,5分钟自制桌面TTL调试神器
  • 提示词工程(Prompt Engineering)-周红伟