串口收到乱码:波特率之外还要检查哪些问题?

1. 前言

串口乱码是嵌入式调试中最常见、也最容易被误判的问题之一。很多人看到终端出现异常字符,第一反应是修改波特率;但如果波特率已经一致,问题往往隐藏在收发线方向、共地、电平标准、供电干扰、帧格式或软件处理逻辑中。

本文按排查效率从高到低整理一套实用方法。核心原则是:先用最小系统确认串口链路,再逐层恢复真实设备和复杂环境。

2. 先区分“乱码”的类型

不同现象对应的排查方向不同。不要只根据“屏幕上的字符不对”就直接调整波特率。

现象 常见原因 优先检查
连续、固定地重复出现错误字符 波特率、数据位、校验位或停止位不一致 串口参数与帧格式
偶尔错字、丢字、夹杂异常字符 共地不良、供电纹波、电磁干扰、缓冲区溢出 电源、接地、线束与缓存
完全没有任何输出 TX/RX 未交叉、端口选错、设备未上电、串口被占用 接线、端口与供电
英文正常、中文显示异常 编码或终端字符集不一致 UTF-8、GBK、终端设置
一开始正常,电机启动后开始乱码 电压跌落、地弹、电机干扰 供电、共地、去耦与布线

3. 串口参数不只包含波特率

常用配置通常写作 115200 8N1,含义如下:

  • 115200:波特率。
  • 8:8 个数据位。
  • N:无校验位;也可能是 E(偶校验)或 O(奇校验)。
  • 1:1 个停止位。

通信两端必须在这些参数上保持一致。仅把波特率设置为相同数值,并不能保证帧边界解释一致。例如发送端使用 8E1,接收端却配置为 8N1,接收端仍会把每个字符拆错,表现为稳定、连续的乱码。

需要同时确认的项目:

  • 波特率。
  • 数据位数。
  • 校验方式。
  • 停止位数。
  • 硬件流控或软件流控是否启用。
  • 是否存在反相串口或特殊电平接口。

4. TX 和 RX 是否正确交叉

普通 UART 连接遵循“发送端接接收端”的原则:

设备 A TXD  ->  设备 B RXD
设备 A RXD  <-  设备 B TXD
设备 A GND  ---- 设备 B GND

TX 与 TX、RX 与 RX 直接相连通常无法正常通信。不过也有例外:部分带有 USB 转串口芯片的开发板已经在板内完成交叉,外部接口的命名可能以“相对主机”的方向标注。遇到这种情况,不应只看接口丝印,应结合原理图、说明书或回环测试确认实际信号方向。

另外,串口终端选错了系统端口,也会造成“怎么看都不对”的假象。接入多个 USB 转串口设备时,建议先拔掉无关设备,再通过设备管理器或系统日志确认端口号。

5. GND 共地是串口通信的基础

UART 用电压高低表示逻辑状态,而电压必须相对于共同参考点测量。两个设备没有可靠共地时,即便 TX 和 RX 已交叉、波特率也正确,接收端仍可能把不稳定电平误判为数据。

共地异常常见表现包括:

  • 近距离偶尔能通信,线稍长就乱码。
  • 手触线缆或改变设备摆放位置后现象变化。
  • 设备各自使用独立电源时更容易出现问题。
  • 电机、舵机或其他大负载工作后开始丢字、错字。

排查方法很直接:确认两个设备的 GND 已可靠连接,并用万用表检查地线连接是否连续。对于大电流系统,还应避免让电机回流与精密数字信号共用细长地线,否则电流变化会引入明显的地电位波动。

6. 先确认接口标准:TTL、RS-232 和 RS-485 不能混接

“都是串口”不代表可以直接连接。UART 协议描述的是异步串行的帧格式,而 TTL、RS-232、RS-485 描述的是不同的电气接口标准。

接口类型 信号形式 是否可与 TTL UART 直接连接
TTL UART 单端逻辑电平,常见 3.3V 或 5V 可以,但两端逻辑电平需兼容
RS-232 单端正负电压,逻辑极性与 TTL 不同 不可以,需要 RS-232 收发器
RS-485 差分 A/B 信号 不可以,需要 RS-485 收发器

将 TTL UART 直接接到 RS-232 接口,轻则无法通信或持续乱码,重则可能因电压范围不匹配损坏芯片。RS-485 则不是单根 TX/RX 线的连接方式,需要通过收发器将 UART 信号转换为差分 A/B 信号,并正确控制收发方向。

即使都是 TTL UART,也要检查 3.3V 与 5V 逻辑是否兼容。5V TX 直接送入仅支持 3.3V 输入的芯片可能造成长期可靠性问题或直接损坏。

