主机波特率不固定、换一台设备就收乱码,多半是自动波特率没配对。按 PIC18F2480 数据手册拆解 0x55 同步字测量、ABDOVF 溢出、三档 BRG 误差与 FERR/OERR 恢复,给出七步寄存器配置与排查表。
一台带 LIN 从机接口的采集板,主站波特率由现场拨码决定,出厂时并不知道现场接的是哪一档。调试时最常见的画面是串口助手满屏乱码,把 SPBRG 按 9600 写死也照样错。结论先给:PIC18F2480 的 EUSART 自带自动波特率检测(ABD),只要主机先发一个 0x55 同步字,模块会用 RX 信号反过来给 BRG 计时,把结果留在 SPBRGH:SPBRG 里;前提是异步模式、WUE=0、计数时钟档位选对,而且 ABD 期间绝对不能碰发送。
本文按 Microchip PIC18F2480/2580/4480/4580 数据手册(DS39637C)第 18 章整理,寄存器行为、计数器时钟与误差表均取自该章。
正常收发时,BRG 产生时钟去采样 RX;进入 ABD 后方向反过来,内部 BRG 变成一个计数器,用 RX 上的边沿去量一个位有多长。手册写得很清楚:置位 ABDEN 后状态机先清 BRG,然后等起始位,必须收到一个 0x55(ASCII 的“U”,也正是 LIN 总线的同步字符)才能算出位速率。
测量跨过一个低电平位和一个高电平位,目的是抵消输入信号的不对称;起始位之后,在 RX 第一个上升沿开始计数,数到第 8 位或第 5 个上升沿(对应停止位)停止,累计值就留在 SPBRGH:SPBRG 里,同时 ABDEN 由硬件自动清零。此刻 RCIF 会置位,但 RCREG 里是哑数据,必须读掉以清除中断,不能当正常字节用。
| BRG16 | BRGH | ABD 期间 BRG 计数时钟 | 20 MHz 下的计数频率 |
|---|---|---|---|
| 0 | 0 | FOSC/512 | 39.0625 kHz |
| 0 | 1 | FOSC/128 | 156.25 kHz |
| 1 | 0 | FOSC/128 | 156.25 kHz |
| 1 | 1 | FOSC/32 | 625 kHz |
这张表决定了自动波特率能测多慢的信号。无论 BRG16 怎么设,ABD 期间 SPBRGH 与 SPBRG 都拼成一个 16 位计数器,计满 65536 个计数就溢出;8 位模式下要回头检查 SPBRGH 是否为 00h,确认没有进位。以 20 MHz、BRGH=1、BRG16=0 为例,计数时钟 156.25 kHz,16 位量程约 419 ms,测 1200 bit/s 的同步字(8 个位约 6.7 ms)绰绰有余。
ABD 只负责把“实测周期”写进寄存器,实际收发仍按 BRGH/BRG16 选的公式走。异步模式的公式只有三条:8 位慢速 FOSC/[64(n+1)]、8 位高速 FOSC/[16(n+1)]、16 位高速 FOSC/[4(n+1)];16 位慢速与 8 位高速同为 16 分频。n 就是 SPBRGH:SPBRG 的值。
| 波特率 | 8 位慢速:SPBRG / 误差 | 8 位高速:SPBRG / 误差 | 16 位高速:SPBRG / 误差 |
|---|---|---|---|
| 9600 | 31 / +1.73% | 129 / +0.16% | 520 / -0.03% |
| 19200 | 15 / +1.73% | 64 / +0.16% | 259 / +0.16% |
| 57600 | 4 / +8.51% | 21 / -1.36% | 86 / -0.22% |
| 115200 | 2 / -9.58% | 10 / -1.36% | 42 / +0.94% |
同一颗 20 MHz 晶振,8 位慢速档跑 115200 的误差是 -9.58%,接收端按 16 倍过采样、每三位取多数,累计偏移早就跨过采样窗口,误帧是必然的;换成 8 位高速档降到 -1.36%,16 位高速档只剩 +0.94%。所以 115200 不是“能不能跑”的问题,而是必须先切 BRGH 或 BRG16。反过来,如果用 ABD 测量 1200 bit/s 这种低速信号,就要用 8 位慢速档把计数时钟压到 39 kHz,否则 16 位计数器根本量不到一个完整位。
第一,ABD 只在异步模式、WUE=0 时有效,同步模式和唤醒监听状态下都不工作。第二,ABD 期间 BRG 时钟是被“反向借用”的,发送器不能用,ABDEN 置位时写 TXREG 会导致不可预期的行为,也不能在发送过程中把 ABDEN 置起来。第三,ABD 一直在跑,不会因为溢出而停:BRG 从 FFFFh 翻到 0000h 时 ABDOVF 置位,ABDEN 保持,测量继续,直到软件把 ABDOVF 清掉。
第三条最容易被忽略。现场把同步字发成 0xAA、或者线路噪声凑不出 5 个上升沿,计数器就会一直数到溢出,ABDEN 永远不清,看上去像“自动波特率卡死”。正确做法是先清 ABDOVF,再等 RCIF;超时后重发 0x55,而不是反复读 SPBRG。
/* PIC18F2480,FOSC=20 MHz,异步 8 位,自动波特率检测 */
void eusart_abd_start(uint8_t brgh, uint8_t brg16)
{
TXSTA = 0x00; /* SYNC=0:异步;TX9=0:8 位 */
if (brgh) TXSTA |= 0x04; /* BRGH=1:计数时钟 FOSC/128 */
BAUDCON = 0x00;
if (brg16) BAUDCON |= 0x08; /* BRG16=1:计数时钟 FOSC/32 */
SPBRGH = 0x00; /* ABD 会重新计时,先清零 */
SPBRG = 0x00;
RCSTA = 0x90; /* SPEN=1,CREN=1 */
BAUDCONbits.ABDOVF = 0; /* 清溢出标志 */
BAUDCONbits.ABDEN = 1; /* 开始测量,等 0x55 */
}
/* 返回 0 未完成,1 成功,2 溢出 */
uint8_t eusart_abd_poll(uint16_t *n)
{
if (!PIR1bits.RCIF) return 0;
(void)RCREG; /* 哑数据:读出即清 RCIF */
if (BAUDCONbits.ABDOVF) {
BAUDCONbits.ABDOVF = 0;
return 2; /* 计数时钟太慢,换档重测 */
}
*n = ((uint16_t)SPBRGH << 8) | SPBRG;
return 1;
}
代码里没有直接写 SPBRG 的“最终值”,因为 ABD 会把实测结果放进去;真正要恢复收发时,只需确认 BRGH/BRG16 与测量时一致,n 不用再算。
自动波特率对了,链路仍可能坏在接收侧。RCSTA 低三位就是三条线索:FERR 表示停止位没采到,通常是波特率偏差或地线干扰;OERR 表示接收溢出,RCREG 与 RSR 组成两级 FIFO,第三个字节到达时前两个还没读走就会置位;RX9D 是第 9 位,做地址/数据位或软件奇偶校验时由固件自己算。手册给的清除路径不一样:FERR 在读 RCREG、收到下一个有效字节后更新,OERR 必须清 CREN 再重新置位,而读 RCREG 本身只能清 RCIF。
| 标志位 | 触发条件 | 清除方式 | 现场处理 |
|---|---|---|---|
| FERR | 停止位采样不到,帧格式不符 | 读 RCREG 并收到下一有效字节 | 核算 BRG 误差,检查共地与屏蔽 |
| OERR | 两级 FIFO 已满仍收到新字节 | 清 CREN 后再置 CREN | 缩短中断响应,或提高波特率前先降负载 |
| RCIF | 接收完成,缓冲有数据 | 读 RCREG | 中断里先读 RCSTA 再读 RCREG |
| ABDOVF | ABD 期间 16 位计数器溢出 | 软件清 ABDOVF | 换更慢的计数时钟,重发 0x55 |
做 RS-485 多点总线时还要打开 9 位地址检测:置 RX9 与 ADDEN 后,只有第 9 位(RSR<8>)为 1 的地址字节才会进缓冲并产生中断;确认自己被寻址后,软件清 ADDEN,后续数据字节才全部放行。地址字节当数据处理,是半双工总线“抢答”的常见原因。
发送 Break 靠 SENDB:置 TXEN 与 SENDB 后往 TXREG 写一个哑字节(值被忽略),硬件发出起始位 + 12 个 0 + 停止位,SENDB 在停止位后自动清零;紧接着写 0x55,就是标准的 LIN 帧头。接收侧有两条路:一条是把波特率按典型值的 9/13 配置,让停止位落在正确的采样点;另一条是置 WUE,让模块在休眠中只监听 RX 的下降沿,被唤醒后产生 RCIF,读 RCREG 清中断,WUE 在随后的上升沿自动清零。
WUE 有个前提:唤醒字符必须是全 0,RS-232 用 00h、LIN 用 000h(12 位),否则字符中途的状态变化会被当成结束标志。另外,进休眠前要先确认 RCIDL=1,表示当前没有接收正在进行,再把 WUE 置起来。
| 现象 | 根因 | 改法 |
|---|---|---|
| ABDEN 一直不清零 | 主机发的不是 0x55,或边沿不足 5 个 | 用示波器确认同步字,超时重发 |
| SPBRG 读回 0 或异常大 | 计数时钟档位不对,或已溢出 | 按表 18-4 换 BRGH/BRG16,清 ABDOVF 重测 |
| 测量成功但数据仍乱码 | 恢复收发时改了 BRGH/BRG16 | 测量与收发保持同一档,或按 n 重算 |
| 发送卡住、TXREG 写不进 | ABDEN 置位期间写了 TXREG | 等测量结束、TRMT=1 再发 |
| 偶发帧错 FERR | 晶振偏差叠加,115200 用了慢速档 | 切 BRGH=1 或 BRG16=1,误差压到 1% 内 |
| 接收一段时间后全丢 | OERR 置位没清 CREN | 中断里判 OERR,清 CREN 再置位 |
一句话总结:自动波特率不是“置个 ABDEN 就完事”,它借的是 BRG 的计数器,计数时钟档位决定能测多慢、误差档位决定能跑多快;先算误差、再选档位、最后按七步恢复收发,0x55 同步字才不会白发。
还想接着看:PIC18F2480 ECAN 验收滤波与发送缓冲、I²C、SPI、UART 总线怎么选、Exusart 模块的 MODBUS 串口实现。
本文基于 Microchip PIC18F2480/2580/4480/4580 数据手册(DS39637C)技术资料整理优化。
沧州艾诺威电子 — 国家高新技术企业,20+项国家专利
嵌入式系统开发 · 物联网方案 · AI智能硬件 · 一站式交付
电话:13930711029 | 邮箱:tech@czinv.com | 24小时内响应