线又断了,或者更准确地说——串口又吐乱码了。这是嵌入式开发里几乎人人都会撞上的场景:固件里配置了 115200bps,串口助手也选了 115200,两边看着都挺对,可收到的就是一堆乱码里偶尔夹着几个英文。我第一次正经调 UART 是在一块 STM32F103 最小系统板上,当时怀疑杜邦线接触不良,重新插了三遍,换了两块 USB 转串口,最后拿示波器一测才发现:板子用的默认 8MHz HSI 时钟,HAL 库却按照 72MHz 去算分频,实际波特率和标称值差了十万八千里,能正常才怪。
那之后我把 115200 这个数字翻来覆去研究了好几遍,包括它为什么是行业默认、字节率到底怎么算、在 STM32 和嵌入式 Linux 上分别该怎么配、出问题又该怎么快速定位。这篇文章不打算讲泛泛的概念,而是把我在实际项目里验证过的计算过程和配置经验整理出来,尤其是那个特别容易踩的坑——把 115200bps 当成每秒 115200 字节。这个误差接近 10 倍,一旦按这个假设去设计通信协议里的超时和流控,整条链路都会翻车。
1. 为什么通用默认值是 115200,而不是整整齐齐的 100000
1.1 16 倍过采样:UART 内部的时钟约束
要理解 115200 的来历,得先搞清楚 UART 接收端是怎么工作的。串口这类异步通信,收发双方没有共用的时钟线,接收端不知道数据什么时候会来,只能靠起始位的下降沿来对齐,然后用本地时钟去采样后续的每一位。
为了保证采样不采到电平翻转的边沿上,UART 普遍采用16 倍过采样:每个 bit 时间里采 16 次,然后取中间若干个采样点的多数判决,这样能滤掉毛刺和噪声。也就是说,如果波特率是 115200bps,那么接收端的采样时钟至少要是:
115200 × 16 = 1,843,200 Hz这个 1.8432MHz 只是最低要求,实际外设时钟要比这高很多。问题是,外设时钟通过分频器生成波特率时,只有能整除,误差才是零。如果分频之后得出一个小数,硬件必须做近似,波特率就会有偏差。
1.2 1.8432MHz 与 14.7456MHz:一拍即合的数字
传统 PC 串口用的经典晶振是 1.8432MHz,拿它直接除 16 就是 115200。后来芯片集成度提高,外部晶振又流行用 14.7456MHz,因为这个频率同样能通过整数分频得到一串标准波特率:
| 目标波特率 | 14.7456MHz 下需要的 16 倍过采样分频数 |
|---|---|
| 9600 | 96 |
| 19200 | 48 |
| 38400 | 24 |
| 57600 | 16 |
| 115200 | 8 |
| 230400 | 4 |
| 460800 | 2 |
| 921600 | 1 |
14.7456MHz 对 9600 是 96 分频,对 115200 是 8 分频,全部是整数。这就是老派硬件设计里真正的“标准答案”,也是一代代工程师默认选 115200 的直接原因。
1.3 那 100000 为什么不行?
有人可能会想,100000 不是更好记吗?换算还方便。但以 14.7456MHz 晶振为例,计算一下 16 倍过采样下的分频系数:
14,745,600 / (16 × 100000) = 9.216分频系数是小数,硬件只能取近似值,实际波特率可能偏出去几百甚至几千 bps。早期 UART 芯片的容错能力有限,差一点就是满屏乱码,所以工程界最终用市场选择淘汰了 100000,把 115200 送上了默认宝座。
还有一层原因:115200 是 9600 一路翻倍上去的。300、600、1200、2400、4800、9600、19200、38400、57600、115200……这套翻倍阶梯在 Modbus、老式工控设备、GPS 模块、蓝牙模块里都有极深的兼容性积累。选 115200 意味着最差的旧设备也大概率支持,而上限在 USB 转串口和常见线缆下又足够稳定。
2. 字节率计算的起点:一帧 UART 数据绝对不止 8 个 bit
2.1 帧结构里藏着的 10 个位
会默认 115200 之后,我最想纠正的一个习惯就是把 bps(bit per second)直接当成 Bps(Byte per second)。UART 传输一帧数据不只有数据位,它长这样:
- 空闲状态下总线是高电平
- 发送方拉低电平,产生1 个起始位
- 传输8 个数据位(常用 8N1 格式),低位在前
- 可选1 个校验位(偶校验/奇校验/无校验)
- 恢复高电平,保持1 个停止位(也可能用 2 个停止位)
所以最常用的 8N1(8 数据位、无校验、1 停止位)一帧一共:
1 起始位 + 8 数据位 + 0 校验位 + 1 停止位 = 10 bit2.2 115200bps 在 8N1 下的字节率推导
逻辑就一句话:波特率代表每秒传输的 bit 总数,里面包含起始位和停止位,每一帧实际占用 10 个 bit。因此:
有效数据字节率 = 波特率 / 每帧总位数 = 115200 / 10 = 11520 字节/秒再换算成以 1024 为基数的二进制单位:
11520 / 1024 ≈ 11.25 KiB/s反过来,传输一个字节需要:
10 / 115200 ≈ 86.8 微秒这就是我最喜欢在项目文档里写的一个速查结论:115200bps 的 UART,8N1 格式下有效带宽只有约 11.52KB/s(十进制),别拿它当大吞吐通道用。
2.3 不同帧格式的字节率对比
如果开校验位或者用 2 个停止位,帧总长变成 11 bit,有效数据率会下降。我做协议移植时专门列过一张表,因为很多人改配置时只改了串口助手的波特率,忘了校验位和停止位也影响速率:
| 帧格式 | 起始位 | 数据位 | 校验位 | 停止位 | 帧总位数 | 有效字节率 |
|---|---|---|---|---|---|---|
| 8N1 | 1 | 8 | 0 | 1 | 10 | 11520 B/s 约 11.25 KiB/s |
| 8E1 | 1 | 8 | 1 | 1 | 11 | 约 10473 B/s 约 10.23 KiB/s |
| 8O1 | 1 | 8 | 1 | 1 | 11 | 约 10473 B/s 约 10.23 KiB/s |
| 8N2 | 1 | 8 | 0 | 2 | 11 | 约 10473 B/s 约 10.23 KiB/s |
| 7E1 | 1 | 7 | 1 | 1 | 10 | 10080 B/s 约 9.84 KiB/s |
这里 7E1 有点反直觉:帧总数同样是 10 位,但每帧只有 7 个数据位,换算成字节还要再乘 7/8,所以有效数据率低于 8N1。如果项目里有老设备用 7E1,它和你之间的数据吞吐差距不是一点半点,协议超时不能照搬 8N1 的经验值。
3. 从理论字节率到实际吞吐:一次传输到底要多久
3.1 三个典型场景的时间预算
算清楚字节率之后,实际工程中还要会做传输时间预算。这里给出 115200bps、8N1 下最常见的三个量级,我每次评审通信方案时都会先拿纸笔过一遍:
- 64 字节协议包:64 / 11520 ≈ 5.56ms
- 1KB 数据:1024 / 11520 ≈ 88.9ms
- 1MiB 固件:1024 × 1024 / 11520 ≈ 91 秒
这就是为什么很多工程师第一次用串口刷 1MB 固件时会觉得特别慢——不是工具卡死了,而是物理层就在那晃悠。如果 bootloader 要传 2MB 的升级包,接近 3 分钟,这个耗时必须在产品交互设计里提前考虑。
通用公式也顺手写出来:
传输时间 = 数据字节数 × 每帧总位数 / 波特率8N1 下简化为:数据字节数 × 10 / 波特率。
3.2 别忽略双工和协议层带来的隐藏开销
字节率算出的 11520 B/s 只是裸物理带宽,真实应用里还要扣两笔账。
第一笔是方向切换。RS232 是三线全双工,发和收可以同时进行,那 11520 B/s 的上限在纯接收或纯发送场景里都能打满。但 RS485 是半双工总线,设备发完数据要切回接收方向,方向切换本身需要时间,一次切换可能吃掉好几个字节的传输时间。Modbus RTU 在 RS485 上跑 115200 时,实际每秒能交互的寄存器数量远低于理论值。
第二笔是协议开销。帧头、地址、长度、CRC、ACK/NACK、转义填充,这些全都要占用有效数据字节率里的额度。如果你们用的是十六进制 ASCII 编码而不是二进制帧,一个字节的数据要发两个字符,等于有效载荷直接腰斩一半。
举个例子:假设系统每 10ms 要上报一帧 100 字节的数据,发端占用带宽是 100B/10ms = 10000 B/s,已经占了 11520 B/s 的 86%。如果再加个协议帧头和 CRC,延时就会接近临界点,实际项目里几乎必出问题。这种量级评估在设计阶段做一次,能省下后面很多联调时间。
4. STM32 实战:分频系数计算与误差控制
4.1 STM32 波特率寄存器的硬件公式
到了 MCU 这边,115200 到底准不准,取决于外设时钟和波特率寄存器的配合。STM32 的标准过采样配置 OVER16 下,波特率由这个公式决定:
波特率 = fck / (16 × USARTDIV)USARTDIV 是写入波特率寄存器的分频值,但 STM32 的 USART_BRR 寄存器里把 16 倍采样时钟和波特率这两层合在了一起,实际写入的 BRR 值通常直接等于:
BRR = fck / 波特率例如 72MHz 外设时钟配 115200,BRR = 625,硬件内部再用 625 分频得到 115200。
4.2 F1 的 USART1:72MHz 下可以做到零误差
STM32F103 的 USART1 挂在 APB2 总线上,APB2 预分频默认 1,也就是如果外部晶振 8MHz 配合 PLL 倍频到 72MHz,USART1 的外设时钟就是 72MHz。代入公式:
BRR = 72,000,000 / 115200 = 625(正好整除)625 不是四舍五入出来的,是精确值。所以 USART1 在 72MHz 下跑 115200,理论误差为 0。我用逻辑分析仪实测过,单比特宽度稳定在 8.68 微秒,波形干净利落。
寄存器初始化可以这样写:
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; GPIOA->CRH &= ~(GPIO_CRH_CNF9 | GPIO_CRH_MODE9); GPIOA->CRH |= GPIO_CRH_MODE9_1 | GPIO_CRH_CNF9_1; // PA9 推挽复用输出 USART1->BRR = 72000000 / 115200; // 625 USART1->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;如果用 HAL 库,本质也一样,只是寄存器被封装进了huart.Init.BaudRate = 115200。我建议初学者调不通时直接读一读USART1->BRR,看看硬件实际收到的分频值是不是你以为的那个数,这比盲改代码靠谱得多。
4.3 USART2/3 在 36MHz 下的非整数分频:误差只有 0.16%
容易踩坑的地方来了。STM32F103 的 USART2 和 USART3 挂在 APB1 总线上,APB1 预分频通常是 2,所以外设时钟是 36MHz,而不是 72MHz。代入公式:
BRR = 36,000,000 / 115200 = 312.5312.5 不是整数。硬件寄存器只能写整数,那么只能选 312 或 313。实际波特率分别为:
36,000,000 / 312 = 115,384.6 bps,误差 +0.16% 36,000,000 / 313 = 115,015.9 bps,误差 -0.16%两个方向都在 ±2% 的容错范围内,实际通信不会有问题。但如果你把代码从 USART1 原样拷到 USART2,却忘了确认 APB1 时钟是不是 36MHz,那才是问题根源。曾经有客户板子把 APB1 预分频配成了 4,APB1 变成 18MHz,BRR 只能写 156,实际波特率就到 115384?不对,18,000,000/156 = 115,384,误差看着不大。但如果时钟配置更偏一点,误差立刻超过 2%,乱码就来了。
这里再补充一个实操小技巧:STM32 的 BRR 支持 16 位整数加 4 位小数的编码,F1 系列的 USART 在 OVER8 模式下还能用小数分频进一步压低误差。但 115200 这种档位其实不需要折腾到那种程度,0.16% 的误差完全够用。真正要盯的是系统主时钟配置,而不是寄存器里那一点点小数取舍。
4.4 实测验证:用逻辑分析仪读位宽
配置完成后不要只信“串口通了”这个结论。拿逻辑分析仪抓一发数据,测一下单个 bit 的时间,理论值是:
1 / 115200 ≈ 8.68 微秒一组 0x55(二进制 01010101)的数据帧里,起始位之后会看到连续八次电平翻转,每个 bit 宽度几乎一致。测出来的宽度偏差如果在 ±3% 以内,基本没有问题;如果明显偏大或偏小,重点查外设时钟源和分频配置。
5. 嵌入式 Linux 下把 115200 节点配置到位
5.1 bootloader 与内核 console 参数的配合
从 MCU 转到嵌入式 Linux,115200 依然是默认中的默认,但它出现在三个地方,必须保持一致:bootloader 环境变量、内核 cmdline、应用程序的 termios。
U-Boot 里常见这样设置:
setenv baudrate 115200 setenv bootargs console=ttymxc0,115200n8 saveenv注意这里的n8意思是无校验、8 数据位,配合固定的 1 停止位,就是 8N1。如果 bootloader 里写 115200,内核 cmdline 却写成 115200n8,或者两边时钟基准不同,控制台输出就会乱码。有一个经验:出现“输命令正常,但 boot 日志后半段乱码”这类现象,先怀疑内核 console 参数和 bootloader 波特率不一致。
5.2 stty 快速验证串口参数
Linux 下临时验证一个串口节点,我最常用stty直接看和改参数:
stty -F /dev/ttyS0 115200 cs8 -cstopb -parenb raw拆开解释一下:115200 是波特率,cs8 是 8 个数据位,-cstopb 是 1 个停止位,-parenb 是无校验,raw 是关闭 termios 的线路加工(比如 echo、ICANON 这类把数据改得面目全非的选项)。很多 Linux 串口测试“发出去正常、收回来多个回车换行”的怪现象,就是没进 raw 模式导致的。
然后可以直接用 cat 和 echo 做双向通断测试:
cat /dev/ttyS0 & echo "hello" > /dev/ttyS0如果对端设备回显自定义报文,会显示在终端里。这种方式足够快速验证通断,但正式联调建议用 minicom、picocom 这类工具,逻辑更完整。
5.3 设备树时钟配置
设备树里 UART 节点的时钟一般由时钟框架自动管理,普通情况下不需要手动指定clock-frequency。如果自己有特殊晶振,比如外部挂了一个非标准频率的有源晶振,才需要在设备树里显式配置:
&uart3 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_uart3>; };这种场景下,内核用的时钟源如果和 bootloader 不一致,也会导致波特率偏差。我一直建议的做法是:先以内核日志或设备树里实际的时钟频率为准,反推 BRR 分频,再和示波器实测值对比,不要凭经验拍脑袋。
6. 乱码与误码的实战排查链路
6.1 先看症状,再动手查源头
串口乱码这个问题,最忌讳上来就换线、换工具瞎试。按症状分类会快很多:
| 现象 | 最可能的根因 |
|---|---|
| 全是乱码,完全不可读 | 波特率严重不匹配或电平不匹配 |
| 首几个字节正常,后面全乱 | 缓冲区溢出、流控没开、对端发送节奏不对 |
| 偶尔丢字节,丢的没有规律 | 时钟抖动、线路干扰、线缆过长 |
| 字符对但末尾多个 0x0D/0x0A | termios 没配 raw,或对端把换行展开了 |
| 反复重启或 CRC 校验失败 | 校验位/停止位配置不一致 |
6.2 用示波器测出真实波特率
定位乱码最直接的一招:示波器探头夹在 TX 引脚上,抓一个数据帧。测出两个相邻 bit 边沿之间的时间,就能反推真实波特率。
真实波特率 = 1 / 实测单 bit 宽度比如你在 MCU 端测到单 bit 宽度是 9.5 微秒,反推只有约 105kHz 的等效速率,和 115200 差太多,那问题一定出在时钟分频上。不要先怀疑线缆,先查 MCU 的系统时钟树配置、PLL 倍频、APB 预分频。这个排查顺序能省掉大量无效劳动。
6.3 2% 误差容限的实际含义
UART 的误码容错边界,行业里常说 ±2%。原理很简单:接收端每个 bit 在中间位置采样,理想情况下有 50% 的位宽余量,两端的时序误差只要不累计到半个位宽,就能正确解码。115200 的单 bit 是 8.68 微秒,一半就是约 4.34 微秒。算上起始位对齐误差和传输噪声,工程上留 2% 已经很合理。
实际项目中,如果两边时钟误差正好在 2% 边缘,短报文测试没问题,长报文一上来就断,就是因为误差在帧内持续累积,最后导致停止位采样越界。这是“短包全好、长包全废”的典型成因。
6.4 自带收发测试的小工具思路
我习惯在固件里放一个串口回环自测:MCU 收到什么就原样发回什么,上位机发一串已知数据,比对回环结果。全双工回环通过,说明 MCU 侧收发都正常;回环失败但单发单收正常,大概率是上位机侧的驱动、缓冲或流控设置问题。这个思路比单纯在串口助手里看乱码要更接近根因,尤其适合现场联调。
7. 何时该放弃 115200:升级速率前的工程评估
7.1 115200 的推力上限
前面算过:115200bps、8N1 下有效数据率约 11.25KiB/s。如果产品需求是每秒通过串口传输 20KB 传感器数据,那 115200 直接出局。这时候不是优化协议能解决的,物理层带宽就摆在那。正确做法是直接评估更高波特率。
常见升级阶梯是 230400、460800、921600,每一档都是上一档的 2 倍,时钟分频关系在 STM32 这类芯片里依然能保持整数关系。但要注意,波特率越高,线缆长度、电平标准和终端电阻的影响变得越敏感。
7.2 高波特率付出的代价不能只看数字
把 115200 升到 460800,理论带宽翻了 4 倍,实际工程代价也翻了不止 4 倍:
- 线缆长度:115200 在普通杜邦线 20cm 内很容易稳定,升到 460800 后同样的线可能开始丢字节;建议用双绞线或差分总线方案。
- 电平标准:TTL 电平的抗干扰能力比 RS232 差,长距离传输应优先考虑 RS485 差分信号。
- 收发器选型:MAX3232 之类常见 RS232 收发器在 115200 下很从容,到 921600 时要仔细看数据手册的压摆率参数。
- 采样率与时钟误差:波特率越高,同样的时钟误差导致的位宽偏差被放大得越明显。如果系统用内部 RC 振荡器,高速率下出错的概率会比晶振大得多,建议高速场景一律外部晶振。
7.3 一个比较实用的选型流程
我评估通信速率时基本按这个流程走:
- 列出最大单帧数据量、每秒传输帧数、协议开销比例。
- 用“数据字节数 × 10 / 波特率”算出每帧需要的时间,再乘帧数算总带宽占用。
- 总带宽占用不超过物理有效带宽的 60% 时,方案才算有余量。注意半双工系统还要额外扣掉方向切换时间。
- 如果 115200 余量不足,再看 230400 或 460800,优先选晶振和分频都能整除的档位。
比如一个项目每秒要传 50 帧,每帧 100 字节有效载荷,8N1 下初始估算 50 × 100 / 11520 ≈ 434ms,占一秒的 43%,115200 还有空间。但如果加了协议封装变成每帧 120 字节,总耗时变成 521ms,风险开始上升,这时候 230400 就成了更稳妥的选择。
在 UART 这个世界里,选波特率不是越大越好,而是够用且稳定。115200 之所以能当这么久的老黄牛,正是因为它处在速率、兼容性、线缆要求三者最均衡的位置上。搞清楚它背后的字节率和分频原理,你不仅能把这个默认值用得明明白白,真到了要提速或者排查乱码的时候,也有一条清晰的思路可走,而不是靠换线换工具碰运气。