
目录
BUG场景:
最近项目有用到STM32和RK3588进行串口通信传输温湿度的数据,然后遇到了CRC8校验失败的BUG
问题描述

如上图所示,在通信的过程中出现了CRC校验失败的问题,一开始以为是偶现的BUG,因为有时候运行1个小时才会出现,有时候运行5分钟就出现了,但是排查多几次发现每次CRC校验的那一帧数据都是一样的,CRC错误也是固定的那个值会校验失败,于是就在STM端打印发送的数据,发现发出的数据是55 15 02 12 0D A1 AA,其中A1是CRC校验值,但是linux端收到却是
55 15 02 12 0A B4 AA,B4是CRC校验值
原因分析:
后来发现串口发送字符是有特殊字符的,例如下面这个表格的都是特殊字符:
| 字符名 | 缩写 | ASCII 码 | 含义 & 典型用途 |
|---|---|---|---|
| 空字符 | NUL | 0x00 | 1. 数据填充(如固定长度数据包补位);2. 部分协议的 "数据包结束标记";3. 初始化串口缓冲区 |
| 标题开始 | SOH | 0x01 | 自定义协议中标记 "数据包头部 / 指令类型段" 开始(如区分数据帧和指令帧) |
| 文本开始 | STX | 0x02 | 标记 "有效数据正文" 开始(与 ETX 配对,界定数据体范围) |
| 文本结束 | ETX | 0x03 | 标记 "有效数据正文" 结束(与 STX 配对,最常用的串口帧分界符之一) |
| 传输结束 | EOT | 0x04 | 标记 "整个传输会话结束"(如一次批量数据传输完成后发送) |
| 询问 | ENQ | 0x05 | 向接收方发起 "状态查询"(如:询问对方是否就绪、是否收到数据) |
| 确认 | ACK | 0x06 | 接收方向发送方反馈 "数据正确接收"(最常用的确认字符) |
| 响铃 | BEL | 0x07 | 触发设备蜂鸣(调试场景,如传输出错时提醒) |
| 回车 | CR | 0x0D | 1. 换行控制(Windows 系串口换行:CR+LF=0x0D+0x0A);2. 多数串口协议的 "帧结束符"(如 Modbus ASCII、AT 指令) |
| 换行 | LF | 0x0A | 1. 换行控制(UNIX 系串口换行仅用 LF);2. 配合 CR 作为帧分隔符 |
| 数据链路转义 | DLE | 0x10 | 协议 "转义符":标记后续字符为 "普通数据"(避免特殊字符被误判为控制符,如传输 0x02 时先送 DLE+0x02) |
| 设备控制 1(XON) | DC1/XON | 0x11 | 软件流控核心:接收方发送 XON,通知发送方 "恢复数据传输"(解决缓冲区溢出) |
| 设备控制 3(XOFF) | DC3/XOFF | 0x13 | 软件流控核心:接收方发送 XOFF,通知发送方 "暂停数据传输" |
| 否定确认 | NAK | 0x15 | 接收方向发送方反馈 "数据错误 / 未收到"(要求重传) |
| 同步空闲 | SYN | 0x16 | 同步串口(如 RS-485 同步模式)的 "同步字符",用于对齐收发时钟 |
| 转义 | ESC | 0x1B | 切换命令模式(如 AT 指令中 ESC 退出当前模式、自定义协议中切换指令集) |
| 记录分隔 | RS | 0x1E | 多组数据的分隔符(如批量传输多条记录时,分隔不同行数据) |
| 字符 | ASCII 码 | 典型用途 |
|---|---|---|
| 空格 | 0x20 | 字段分隔(如调试输出时分隔参数:temp: 25 hum: 60) |
| :(冒号) | 0x3A | 1. Modbus ASCII 帧的地址 / 功能码分隔;2. 自定义协议的键值分隔(如addr:10) |
| =(等号) | 0x3D | AT 指令 / 自定义协议的参数赋值(如BAUD=9600) |
| ,(逗号) | 0x2C | 多参数分隔(如x:100,y:200,z:300) |
| *(星号) | 0x2A | 校验和分隔(如 Modbus ASCII 帧::010300000001840A\r\n 中 * 分隔数据和校验和) |
| ?(问号) | 0x3F | AT 指令查询(如AT+CSQ? 查询信号强度) |
这些特殊字符在linux端收到会自动转换成其它的字符,而温湿度值又不是每次都是固定值,所以才会造成是CRC校验失败偶现BUG的现象,实际上它是必现的BUG
解决方案:
解决的办法就是在linux的串口初始化函数里面加一句限制,清除串口输入阶段的一系列自动处理 / 控制行为,让输入数据以 "原始、无修改" 的方式被读取,就是下面这段代码了
struct termios options;
options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON
| IXOFF | IUCLC | IXANY | IMAXBEL);
配置之后,linux串口读取的数据就是原始的数据了,不会再被篡改了,下面就是接收到的原始数据,可以看到已经能够正常接收0X0D、0X0A这种特殊字符的数据了

总结:
项目中使用STM32与RK3588串口通信传输温湿度数据时,出现CRC8校验失败问题。经排查发现,Linux端会将特殊字符(如0x0D、0x0A等)自动转换,导致接收数据被篡改,CRC校验值错误。解决方案是在Linux串口初始化时清除输入阶段的自动处理标志(IGNBRK、ICRNL等),使数据以原始形式接收。修改后,系统能正确接收特殊字符,CRC校验恢复正常。该问题表面偶现实为必现,关键在于禁用串口的自动转换功能。