CAN帧结构:数据帧、远程帧、错误帧、过载帧、帧间隔

各位同学,咱们今天聊聊CAN总线上的五种帧结构。说实话,我刚接触CAN协议时,也被这五种帧搞得有点晕。但后来我发现,只要搞懂了它们各自出现的场景,其实没那么复杂。

CAN总线上的通信,说白了就是靠这五种帧来完成的。它们分别是:数据帧、远程帧、错误帧、过载帧和帧间隔。咱们一个一个来拆解。

2.1 数据帧(Data Frame)

数据帧是最常用的帧类型。它负责把数据从一个节点发送到另一个节点。我个人习惯把数据帧想象成一个快递包裹——有收件地址、有内容、还有校验码。

数据帧分为两种格式:标准帧(11位ID)和扩展帧(29位ID)。它们的结构对比如下:

字段 标准帧 扩展帧
SOF(帧起始) 1位显性位 1位显性位
仲裁场 11位ID + RTR + IDE + r0 29位ID + SRR + IDE + RTR + r1 + r0
控制场 DLC(4位) DLC(4位)
数据场 0~8字节 0~8字节
CRC场 15位CRC + 1位CRC界定符 15位CRC + 1位CRC界定符
ACK场 1位ACK槽 + 1位ACK界定符 1位ACK槽 + 1位ACK界定符
EOF(帧结束) 7位隐性位 7位隐性位

这里有个关键点:RTR位。数据帧的RTR位是显性(0),而远程帧的RTR位是隐性(1)。这是区分数据帧和远程帧的唯一标志。

我的经验:在调试NMT从站节点时,我经常用逻辑分析仪抓数据帧。如果发现某个节点一直发数据但其他节点没反应,先检查ID对不对,再检查DLC长度是否匹配。我曾经被一个DLC不匹配的问题折腾了两天...

2.2 远程帧(Remote Frame)

远程帧的作用是请求数据。说白了,就是一个节点对另一个节点说:“嘿,把你那个ID的数据发给我看看。”

远程帧的结构和数据帧几乎一样,唯一的区别就是RTR位为隐性(1),而且没有数据场。嗯,这里要注意:远程帧的DLC字段表示请求的数据长度,但实际不发送数据字节。

举个例子:

// 标准远程帧示例
// ID = 0x123, DLC = 8, RTR = 1
// 发送节点请求ID为0x123的节点发送8字节数据

// 标准数据帧示例(响应)
// ID = 0x123, DLC = 8, RTR = 0
// 数据 = 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08
避坑指南:我曾经在项目中遇到一个奇怪的问题——从站节点收到远程帧后没响应。查了半天发现,原来从站固件里把远程帧当成数据帧处理了,根本没解析RTR位。记住:收到远程帧后,你要发送的是数据帧,不是远程帧!

2.3 错误帧(Error Frame)

错误帧是CAN总线上的“报警器”。当节点检测到总线错误时,就会主动发送错误帧。错误帧由两部分组成:

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

错误帧的触发条件有五种:

  1. 位错误:发送节点监控总线,发现实际电平与发送电平不一致
  2. 填充错误:连续5个相同电平后没有插入填充位
  3. CRC错误:接收节点计算的CRC与发送的CRC不匹配
  4. 形式错误:固定格式的位段出现非法电平
  5. ACK错误:发送节点在ACK槽没有收到显性位

关键概念:主动错误 vs 被动错误

每个CAN节点都有一个错误计数器。当错误计数超过127时,节点从主动错误状态变为被动错误状态。主动错误节点发送6个显性位,被动错误节点发送6个隐性位。说白了,主动错误能“打断”总线,被动错误只能“小声嘀咕”。

2.4 过载帧(Overload Frame)

过载帧的作用是告诉总线:“我忙不过来了,慢点发。”它和错误帧结构类似,但触发条件不同。

过载帧的触发条件:

  • 接收节点内部处理不过来,需要延迟下一个数据帧或远程帧
  • 在帧间隔的间歇场检测到显性位(非法情况)

过载帧的结构:

  • 过载标志(Overload Flag):6个连续显性位
  • 过载界定符(Overload Delimiter):8个连续隐性位
我的经验:在实际项目中,过载帧其实很少见。我做了这么多年CAN开发,真正遇到主动发送过载帧的情况不超过三次。大多数时候,如果节点处理不过来,更好的做法是优化软件架构,而不是靠过载帧来“拖慢”总线。

2.5 帧间隔(Interframe Space)

帧间隔不是一种独立的帧,而是帧与帧之间的“休息时间”。它确保总线上的节点有足够的时间准备接收下一帧。

帧间隔由三部分组成:

字段 长度 说明
间歇场(Intermission) 3个隐性位 所有节点必须等待这3位才能开始发送
总线空闲(Bus Idle) 任意长度隐性位 总线空闲,节点可以开始发送
暂停场(Suspend Transmission) 8个隐性位 仅被动错误节点需要,主动错误节点跳过

这里有个细节:被动错误节点在发送完一帧后,必须额外等待8个隐性位的暂停场才能继续发送。这是为了防止故障节点“霸占”总线。

2.6 五种帧的关系与状态机

为了让大家更直观地理解这五种帧在总线上的流转关系,我画了一张图:

CAN五种帧关系图 CAN总线五种帧流转关系 总线空闲 数据帧 远程帧 错误帧 过载帧 帧间隔 有数据要发送 请求数据 检测到错误 发送完成 发送完成 错误恢复 间歇结束 过载结束 节点忙 说明:实线为正常流程,虚线为异常/特殊流程。帧间隔是每帧发送后的必要等待。

2.7 实战要点总结

好了,咱们把五种帧的核心要点总结一下:

数据帧:最常用,带数据内容,RTR=0

远程帧:请求数据,无数据场,RTR=1

错误帧:总线错误时触发,6+8位结构

过载帧:节点忙时触发,6+8位结构

帧间隔:帧与帧之间的休息时间,3+8位

在NMT从站节点开发中,你最常打交道的是数据帧和远程帧。错误帧和过载帧更多是调试时用来定位问题的。帧间隔则是你写驱动时必须考虑的时间参数。

避坑指南:我曾经在调试一个NMT从站时,发现节点偶尔会丢失心跳报文。抓波形一看,原来是帧间隔没处理好——从站在发送完响应帧后,没有等待足够的间歇场就直接发下一帧,导致总线仲裁失败。记住:帧间隔不是可选项,是强制要求。