在嵌入式软件开发领域,串口通信或网络通信,对不定长数据进行收发是比较常见的需求之一。
因为直接在串口中断函数里面处理数据,可能会导致阻塞/丢包/数据错乱等问题,而环形队列是解决异步数据缓存的最优方案。
搭配上自定义的数据通信协议,可以精准地解析帧头/帧尾/数据长度/校验位,完美地解决了不定长数据的分包/粘包问题。
本文提供一个可以直接移植的C语言代码模板,讲解一下环形队列+自定义协议的核心原理,设计一个串口数据收发的完整流程,适用于单片机(比如STM32、C51、ESP32)、Linux串口编程、网络Socket等数据接收场景。
01
核心原理
(1)为什么要用环形队列?
因为数据通常是连续和不定长的,中断接收的速度比较快,如果主程序处理数据比较慢,就需要使用先进先出(FIFO)的环形队列进行数据缓冲,并且在中断里面做入队操作,主程序进行出队解析。
(2)为什么要自定义协议?
如果不对数据进行协议封包,就无法判断一帧数据从哪里开始、到哪里结束、是否完整、是否出错。
自定义协议可以标准化数据格式,实现帧定位(帧头+帧尾)、数据长度标识、数据校验(防止传输错误)、精准解析不定长数据。
02
设计自定义通信协议
协议帧数据字段格式如下:
【帧头】【数据长度】【数据内容】【校验位】【帧尾】
字段定义:
协议优势:
采用双帧头+帧尾设计,抗干扰能力强,避免错误解析,长度字段可以明确知道需要接收多少数据,校验位用来保证数据传输的完整性,完美地适配了不定长数据的解析。
03
环形队列的代码实现
环形队列是整个代码模板的核心,支持入队、出队、判空、判满、查询长度等基础操作。
04
串口驱动+环形队列对接
在串口中断里面,将接收到的字节压入队列,不做任何解析处理,保证中断函数的执行时间极短。
串口中断服务函数
05
自定义协议的解析函数
主程序的大循环从环形队列里面读取数据,按照自定义协议进行解析,自动识别完整的数据帧,处理粘包和分包问题。
1、协议解析配置
2、协议解析主函数
06
串口数据发送函数
按照自定义协议对数据进行封装然后发送,确保收发双方的数据格式统一。
07
主程序使用示例
主程序无限循环调用解析函数,对数据进行解析成功后,处理业务逻辑,非阻塞并且不丢包。
08
代码模板的优势
(1)数据接收与处理已经解耦,中断只做入队操作,主程序进行数据解析。
(2)通过协议长度字段,自动适配任意长度的数据,完美支持不定长数据。
(3)采用帧头+帧尾+校验位,抗干扰防粘包,解决串口数据传输问题。
(4)C语言编写的代码非常通用,无平台依赖,方便移植。
(5)资源占用极少,非常适合在MCU等嵌入式环境中使用。
09
总结
总的来说,本文提供了环形队列+自定义协议的代码模板,是串口和网络不定长数据接收的通用解决方案。
环形队列解决了异步数据缓存、丢包、阻塞等问题,自定义协议解决了不定长数据解析、粘包分包、数据校验等问题。
代码结构清晰并且可以直接移植,适配了大多数嵌入式通信场景,可以直接将代码复制到项目中使用,也可以根据业务需求扩展协议功能。