CAN 节点该收的收不到、发送顺序反了、偶发 bus-off,多半是滤波器和缓冲区的账没算清。本文按 PIC18F2480 数据手册拆解 Mode 0 的 6 只滤波器与 2 个掩码、3 个发送缓冲、位定时与错误状态,给出七步配置与排查表。
一条 250 kbit/s 的 CAN 总线,主控用 PIC18F2480 做网关:只收 0x180、0x200、0x300 三组报文,发 0x080 心跳。调试时遇到三个问题:广播帧把接收缓冲占满,该收的 0x200 反而丢了;两条报文同时排队,先发出去的却是优先级低的那条;跑一晚偶尔进 bus-off。结论先给:先算清 Mode 0 的资源账——3 个发送缓冲、2 个接收缓冲、6 只验收滤波器、2 个掩码,再按“模式、位定时、滤波器、缓冲、发送、错误、验证”七步配,问题基本都能定位。
本文按 Microchip PIC18F2480/2580/4480/4580 数据手册(DS39637C)第 23 章整理,所有资源数量与寄存器行为均取自该章。
ECAN 模块有三套功能模式,由 ECANCON 的 MDSEL<1:0> 选择,而且只能在配置模式下改:00 是传统模式(Mode 0),01 是增强传统模式(Mode 1),10 是增强 FIFO 模式(Mode 2)。Mode 0 与 PIC18CXX8、PIC18FXX8 的 CAN 模块完全兼容,默认复位后就在这个模式。
| 资源 | Mode 0 | Mode 1 | Mode 2 |
|---|---|---|---|
| 专用发送缓冲 | TXB0~TXB2,3 个 | 3 个 + B0~B5 可配 | 3 个 + B0~B5 可配 |
| 专用接收缓冲 | RXB0、RXB1,2 个 | 2 个 + B0~B5 可配 | 2 个 + B0~B5 组成 FIFO |
| 验收滤波器 | 6 只(RXF0~RXF5) | 16 只(RXF0~RXF15) | 16 只(RXF0~RXF15) |
| 验收掩码 | 2 个(RXM0、RXM1) | 2 个,RXF15 可当第 3 个 | 2 个,RXF15 可当第 3 个 |
| 接收 FIFO | 无 | 无 | 2~8 级可配 |
| 自动 RTR 应答 | 无 | B0~B5 支持 | B0~B5 支持 |
只做“三组报文 + 一条心跳”的网关,Mode 0 的 6 只滤波器和 3 个发送缓冲足够;一旦要接 DeviceNet 这类带数据字节过滤的协议,或者需要 8 级接收 FIFO 扛突发,才切到 Mode 1/2。注意模式切换只能在配置模式下写,运行中改 MDSEL 不会生效。
掩码决定标识符的哪几位参与比较:掩码位为 1,滤波器对应位必须与报文一致;掩码位为 0,这一位直接放行,滤波器写什么都无所谓。手册的滤波/掩码真值表就是这个逻辑。
Mode 0 的分配是固定的:RXF0、RXF1 配 RXM0 进 RXB0;RXF2~RXF5 配 RXM1 进 RXB1。RXB0 的滤波器少、匹配更严格,所以它的优先级更高;如果 RXB0 已满又收到新报文,把 RXB0CON 的 RXB0DBEN 置 1,新报文会溢出到 RXB1,不再按 RXB1 的滤波条件判断。
| 接收缓冲 | 可用滤波器 | 掩码 | 典型用途 |
|---|---|---|---|
| RXB0(高优先级) | RXF0、RXF1 | RXM0 | 只放实时性最高的 1~2 组报文 |
| RXB1(低优先级) | RXF2~RXF5 | RXM1 | 放状态量、广播和非实时报文 |
| RXB0 溢出 | RXF0/RXF1(RXB0DBEN=1) | 沿用 RXB1 | 突发时借用 RXB1,避免丢帧 |
以只收 0x180~0x18F 为例:滤波器高字节 RXF0SIDH 写 SID10~SID3,即 0x180 右移 3 位得到 0x30;掩码要比较高 7 位、放行低 4 位,RXM0SIDH 写 0xFE,RXM0SIDL 的 SID2~SID0 三位写 0。要精确收 0x200,则掩码高字节写 0xFF、低字节的 SID2~SID0 三位写 1(0xE0),11 位全比较。
报文先进入消息装配缓冲 MAB,命中滤波器后才整块复制到 RXB0 或 RXB1。手册特别提示:复制的是整个 MAB,不管标准帧还是扩展帧、不管 DLC 是几,接收缓冲里所有寄存器都要当作已被改写。只读自己关心的两个数据字节、其余沿用上一帧,是典型的“数据串帧”来源。
报文进缓冲后 RXFUL 置 1,必须由软件清零;只要 RXFUL 还是 1,新报文就进不来。如果只清了 RXB0IF 没清 RXFUL,中断会再次置位。用 RXB0CON 的 FILHIT<3:0> 可以判断是哪只滤波器命中的,用 RXRTRRO 判断是不是远程帧,用 EXID 判断标准帧还是扩展帧。
RXM<1:0> 是另一道闸门:00 按滤波器收,01 只收标准帧,10 只收扩展帧,11 连错误帧都收。选 01 或 10 时,滤波器的 EXIDEN 必须与 RXM 对应,否则这只滤波器直接失效。11 只适合台架调试,真实总线一定有错误帧,全收会把缓冲冲垮。
TXB0~TXB2 每个占 14 字节,包含控制寄存器、四只标识符寄存器、DLC 和 8 个数据字节。TXREQ 置 1 只是把缓冲标记为“待发”,真正发送要等总线空闲;从置位到发完,缓冲寄存器是只读的,写进去会被忽略。发送成功清 TXREQ、置 TXBIF;丢仲裁置 TXLARB;总线错误置 TXERR。
优先级由 TXPRI<1:0> 给四档,与 CAN 标识符仲裁无关。两条报文 TXPRI 相同时,缓冲号大的先发,也就是 TXB2 先于 TXB1、TXB1 先于 TXB0。很多人以为“先写先发”,结果心跳被状态帧压在后面,就是漏了这一条。
| TXBnCON 位 | 含义 | 使用要点 |
|---|---|---|
| TXREQ | 发送请求 | 置 1 排队,成功后硬件清 0 |
| TXPRI1:TXPRI0 | 发送优先级 0~3 | 3 最高;同级时缓冲号大的先发 |
| TXBIF | 发送完成标志 | 置 TXREQ 时自动清 |
| TXABT / TXLARB / TXERR | 中止 / 丢仲裁 / 总线错误 | 三者都在置 TXREQ 时自动清 |
| RTREN | 自动 RTR 应答 | 仅 Mode 1/2 的 B0~B5 支持 |
中止发送有两个入口:清对应缓冲的 TXREQ,或把 CANCON 的 ABAT 置 1 一次清掉所有待发。若报文还没开始发,或刚开始就丢仲裁/出错,中止会被执行并置 TXABT;若已经开始且顺利发完,TXABT 不会置位,因为报文已经成功发出。中止请求一旦发出不能撤销,只能等硬件或上电复位清除。
一个 TQ 由 BRGCON1 的 BRP<5:0> 决定:TQ = 2 × (BRP+1) / FOSC。一个位时间由同步段(固定 1 TQ)、传播段 PRSEG、相位段 1 SEG1PH、相位段 2 SEG2PH 组成,采样点落在相位段 1 结束处。SJW 可选 1~4 TQ,用来吸收节点间的时钟误差;SEG2PHTS=1 时相位段 2 由 BRGCON3 自由设定,=0 时取 SEG1PH 与信息处理时间 IPT(本系列固定 2 TQ)中的较大值。
| 波特率 | FOSC | BRP | PRSEG | SEG1PH | SEG2PH | 采样点 |
|---|---|---|---|---|---|---|
| 125 kbit/s | 20 MHz | 4 | 5 TQ | 6 TQ | 4 TQ | 75%(16 TQ) |
| 250 kbit/s | 20 MHz | 4 | 2 TQ | 3 TQ | 2 TQ | 75%(8 TQ) |
| 500 kbit/s | 40 MHz | 4 | 2 TQ | 3 TQ | 2 TQ | 75%(8 TQ) |
以 20 MHz、125 kbit/s 为例:BRP=4 时 TQ=0.5 µs,16 个 TQ 正好 8 µs;采样点 (1+5+6)/16=75%。同样的 TQ 下把总段数压到 8 段,就是 250 kbit/s。注意陶瓷谐振器在 125 kbit/s 以上已接近极限,手册给的节点间时钟偏差上限是 1.7%,全速段建议用石英晶振。
模块内部有接收错误计数器 REC 和发送错误计数器 TEC,COMSTAT 可以读到当前状态。两个计数器都低于 96 时正常;任一达到 96,EWARN 置位,这是最早的健康预警;任一达到 128,节点进入 error-passive,只能发隐性错误帧;TEC 达到 256,节点进入 bus-off,收发都停止。
| 计数条件 | 状态 | 现场含义 |
|---|---|---|
| REC、TEC < 96 | error-active | 正常收发,可发显性错误帧 |
| 任一 ≥ 96 | EWARN 置位 | 误码在累积,查终端电阻与屏蔽 |
| 任一 ≥ 128 | error-passive | 只能发隐性错误帧,网络容错下降 |
| TEC ≥ 256 | bus-off | 退出总线,需 128 次 11 位隐性序列恢复 |
bus-off 恢复不需要 MCU 干预:总线空闲满 128 × 11 个位时间,模块会自动回到 error-active。但恢复瞬间不会补发丢失的报文,心跳这类周期帧要靠上层重新触发;如果总线上一直有错误源,节点会反复进出 bus-off,这时候该查的是线束和终端电阻,而不是加长恢复时间。
/* PIC18F2480,FOSC=20 MHz,125 kbit/s,Mode 0 */
void can_init_125k(void)
{
CANCON = 0x80; /* REQOP=100:请求配置模式 */
while ((CANSTAT & 0xE0) != 0x80); /* 等 OPMODE=100 */
ECANCON = 0x00; /* MDSEL=00:传统模式 */
BRGCON1 = 0x04; /* SJW=1 TQ,BRP=4 → TQ=0.5 µs */
BRGCON2 = 0xAC; /* SEG2PHTS=1,SEG1PH=6 TQ,PRSEG=5 TQ */
BRGCON3 = 0x03; /* SEG2PH=4 TQ → 共 16 TQ = 125 kbit/s */
TRISBbits.TRISB3 = 1; /* RB3/CANRX 输入;TX 由模块接管 */
RXF0SIDH = 0x30; /* 0x180:SID10~SID3 */
RXF0SIDL = 0x00; /* 标准帧,EXIDEN=0 */
RXM0SIDH = 0xFE; /* 比较高 7 位 */
RXM0SIDL = 0x00; /* 低 4 位不关心:收 0x180~0x18F */
RXB0CON = 0x00; /* RXM=00:按滤波器接收 */
RXB1CON = 0x00;
CANCON = 0x00; /* REQOP=000:正常模式 */
while (CANSTAT & 0xE0); /* 等 OPMODE=000 */
}
/* 发 0x080 心跳,最高优先级 */
void can_send_heartbeat(void)
{
TXB0SIDH = 0x10; /* 0x080 右移 3 位 */
TXB0SIDL = 0x00;
TXB0DLC = 0x02;
TXB0D0 = 0x55; TXB0D1 = 0xAA;
TXB0CON = 0x03; /* TXPRI=11,最高优先级 */
TXB0CONbits.TXREQ = 1; /* 置位后缓冲只读 */
}
| 现象 | 根因 | 改法 |
|---|---|---|
| 该收的报文收不到 | 滤波器高字节没按“右移 3 位”装,或 EXIDEN 与 RXM 不匹配 | 按 SID10~SID3 装 RXFnSIDH,标准帧把 EXIDEN 清 0 |
| 广播帧把缓冲占满 | 广播进了 RXB0,挤掉高实时报文 | 广播放 RXB1,或打开 RXB0DBEN 溢出借用 |
| 数据偶尔串上一帧 | MAB 整块覆盖,只读了部分字节 | 每次按 DLC 读满,读完清 RXFUL |
| 接收中断清了又来 | 只清 RXB0IF,没清 RXFUL | 先清 RXFUL,再清中断标志 |
| 发送顺序与预期相反 | TXPRI 相同,缓冲号大的先发 | 用 TXPRI 明确分级,别靠写入顺序 |
| 中止没生效 | 报文已开始发送且未丢仲裁/出错 | 清 TXREQ 后等 TXABT,中止请求不可撤销 |
| 跑一晚进 bus-off | TEC 累到 256,多为线束或终端电阻 | 查 120 Ω 终端与屏蔽,恢复需 128×11 位 |
一句话总结:ECAN 的配置顺序是“先模式、再位定时、后滤波器”,运行时的顺序是“先清 RXFUL、再清中断”。把 6 只滤波器、2 个掩码、3 个发送缓冲当成固定预算来分配,CAN 节点就不会在“该收的没收、该先发的没发”上来回打转。
还想接着看:STM32 bxCAN 滤波器组与收发配置、SJA1000 验收滤波与 CAN 网络 ID 规划、MCP2551 收发器斜率与终端匹配。
本文基于 Microchip PIC18F2480/2580/4480/4580 数据手册(DS39637C)技术资料整理优化。
沧州艾诺威电子 — 国家高新技术企业,20+项国家专利
嵌入式系统开发 · 物联网方案 · AI智能硬件 · 一站式交付
电话:13930711029 | 邮箱:tech@czinv.com | 24小时内响应