1. 为什么需要自定义通信协议?
在嵌入式视觉项目中,K210和STM32的串口通信就像两个说不同方言的人交流。如果直接用ASCII码传输数据,相当于让一个人用英语念字母表,另一个人需要自己拼出单词——效率低且容易出错。我最初尝试用uart.write发送"123,456"这样的字符串时,发现STM32端经常收到乱码或截断的数据。
二进制协议的核心优势在于数据密度和解析效率。举个例子,传输坐标值200如果用ASCII码需要3字节('2','0','0'),而用二进制只需1字节(0xC8)。在115200波特率下,前者传输需要260μs,后者仅87μs——这对于需要实时响应的视觉控制系统至关重要。
更严重的问题是数据边界识别。原始文章提到的ASCII码方案存在两个致命缺陷:
- 需要额外分隔符(如逗号)来区分数据字段
- 接收端必须进行字符串转换(atoi等函数),既消耗CPU资源又增加延迟
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 帧头帧尾协议设计实战
2.1 协议格式定义
我们的协议结构像快递包裹一样有明确的包装标识:
code复制[帧头1][帧头2][数据区][校验和][帧尾1][帧尾2]
具体实现时,我选择了0xB3和0xA3作为双帧头,0x0D和0x0A作为帧尾。这种设计有三大好处:
- 抗干扰性:连续两个特定字节作为帧头的概率极低
- 快速同步:接收端可以通过搜索0xB3A3快速定位数据起始
- 兼容性:0x0D0A是串口终端默认的行结束符,方便调试
数据打包的Python代码示例:
python复制def pack_data(x, y):
checksum = (x + y) & 0xFF
return bytearray([0xB3, 0xA3, x, y, checksum, 0x0D, 0x0A])
2.2 STM32端的智能接收策略
STM32的中断服务程序(ISR)需要像安检机一样高效工作。参考原始文章的代码,我优化后的处理流程如下:
- 状态机设计:
c复制typedef enum {
WAIT_HEADER1,
WAIT_HEADER2,
RECEIVING_DATA,
CHECK_FOOTER
