4、CAN数据链路层(帧格式:数据帧、远程帧、错误帧、过载帧)归纳总结

CAN 总线通信核心在数据链路层。你想想看,物理层只是把电平拉高拉低,真正决定“这个消息是干啥的”全靠这一层。四种帧格式——数据帧、远程帧、错误帧、过载帧——把总线上的所有活动都定义清楚了。我用 CAN 这些年,踩过不少坑,今天把这四种帧的细节给你掰开揉碎了讲。

4.1 数据帧 —— 最常用的“快递员”

数据帧负责把数据从发送节点送到接收节点。说白了,它就是 CAN 网络里最主要的快递包裹。标准格式(11 位 ID)和扩展格式(29 位 ID)结构略有差异,但核心字段一致。

字段位数(标准/扩展)说明
SOF1帧起始,显性电平,同步信号
ID11 / 29标识符,决定优先级(值越小优先级越高)
RTR1远程帧标志位,数据帧为显性 0
IDE1扩展标志,标准帧为显性,扩展帧为隐性
r0/r11+1保留位,显性
DLC4数据长度,0~8 字节
Data0~64实际数据,最多 8 字节
CRC15+1循环冗余校验 + 隐性定界符
ACK1+1应答位 + 隐性定界符,接收节点拉低应答
EOF7帧结束,全部隐性

小经验:我刚开始调 CAN 时,总以为 DLC 固定是 8,结果有一台设备只发 1 字节数据,我的接收代码却一直等 8 字节 — 那叫一个折腾。后来我习惯:收到帧先看 DLC,再决定怎么解析。

注意:数据帧的 RTR 位必须为显性(0),如果误设为隐性,接收端会当成远程帧处理。我曾经在线束信号干扰时碰见过这个现象,排查了两天才发现是电平抖动导致 RTR 位采样出错。

4.2 远程帧 —— 只问不拿的“查询请求”

远程帧的用途很单纯:请求某个 ID 的节点发送数据。它没有数据段,DLC 只是告诉接收方你该发多少字节。实际项目中远程帧用得不多,但在一些诊断协议(比如 UDS 的请求/响应)里是标配。

对比:数据帧和远程帧的区别

  • RTR 位:数据帧=0(显性),远程帧=1(隐性)
  • 数据段:远程帧没有,DLC 仍是显性编码但无实际 data
  • CRC 范围:远程帧的 CRC 基于控制场+数据场(数据场长度为 0)

我记得有一次在做车载网关转发时,某个节点莫名其妙一直收不到响应。抓波形才发现:发送的其实是远程帧,对方收到后认为是在“请求”,所以回了另一个帧,而我方的接收逻辑没把他当数据帧处理。嗯,后来我在代码里加了帧类型判断,再没出过这种乌龙。

4.3 错误帧与过载帧 —— 总线的“维护工”和“保安”

错误帧

当节点检测到总线错误(比如位错误、填充错误、CRC 错误等),就会主动拉错。错误帧由两部分组成:错误标志(6 个相同位)和错误界定符(8 个隐性位)。

  • 主动错误节点:输出 6 个显性位(破坏当前帧)
  • 被动错误节点:输出 6 个隐性位(不破坏总线,但会暴露自己的错误状态)

千万别小看错误帧。我曾经在一个电机控制项目里,因为电源纹波太大,导致一个节点频繁发错误帧。整个总线被它拉垮,其他节点数据也发不出去。后来用 CAN 异常计数法找到了“捣乱分子”——就是那个错误计数器飙升的节点。

过载帧

过载帧跟错误帧长得有点像,但目的完全不同。它用来告诉对方:“我太忙了,先别发数据,等一下。” 过载帧的结构:6 个显性位(过载标志)+ 8 个隐性位(界定符)。

实际工作中,过载帧很少被主动发出。多数 CAN 控制器(像 SJA1000、MCP2515)在硬件层面已经处理了接收缓冲区溢出,不需要软件干预。只有极其特殊的需求才需要手动触发过载帧。

4.4 四种帧对比总结

为了让你一眼看清区别,我列了个简表:

帧类型主要用途数据段RTR 位何时产生
数据帧传递有效数据有(0~8字节)0正常发送
远程帧请求数据无1主动请求
错误帧报告总线错误无—检测到违规时
过载帧延迟下一次发送无—接收繁忙时

另一个坑:过载帧和错误帧的区别经常被混淆。记住三点——错误帧由错误计数触发,过载帧由接收缓冲区满触发;错误帧有 6 个显性位,过载帧也是 6 个显性位,但错误界定符和过载界定符在格式上完全一样。区分它们的唯一办法:看是在帧间空间还是帧内被触发的。

CAN 数据链路层四种帧格式结构示意图 数据帧 SOF (1) 显性 ID (11/29) + RTR=0 控制+DLC+Data(0~8B) CRC + ACK + EOF 远程帧 SOF (1) 显性 ID (11/29) + RTR=1 控制+DLC (无数据) CRC + ACK + EOF 错误帧 错误标志 (6位) 主动: 显性 被动: 隐性 错误界定符 (8位隐性) 过载帧 过载标志 (6位显性) 过载界定符 (8位隐性) 在帧间空间触发 数据帧与远程帧通过 RTR 位区分 错误帧与过载帧结构类似,但触发条件不同

这张图把四种帧的结构和关系串起来了。数据帧和远程帧共享相同的 ID + 控制场布局,唯一的区别是 RTR 位。错误帧和过载帧则出现在异常场景,它们不需要 ID,直接污染总线来通知全体节点。

4.5 帧间空间 —— 保证总线整洁的“空档”

你不一定非要记住帧间空间所有细节,但有一点很重要:任何帧(包括错误/过载帧)之后,必须有一段隐性电平的帧间空间,至少 3 个位时间。这是 CAN 协议规定的总线“休息时间”,用来让所有节点同步。

如果你用逻辑分析仪抓波形,看到连续两个帧之间有一段很长的隐性段(>3 bit),别紧张——那可能是总线空闲。但如果只有 3 个隐性位,那就是标准的帧间空间。

4.6 实践小建议

  • 调试初期:用示波器或 CAN 协议分析仪先看波形,确认能看到 SOF 的显性下降沿和 EOF 的 7 个隐性位。这能快速判断总线是否正常。
  • 接收处理:永远先解析控制场(IDE、DLC、RTR),再决定是否需要读数据段。远程帧不需要 data,但很多新手会误解成“0 字节数据”,导致读取超时。
  • 错误恢复:一旦发现错误帧频繁出现,第一时间检查错误计数器。哪个节点的发送错误计数或接收错误计数超过 127,它就会进入“被动错误”状态——这个节点将不再主动发错误帧,但总线仍然会被它干扰。

好了,数据链路层的四种帧就盘完了。下回你调 CAN 的时候,心里应该清楚:总线上的每一段显性/隐性脉冲,都属于这四种里的某一种。抓住了帧类型,就抓住了 CAN 通信的命脉。