2、CAN报文帧结构:数据帧、远程帧、错误帧、过载帧、帧间隔、位填充机制

CAN总线上跑的数据,说白了就是几种固定格式的“信封”。我刚开始接触CAN协议栈时,总觉得帧结构就是背几个表格的事。直到有一次调试一个电机控制器,总线莫名其妙挂掉,翻来覆去找不到原因——最后发现是远程帧的RTR位被硬件自动置位了,而我根本没处理这个细节。嗯,从那以后我再看帧结构,就不敢只看表面了。

这一章咱们把CAN的5种帧类型和位填充机制彻底讲透。你想想看,搞懂这些,你就能读懂总线上的任何一段波形。

2.1 帧类型概览

CAN总线定义了5种帧,每种干不同的活:

帧类型 用途 谁发送 关键特征
数据帧 发送节点传输数据 任意节点 携带0~8字节数据
远程帧 请求其他节点发送数据 任意节点 无数据场,RTR位=1
错误帧 检测到总线错误时通知所有节点 检测到错误的节点 6个显性位+8个隐性位
过载帧 接收节点请求延迟下一帧 接收节点 与错误帧结构类似
帧间隔 分隔数据帧/远程帧 总线空闲 3个隐性位

核心要点:数据帧和远程帧是“业务帧”,错误帧和过载帧是“管理帧”,帧间隔是“分隔符”。调试时90%的问题出在数据帧和远程帧上。

2.2 数据帧——最常用的帧

数据帧是CAN总线上的主角。我习惯把它拆成7段来看:

  1. 帧起始(SOF):1个显性位,同步所有节点时钟。
  2. 仲裁场:11位(标准帧)或29位(扩展帧)ID + RTR位 + IDE位 + r0/r1。
  3. 控制场:6位,包含DLC(数据长度码)。
  4. 数据场:0~8字节,真正的载荷。
  5. CRC场:15位CRC + 1位CRC界定符(隐性)。
  6. 应答场:1位应答间隙 + 1位应答界定符。
  7. 帧结束(EOF):7个隐性位。

标准帧和扩展帧的区别主要在仲裁场:

特性 标准帧(CAN 2.0A) 扩展帧(CAN 2.0B)
ID长度 11位 29位
IDE位 0(显性) 1(隐性)
SRR位 无 替代RTR位,始终隐性
最大数据长度 8字节 8字节
总线利用率 较高 较低(帧更长)

我的经验:在J1939协议栈中,扩展帧是标配。但如果你做的是简单的传感器网络,标准帧就够了——帧短,实时性更好。我曾经在一个项目里硬用扩展帧,结果总线负载率飙到80%,后来全改成标准帧才压下来。

2.3 远程帧——请求数据的“问句”

远程帧的结构和数据帧几乎一样,唯一的区别是:RTR位=1(隐性),且没有数据场。

远程帧的典型应用场景:

  • 主节点轮询从节点数据
  • 诊断工具请求ECU信息
  • 网关转发数据请求

避坑指南:我曾经在一个项目中,远程帧的DLC设置错误——请求8字节数据,但目标节点只发了4字节。结果接收节点一直等数据,总线直接卡死。记住:远程帧的DLC必须与目标节点实际发送的数据长度一致。

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

错误帧由两部分组成:

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

CAN协议定义了5种错误类型:

错误类型 检测位置 触发条件
位错误 发送节点 发送的位与总线电平不一致
填充错误 所有节点 连续6个相同位出现
CRC错误 接收节点 计算CRC与接收CRC不匹配
形式错误 所有节点 固定格式位(如EOF)电平错误
应答错误 发送节点 应答间隙未检测到显性位

调试技巧:用示波器抓波形时,看到6个连续显性位,基本可以断定是错误帧。这时候别慌,先看是哪种错误——我遇到过最多的是位错误和填充错误,往往是终端电阻没接好或者波特率不匹配。

2.5 过载帧——接收节点的“刹车”

过载帧的结构和错误帧很像:

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

触发过载帧的条件:

  1. 接收节点内部缓冲区满,来不及处理
  2. 接收节点在帧间隔期间检测到显性位(非法)

我的建议:过载帧在低速CAN网络中很少见。如果你频繁看到过载帧,说明接收节点的处理能力跟不上总线速率——要么降低波特率,要么优化接收中断处理逻辑。我曾在125kbps的网络上见过过载帧,后来发现是接收中断里做了太多浮点运算,改成查表法就解决了。

2.6 帧间隔——总线的“呼吸”

帧间隔是数据帧/远程帧之间的空闲时段,由3个隐性位组成。注意:错误帧和过载帧之间不需要帧间隔。

帧间隔的作用:

  • 给接收节点时间处理刚收到的帧
  • 让总线状态从“活跃”切换到“空闲”
  • 为下一次仲裁做准备

2.7 位填充机制——保证同步的“小聪明”

位填充是CAN协议里一个巧妙的设计。规则很简单:连续发送5个相同位后,自动插入1个相反位。

为什么要这么做?

  • 防止接收节点失去同步(长时间无跳变沿)
  • 保证时钟恢复的可靠性
  • 提供错误检测手段(连续6个相同位就是填充错误)

位填充的适用范围:

帧段 是否位填充 说明
帧起始(SOF) 否 固定显性位
仲裁场 是 ID + RTR + IDE + r0/r1
控制场 是 DLC + 保留位
数据场 是 0~8字节数据
CRC场 是 CRC序列(不含界定符)
应答场 否 固定格式
帧结束(EOF) 否 7个隐性位
错误帧/过载帧 否 固定格式
帧间隔 否 固定隐性位

注意:位填充会增加帧长度。一个8字节数据帧,理论上最多可能插入多少个填充位?答案是:数据场全0x00或全0xFF时,填充位最多。我算过,标准帧最多能到~130位左右。设计实时系统时,这个“最差情况”必须考虑进去。

2.8 帧结构全景图

下面这张图把标准数据帧的完整结构画出来了,包括位填充的位置:

标准数据帧结构(含位填充区域) SOF 仲裁场(11位ID + RTR + IDE + r0) 位填充区域 控制场 位填充区域 数据场(0~8字节) 位填充区域 CRC场(15位+界定符) CRC序列位填充 应答场 无填充 EOF 无填充 位填充规则:连续5个相同位后插入1个相反位 SOF、应答场、EOF、错误帧、过载帧、帧间隔不参与位填充 帧间隔(3个隐性位)—— 分隔数据帧/远程帧 错误帧结构 错误标志(6位) 错误界定符(8位)

2.9 实战中的帧分析

我调试CAN总线时,最常用的工具就是逻辑分析仪。拿到一段波形,我习惯按这个顺序排查:

  1. 看帧起始:找到SOF的下降沿,确认同步点
  2. 读ID:从SOF后开始,按位解析ID(注意标准帧和扩展帧的IDE位)
  3. 看RTR位:判断是数据帧还是远程帧
  4. 读DLC:确认数据长度
  5. 读数据:注意位填充的影响,实际数据可能比理论值长
  6. 检查CRC:手动计算CRC,确认数据完整性
  7. 看应答:确认是否有节点应答(应答间隙应为显性)

一句话总结:CAN的帧结构设计得非常精巧——位填充保证同步,CRC保证完整性,错误帧保证可靠性。搞懂这5种帧和位填充机制,你就能读懂总线上的任何一段对话。