4、串口通信故障:串口无输出、乱码问题、波特率不匹配、流控设置错误

串口通信,咱们硬件工程师的老朋友了。从大学做单片机实验开始,到后来调试各种嵌入式板卡,串口几乎天天见。但越是熟悉的东西,出问题的时候越让人抓狂。我见过不少同事,拿着示波器戳了半天,最后发现是杜邦线松了。

这一节,咱们就把串口通信的常见故障掰开揉碎了讲。说白了,串口通信就三件事:电平对不对、速率对不对、数据格式对不对。只要这三样没问题,串口基本不会罢工。

核心要点:串口通信故障排查,永远遵循「先硬件后软件,先物理层后协议层」的顺序。

4.1 串口无输出——板子「哑巴」了

上电之后,终端啥也不显示。这种情况我遇到得最多。别慌,咱们一步步来。

第一步:检查硬件连接

我个人习惯,先看电源指示灯亮不亮。板子都没电,串口当然没输出。然后检查串口线——TX接RX,RX接TX,这个交叉连接很多人会搞反。我记得有一次帮同事调一块STM32的板子,他信誓旦旦说线接对了,结果我拿万用表一量,TX对TX,RX对RX。嗯,这种低级错误我也犯过。

第二步:检查电平转换芯片

现在的板子大多用USB转串口芯片,比如CH340、CP2102、FT232。这些芯片容易虚焊,或者本身坏了。你可以用手摸一下芯片温度,如果烫手或者冰凉,大概率有问题。我建议用示波器量一下芯片的TXD引脚,看看有没有波形跳变。如果有波形但终端没显示,那问题出在USB端或者驱动上。

注意:有些USB转串口模块的供电能力有限。如果板子功耗大,会导致电压被拉低,串口芯片工作不正常。我曾经遇到过一块4G模块的板子,串口时好时坏,最后发现是USB线太长,压降太大。

第三步:检查软件配置

代码里串口初始化了吗?GPIO复用功能配对了没?我见过有人把串口引脚配成了普通GPIO,结果折腾了一下午。还有,中断使能了吗?如果用了DMA,DMA配置正确吗?

小技巧:调试时可以先不用中断,直接用轮询方式发送一个固定字符串。比如在main函数里写个死循环,每隔1秒发一次"Hello World"。如果轮询能发出去,那就是中断或DMA配置的问题。

4.2 乱码问题——板子说「外星语」

串口有输出了,但全是乱码。这比没输出还让人头疼。乱码的原因其实不多,咱们一个一个排查。

原因一:波特率不匹配

这是最常见的原因。发送端用115200,接收端用9600,出来的肯定是乱码。你想想看,两边说话的节奏都不一样,怎么可能听懂?

怎么确认?看乱码的规律。如果乱码是固定的、有规律的字符,比如全是"ÿ"或者"??",那八成是波特率问题。如果乱码是随机的、跳变的,那可能是电平问题或者干扰。

发送端波特率 接收端波特率 乱码特征
115200 9600 字符重复、乱码密集
9600 115200 字符丢失、乱码稀疏
19200 9600 部分字符正确,部分乱码

原因二:数据位/停止位/校验位不一致

这个容易被忽略。发送端配的是8N1(8数据位、无校验、1停止位),接收端配的是7E1(7数据位、偶校验、1停止位),那肯定对不上。我建议统一用8N1,这是最通用的配置,兼容性最好。

原因三:时钟精度不够

有些低端MCU的内部RC振荡器精度很差,标称9600,实际可能只有9200。时间长了误差累积,就会出乱码。我遇到过用内部晶振的芯片,波特率高了之后乱码严重,换成外部晶振就好了。

避坑指南:我曾经用一颗国产MCU做项目,内部RC振荡器误差达到±3%。115200波特率下,每10个字节就错一个。后来我换成外部12MHz晶振,问题彻底解决。所以,高波特率下一定要用外部晶振。

4.3 波特率不匹配——如何精准计算

很多人觉得波特率设置就是填个数字,其实没那么简单。MCU的串口时钟来自系统时钟,经过分频得到目标波特率。如果分频系数不是整数,就会产生误差。

计算公式是这样的:

// 以STM32为例,USART波特率计算公式
// 过采样模式:OVER8 = 0 时(16倍过采样)
// Baud = fCK / (16 * USARTDIV)
// 
// 其中USARTDIV是一个小数,整数部分写在USART_BRR的高12位
// 小数部分写在低4位,精度为1/16

// 假设fCK = 72MHz,目标波特率115200
// USARTDIV = 72000000 / (16 * 115200) = 39.0625
// 整数部分 = 39 = 0x27
// 小数部分 = 0.0625 * 16 = 1 = 0x1
// USART_BRR = (39 << 4) | 1 = 0x271

// 实际波特率 = 72000000 / (16 * 39.0625) = 115200
// 误差 = 0%

你看,如果时钟频率选得好,误差可以做到0%。但有些频率就不行。比如fCK = 50MHz,目标115200:

USARTDIV = 50000000 / (16 * 115200) = 27.1267
整数部分 = 27
小数部分 = 0.1267 * 16 = 2.027 ≈ 2
实际USARTDIV = 27 + 2/16 = 27.125
实际波特率 = 50000000 / (16 * 27.125) = 115207
误差 = (115207 - 115200) / 115200 = 0.006%

0.006%的误差完全没问题。但如果误差超过2%,通信就可能不稳定。我建议误差控制在1%以内。

实用建议:选系统时钟频率时,尽量选能被常用波特率整除的。比如8MHz、16MHz、72MHz这些频率,对115200、9600都很友好。别用一些奇奇怪怪的频率,比如11.0592MHz虽然对9600友好,但对115200就不太行了。

4.4 流控设置错误——硬件流控的坑

流控分两种:软件流控(XON/XOFF)和硬件流控(RTS/CTS)。嵌入式开发中,硬件流控用得更多。

硬件流控的原理很简单:发送数据前,先检查CTS引脚是否为低电平。如果CTS为高,说明接收端还没准备好,发送端就得等着。RTS引脚则是发送端告诉接收端:「我准备好了,你可以发了」。

问题出在哪?很多人代码里使能了硬件流控,但硬件上根本没接RTS/CTS引脚。或者接了,但电平逻辑搞反了。结果就是:发送端一直在等CTS变低,但CTS一直悬空或者被拉高,数据永远发不出去。

警告:如果不需要流控,千万别在代码里使能。我见过一个项目,串口偶尔卡死,查了三天,最后发现是有人不小心把流控使能打开了。CTS引脚悬空,导致发送端一直等待,系统假死。

怎么判断是不是流控问题?很简单:用示波器看TX引脚,如果TX一直为高电平(空闲状态),但程序确实在调用发送函数,那八成是流控在作怪。把流控关了试试,如果数据能发出去,那就是流控的问题。

另外,有些USB转串口模块默认使能了流控。比如某些FT232模块,RTS和CTS引脚默认有上拉。如果你不需要流控,最好在代码里把对应的GPIO配置成普通IO,或者直接禁用流控功能。

总结一下串口故障排查流程:

  1. 先看硬件:电源、连接线、电平转换芯片
  2. 再看配置:波特率、数据位、停止位、校验位
  3. 再看时钟:系统时钟频率是否准确,分频误差是否在1%以内
  4. 最后看流控:不需要就别使能,使能了就检查硬件连接

串口通信其实不难,难的是细心。我做了这么多年硬件,串口问题十有八九都是低级错误。接线错了、配置错了、忘记初始化了。嗯,下次遇到串口问题,别急着怀疑芯片坏了,先按上面的步骤排查一遍,大概率能解决问题。

串口通信故障排查流程图 串口通信故障 第一步:硬件连接检查 电源 / 杜邦线 / TX-RX交叉 / 电平转换芯片 有输出? 是 否 修复硬件连接 第二步:软件配置检查 波特率 / 数据位 / 停止位 / 校验位 正常? 是 否 修改配置参数 继续 第三步:时钟精度与流控 外部晶振 / 分频误差 / RTS/CTS硬件连接 乱码专项排查 波特率误差 / 时钟精度 乱码 先硬件 → 再软件 → 后时钟 排查顺序决定效率

最后说一句:串口调试工具也很重要。我个人习惯用Putty或者SecureCRT,但调试阶段我更喜欢用串口调试助手,因为它能显示十六进制数据,方便看原始数据。如果你怀疑是文本编码问题,比如UTF-8和GBK混了,用十六进制一看就明白了。