第2章:CAN总线数据链路层:CAN帧结构(SOF、仲裁场、控制场、数据场、CRC、ACK、EOF)、显性/隐性位仲裁机制
各位好,欢迎来到第二讲。今天咱们聊聊CAN总线数据链路层的核心——帧结构和仲裁机制。说实话,我见过不少工程师,协议栈调了大半年,最后发现问题是帧结构理解有偏差。所以这一章,咱们把底子打扎实。
2.1 CAN帧的“骨架”:从SOF到EOF
一条完整的CAN数据帧,就像一列火车。车头是SOF,车厢是数据场,车尾是EOF。每一节都有它的使命。我习惯把帧结构拆成七个部分来记:
| 字段 | 位数(标准帧) | 作用 |
|---|---|---|
| SOF(帧起始) | 1 bit | 同步所有节点,标志总线从空闲进入忙碌 |
| 仲裁场 | 12 bits(ID + RTR) | 决定哪个节点获得总线控制权 |
| 控制场 | 6 bits | 包含IDE位、保留位、DLC(数据长度码) |
| 数据场 | 0~64 bits | 实际要传输的数据 |
| CRC场 | 16 bits(15位CRC + 1位定界符) | 校验数据完整性 |
| ACK场 | 2 bits | 接收节点确认收到正确帧 |
| EOF(帧结束) | 7 bits | 标志帧结束,总线恢复空闲 |
嗯,这里要注意:标准帧和扩展帧的区别主要在仲裁场。标准帧ID是11位,扩展帧是29位。我刚开始做J1939时,就因为在扩展帧的IDE位上栽过跟头——IDE位没置对,接收端死活不认。
2.2 仲裁场:谁先说话,谁就赢
仲裁机制是CAN总线最巧妙的设计之一。说白了,就是多个节点同时发数据时,ID小的那个抢到总线。
为什么会这样?因为CAN总线用“显性位”(逻辑0)和“隐性位”(逻辑1)来竞争。显性位会覆盖隐性位。你想想看,如果节点A发ID=0x100,节点B发ID=0x200,在仲裁场上,A的ID更小(二进制中0更多),所以A的显性位会把B的隐性位“压下去”。
关键点:仲裁不丢数据,不浪费带宽。输掉仲裁的节点自动转为接收模式,等总线空闲再重发。这是CAN总线实时性的基石。
我在项目中遇到过一个问题:两个ECU的ID设得太接近,导致仲裁时频繁冲突,总线负载飙升。后来我建议把ID间隔拉大,问题就解决了。所以,ID分配不是随便写的,得留出余量。
2.3 控制场与数据场:DLC别填错
控制场里最容易被忽略的是DLC(数据长度码)。DLC告诉接收方数据场有多少字节。J1939协议里,数据场固定是8字节,但DLC必须填8。如果你填了其他值,接收方可能解析出错。
我曾经调试过一个故障:发动机转速数据偶尔跳变。查了半天,发现是某个节点在发送时DLC填了4,但数据场实际发了8字节。接收方只读了前4字节,后4字节被当成垃圾数据。嗯,从那以后,我每次写CAN驱动都会加一句DLC校验。
2.4 CRC场:数据完整性的“守门员”
CRC场包含15位CRC校验码和1位定界符。CRC算法是固定的,硬件自动计算。但要注意:CRC只校验SOF到数据场的内容,不包括CRC本身和ACK/EOF。
如果CRC校验失败,接收节点会发错误帧。我见过一个案例:总线线缆过长,信号反射导致CRC频繁出错。解决办法是降低波特率或加终端电阻匹配。
避坑指南:CRC错误不一定是硬件问题。我曾经遇到过软件把数据场字节序搞反了,CRC校验自然过不了。所以,先查软件,再查硬件。
2.5 ACK场:接收节点的“回执”
ACK场只有2位:ACK Slot和ACK Delimiter。发送节点在ACK Slot发隐性位,如果接收节点正确收到帧,就把这个位拉成显性。如果没收到ACK,发送节点会重发。
我调试过一个现象:总线上的节点都能收到数据,但发送节点一直报ACK错误。后来发现是接收节点的ACK响应逻辑有bug——它只对特定ID的帧回ACK。所以,ACK错误不一定是总线断了,也可能是接收节点“选择性失聪”。
2.6 EOF:帧的“句号”
EOF是7个连续的隐性位。如果总线在EOF期间出现显性位,所有节点都会认为帧格式错误,发送错误帧。我建议你在调试时用示波器抓EOF波形,如果看到EOF中间有毛刺,大概率是总线干扰或节点时序问题。
2.7 显性/隐性位仲裁机制:一张图看懂
下面这张SVG图展示了两个节点同时发送时的仲裁过程。节点A的ID更小,所以它赢了仲裁,节点B自动转为接收。
2.8 实战经验:帧结构调试的“三板斧”
调试CAN帧结构问题,我一般按这个顺序来:
- 看波形:用示波器抓CAN_H和CAN_L的差分信号,确认SOF、EOF位置是否正确。
- 看ID:用CAN分析仪过滤特定ID,确认仲裁场没有位错误。
- 看CRC:如果数据偶尔出错,先查CRC校验是否通过。不通过就查硬件,通过就查软件。
小技巧:调试时可以在数据场里塞一个递增计数器。如果接收端发现计数器跳变,说明有帧丢失或重发。这个方法我用了十年,屡试不爽。
好了,这一章的内容就到这里。帧结构是CAN通信的“语法”,仲裁机制是“规则”。语法和规则都搞清楚了,后面调试协议栈才能游刃有余。