7. 供电不稳和电磁干扰会制造“偶发乱码”

当乱码不是每个字符都稳定复现,而是偶尔出现、与电机动作或继电器切换有关时,应优先怀疑电源与干扰,而不是继续反复尝试波特率。

重点检查以下问题:

  • 电机启动时,主控电源是否瞬间下降。
  • USB 供电、DC-DC 模块或电池是否有足够余量。
  • 主控和串口模块附近是否有合适的去耦电容。
  • 串口线是否与电机线、继电器线、变频器输出线长距离并行。
  • 线缆是否过长、无屏蔽或没有可靠地线。

常用改善方式:

  • 给逻辑电路和电机电源做合理隔离,至少保证可靠共地。
  • 在模块电源入口增加合适的储能电容,并在芯片电源脚附近保留去耦电容。
  • 缩短 UART 线缆,避免与大电流线束平行走线。
  • 长距离通信改用 RS-485、CAN 等更适合抗干扰传输的物理层。
  • 对外部接口增加 ESD 防护和必要的滤波措施。

8. 不要忽略硬件流控和软件流控

部分设备使用 RTS、CTS 硬件流控;另一些系统可能启用了 XON/XOFF 软件流控。如果一端启用流控,另一端没有正确接线或配置,可能造成发送被暂停、数据缺失,甚至在终端中表现为异常内容。

调试阶段建议先使用最简单的无流控配置:

115200 8N1,无硬件流控,无软件流控

在基础收发已经稳定后,再根据设备协议启用 RTS/CTS 或其他流控机制。

9. 数据帧正确,不代表显示文本正确

有些问题不是串口电气层错误,而是上层数据被错误地当作文本显示。

典型场景包括:

  • 设备发送的是二进制协议,终端却按文本显示。
  • 数据中包含 0x00、0xFF、校验和等不可打印字节。
  • 接收端按 UTF-8 显示,发送端却使用 GBK、GB2312 或其他编码。
  • 使用 printf("%s", buffer) 输出未以 \0 结尾的接收缓冲区。
  • 将二进制数据打印为字符,而不是十六进制。

遇到这类情况,应优先使用十六进制视图观察原始字节。例如设备预期发送 55 AA 03 01 02 03,在普通文本窗口中本来就不一定能显示为可读字符。确认原始字节正确后,再按通信协议解析字段。

嵌入式端调试二进制数据时,可以使用类似以下方式输出:

for (size_t i = 0; i < length; ++i) {
    printf("%02X ", rx_buffer[i]);
}
printf("\r\n");

10. 缓冲区和程序逻辑也会导致“看似乱码”

当接收频率较高或主循环处理较慢时,串口硬件 FIFO、驱动缓冲区或应用层缓冲区可能溢出。此时即使线缆与参数完全正常,接收到的数据也会缺字、错位或帧不同步。

需要检查:

  • 中断服务程序是否足够短,是否及时取走接收寄存器数据。
  • DMA 或环形缓冲区大小是否足够。
  • 主循环是否有长时间阻塞操作。
  • 是否按协议的帧头、长度和校验恢复同步。
  • 接收缓冲区是否发生越界。

对于带帧头和校验的数据协议,不应假设每次 read() 或串口中断都恰好拿到一帧完整数据。更稳妥的做法是将字节持续放入接收缓存,再用状态机按“帧头 → 长度 → 数据 → 校验”的顺序解析;校验失败后丢弃无效字节并重新寻找帧头。

11. 推荐的最小化排查顺序

按下面顺序操作,通常比一次性替换线缆、代码和终端软件更快定位问题:

  1. 确认两端波特率、数据位、校验位、停止位和流控全部一致。
  2. 确认 TX/RX 交叉连接,且主机实际打开的是正确串口。
  3. 确认两端 GND 可靠共地。
  4. 确认接口是 TTL、RS-232 还是 RS-485,电平和收发器匹配。
  5. 用 USB 转串口模块做 TX-RX 回环测试,排除电脑、驱动和终端问题。
  6. 断开电机、继电器等高干扰负载,只保留主控与串口设备测试。
  7. 用十六进制方式观察原始字节,确认不是编码或二进制协议显示问题。
  8. 最后检查缓冲区、帧解析、超时和溢出处理。

12. 小结

串口乱码不等于波特率错误。波特率只是 UART 帧的一部分;接线方向、共地、电平标准、供电质量、干扰环境、流控、字符编码和接收程序都会影响最终结果。

将问题按“参数 → 接线 → 电平 → 电源与干扰 → 数据与软件”分层排查,先在最小系统中验证,再恢复真实负载,通常能够快速区分是配置问题、硬件问题还是协议解析问题。