2. CAN数据帧结构:从SOF到EOF,逐位拆解
各位同学,今天我们来聊聊CAN总线最核心的东西——数据帧结构。说实话,我刚入行那会儿,看着CAN帧的七段结构,感觉就像在看天书。但干这行十几年后,我越来越觉得,搞懂这七个字段,就等于拿到了CAN总线的钥匙。
CAN总线上的数据,不是一股脑儿扔出去的。它被包装成一个“帧”,就像寄快递要填快递单一样。这个帧有固定的格式,一共7个部分:SOF、仲裁场、控制场、数据场、CRC场、ACK场、EOF。咱们一个一个来拆。
2.1 SOF(帧起始)—— 一声号令
SOF全称Start of Frame,就1个显性位(逻辑0)。它干的事很简单:告诉总线上所有节点,“我要发数据了,大家注意!”
为什么是显性位?因为总线空闲时是隐性位(逻辑1)。SOF用显性位,就像在安静的教室里突然拍一下桌子——所有人都知道有动静了。
2.2 仲裁场 —— 谁说了算?
仲裁场是CAN总线最精彩的设计。它包含两部分:标识符(ID)和RTR位(远程帧请求位)。
标准帧(2.0A)的ID是11位,扩展帧(2.0B)是29位。ID越小,优先级越高。为什么?因为显性位(0)会覆盖隐性位(1)。
举个例子:节点A发ID=0x100,节点B发ID=0x200。在仲裁过程中,谁先出现1(隐性)而对方是0(显性),谁就退出。0x100的二进制是0001 0000 0000,0x200是0010 0000 0000。比较到第3位时,0x100是0(显性),0x200是1(隐性)——0x100胜出。
RTR位呢?如果是数据帧,RTR=0(显性);如果是远程帧,RTR=1(隐性)。远程帧是用来请求对方发数据的,实际项目中用得不多。我记得有一次调试一个电机控制器,对方一直发远程帧请求转速数据,我这边没配置好,结果总线上一堆远程帧在空转……排查了半天。
2.3 控制场 —— 数据有多长?
控制场共6位:IDE位(1位)、保留位r0(1位)、DLC(4位)。
IDE位:标识是标准帧(0)还是扩展帧(1)。保留位留给未来扩展,目前必须发显性位(0)。
DLC(Data Length Code):告诉接收方数据场有多少字节。取值范围0~8,对应0~8字节。注意,DLC是4位二进制,理论上能表示0~15,但CAN协议规定数据场最多8字节。为什么是8?早期设计时觉得8字节够用了,后来发现确实够用——汽车上大部分信号也就几个字节。
| DLC值 | 二进制 | 数据场长度 |
|---|---|---|
| 0 | 0000 | 0字节 |
| 1 | 0001 | 1字节 |
| 2 | 0010 | 2字节 |
| 3 | 0011 | 3字节 |
| 4 | 0100 | 4字节 |
| 5 | 0101 | 5字节 |
| 6 | 0110 | 6字节 |
| 7 | 0111 | 7字节 |
| 8 | 1000 | 8字节 |
2.4 数据场 —— 真正的内容
数据场就是你要传输的实际数据,0~8字节。怎么编排这些字节,由上层协议决定。比如J1939、CANopen、或者你们自己定义的私有协议。
在Wireshark里,数据场会以十六进制显示。比如:01 02 03 04 05 06 07 08。但你要知道,这些字节的排列顺序(大小端)取决于协议定义。有些协议用Motorola格式(大端),有些用Intel格式(小端)。
我个人习惯:拿到一个CAN报文,先看DLC,再看数据场。如果DLC=8但数据场全是0x00或0xFF,多半是空报文或者初始化值,别被误导了。
2.5 CRC场 —— 数据对不对?
CRC场包含两部分:CRC序列(15位)和CRC界定符(1位隐性位)。
CRC算法从SOF开始算,一直算到数据场结束。生成多项式是:x¹⁵ + x¹⁴ + x¹⁰ + x⁸ + x⁷ + x⁴ + x³ + 1。接收方收到后重新算一遍CRC,如果和发送方的不一致,就知道数据被破坏了。
CRC界定符固定为隐性位(1)。为什么?为了和后面的ACK场区分开。
2.6 ACK场 —— 有人收到吗?
ACK场就2位:ACK槽(1位)和ACK界定符(1位)。
发送方在ACK槽发隐性位(1)。所有接收方如果正确收到帧,就在ACK槽发显性位(0)来应答。这样,发送方看到总线被拉成显性,就知道至少有一个节点收到了。
如果发送方发现ACK槽还是隐性位(1),说明没人应答——帧发送失败。这时候发送方会重发。
你想想看,这个设计多巧妙。不需要单独的应答帧,一个位就搞定了确认机制。
2.7 EOF(帧结束)—— 收工
EOF共7位,全部是隐性位(1)。它告诉总线上所有节点:这帧发完了,大家可以准备接收下一帧了。
为什么是7位?因为位填充机制最多连续出现5个相同位。EOF用7个连续的隐性位,不可能被误认为是填充位。这样接收方就能明确知道帧的结束位置。
EOF之后,总线进入帧间空间(ITM),至少3个隐性位。然后才能开始下一帧的SOF。
2.8 位填充 —— 隐藏的规则
从SOF到CRC场(包括CRC界定符之前),如果出现连续5个相同位,发送方会自动插入一个相反位。这叫位填充。
为什么要有位填充?为了保证时钟同步。CAN总线没有单独的时钟线,接收方靠跳变沿来同步时钟。如果长时间没有跳变,时钟就会漂移。
举个例子:如果数据是00000000,发送方会变成0000010000(在第5个0之后插入1)。接收方看到这个1,就知道是填充位,把它去掉。
2.9 总结一下
一个完整的CAN数据帧,从SOF到EOF,总共加起来:
- 标准帧(11位ID):1 + 12 + 6 + 0~64 + 16 + 2 + 7 = 44~108位
- 扩展帧(29位ID):1 + 32 + 6 + 0~64 + 16 + 2 + 7 = 64~128位
再加上位填充,实际长度会略有增加。但不管怎么变,这7个字段的结构是固定的。搞懂了它们,你就能看懂Wireshark里任何一条CAN报文。
下一章,我们聊聊CAN的帧类型——数据帧、远程帧、错误帧、过载帧。嗯,到时候你会发现,错误帧才是真正让人头疼的东西。