4、CAN协议层(数据帧):标准帧与扩展帧格式、帧起始、仲裁场、控制场、数据场、CRC场、应答场、帧结束

各位工程师朋友,咱们今天来聊聊CAN协议层里最核心的东西——数据帧。说白了,CAN总线上的所有通信,最终都是靠数据帧来完成的。你想想看,一个数据帧就像一封信,里面包含了从哪来、到哪去、带什么消息、对不对得上暗号等等信息。

我个人习惯把数据帧拆成七个部分来理解:帧起始、仲裁场、控制场、数据场、CRC场、应答场、帧结束。咱们一个一个过,我保证讲得接地气。

4.1 标准帧 vs 扩展帧:到底差在哪?

先解决一个最基础的问题:标准帧和扩展帧有什么区别?

嗯,说白了就是ID的长度不一样。标准帧的ID是11位,扩展帧的ID是29位。为什么要有两种?因为早期CAN总线节点少,11位ID够用了。后来系统越来越复杂,节点越来越多,11位不够分,于是就有了29位的扩展帧。

我在项目中遇到过这样一个坑:有个客户的产品,标准帧和扩展帧混着用,结果仲裁的时候出了乱子。后来我查了协议才发现——标准帧的优先级永远高于扩展帧。为什么?因为标准帧的IDE位是显性(0),扩展帧的IDE位是隐性(1),仲裁时显性位胜出。这个细节,很多新手容易忽略。

关键区别总结:

  • 标准帧:11位ID,IDE位=0(显性),优先级高
  • 扩展帧:29位ID,IDE位=1(隐性),优先级低
  • 两者可以共存于同一总线,但标准帧仲裁时占优

4.2 帧起始(SOF):一切从这里开始

帧起始就是一个显性位(0)。它告诉总线上所有节点:“注意了,我要开始发数据了!”

这个位的作用很简单,但很重要。你想想看,如果总线上没有这个起始信号,节点怎么知道什么时候开始接收?所以SOF就是那个“发令枪”。

我记得有一次调试,发现某个节点总是收不到数据。查了半天,原来是它的SOF检测电路出了问题,把显性位误判成了隐性位。嗯,这种硬件问题最难查,因为波形看起来都正常,但就是差那么一点点阈值。

4.3 仲裁场:谁说了算?

仲裁场是CAN协议最巧妙的设计之一。它包含ID和RTR位(标准帧)或ID、SRR、IDE、RTR位(扩展帧)。

仲裁的原理很简单:谁先发显性位,谁就赢。因为CAN总线是“线与”逻辑,显性位会覆盖隐性位。所以ID值越小,优先级越高。

这里有个细节:标准帧的仲裁场是12位(11位ID + 1位RTR),扩展帧的仲裁场是32位(29位ID + SRR + IDE + RTR)。

避坑指南:

我曾经在设计一个多节点系统时,把两个节点的ID设成了相同的值。结果呢?仲裁时两个节点同时发送,数据全乱了。后来我才意识到,CAN总线要求每个节点的ID必须唯一,否则仲裁机制就失效了。所以设计时一定要做好ID分配表,别偷懒。

4.4 控制场:告诉你怎么解读数据

控制场有6位,包含IDE位、保留位r0/r1,以及DLC(数据长度码)。

IDE位我们刚才说过了,标准帧是0,扩展帧是1。保留位目前都是显性(0),留给未来扩展用。DLC用4位表示数据场的字节数,范围是0到8。

你可能会问:为什么DLC最大是8?因为CAN协议设计时认为,8字节的数据足够覆盖大多数工业控制场景。而且数据太长会影响实时性。我个人的经验是,大部分应用4个字节就够用了,比如传个转速、温度、状态什么的。

DLC值 数据字节数 常见用途
0 0 远程帧(无数据)
1-7 1-7 短数据包
8 8 标准数据包

4.5 数据场:真正的“干货”

数据场就是你要传输的实际数据,长度由DLC决定,最多8个字节。数据从MSB(最高有效位)开始发送。

这里有个容易犯的错误:数据场的字节顺序。不同的厂商可能有不同的定义,比如Intel格式和Motorola格式。我建议在项目一开始就统一约定好,否则联调时会出现“鸡同鸭讲”的情况。

举个例子:你要传一个16位的转速值0x1234。如果按大端序(Motorola),先发0x12再发0x34;如果按小端序(Intel),先发0x34再发0x12。两边不一致,数据就全错了。

注意:

我曾经在一个项目中,因为数据场字节序没统一,导致两个ECU之间通信一直报错。排查了整整两天,最后发现是字节顺序反了。从那以后,我每个项目都会在文档里明确标注字节序,并在代码里加注释。

4.6 CRC场:数据有没有被篡改?

CRC场包含15位CRC校验码和1位CRC界定符(隐性位)。CRC算法用的是CRC-15,多项式是x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1。

你不需要记住这个多项式,但要知道它的作用:检测数据传输过程中有没有发生错误。接收节点会重新计算CRC,如果和发送的CRC不一致,就说明数据出错了,然后发送错误帧请求重传。

嗯,这里有个小技巧:CRC计算的范围是从帧起始到数据场结束,但不包括CRC场本身和后面的应答场。这个范围定义清楚了,才能保证收发双方算出来的CRC一致。

4.7 应答场:收到请回复

应答场只有2位:应答间隙(ACK Slot)和应答界定符(ACK Delimiter)。

发送节点在应答间隙发送一个隐性位(1),然后释放总线。接收节点如果正确收到了数据,就在这个间隙发送一个显性位(0)来应答。如果没有任何节点应答,发送节点就知道出问题了。

这个机制很巧妙,它实现了“一对多”的确认。你想想看,总线上可能有几十个节点,发送节点不需要知道具体是谁应答了,只要知道“有人收到了”就行。

实际经验:

我在调试一个CAN网络时,发现某个节点总是发送失败。用示波器一看,应答间隙一直是隐性位,说明没有节点应答。后来发现是终端电阻没接好,信号反射导致接收节点无法正确识别数据。所以,应答场的问题很多时候是物理层引起的,别光盯着协议层看。

4.8 帧结束:画上句号

帧结束由7个隐性位(1)组成。它的作用很简单:告诉总线上所有节点,这一帧数据发完了,大家可以准备接收下一帧了。

为什么是7个隐性位?因为CAN协议规定,连续6个相同位就要插入一个填充位。帧结束用7个隐性位,可以确保不会触发填充机制,同时给节点足够的时间处理接收到的数据。

好了,数据帧的七个部分咱们都过了一遍。你可能会觉得内容有点多,但实际用起来,这些字段都是硬件自动处理的,你只需要配置好ID和数据就行。不过,理解这些细节对排查问题非常有帮助。

下一章咱们聊聊远程帧和错误帧,这两个在实际调试中也很常见。到时候我会分享一些我踩过的坑,保证让你少走弯路。