PIC18F2480 EUSART 自动波特率怎么校准:0x55 同步字、ABDOVF 溢出与三档 BRG 误差七步落地

主机波特率不固定、换一台设备就收乱码,多半是自动波特率没配对。按 PIC18F2480 数据手册拆解 0x55 同步字测量、ABDOVF 溢出、三档 BRG 误差与 FERR/OERR 恢复,给出七步寄存器配置与排查表。

2026-11-04
PIC18F2480EUSART自动波特率LIN总线串口通信误差校准

一台带 LIN 从机接口的采集板,主站波特率由现场拨码决定,出厂时并不知道现场接的是哪一档。调试时最常见的画面是串口助手满屏乱码,把 SPBRG 按 9600 写死也照样错。结论先给:PIC18F2480 的 EUSART 自带自动波特率检测(ABD),只要主机先发一个 0x55 同步字,模块会用 RX 信号反过来给 BRG 计时,把结果留在 SPBRGH:SPBRG 里;前提是异步模式、WUE=0、计数时钟档位选对,而且 ABD 期间绝对不能碰发送。

本文按 Microchip PIC18F2480/2580/4480/4580 数据手册(DS39637C)第 18 章整理,寄存器行为、计数器时钟与误差表均取自该章。

一、ABD 是“反着用”BRG:RX 给 BRG 计时

正常收发时,BRG 产生时钟去采样 RX;进入 ABD 后方向反过来,内部 BRG 变成一个计数器,用 RX 上的边沿去量一个位有多长。手册写得很清楚:置位 ABDEN 后状态机先清 BRG,然后等起始位,必须收到一个 0x55(ASCII 的“U”,也正是 LIN 总线的同步字符)才能算出位速率。

测量跨过一个低电平位和一个高电平位,目的是抵消输入信号的不对称;起始位之后,在 RX 第一个上升沿开始计数,数到第 8 位或第 5 个上升沿(对应停止位)停止,累计值就留在 SPBRGH:SPBRG 里,同时 ABDEN 由硬件自动清零。此刻 RCIF 会置位,但 RCREG 里是哑数据,必须读掉以清除中断,不能当正常字节用。

BRG16BRGHABD 期间 BRG 计数时钟20 MHz 下的计数频率
00FOSC/51239.0625 kHz
01FOSC/128156.25 kHz
10FOSC/128156.25 kHz
11FOSC/32625 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)绰绰有余。

二、三档 BRG 的误差账,先算再选

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 / 误差
960031 / +1.73%129 / +0.16%520 / -0.03%
1920015 / +1.73%64 / +0.16%259 / +0.16%
576004 / +8.51%21 / -1.36%86 / -0.22%
1152002 / -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 的三条硬约束

第一,ABD 只在异步模式、WUE=0 时有效,同步模式和唤醒监听状态下都不工作。第二,ABD 期间 BRG 时钟是被“反向借用”的,发送器不能用,ABDEN 置位时写 TXREG 会导致不可预期的行为,也不能在发送过程中把 ABDEN 置起来。第三,ABD 一直在跑,不会因为溢出而停:BRG 从 FFFFh 翻到 0000h 时 ABDOVF 置位,ABDEN 保持,测量继续,直到软件把 ABDOVF 清掉。

第三条最容易被忽略。现场把同步字发成 0xAA、或者线路噪声凑不出 5 个上升沿,计数器就会一直数到溢出,ABDEN 永远不清,看上去像“自动波特率卡死”。正确做法是先清 ABDOVF,再等 RCIF;超时后重发 0x55,而不是反复读 SPBRG。

四、七步落地:从清标志到恢复收发

  1. 先按“最慢可能波特率”选 BRGH/BRG16,让 16 位计数器的量程覆盖同步字长度;计数时钟越慢,能测的波特率越低。
  2. 清 SYNC 进异步模式,置 SPEN、CREN,确认 WUE=0;TRISC 的 RC7 保持输入,RC6 由模块接管输出。
  3. 清 ABDOVF,置 ABDEN,然后把总线让给主机,等它发 0x55。
  4. 第 5 个上升沿后 ABDEN 自动清零、RCIF 置位;读一次 RCREG 丢弃哑数据并清中断。
  5. 读 SPBRGH:SPBRG 得到实测 n;8 位模式检查 SPBRGH 是否为 0,非 0 说明已经溢出。
  6. 核对 ABDOVF:置位就换更慢的计数时钟重测,不要拿溢出值去收发。
  7. 按实测 n 恢复收发;主机每次改波特率前重新置 ABDEN,且必须等上一帧发完(TRMT=1)。
/* 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 不用再算。

五、收不到数据时,先看 FERR、OERR 与 RX9D

自动波特率对了,链路仍可能坏在接收侧。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
ABDOVFABD 期间 16 位计数器溢出软件清 ABDOVF换更慢的计数时钟,重发 0x55

做 RS-485 多点总线时还要打开 9 位地址检测:置 RX9 与 ADDEN 后,只有第 9 位(RSR<8>)为 1 的地址字节才会进缓冲并产生中断;确认自己被寻址后,软件清 ADDEN,后续数据字节才全部放行。地址字节当数据处理,是半双工总线“抢答”的常见原因。

六、Break 与唤醒:LIN 从机的另一半

发送 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小时内响应