3、CAN总线数据帧结构:数据帧格式、帧起始、仲裁场、控制场、数据场、CRC场、应答场、帧结束

好,咱们今天来啃一块硬骨头——CAN总线的数据帧结构。

说实话,我刚入行那会儿,看着协议手册上那一堆场、段、位,头都大了。什么SOF、IDE、RTR、DLC、CRC……感觉像在看天书。但后来我发现,只要你搞懂了一个数据帧是怎么从发送节点“走”到接收节点的,整个CAN协议就通了一半。

数据帧,说白了就是CAN总线上最常用的“信使”。它负责把传感器、控制器里的数据,从一个节点搬到另一个节点。标准格式有11位ID,扩展格式有29位ID。我个人习惯用标准格式,因为大部分车身控制应用,11位ID完全够用。

3.1 帧起始(SOF)

帧起始,英文叫Start of Frame,缩写SOF。它只有一个显性位(逻辑0)。

为什么只有一个位?因为总线空闲时是隐性电平(逻辑1)。发送节点想说话,就得先拉低总线,告诉大家:“嘿,我要发数据了!”

关键点:SOF是同步信号。所有接收节点看到这个下降沿,都会重新同步自己的时钟。所以,帧起始的边沿质量很重要。我曾经在项目里遇到过,因为总线电容太大,SOF边沿变缓,导致远端节点同步失败。后来加了终端电阻匹配,问题才解决。

3.2 仲裁场

仲裁场,这是CAN总线最聪明的地方。它由ID和RTR位组成。

ID就是标识符,标准帧11位,扩展帧29位。ID越小,优先级越高。为什么?因为显性位(0)会覆盖隐性位(1)。

你想想看,如果两个节点同时发送,它们会逐位比较ID。谁的ID先出现隐性位(1),谁就自动退出。整个过程无破坏、无延时。这就是所谓的“非破坏性逐位仲裁”。

RTR位,Remote Transmission Request,远程传输请求位。数据帧的RTR是显性(0),远程帧的RTR是隐性(1)。所以,相同ID下,数据帧优先级高于远程帧。

避坑指南:我曾经在设计网关时,把两个节点的ID设成了相同值。结果发现,低优先级节点的数据永远发不出去。嗯,这里要注意:同一总线上,每个数据帧的ID必须唯一。否则,仲裁赢了的那一方会一直发,另一方永远没机会。

3.3 控制场

控制场由6个位组成:IDE位、保留位r0(和r1,扩展帧用)、以及DLC(数据长度码)。

IDE位,Identifier Extension,标识符扩展位。标准帧的IDE是显性(0),扩展帧的IDE是隐性(1)。接收节点通过IDE位来判断后面跟的是11位ID还是29位ID。

DLC,Data Length Code,数据长度码。它用4个位表示数据场的字节数,范围是0到8。注意,DLC只能表示0~8,超过8的值会被当作8处理。我见过有人把DLC设成9,结果接收方只收到8个字节,剩下的丢了。所以,DLC一定要和数据场的实际长度匹配。

DLC值 数据字节数
0000 0
0001 1
... ...
1000 8
1001~1111 8(实际按8处理)

3.4 数据场

数据场,就是真正要传输的数据。长度0~8字节,由DLC决定。

数据场的内容完全由用户定义。你可以放车速、转速、温度、开关状态……什么都可以。但要注意字节序的问题。CAN协议规定,数据场的高字节在前(Big-Endian)。但有些控制器用Little-Endian。我建议在项目初期就统一约定好字节序,否则联调时会很痛苦。

经验之谈:我习惯把数据场的第一个字节作为信号ID或状态字,后面依次放数据。这样解析起来方便。比如,第一个字节0x01表示发动机数据,0x02表示变速箱数据。接收方一看第一个字节,就知道后面怎么解析。

3.5 CRC场

CRC场,循环冗余校验场。它包含15位CRC序列和1位CRC界定符(隐性位)。

CRC的作用是检测数据传输过程中是否出错。发送节点计算整个帧(从SOF到数据场)的CRC值,放在CRC场里。接收节点收到后,重新计算一遍,如果结果一致,说明数据没被篡改。

CAN用的CRC多项式是:x¹⁵ + x¹⁴ + x¹⁰ + x⁸ + x⁷ + x⁴ + x³ + 1。这个多项式能检测出所有单比特错误、双比特错误、奇数个错误,以及大部分突发错误。

注意:CRC只能检测错误,不能纠正错误。一旦发现CRC错误,接收节点会发送错误帧,要求发送节点重传。我曾经在一条长总线上遇到过CRC错误率偏高的问题,后来发现是线缆阻抗不匹配导致信号反射。换了双绞线并加了磁环,错误率就降下来了。

3.6 应答场

应答场,ACK场,由2个位组成:ACK Slot(应答间隙)和ACK Delimiter(应答界定符)。

发送节点在ACK Slot发送隐性位(1)。所有接收节点,如果正确收到了帧,就会在ACK Slot发送显性位(0)。这样,发送节点就能知道至少有一个节点正确接收了数据。

如果发送节点在ACK Slot检测到的是隐性位(1),说明没有节点应答。它会认为传输失败,然后重发。我调试时经常用示波器看ACK Slot,如果一直是隐性,说明总线上没有其他节点在线,或者节点地址配置错了。

小技巧:ACK Slot是“线与”逻辑。只要有一个节点应答,总线就是显性。所以,发送节点不需要知道具体是谁应答了,只要知道有人应答就行。这种设计大大简化了协议。

3.7 帧结束(EOF)

帧结束,End of Frame,EOF。它由7个隐性位(1)组成。

为什么是7个?因为CAN协议规定,帧结束之后,总线要进入3个位的间歇场(Intermission),然后才能开始下一帧。7个隐性位加上3个间歇位,总共10个隐性位,保证了总线有足够的时间恢复隐性电平,避免误判。

EOF之后,如果总线一直隐性,就进入空闲状态。如果有节点想发数据,就可以开始下一帧的SOF了。

3.8 完整数据帧结构总结

好了,我们把所有场串起来,看看一个完整的数据帧长什么样:

SOF + 仲裁场(11/29位ID + RTR) + 控制场(IDE + r0 + DLC) + 数据场(0~8字节) + CRC场(15位CRC + 界定符) + 应答场(ACK Slot + 界定符) + EOF(7位)

标准帧总位数:1 + 12 + 6 + 最多64 + 16 + 2 + 7 = 108位(数据为8字节时)。

扩展帧总位数:1 + 32 + 6 + 最多64 + 16 + 2 + 7 = 128位(数据为8字节时)。

你想想看,这么一帧数据,从SOF开始,到EOF结束,中间经历了仲裁、控制、数据传输、校验、应答,每一步都设计得严丝合缝。这就是CAN总线能在恶劣的汽车环境中稳定工作30年的原因。

最后说一句:搞懂数据帧结构,是理解CAN协议的第一步。我建议你找个CAN分析仪,抓一帧实际数据,对照着协议手册逐位分析。相信我,亲手拆解一帧数据后,你会对CAN总线有全新的认识。