为什么波特率误差会变成乱码
UART 没有时钟线,收发双方各自用本地时钟采样。接收端在起始位下降沿对齐, 然后每隔一个位时间采一次。如果发送方实际偏快 1%,到第 10 位时采样点已经偏了 0.1 个位宽, 还能落在中间;偏 5% 就是半个位宽,正好踩在边沿上——于是这一帧随缘。
所以误差要按整帧累积看,不是逐位看。常见做法是单向误差控制到 2% 以内, 115200 这种高速率还要更严。工具里的误差表就是拿来一眼扫的:全绿说明这个时钟配这组波特率没问题。
选 PSC 还是选 ARR
两个都能凑出同一个频率,但影响不同:PSC 决定计数一次多久,ARR 决定数多少次。 PWM 的占空比档位数等于 ARR + 1,所以要分辨率就把 ARR 拉大、PSC 压小。
反过来说,如果需要很长的定时(比如几秒一次的看门狗喂狗),ARR 已经到 65535 还不够, 就只能加 PSC。工具的反解会优先保证 ARR 尽量大,正是这个道理。
差一个数的地方
PSC=71 是除以 72,ARR=999 是数 1000 次再溢出,
CCR=250 在 ARR=999 时是 25%。寄存器里的值永远比「除数」小 1,
这是嵌入式里最常见的 off-by-one。
常见问题
为什么 8MHz 时钟配不出准确的 115200?
STM32 的波特率分频是 USARTDIV = PCLK / (16 × baud),小数部分只有 4 位,也就是只能表示 1/16 的倍数。8MHz 下算出来是 4.3403,最接近的是 4 + 5/16 = 4.3125,实际波特率 115942,误差 +0.64%。要让误差归零,PCLK 必须是 16 × 115200 的整数倍——比如 7.3728MHz、14.7456MHz,或者从 8MHz 倍频到 72MHz。
误差多少算可以接受?
UART 是异步的,收发双方各自用本地时钟采样,累积误差必须在半个位时间内完成一帧才不出错。工程上一般要求单向误差小于 2%~3%,留足余量;超过 5% 就很容易偶发乱码,尤其是长帧、多字节连发。表里那些 0.0x% 的都可以放心用。
定时器的 ARR 和 PSC 有什么区别?
PSC 是预分频,把定时器时钟先除一次,决定「计数一次」对应多长时间;ARR 是自动重装值,决定数多少次溢出。两者相乘才是总的分频比。选择上优先把 PSC 调小、ARR 调大——因为 PWM 占空比的分辨率由 ARR 决定,ARR 只有 100 的话占空比只能调 1% 一档。
72MHz、PSC=71、ARR=999 为什么正好是 1kHz?
因为 (71+1) × (999+1) = 72 × 1000 = 72000,72000000 / 72000 = 1000。注意两个寄存器都要 +1:PSC=0 表示不分频(除以 1),ARR=0 表示数 1 次就溢出。这一点写代码时最容易差一个数。
占空比为什么是 CCR / (ARR + 1) 而不是 CCR / ARR?
因为计数器从 0 数到 ARR,一共经过 ARR+1 个状态。CCR 表示「计数值小于 CCR 时输出有效」,所以有效状态正好是 CCR 个。ARR=999 时周期是 1000 个计数,CCR=250 就是 25%。想让 CCR 直接等于百分比,就把 ARR 设成 99 或 999。