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诊断的“字典”,没有它,报文就是一堆乱码。