2. CAN数据帧结构:标准帧与扩展帧、ID仲裁机制、数据场与CRC校验

CAN总线上的数据,说白了就是靠一个个“帧”来传递的。帧的结构长什么样,直接决定了你能不能把数据发出去、别人能不能收到、收到的是不是对的。我刚开始接触CAN的时候,总觉得帧结构就是一堆二进制位,背下来就行了。后来在项目里踩了几次坑才明白——不理解帧结构,你连示波器上的波形都看不懂。

这一节,咱们就把CAN数据帧掰开揉碎了讲。重点看三个东西:标准帧和扩展帧的区别、ID怎么决定谁先说话、数据场和CRC怎么保证数据靠谱。

2.1 标准帧 vs 扩展帧:差的不只是位数

CAN 2.0规范定义了两种帧格式:标准帧(CAN 2.0A)和扩展帧(CAN 2.0B)。很多人以为区别就是ID位数不同——标准帧11位,扩展帧29位。对,但不全对。

我个人的习惯是,先看帧的“骨架”。一个完整的数据帧,从SOF(帧起始)到EOF(帧结束),总共包含以下几个段:

段名称 标准帧 扩展帧 说明
SOF 1 bit 1 bit 显性位,同步所有节点
仲裁场 12 bits 32 bits ID + RTR + IDE + 扩展ID
控制场 6 bits 6 bits IDE + r0 + DLC
数据场 0~64 bits 0~64 bits 实际载荷
CRC场 16 bits 16 bits 15位CRC + 1位定界符
ACK场 2 bits 2 bits 应答槽 + 定界符
EOF 7 bits 7 bits 隐性位,帧结束

你看,标准帧和扩展帧最大的差异就在仲裁场。标准帧的仲裁场只有12位(11位ID + 1位RTR),扩展帧的仲裁场有32位(11位基础ID + SRR + IDE + 18位扩展ID + 1位RTR)。

这里有个容易搞混的地方:IDE位。标准帧的IDE位在控制场,是显性(0);扩展帧的IDE位在仲裁场,是隐性(1)。接收节点就是靠IDE位来判断当前帧是标准还是扩展的。

我的经验: 同一个网络里,标准帧和扩展帧可以混用。但我不建议这么做。曾经在一个项目中,标准帧和扩展帧的ID数值一样,结果仲裁的时候出了乱子。能统一就统一,省心。

2.2 ID仲裁机制:谁的数字小,谁先走

CAN总线是“多主”架构——任何节点都可以随时发数据。那问题来了:如果两个节点同时发,谁说了算?

答案是:ID仲裁。

仲裁的规则特别简单:ID值越小,优先级越高。发送的时候,所有节点同时往总线上送ID位。如果某个节点送的是隐性位(1),但总线上被别的节点拉成了显性位(0),那这个节点就知道自己输了,立刻停止发送,转为接收。

你想想看,这就像一群人同时喊话,谁嗓门大谁先说完。在CAN里,“嗓门大”就是显性位(0)压倒隐性位(1)。

标准帧的ID是11位,取值范围0x000~0x7FF。扩展帧的ID是29位,取值范围0x00000000~0x1FFFFFFF。数值越小,优先级越高。所以0x000是最高优先级,0x7FF是最低优先级。

关键点: 仲裁过程中,数据场和CRC场不参与仲裁。仲裁只发生在SOF之后、控制场之前。一旦某个节点赢得了仲裁,它就可以独占总线直到帧结束。

我在项目中遇到过一个问题:两个节点同时发送,ID分别是0x100和0x101。按理说0x100优先级更高,但实际测试发现0x101先发了。查了半天,原来是0x100节点的发送电路有延迟,导致显性位没及时拉低。嗯,硬件问题也会影响仲裁,别光看协议。

2.3 数据场:最多8字节,别嫌少

CAN数据帧的数据场长度是0~8字节。为什么是8?因为CAN总线最早是为汽车设计的,汽车上的控制信号(转速、车速、油门位置)每个信号也就几个字节,8字节完全够用。

数据场的长度由控制场里的DLC(Data Length Code)决定。DLC是4位,理论上可以表示0~15,但CAN协议规定DLC > 8时,数据场长度按8算。也就是说,你DLC设成9,实际也只发8字节。

DLC值 数据场长度(字节) 备注
0 0 远程帧常用
1~8 1~8 正常数据帧
9~15 8 协议规定截断

数据场的字节顺序,CAN协议没有强制规定。但行业内有个不成文的规矩:小端序(Little-Endian)。也就是低地址存低位数据。比如你要发一个16位的值0x1234,Byte0存0x34,Byte1存0x12。

注意: 不同厂商的ECU可能用不同的字节序。我曾经在对接一个德国供应商的控制器时,他们用大端序,我们用小端序,结果解析出来的车速直接翻了好几倍。后来加了个字节序转换函数才搞定。所以,通信矩阵里一定要写明字节序!

2.4 CRC校验:15位多项式,误检率极低

CAN总线的物理环境其实挺恶劣的——电磁干扰、线束老化、连接器松动,都可能让数据出错。CRC(循环冗余校验)就是用来检测这些错误的。

CAN数据帧的CRC场包含15位CRC序列和1位CRC定界符(隐性位)。生成多项式是:

x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1

这个多项式是CAN协议规定的,硬件自动计算,你不需要自己实现。但理解它的原理有助于排查问题。

CRC的计算范围包括:SOF、仲裁场、控制场和数据场。接收节点收到帧后,会用同样的多项式重新计算CRC,然后跟发送节点附带的CRC值对比。如果不一样,说明帧在传输过程中被破坏了,接收节点会发送错误帧,要求发送节点重传。

我个人的经验是:CRC校验的误检率极低,但也不是100%。在极端情况下(比如多个位同时翻转),CRC可能检测不出来。不过对于汽车级应用,这个概率已经足够小了。如果你做的是安全关键系统(比如刹车、转向),建议在应用层再加一层校验,比如和校验或更高级的CRC。

避坑指南: 我曾经遇到过一个问题:总线上的CRC错误率突然升高,但示波器看波形又没问题。后来发现是某个节点的晶振频率偏了,导致位时序错位,CRC计算不一致。换了个晶振就好了。所以,CRC出错不一定是干扰,也可能是时钟问题。

2.5 知识结构图:CAN数据帧全景

下面这张图把标准帧和扩展帧的结构、仲裁流程、CRC校验范围串在了一起。你可以把它当作一个快速参考。

CAN数据帧结构全景图 标准帧 (CAN 2.0A) SOF 仲裁场 (12b) 控制场 数据场 CRC场 扩展帧 (CAN 2.0B) SOF 仲裁场 (32b) 控制场 数据场 核心要点 1. ID仲裁规则: ID值越小,优先级越高 显性位(0)压倒隐性位(1) 2. 标准帧 vs 扩展帧: 标准帧:11位ID 扩展帧:29位ID IDE位决定帧格式 3. 数据场: 0~8字节,DLC控制长度 默认小端序 4. CRC校验: 15位多项式 覆盖SOF~数据场 硬件自动计算

2.6 小结:记住这三句话

  • 标准帧和扩展帧——差在ID位数,IDE位决定格式,别混用。
  • ID仲裁——谁的数字小谁先走,显性位压倒隐性位。
  • 数据场和CRC——最多8字节,CRC硬件算,出错就重传。