2. CAN报文结构:标准帧与扩展帧、数据帧、远程帧、错误帧、过载帧、帧间隔

好,咱们直接切入正题。CAN总线上的通信,说白了就是靠“报文”来传递信息。你想想看,一辆车上几十个ECU,大家怎么约定谁什么时候说话、说什么、说多久?全靠这套报文结构来管。

我个人习惯把CAN报文比作一封信。信封上得写清楚“谁发的”、“给谁的”、“多长内容”,还得有校验位防止中途被篡改。CAN 2.0时代定义了两种帧格式,到了CAN FD和CAN XL时代,虽然速率和长度变了,但底层的“信封结构”基本逻辑没变。今天咱们就把这六种帧类型掰开揉碎了讲清楚。

2.1 标准帧 vs 扩展帧:ID长度的博弈

先解决最基础的问题:标准帧和扩展帧到底差在哪?

标准帧的标识符(ID)是11位。什么意思?就是总线上最多能区分2048个不同的报文。早期够用,现在嘛……你想想看,一辆豪华车上的ECU数量早就超过这个数了。

扩展帧把ID扩展到了29位。这一下子,可用的标识符数量就变成了2^29,也就是5亿多个。嗯,短期内肯定够用了。

我在项目中遇到过一个问题:某供应商的网关只支持标准帧,结果我们新设计的域控制器发的是扩展帧。两边对不上,总线直接报错。后来我加了一个ID转换表,把扩展帧映射成标准帧才解决。所以啊,设计初期一定要确认好:你的网络用标准帧还是扩展帧?还是两者混用?

关键区别一览:

特性 标准帧 扩展帧
ID长度 11位 29位
仲裁场长度 12位 32位
最大报文数 2048 5亿+
兼容性 所有CAN控制器 需支持扩展帧的控制器

这里有个小技巧:标准帧和扩展帧可以在同一条总线上共存。但要注意,标准帧的优先级永远高于扩展帧——因为标准帧的IDE位是显性(0),而扩展帧的IDE位是隐性(1)。仲裁的时候,显性位赢。嗯,这就是为什么有些关键报文(比如刹车、转向)我建议用标准帧,优先级更高。

3.2 数据帧:最常用的“快递员”

数据帧,顾名思义,就是用来装数据的。CAN FD和CAN XL的数据帧在结构上继承了经典CAN,但做了几个关键升级。

一个完整的数据帧包含以下几个部分:

  • 帧起始(SOF):1位显性,告诉总线“我要开始说话了”
  • 仲裁场:包含ID和RTR位(远程帧请求位)
  • 控制场:包含IDE位、保留位、DLC(数据长度码)
  • 数据场:0~8字节(经典CAN),CAN FD最多64字节,CAN XL最多2048字节
  • CRC场:校验数据完整性
  • ACK场:接收节点回应“收到了”
  • 帧结束(EOF):7位隐性,表示帧结束

我个人觉得,数据帧里最容易出问题的是DLC。DLC是4位二进制,理论上能表示0~15,但经典CAN只用到0~8。CAN FD扩展到了0~15,但映射关系变了。举个例子:DLC=9在经典CAN里是非法的,但在CAN FD里表示12字节数据。我曾经见过一个工程师把DLC设成了9,结果老款CAN控制器直接报错,总线卡死。嗯,兼容性问题,永远要小心。

避坑指南: 我曾经在调试CAN FD时,发现数据长度总是对不上。查了半天,原来是DLC编码方式搞错了。经典CAN的DLC是线性映射(DLC=8表示8字节),但CAN FD的DLC是分段映射(DLC=9表示12字节,DLC=10表示16字节,以此类推)。记住这张表:

DLC值经典CAN数据长度CAN FD数据长度
0~80~8字节0~8字节
9非法12字节
10非法16字节
11非法20字节
12非法24字节
13非法32字节
14非法48字节
15非法64字节

2.3 远程帧:问别人要数据

远程帧,说白了就是“请求帧”。一个节点发远程帧,意思就是:“谁有某某数据?请发给我。”

