3. CAN数据链路层:CAN帧格式分类、数据帧结构详解、远程帧、错误帧、过载帧、帧间隔

好,咱们今天聊聊CAN总线最核心的部分——数据链路层。说白了,这一层就是定义“数据长什么样、怎么发、怎么收”的规则。我刚开始接触CAN的时候,总觉得帧格式特别绕,后来亲手抓了几次波形,才真正搞明白。你想想看,总线上的电平跳来跳去,如果没有统一的格式,那不就乱套了吗?

3.1 CAN帧格式分类:五种帧,各司其职

CAN总线上一共定义了五种帧。嗯,这里要注意,不是所有帧都在传数据。有些帧是拿来“吵架”的,有些是拿来“喊停”的。我个人习惯把它们分成两类:一类是正经传数据的,另一类是管秩序的。

  • 数据帧:发送节点把数据扔到总线上,接收节点拿走。这是最常用的帧。
  • 远程帧:节点说“喂,谁有数据?给我发一份”。它自己不传数据,只发请求。
  • 错误帧:节点发现总线出错了,立刻广播“停!刚才那包有问题!”
  • 过载帧:节点忙不过来了,说“慢点慢点,我消化不了”。
  • 帧间隔:不是真正的帧,是帧与帧之间的“喘口气”时间。

核心要点:数据帧和远程帧是“业务帧”,错误帧和过载帧是“管理帧”。帧间隔是“分隔符”。

3.2 数据帧结构详解:从SOF到ACK,一个都不能少

数据帧是咱们最常打交道的。我当年调试一个电机控制器,就是靠分析数据帧的ACK位,才发现对方节点根本没收到报文。咱们一步步拆开看。

一个标准数据帧(11位ID)的结构如下:

SOF + 11位ID + RTR + IDE + r0 + 4位DLC + 0~8字节数据 + 15位CRC + CRC分隔符 + ACK槽 + ACK分隔符 + EOF

咱们挑几个重点说说:

  • SOF(帧起始):一个显性位(0)。告诉所有节点:“注意,我要开始说话了!”
  • ID(标识符):11位(标准帧)或29位(扩展帧)。这玩意儿决定了优先级。ID越小,优先级越高。我在项目中遇到过两台设备同时发数据,就是靠ID仲裁,谁ID小谁先走。
  • RTR(远程发送请求位):数据帧里是显性(0),远程帧里是隐性(1)。这是区分数据帧和远程帧的关键。
  • DLC(数据长度码):4位,表示后面跟了多少字节数据。范围是0~8。注意,DLC可以写0,表示不传数据,但帧结构还在。
  • 数据段:0~8字节。这就是咱们真正关心的内容。比如车速、转速、温度,都塞在这里。
  • CRC(循环冗余校验):15位CRC值 + 1位分隔符。用来检查数据有没有传错。我曾经遇到一个案例,CRC算出来总是不对,最后发现是晶振频率漂了。
  • ACK(应答):发送节点发隐性位,接收节点如果收到正确的帧,就在ACK槽里拉成显性。如果没人应答,发送节点就知道“坏了,没人理我”。
  • EOF(帧结束):7个隐性位。表示“我说完了,下一个谁上?”

实战技巧:抓波形时,先找SOF的下降沿(从隐性变显性),然后数11位ID,再找RTR位。熟练了以后,一眼就能看出帧类型和ID值。

3.3 远程帧:只问不答

远程帧的结构跟数据帧几乎一样,唯一的区别是RTR位是隐性(1),而且没有数据段。你想想看,它本来就是来要数据的,自己当然不带数据。

举个例子:节点A想获取节点B的温度值。节点A就发一个远程帧,ID填温度对应的ID,DLC填0。节点B收到后,如果它支持这个ID,就会发一个数据帧回来,把温度值带上。

避坑指南:我曾经犯过一个错——远程帧的DLC必须跟对方数据帧的DLC一致。比如对方发8字节数据,你远程帧的DLC也得写8。否则有些控制器会不理你。嗯,这个坑我踩过。

3.4 错误帧:总线的“紧急刹车”

错误帧是CAN总线最牛的设计之一。任何节点发现错误,都可以立刻打断当前传输。错误帧由两部分组成:

  • 错误标志:6个相同位(显性或隐性,取决于错误类型)。
  • 错误分隔符:8个隐性位。

错误帧有几种触发场景:

  1. 位错误:发送节点发现总线上的电平跟自己发的不一样。比如你发了显性,却读到隐性,那肯定出问题了。
  2. 填充错误:CAN总线规定,连续5个相同位后必须插一个相反位。如果没插,就是填充错误。
  3. CRC错误:接收节点算出来的CRC跟发过来的对不上。
  4. 格式错误:帧格式不对,比如EOF不是7个隐性位。
  5. ACK错误:发送节点没收到应答。

关键点:错误帧一旦发出,当前帧就作废了。发送节点会自动重发。但如果错误太多,节点会进入“总线关闭”状态,彻底闭嘴。我见过一个项目,就是因为线束接触不良,导致节点反复报错,最后把自己关掉了。

3.5 过载帧:别催,我忙

过载帧跟错误帧长得有点像,但用途完全不同。过载帧是节点主动说“我忙不过来,等一下”。它由两部分组成:

  • 过载标志:6个显性位。
  • 过载分隔符:8个隐性位。

过载帧通常在两种情况下触发:

  1. 节点内部接收缓冲区满了,来不及处理。
  2. 节点在帧间隔期间检测到一个显性位(正常情况下帧间隔应该是隐性的)。

说实话,过载帧在实际项目中不太常见。现在的CAN控制器处理能力都很强,缓冲区也大。但如果你在做一些低成本的MCU方案,还是要注意一下。

3.6 帧间隔:总线的“中场休息”

帧间隔不是真正的帧,它是帧与帧之间的空闲时间。说白了,就是让总线喘口气,也让节点有时间准备下一帧。

帧间隔由三部分组成:

  • 间歇场:3个隐性位。
  • 总线空闲:任意长度的隐性位。只要没人发数据,总线就一直保持隐性。
  • 暂停发送场:只有被动错误节点才有。8个隐性位,让它在重发前多等一会儿。

小技巧:抓波形时,如果你看到一段长时间的隐性电平,那就是总线空闲。这时候任何节点都可以开始发送。我习惯用这个来判断总线负载率——空闲时间越短,负载越高。

3.7 实战总结:一张表看懂所有帧

为了方便你记忆,我整理了一张对比表。这张表我当年贴在工位上,看了好几个月才彻底记住。

帧类型 用途 数据段 RTR位 触发方式
数据帧 发送数据 0~8字节 显性(0) 节点主动发送
远程帧 请求数据 隐性(1) 节点主动发送
错误帧 报告错误 检测到错误时自动触发
过载帧 请求延迟 节点忙或检测到异常
帧间隔 帧间分隔 每帧结束后自动出现

好了,这一章的内容就到这儿。数据链路层是CAN总线的灵魂,搞懂了帧结构,你就能看懂波形图上那些跳来跳去的电平到底在说什么。下一章咱们聊聊物理层,看看这些0和1到底是怎么在差分线上跑的。