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

好,咱们接着聊。上一章我们把CAN总线的物理层和位时序讲透了,这一章我带你看看CAN总线上到底跑的是什么“信使”。

说白了,CAN总线上就五种帧:数据帧远程帧错误帧过载帧,还有帧间隔。你想想看,一个系统要正常工作,总得有人发数据、有人要数据、有人报错、有人喊“慢点”,还得有人负责中场休息。这五种帧就是干这些活的。

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

数据帧,顾名思义,就是用来传输数据的。我习惯把它叫做“标准快递包裹”。

数据帧分两种格式:标准帧(11位ID)扩展帧(29位ID)。结构上大同小异,我直接给你拆开看:

标准帧结构:
SOF + 11位ID + RTR + IDE + r0 + DLC + 数据段(0-8字节) + CRC + ACK + EOF

扩展帧结构:
SOF + 11位ID + SRR + IDE + 18位ID + RTR + r1 + r0 + DLC + 数据段(0-8字节) + CRC + ACK + EOF

这里有几个关键点,我得跟你强调一下:

  • SOF(帧起始):一个显性位,告诉总线上所有人“我要发数据了”。
  • ID(标识符):决定优先级。数值越小,优先级越高。我在项目中遇到过有人把ID设成全0,结果那个节点一直抢总线,其他节点根本发不出去——嗯,这是个坑。
  • RTR(远程发送请求位):数据帧里RTR=0(显性),远程帧里RTR=1(隐性)。这是区分数据帧和远程帧的关键。
  • DLC(数据长度码):4位,表示数据段有多少字节。范围0~8,注意DLC=0也是合法的,就是发个空包。
  • 数据段:0~8字节,这就是你要传的“货”。
  • CRC(循环冗余校验):15位CRC值+1位CRC界定符。用来检查数据有没有传错。
  • ACK(应答):发送节点发隐性位,接收节点如果收到正确的帧,就在这个位置拉成显性。我曾经调试一个节点,发现它总是不应答,查了半天是CAN控制器初始化没配好——低级错误,但很隐蔽。
  • EOF(帧结束):7个隐性位,表示帧结束了。

重点记忆:数据帧的RTR位是显性(0),远程帧的RTR位是隐性(1)。这是区分两者的唯一标志。

2.2 远程帧——“给我发点数据过来”

远程帧,说白了就是“请求帧”。一个节点不发数据,而是问另一个节点:“嘿,把你那个数据给我发一份。”

远程帧的结构和数据帧几乎一样,区别只有两点:

  • RTR = 1(隐性):这是远程帧的标志。
  • 没有数据段:DLC虽然写了长度,但数据段是空的。DLC在这里表示“我想要多少字节的数据”。

举个例子:你有一个温度传感器节点,它平时不主动发数据。主控节点发一个远程帧,ID是温度传感器的ID,DLC=2,意思是“温度传感器,把你的2字节温度数据发给我”。温度传感器收到后,就会发一个数据帧回来。

我的经验:远程帧在实际项目中用得不多。我更喜欢让传感器节点定时主动上报数据,而不是靠主控去轮询。轮询会增加总线负载,而且实时性不好。但有些场合,比如诊断模式,远程帧还是很方便的。

2.3 错误帧——出问题了,大家注意!

错误帧是CAN总线最强大的特性之一。任何一个节点检测到错误,就会立刻发送错误帧,通知总线上所有节点:“刚才那帧有问题,别用了。”

错误帧由两部分组成:

  • 错误标志:6个连续相同位(显性或隐性)。主动错误节点发6个显性位,被动错误节点发6个隐性位。
  • 错误界定符:8个隐性位。

为什么会是6个?你想想看,CAN总线用的是“显性位覆盖隐性位”的机制。6个连续显性位,足以破坏任何正常帧的位填充规则(正常帧最多5个连续相同位)。这样一来,所有节点都会检测到位填充错误,进而发送自己的错误帧——这就是所谓的“错误扩散”。

避坑指南:我曾经调试过一个设备,总线上老是出现错误帧。查了半天,发现是一个节点的晶振偏差太大,导致位时序对不上。嗯,从那以后我每次选型都会仔细看晶振的精度,尤其是高温环境下的漂移。

2.4 过载帧——“慢点,我处理不过来了”

过载帧和错误帧长得很像,但用途完全不同。过载帧是接收节点主动发出的,意思是“我太忙了,你慢点发”。

过载帧的结构:

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

触发过载帧的条件有三个:

  1. 接收节点内部条件没准备好,比如缓冲区满了。
  2. 在帧间隔期间,检测到显性位(这属于总线错误,但用过载帧来处理)。
  3. 过载标志的采样问题(这个比较少见,我就不展开了)。

说实话,我在实际项目中很少见到过载帧。现在的CAN控制器缓冲区都挺大的,而且大家设计时都会算好总线负载率。如果你发现总线上频繁出现过载帧,那说明你的系统设计有问题——要么总线负载太高,要么某个节点处理能力不够。

2.5 帧间隔——中场休息

帧间隔,就是两帧之间的“空闲时间”。它让总线上的节点有时间准备接收下一帧。

帧间隔的结构:

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

对于被动错误节点,帧间隔还会多一个挂起传输场(8个隐性位)。这是对被动错误节点的“惩罚”——它发完一帧后,必须多等8个位时间才能发下一帧。

关键点:帧间隔不是可选的,是强制存在的。任何两帧之间都必须有至少3个隐性位的间歇场。否则,总线仲裁会出问题。

2.6 五种帧的关系——一张表说清楚

我整理了一张表,方便你对比记忆:

帧类型 用途 RTR位 数据段 谁发送
数据帧 传输数据 0(显性) 0~8字节 发送节点
远程帧 请求数据 1(隐性) 请求节点
错误帧 报告错误 检测到错误的节点
过载帧 请求延迟 接收节点
帧间隔 帧间分隔 总线自动产生

嗯,这一章的内容就这些。五种帧,各有各的职责。数据帧和远程帧是“业务帧”,负责干活;错误帧和过载帧是“管理帧”,负责维护秩序;帧间隔是“休息时间”,让总线喘口气。

下一章,我会带你深入CAN总线的仲裁机制——为什么两个节点同时发数据不会冲突?为什么ID小的节点优先级高?这些问题的答案,都在下一章里。