远程帧的结构和数据帧几乎一样,唯一的区别是RTR位。数据帧的RTR是显性(0),远程帧的RTR是隐性(1)。另外,远程帧没有数据场——你想想看,既然是请求别人发数据,你自己带数据干嘛?

我建议你少用远程帧。为什么?因为它在实际项目中容易引发“帧冲突”。举个例子:节点A发远程帧请求数据,节点B收到后回复数据帧。但如果节点B还没来得及回复,节点A又发了一个远程帧……总线就乱了。我在一个项目中见过这种“远程帧风暴”,最后不得不改成定时发送数据帧的方案。

注意: 远程帧在CAN FD和CAN XL中仍然存在,但使用场景越来越少。我个人建议,能用数据帧轮询就别用远程帧。除非你有非常明确的请求-响应模型,而且确保不会出现并发请求。

2.4 错误帧:总线的“警报器”

错误帧是CAN总线自我保护的机制。当任何一个节点检测到错误,就会立刻发送错误帧,通知所有节点:“刚才那帧数据有问题,大家别用。”

错误帧由两部分组成:

  • 错误标志:6个连续显性位(主动错误)或6个连续隐性位(被动错误)
  • 错误界定符:8个连续隐性位

为什么会这样设计?你想想看,6个显性位在CAN总线协议里是“非法”的——因为正常帧里不可能连续出现6个相同电平。所以一旦出现6个显性位,所有节点都知道“出错了”。

我在调试时最怕看到错误帧。有一次,一辆测试车的CAN总线每隔几秒就冒一个错误帧。查了三天,最后发现是一个ECU的CAN收发器焊脚虚焊,导致信号电平不稳定。嗯,硬件问题往往比软件问题更难排查。

2.5 过载帧:说“我忙,等会儿”

过载帧和错误帧长得很像,但用途完全不同。过载帧的意思是:“我太忙了,处理不过来,你慢点发。”

过载帧的结构:

  • 过载标志:6个显性位
  • 过载界定符:8个隐性位

你可能会问:这和错误帧有什么区别?区别在于触发条件。错误帧是检测到总线错误时触发,而过载帧是接收节点主动发出的——比如接收缓冲区满了,或者内部处理延迟。

说实话,我在实际项目中很少见到过载帧。因为现在的CAN控制器缓冲区都很大,而且CAN FD的速率足够快。但如果你在做低端MCU或者老旧硬件的兼容性测试,还是要注意一下——有些老芯片的接收缓冲区只有2帧,稍微一忙就发过载帧。

2.6 帧间隔:总线的“呼吸时间”

帧间隔,就是两帧报文之间的空闲时间。它让总线上的节点有时间处理上一帧数据,准备接收下一帧。

帧间隔包含:

  • 间歇场:3个隐性位
  • 总线空闲:任意长度的隐性位(直到有节点开始发送)

这里有个细节:被动错误节点在发送完一帧后,还需要额外发送8个隐性位的“挂起传输”场。为什么?因为被动错误节点不能主动发起传输,必须等总线空闲一段时间后才能尝试。这是为了防止错误节点一直霸占总线。

我记得有一次,一个同事问我:“为什么我的节点发完一帧后,要等好久才能发下一帧?”我一看,原来他的节点进入了被动错误状态,每次发完都要等8个位的挂起时间。嗯,这就是帧间隔在起作用。

总结一下:

  • 标准帧 vs 扩展帧:ID长度不同,优先级不同,混用时注意兼容性
  • 数据帧:最常用,注意DLC编码在CAN FD中的变化
  • 远程帧:请求数据用,但建议少用,容易引发冲突
  • 错误帧:总线自我保护,出现时说明有硬件或软件问题
  • 过载帧:接收节点忙不过来时触发,现在很少见
  • 帧间隔:总线休息时间,被动错误节点有额外等待

这六种帧类型,就是CAN总线通信的全部“语法”。你只要掌握了它们,就能读懂总线上的每一句话。下一章,咱们聊聊CAN FD和CAN XL的物理层——为什么它们能跑那么快?