串口乱码但波特率是对的:数据位、校验位、停止位怎么排
串口乱码里最难受的一种,是波特率已经反复核对过、两端填的一模一样,收回来还是一串认不出的字符。这时候该查的不再是速率,而是一帧数据里剩下的三项:数据位、校验位、停止位。这三项错配的后果完全不同——一个丢掉字节最高位,一个根本不改数据只改”有没有报错”,一个平时不发作、只在连续数据流里崩。本文按这三种后果反推排查顺序。
先把”波特率是对的”这句话坐实
“波特率是对的”这句话,通常指的是两端配置界面里填的数字一样。这和两端实际速率一样并不是一回事。
串口的实际速率由收发双方各自的时钟分频得到,分频比不一定能整除出标称值,两端的实际速率就会各偏一点。异步串口没有独立的时钟线,接收端靠每一帧的起始位重新对齐一次,之后按自己的节拍往后数。两端节拍不一致就会导致判读错位;至于错位在帧内如何分布,取决于收发器的采样实现,不同芯片不一样。
这带来一个容易误判的现象:同一条链路上,位数少的帧格式可能”碰巧能通”,位数多的就开始出错。所以”换成 7 位数据之后好了”并不能证明数据位本来就该是 7 位,也可能只是帧被缩短了、累积偏差还没越界。
坐实波特率的办法不是再看一眼配置,而是数字节:让发送端发出已知长度的一串数据,看接收端收到多少个字节。波特率方向错了,接收端的采样节拍和实际位宽对不上,一帧会被切成多帧、或者多帧被并成一帧,字节数会明显地胀或者缩(它给的是方向,不是精确倍数);波特率方向没错时,字节数与发送数一般能对上,错的只是每个字节的取值。★ 但这个判据只在两端速率相差较大时才明显:相差不到一档时字节数可能仍然一一对应、只是内容错位,所以字节数对得上并不能直接排除波特率。怎么把链路接成旁路、怎么读这段字节流,见用串口调试助手定位问题。
字节数能对上,才轮到下面这三项。
一帧串口数据里到底有几位
异步串口的一帧固定由这几段拼成:一位起始位(把空闲的高电平拉低,接收端靠这个下降沿触发对齐)、若干数据位(低位在前)、可选的一位校验位、一位或两位停止位(回到高电平)。注意空闲状态本身也是高电平,所以停止位和”线上没在发东西”在电平上长得一样——这一点后面会变成停止位错配的关键。
可配的只有三项:数据位取 7 或 8、校验位取无/奇/偶、停止位取 1 或 2,组合总数是 2×3×2 = 12 种。和波特率的候选值是个开放集合不同,帧格式的候选有限且可穷举。这是这类故障唯一的好消息:真到没辙的时候,12 次一定试得完,而下面的判据能把 12 缩到两三次。
关键的量是帧的总位数。两端总位数一致时,接收端不会失步,只是把某些位当成了别的角色;总位数不一致时,接收端会跑到错误的位置上去找停止位。这两类后果差别很大,先分清是哪一类,比逐个试参数有效得多。
数据位错配:英文看着正常,中文和 HEX 全乱
先看一个反直觉的组合:发送端 8N1、接收端 7E1。
两边总位数都是 10 位(1 起始 + 8 数据 + 0 校验 + 1 停止,对 1 起始 + 7 数据 + 1 校验 + 1 停止),接收端不会失步,每一帧照样能切出来。它做的事情是:把发送端的第 8 个数据位(也就是字节的最高位)当成校验位读走了,剩下 7 位交给上层。
后果很有辨识度。字节值低于 0x80 的内容,最高位本来就是 0,丢掉不影响——英文字母、数字、常见标点照样正常显示。而中文(汉字编码的首字节最高位必为 1)和十六进制指令里的高位字节,最高位一律被抹掉,全部错位。所以**“英文可读、中文和 HEX 全乱”这个组合,指向的是数据位,不是波特率**。
反过来,发送端 7E1、接收端 8N1,同样是 10 位对 10 位、同样不失步,但方向相反:发送端的校验位被接收端当成第 8 个数据位,塞进了字节的最高位。凡是发送端算出校验位为 1 的那些字节,收到的值会比正确值多出 0x80;校验位算出来是 0 的那些,值正好相等。表现是一部分字节对、一部分字节整齐地偏了 0x80,而不是全篇随机乱。
验证办法:如果设备有一段确定只含 ASCII 可见字符的输出(版本号、型号字符串这类),先让它吐这一段,再让它吐一段带高位字节的内容,两段对比。前者对、后者错,范围就锁在数据位与校验位这一组上了。
校验位错配:数据其实是对的,错的是”有没有报错”
校验位是硬件在每个字节上单独算的一位,它不参与数据位的取值。这句话是理解这类故障的关键:校验位配错,接收端拿到的数据位本身没有被改动,变的是它对这一帧”合不合法”的判定。
发送端 8N1、接收端 8E1:接收端期望 11 位,实际来的是 10 位。它把发送端的停止位(恒为高电平,也就是 1)当成校验位读走,再往下一位去找停止位——如果字节之间有间隔,那一位是空闲高电平,停止位检查照样通过。于是数据位全对,但”校验位本该是多少”这件事时对时错,取决于每个字节自身的奇偶性,表现就是驱动的校验错误计数在涨。至于错了之后你看到什么,取决于软件怎么处理错误帧:有的直接丢弃这个字节,有的用一个替代字符顶上,有的原样交给上层、只在状态里记一笔。所以同样是校验位配错,在不同的串口工具里可能表现成”少字符”、“多问号”或者”内容看着全对但状态栏有红字”。若字节之间没有间隔,接下来那一位落的是下一帧的起始位(低电平),还会附带帧错误。
发送端 8E1、接收端 8N1:接收端只期望 10 位,它把发送端的校验位当成了停止位。校验位算出来是 1 的那些字节,正好顶上停止位要求的高电平,通过;算出来是 0 的那些字节,停止位位置上是低电平,直接判帧错误。这一类的计数落在帧错误那一栏,不是校验错误那一栏。
这两栏计数因此是能分流的:只有校验错误在涨,往”接收端比发送端多要了一位校验”的方向查;帧错误在涨,往相反方向查。串口工具如果不显示这两个计数,换一个显示的工具——在这一步上它是唯一的客观依据,肉眼看字符看不出区别。
顺带把一个高频混淆点讲清楚:串口的”校验位”和指令帧末尾的”校验和”不是一回事。前者是硬件在每个字节上加的一位,由串口参数决定;后者是协议层在一串字节末尾附加的一个或几个字节,由设备厂商的指令格式决定,改动其中任一字节都得重算。参数配错影响前者,指令抄错影响后者,现象和修法都不同。指令帧这一层可以对照 RS232 与 RS485 串口控制速查。
停止位错配:单条命令测得好好的,连续流才崩
这一项最容易漏,因为它在常规测试里根本不发作。
发送端 1 位停止位、接收端配 2 位:接收端读完第一位停止位后还要再检查一位。如果两个字节之间有任何间隔,线路处在空闲高电平,第二位停止位的检查照样通过——你发一条命令、隔一会儿再发一条,全程正常。可一旦设备连续吐一段数据、字节背靠背没有间隙,第二位停止位的位置上落的就是下一帧的起始位,起始位是低电平,检查失败,判帧错误;接下来接收端还得重新找下一个下降沿来对齐,这个过程里会再丢掉若干字节。
现象因此很有特征:短指令的应答完全正常,一读长数据或者一开连续上报就成片乱码,而且乱的位置不固定。如果据此去怀疑波特率或者线缆,方向就偏了。
反过来,发送端 2 位停止位、接收端配 1 位:接收端读完它要的那一位停止位就结束这一帧,多出来的那一位停止位是高电平,和空闲状态没有区别,被自然跳过。这个方向不会出问题。所以停止位的错配是单向的,只有”发得少、收得多”才出事。
由此得到一条容易被跳过的推论:用单条短命令验证串口参数是不充分的。要把停止位这一项也验掉,必须让链路跑一段连续的数据流。
把 12 种组合按总位数分组,缩成两三次尝试
前面反复用到”总位数”这个量,把它列出来就是一张能直接用的表:
| 帧格式 | 起始 | 数据 | 校验 | 停止 | 总位数 |
|---|---|---|---|---|---|
| 7N1 | 1 | 7 | 0 | 1 | 9 |
| 7N2 | 1 | 7 | 0 | 2 | 10 |
| 7E1 / 7O1 | 1 | 7 | 1 | 1 | 10 |
| 8N1 | 1 | 8 | 0 | 1 | 10 |
| 7E2 / 7O2 | 1 | 7 | 1 | 2 | 11 |
| 8N2 | 1 | 8 | 0 | 2 | 11 |
| 8E1 / 8O1 | 1 | 8 | 1 | 1 | 11 |
| 8E2 / 8O2 | 1 | 8 | 1 | 2 | 12 |
总位数相同的几种格式互相错配时不会失步,字节数与发送数一一对应,错的是每个字节的取值或者错误标志;跨组错配则会失步,字节数和出错位置都不稳定。
排查顺序可以这样收:
- 字节数一一对应吗? 不对应,回去查波特率方向,别在帧格式上耗时间。
- 错误计数落在哪一栏? 只有校验错误 → 校验位方向;帧错误 → 停止位,或校验位的反方向;两栏都是零、内容却不对 → 多半不是串口参数问题,而是字符编码那一层(数据位错配通常会在校验或帧错误那一栏留下痕迹,见上文机制)(见串口日志乱码)。
- 短命令正常、连续流才崩? 直接看停止位,这个现象几乎只有它产生。
- 以上都指不出方向时,才在同一总位数那一组里穷举——以 8N1 为例,与它同组的只有 7E1、7O1、7N2 三种,加起来也就三次尝试。
最容易栽的坑:你以为的”两端”其实是三端
参数要对齐的不是”两端”,是链路上每一个自己带串口参数的节点。控制端到设备之间如果串了串口服务器、协议转换器,或者中控主机的串口板,这些中间设备各自有一套独立的串口配置,它们既要和上游对齐、也要和下游对齐。只把电脑和设备这两头设成一样,中间那一段仍可能按出厂值在跑,呈现出的现象和参数错配一模一样,而你在两头怎么改都改不动它。
另一个常被跳过的动作:判断乱码之前,先在串口工具里打开十六进制显示。文本模式下看到的字符已经过了一层编码解释,同一串字节在不同编码设置下长相完全不同,很容易把编码问题误判成参数问题。
如果这三项都逐一验过、字节数也对得上,仍然乱,说明问题不在帧格式这一层,该回到电平和接线上去查:TX/RX 方向、公共地、以及 RS232 与 TTL/RS485 的电平类型是否匹配。这条总体路径在 RS232 串口乱码 6 步定位 里已经从简到难排过一遍,可以直接接上。
留言讨论
评论发布后会被人工复核,违规内容将被删除。
如果发表没有反应,可以前往联系我们告诉我们。