3、CAN总线基础(下):数据链路层与报文帧格式

好,咱们接着聊CAN总线。上一节我们把物理层的那些事儿捋了一遍,什么差分电平、终端电阻,说白了就是让信号能在线上跑起来。但光能跑还不够,车上的节点那么多,谁先说话、谁后说话、说错了怎么办?这些规矩,就归数据链路层管。

我个人习惯把数据链路层理解成「交通规则」。物理层是路,数据链路层就是红绿灯和交警。今天咱们重点看报文帧格式和仲裁机制,这两个是CAN总线的灵魂。

3.1 数据链路层的核心职责

数据链路层在CAN协议里干三件事:

  • 帧封装:把要发的数据打包成标准格式
  • 总线访问:决定什么时候能发、发不了怎么办
  • 错误检测:发现传输过程中有没有被干扰

你想想看,如果没这层,所有节点同时往总线上怼数据,那不乱套了?

重要概念:CAN总线是「多主从」结构。任何节点都可以主动发消息,不需要等主机轮询。这跟LIN总线完全不一样,LIN是主从模式,从机只能等主机叫它。

3.2 报文帧的四种类型

CAN协议定义了四种帧。我在项目里调试时,90%的时间都在跟数据帧和错误帧打交道。远程帧偶尔用,过载帧说实话我干了十年也就见过几次。

帧类型 用途 常见场景
数据帧 发送数据 ECU之间交换信号
远程帧 请求数据 某个节点主动要数据
错误帧 报告总线错误 节点发现数据异常
过载帧 请求延迟 接收节点忙不过来

3.3 数据帧的完整结构

数据帧是咱们最常用的。它分标准帧和扩展帧两种。标准帧11位ID,扩展帧29位ID。嗯,这里要注意:虽然扩展帧ID更长,但优先级反而可能更低,为什么?后面讲仲裁你就明白了。

一个标准数据帧长这样:

SOF + 11位ID + RTR + IDE + r0 + DLC + 数据段(0-8字节) + CRC + ACK + EOF

拆开来看:

  • SOF(Start of Frame):1位显性位,告诉所有人「我要发消息了」
  • ID(Identifier):11位,决定优先级。数值越小优先级越高
  • RTR(Remote Transmission Request):数据帧里是显性0,远程帧里是隐性1
  • IDE(Identifier Extension):标准帧里是显性0,扩展帧里是隐性1
  • r0:保留位,必须为显性
  • DLC(Data Length Code):4位,表示数据段有多少字节。范围0-8
  • 数据段:0到8字节,真正要传的内容
  • CRC:15位校验码+1位定界符
  • ACK:2位,发送方发隐性,接收方拉显性表示收到
  • EOF:7位隐性位,表示帧结束

我的经验:调试时最容易忽略的是DLC。有一次客户说某个信号收不到,我抓了半天波形,最后发现是DLC设成了2,但实际发了4个字节。接收方只读了前两个字节,后面的全丢了。所以DLC一定要跟发送方匹配。

3.4 远程帧:我要,你给

远程帧说白了就是「请求帧」。一个节点发远程帧,另一个节点收到后,就会发对应的数据帧。远程帧的结构跟数据帧几乎一样,唯一的区别是RTR位是隐性1,而且没有数据段。

我记得有一次做BCM(车身控制器)调试,发现某个传感器数据一直没更新。抓波形一看,原来是网关发了远程帧,但传感器没响应。后来查手册才发现,那个传感器只支持数据帧主动上报,不支持远程帧请求。所以用远程帧之前,一定要确认对方支不支持。

3.5 错误帧:出事了

错误帧是CAN总线自我保护的机制。任何一个节点发现错误,就会立刻发错误帧,把当前传输打断。

错误帧分两种:

  • 主动错误帧:6个连续显性位,把总线拉低
  • 被动错误帧:6个连续隐性位,比较温和

节点内部有两个计数器:发送错误计数器(TEC)和接收错误计数器(REC)。

  • TEC或REC > 127:节点进入被动错误状态
  • TEC > 255:节点进入总线关闭状态,彻底离线

避坑指南:我曾经遇到一个案例,某ECU频繁发错误帧,导致整个CAN网络瘫痪。查了半天发现是它的晶振偏差太大,位时序对不上。换了个晶振就好了。所以做硬件设计时,晶振精度一定要选够,别省那几毛钱。

3.6 过载帧:慢点,我跟不上

过载帧用得很少。它表示接收节点忙不过来了,请求发送方等一等。结构跟错误帧有点像,但触发条件不同。过载帧只在帧间隔期间发,不会打断正在传输的帧。

说实话,我在实际项目中几乎没见过过载帧。现在的MCU处理能力都够,很少出现来不及收的情况。但如果你在做一些老平台的项目,或者总线负载率特别高(超过80%),还是有可能遇到的。

3.7 仲裁机制:谁优先级高谁先走

这是CAN总线最巧妙的设计。多个节点同时发消息怎么办?靠ID仲裁。

原理很简单:总线是「线与」逻辑。显性位(0)会覆盖隐性位(1)。所以ID数值越小,显性位越多,优先级越高。

举个例子:

  • 节点A发ID=0x100(二进制:001 0000 0000)
  • 节点B发ID=0x200(二进制:010 0000 0000)

从第一位开始比。A的第一位是0(显性),B的第一位是1(隐性)。总线被A拉成显性,B检测到总线跟自己发的不一样,就知道自己输了,立刻停止发送,转为接收模式。

整个过程在微秒级完成,完全不影响正常通信。这就是为什么CAN总线适合实时控制——优先级高的消息永远能抢到总线。

关键点:仲裁不丢数据。输了的节点自动重发,赢了的不受影响。所以CAN总线的实时性是有保障的,不像以太网那样会有碰撞丢包。

3.8 位填充机制

还有一个细节:位填充。CAN协议规定,连续发送5个相同电平后,必须插入一个相反电平。比如连续发了5个显性位,第6位必须发隐性位。

为什么这么做?两个原因:

  1. 保证时钟同步:接收方靠电平跳变来同步时钟,如果长时间没跳变,时钟会漂移
  2. 防止误判:连续相同电平太长,可能被误认为是帧结束或错误标志

位填充的范围是从SOF到CRC段结束。EOF和ACK段不填充。

我刚开始学CAN的时候,总觉得位填充是个小细节。直到有一次用示波器抓波形,发现数据段里多了一个位,怎么都对不上。后来才想起来是位填充搞的鬼。所以做协议分析时,一定要把位填充考虑进去。

3.9 小结

今天的内容有点多,但都是硬核知识。总结一下:

  • 数据链路层管帧封装、总线访问和错误检测
  • 四种帧:数据帧、远程帧、错误帧、过载帧
  • 数据帧分标准帧和扩展帧,结构要记熟
  • 仲裁靠ID,数值越小优先级越高
  • 位填充保证时钟同步,别忽略它

下一节咱们讲CAN总线的错误处理机制和故障诊断,那才是真正考验工程师功底的地方。到时候我会分享几个我亲手解决的疑难杂症,保证让你少走弯路。