串口波特率误差计算
UART 是异步通信,收发双方没有共享时钟。主频除不出整数分频时波特率就有误差,超过约 2% 就会开始丢字节。
结果
0.1603%
误差可接受 门限是 ±2%。
误差很小,任何场合都足够
换个晶振能不能更准
误差的根源是主频除不出整数分频。换一颗晶振往往比调软件参数有效得多,下面是常用频率按误差从小到大排。
| 主频 | 误差 | 结论 |
|---|---|---|
| 11.059 MHz | 0% | 零误差 |
| 24 MHz | 0.1603% | 实际偏快 0.16% |
| 48 MHz | 0.1603% | 实际偏快 0.16% |
| 26 MHz | 0.7564% | 实际偏快 0.756% |
| 20 MHz | -1.3573% | 实际偏慢 1.357% |
| 40 MHz | -1.3573% | 实际偏慢 1.357% |
| 32 MHz | 2.1242% | 实际偏快 2.124% |
| 25 MHz | -3.1188% | 实际偏慢 3.119% |
| 16 MHz | -3.5494% | 实际偏慢 3.549% |
| 12 MHz | -6.994% | 实际偏慢 6.994% |
| 8 MHz | 8.5069% | 实际偏快 8.507% |
排在前面的是 11.0592 MHz 这类「波特率友好」的频率,它们本身就是常用波特率的整数倍。 代价是对定时和运算不方便,很多板子会挂两颗晶振各管一摊。
串口配置的常见坑
波特率误差要卡在 2% 以内。UART 是异步通信,收发双方没有共享时钟,靠各自的波特率对齐。一帧 10 位里采样点会累积偏移,误差超过约 2% 时最后一位就可能采错。9 位数据或连续长帧对误差更敏感,建议压到 1% 以内。这就是为什么串口常用 11.0592 MHz 这种「怪」晶振。
11.0592 MHz 是为了整除。11.0592 MHz = 9600 × 1152,用它做 51 单片的时钟,9600、19200、115200 这些波特率都能整除,误差为零。换成 12 MHz 时 9600 的误差约 0.16%、115200 超过 8% 根本不通。代价是这个频率对定时和运算不方便,所以很多板子会同时挂两颗晶振。
PSC 和 ARR 都要减一。定时器里预分频寄存器写的是「分频系数减一」,自动重载寄存器写的是「计数次数减一」,因为两者都是从 0 开始数。忘了减一会导致定时差一个 tick,在长定时里误差虽小,但在精确定时(比如做波特率或 PWM 频率)时会直接体现出来。
定时越长分辨率越粗。定时器的计数位宽固定(常见 16 位即 65536 个计数),定时越长就必须用越大的预分频,于是每个 tick 变长、可调整的档位变少。想要 10 秒定时又想要 1 μs 精度,在 16 位定时器上是做不到的,得用主频更高的定时器或级联。
过采样倍数写错会差一倍。多数 MCU 的 UART 是 16 倍过采样,但有些型号(如 STM32 的 OVER8 模式)可以改成 8 倍提高速度上限。这时分频系数要按 8 算而不是 16,照旧按 16 算出来的波特率会正好差一倍。配置前查一下芯片手册里的过采样设置。