目录前言环形缓冲区CubeMX配置代码编写帧格式环形缓冲区结构体和初始化串口环形缓冲写入环形缓冲区读取结语前言按上篇文章基于 STM32 的空调蓝牙遥控器设计与实现红外发射篇的步骤笔者测试中发现如果不小心按错先发了一个0x01或者别的数据即使再发送正常的三字节控制指令也不能响应了。因为错误的字节会占据串口接收数组的内容导致预期接收的三字节指令出现偏移。为解决这个问题笔者将串口环形缓冲与接收DMA相结合配合自定义的协议设置指令帧的帧头格式实现稳定可靠响应由串口接收到的控制指令。环形缓冲区环形缓冲区也叫循环缓冲区、循环队列或 Ring Buffer是计算机科学中一种非常常见的数据结构。它的核心特点是使用一块固定大小的连续存储空间并将其逻辑上看作首尾相连因此特别适合处理连续不断的数据流。环形缓冲区在初始时通常为空数据写入后会按照先进先出的顺序被读取也就是最早进入的数据最先被取出。需要注意的是读取数据时往往只是移动读指针被“读走”的内容并不会立刻从内存中消失而是保留在原位置直到后续被新数据覆盖。当缓冲区被写满后如果继续写入新数据就可能覆盖最旧的数据当然也有一些实现会选择禁止覆盖并在缓冲区满时返回错误或抛出异常。是否允许覆盖取决于具体的设计目标和应用场景。正因为它结构简单、效率高、非常适合流式数据处理所以环形缓冲区被广泛应用于串口通信、音视频缓存、嵌入式系统和操作系统底层等场景。CubeMX配置将串口DMA接收配置改成为环形当收到数据超过数组大小时MCU会自动覆盖数组最开始的内容。代码编写帧格式帧头0xAA命令长度信息0x033字节XX XX XX3字节的命令可选帧尾0x55环形缓冲区结构体和初始化环形缓冲区结构体主要包含指向缓冲数组的指针头索引尾索引等。typedef struct { uint8_t *buffer; uint16_t head; uint16_t tail; uint16_t count; uint8_t overflow; } RingBuffer_t; void RingBuffer_Init(RingBuffer_t *ringBuffer) { ringBuffer-head 0; ringBuffer-tail 0; ringBuffer-count 0; ringBuffer-overflow 0; ringBuffer-buffer buf; ringBuffer-count 0; HAL_UARTEx_ReceiveToIdle_DMA(huart1, ringBuffer-buffer, RINGBUFFER_SIZE); }初始化函数负责将环形缓冲区变量赋值设置串口空闲中断。串口环形缓冲写入缓冲区的写入由串口空闲中断完成仅对空闲中断触发做处理简化操作。当串口空闲时头索引等于接收到的字节数。uint8_t size; void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if (huart huart1) { if (HAL_UARTEx_GetRxEventType(huart) HAL_UART_RXEVENT_IDLE) { size Size; ringBuffer.head size % RINGBUFFER_SIZE; } } }环形缓冲区读取如果环形缓冲区是空就直接返回否则读取一个字节的内容并使用指针传递出去。uint8_t RingBuffer_Get(RingBuffer_t *ringBuffer, uint8_t *data) { if (RingBuffer_IsEmpty(ringBuffer)) { return 0; } *data ringBuffer-buffer[ringBuffer-tail]; ringBuffer-tail (ringBuffer-tail 1) % RINGBUFFER_SIZE; ringBuffer-count--; return 1; }在周期函数中读取缓冲区内容寻找帧头尝试匹配格式判断是否出现控制指令。char str[40]; void AC_cycle(void) { uint8_t data; RingBuffer_Get(ringBuffer, data); if (data 0xAA) { RingBuffer_Get(ringBuffer, data); if (data 0x03) { RingBuffer_Get(ringBuffer, AC_CMD_Receive[0]); RingBuffer_Get(ringBuffer, AC_CMD_Receive[1]); RingBuffer_Get(ringBuffer, AC_CMD_Receive[2]); AC_CMD_Request 1; } } if (AC_CMD_Request 1) { Normal_Code(AC_CMD_Receive[0], AC_CMD_Receive[1], AC_CMD_Receive[2]); sprintf((char*)str, BLUE CMD: %02X %02X %02X\r\n, AC_CMD_Receive[0], AC_CMD_Receive[1], AC_CMD_Receive[2]); HAL_UART_Transmit(huart1, (uint8_t*)str, strlen(str), HAL_MAX_DELAY); AC_CMD_Request 0; display_new_cmd_flag 1; display_showCMD(str); } }蓝牙发送0x01 0x02 0x03 0x04 buf存放接收到的内容头索引的值变成4继续运行由于没有检测到帧头就不会执行红外发射。同时尾索引的值也等于头索引这是在周期内不停读取环形缓冲的缘故。此时环形缓冲区为空。发送正常指令例如0xAA 0x03 0xFF 0xFF 0xFF数组第一个元素被覆盖头索引变成1而尾索引还停留在上次的4。对环形缓冲区而言多了从4到7这4个元素0xAA 0x03 0xFF 0xFF还有00xFF。 执行完RingBuffer_Get函数尾索引data是0xAA接着读取环形缓冲区下一个元素。将剩余指令全部取出。如果蓝牙发送0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09共9字节的数据数组溢出第一个元素变成0x09头索引的值变成1此时再发送正常指令例如0xAA 0x03 0xFF 0xFF 0xFF也可以正常响应因为周期函数会一直读取环形缓冲尾索引也变成1环形缓冲区再次为空……结语经过测试使用环形缓冲和帧格式后系统的鲁棒性大大提高无效数据得以滤除真正有效的指令信息可以被稳定执行。
企业数字化 ERP 产品动态
相关推荐
Python+ECharts数据可视化大屏实战:10套案例与避坑指南 简介:一套面向数据分析师、开发者和可视化爱好者的PythonEcharts大屏案例合集,收录10套可直接运行与二次改造的完整数据看板,适用于多类业务数据的可视化展示与汇报,解决从数据清洗、图表配置到界面发布的全流程问题。资源包共124… · 2026/9/26 17:34:47
PHU批处理实战手册:任务定义、调度避坑与幂等设计 简介:一份面向4G/5G无线网络优化工程师的华为PHU-Smart手机路测APP使用手册,选自前景培训教材第十六章,聚焦云端协同的路测作业数字化流程。文档以实际操作视角,系统讲解GC平台上的账号开通、工参模板下载与导入、用户权限同步、队… · 2026/9/26 17:34:40
智谱GLM-5.3-Flash生产接入全链路指南:额度、模型与API避坑实录 1. 这不是“薅羊毛”指南,而是一份面向开发者的智谱GLM-5.3-Flash生产级接入实录我从去年开始把智谱系列模型深度集成进三个企业级AI工作流里:一个是金融合规文档自动初审系统,一个是制造业设备故障日志语义归因平台,还有一个是教… · 2026/9/26 17:34:40
Qwen-Image GGUF版本地部署:ComfyUI多模态工作流实战指南 1. 项目概述:为什么是Qwen-Image GGUF版 ComfyUI本地运行?最近在图像生成领域,Qwen-Image这个模型越来越受关注——它不是单纯的文字转图片(text-to-image),而是能真正理解中文图文混合输入、支持“以图搜… · 2026/9/26 18:11:03
YOLO26+室内家居数据集:一条命令搞定智能家居目标检测与实例分割 最近 YOLO 又搞了个大动作:官方放出一套室内家居数据集,并且把 YOLO26 的训练流程做到了“一条命令”级别。智能家居圈里这两天讨论得很热闹,原因很简单——过去做室内场景识别,最头疼的从来不是没有模型,而是没有一套… · 2026/9/26 18:11:03
FastAPI MCP 快速入门教程:用 TaoToken 统一 Key 打通本地工具链 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 18:11:03
2026深度实测|Work模式与Composer vibe coding迭代对比:开发者工具选型必看 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 18:11:03
AI Agent工程化实战:从LLM大脑到可上线的系统 1. 先搞明白:Agent和LLM到底差在哪这几年做AI落地最常被问到的一个问题,就是"你搞的AI Agent,和ChatGPT、和DeepSeek到底有什么区别?"。我习惯把答案浓缩成一句话:大模型是一个会说话的大脑,Agen… · 2026/9/26 18:10:57
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46