3、CAN通信基础:从物理层到错误处理

各位同学,大家好。今天我们聊CAN通信基础。

说实话,CAN总线这东西,你搞UDS诊断,绕不开它。我当年刚入行时,以为诊断就是发几个报文,结果第一天上台架,总线直接挂死,查了三天才发现是终端电阻没焊好。嗯,从那以后,我老老实实把CAN协议啃了一遍。

3.1 CAN总线物理层与数据链路层

先说说物理层。CAN总线用两根线——CAN_H和CAN_L,差分传输。什么意思?就是信号不是对地量,而是看两根线的电压差。

我习惯这么记:显性电平(Dominant)对应逻辑0,隐性电平(Recessive)对应逻辑1。显性时,CAN_H比CAN_L高约2V;隐性时,两根线电压几乎相等,大概2.5V。

关键参数:

  • 总线终端电阻:120Ω,两端各一个。少了反射,多了信号衰减。
  • 最大通信距离:1Mbps时约40米,速率越低距离越远。
  • 节点数:理论上110个,实际我建议别超过30个,否则负载太重。

数据链路层呢?说白了,就是CAN控制器干的活。它负责组帧、仲裁、校验、应答。你写应用层代码时,这些基本不用操心,但理解它有助于你排查问题。

我记得有一次,客户说报文收不到。我一看,波特率设成了500k,但实际总线是250k。物理层没问题,链路层对不上,白搭。

3.2 CAN报文帧结构

CAN报文有四种帧:数据帧、远程帧、错误帧、过载帧。我们最常用的是数据帧。

数据帧长这样(标准帧,11位ID):

| SOF | 仲裁场(11位ID+RTR) | 控制场(IDE+r0+DLC) | 数据场(0-8字节) | CRC场 | ACK场 | EOF |
|  1  |       12位          |       6位          |    0-64位       |  16位  |  2位  |  7位 |

你想想看,一帧最多8字节数据。为什么是8?因为CAN总线设计时,考虑到实时性和错误率,短帧更可靠。我做过测试,8字节的误码率远低于长帧。

扩展帧(29位ID)呢?结构类似,只是仲裁场多了18位。我个人习惯,能用标准帧就别用扩展帧,仲裁更快,兼容性更好。

避坑指南:我曾经在项目中,把DLC设成了8,但数据场只填了2个字节。结果接收方一直等剩下的6个字节,总线直接卡死。记住:DLC必须与实际数据长度一致。

3.3 CAN ID与仲裁机制

CAN总线是广播式的,所有节点都能收到报文。那怎么决定谁先发?靠ID仲裁。

仲裁机制很简单:ID越小,优先级越高。显性位(0)会覆盖隐性位(1)。所以,如果两个节点同时发,ID小的那个会赢得仲裁,ID大的自动退出发送,转为接收。

我举个例子:

节点A:ID = 0x100 (二进制: 001 0000 0000)
节点B:ID = 0x200 (二进制: 010 0000 0000)

同时发送时,第2位A是0,B是1。A的显性位覆盖了B的隐性位,A胜出。

为什么会这样?因为CAN总线是“线与”逻辑。只要有一个节点拉低总线,总线就是低电平。所以ID小的节点,能更快地把总线拉低。

这里有个坑:UDS诊断通常用功能寻址(0x7DF)和物理寻址(0x7E0-0x7E7)。功能寻址ID是0x7DF,物理寻址ID是0x7E0。你想想看,哪个优先级高?

0x7DF = 011 1111 1101,0x7E0 = 011 1111 1110。0x7DF更小,所以功能寻址优先级更高。但实际项目中,我建议物理寻址用更小的ID,比如0x700-0x70F,避免被功能寻址抢了总线。

注意:仲裁机制只解决“谁先发”,不解决“谁该发”。如果两个节点同时发相同ID的报文,那就会产生错误帧。我曾经见过一个项目,两个ECU配置了相同的发送ID,结果总线上一片混乱。

3.4 CAN总线错误处理

CAN总线有五种错误:位错误、填充错误、CRC错误、形式错误、应答错误。每个节点都有发送错误计数(TEC)和接收错误计数(REC)。

错误状态有三种:

状态 条件 行为
主动错误 TEC < 128 且 REC < 128 发送主动错误帧(6个显性位)
被动错误 TEC > 127 或 REC > 127 发送被动错误帧(6个隐性位),且发送前等待8个隐性位
总线关闭 TEC > 255 完全退出总线,不再发送任何报文

我遇到过最头疼的问题:一个节点频繁发送错误帧,导致整个总线吞吐量下降。查了半天,发现是它的CAN控制器晶振不准,位时序漂移了。换了个晶振,问题解决。

还有一次,总线偶尔出现CRC错误。我以为是干扰,加了屏蔽线没用。后来发现是某个连接器端子松动,接触不良。嗯,硬件问题往往比软件更难查。

个人经验:调试CAN总线时,我习惯先看错误计数器。如果REC持续增长,说明总线有干扰或位时序问题。如果TEC增长,说明本节点发送有问题。用CANalyzer或PCAN抓一下错误帧,基本能定位。

最后说一句:CAN总线设计时,一定要留好错误处理逻辑。比如,检测到总线关闭后,可以尝试延迟复位,而不是立即重连。我曾经见过一个ECU,总线关闭后每秒复位一次,结果整个网络都被它拖垮了。

好了,CAN通信基础就聊到这。下一章我们讲CANdb和DBC文件,那是UDS诊断的“字典”,没有它,报文就是一堆乱码。