3. CAN数据链路层:帧结构与核心机制

数据链路层,说白了就是CAN通信的“交通规则”。它规定了数据怎么打包、怎么发、怎么避免冲突。我做了这么多年CAN开发,大部分现场问题都出在这一层——不是帧结构配错了,就是仲裁没搞明白。

咱们先看整体框架,再逐个拆解。

CAN数据链路层核心知识体系 CAN数据链路层核心知识体系 数据链路层 帧结构 仲裁机制 位填充 CRC校验 数据帧 远程帧 错误帧 过载帧 CSMA/CA 非破坏性仲裁 5个相同位后插入 保证同步 15位CRC序列 CRC界定符 四种帧类型 + 仲裁 + 位填充 + CRC = CAN数据链路层的完整拼图

3.1 四种帧结构

CAN总线上一共就四种帧。我见过不少工程师把远程帧和数据帧搞混,结果调试时死活收不到响应。咱们一个一个说清楚。

3.1.1 数据帧

数据帧是干活的主力。它负责把数据从发送节点送到接收节点。标准格式有11位ID,扩展格式有29位ID。我个人习惯在乘用车项目里用标准格式,够用且效率高。

数据帧的结构分为7个段:

  • 帧起始(SOF):1个显性位,告诉总线“我要发数据了”。
  • 仲裁段:包含ID和RTR位。标准帧11位ID,扩展帧29位ID。
  • 控制段:IDE位、保留位、DLC(数据长度码)。DLC范围0~8。
  • 数据段:0~8字节,真正要传的数据。
  • CRC段:15位CRC序列 + 1位CRC界定符。
  • ACK段:1位ACK槽 + 1位ACK界定符。
  • 帧结束(EOF):7个隐性位。

关键点:数据帧的RTR位必须为显性(0),表示这是数据帧。远程帧的RTR位为隐性(1)。这是两者唯一的区别——嗯,就这一位。

3.1.2 远程帧

远程帧是用来“请求数据”的。节点发一个远程帧,告诉目标节点:“把你的数据发给我”。

我曾经在项目里遇到一个问题:某个传感器节点一直不发数据,查了半天发现是远程帧的DLC没配对。接收节点检查DLC,发现对不上,直接丢弃了请求。所以远程帧的DLC必须和目标数据帧的DLC一致。

对比项数据帧远程帧
RTR位显性(0)隐性(1)
数据段有(0~8字节)无
用途发送数据请求数据
DLC实际数据长度请求的数据长度

3.1.3 错误帧

错误帧是CAN总线的“自我修复机制”。任何节点检测到错误,就会主动发送错误帧。错误帧由两部分组成:

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

你想想看,如果总线上有节点一直发错误帧,那整个网络就瘫痪了。我遇到过这种情况——一个节点的CAN收发器坏了,不停地发错误帧,其他节点全被拖死。排查时看示波器,总线上全是显性位,根本看不到正常帧。

避坑指南:我曾经在批量生产时发现一批ECU在高温下频繁进入错误被动状态。后来查出来是晶振精度不够,导致位时序漂移。所以选晶振时一定要看全温度范围的精度,别只看25°C的标称值。

3.1.4 过载帧

过载帧是节点用来“喊停”的。当接收节点来不及处理数据时,就发过载帧让发送节点等一等。过载帧的结构和错误帧很像:

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

说实话,过载帧在实际项目中用得不多。现在的MCU处理速度都够快,很少出现来不及处理的情况。但在一些老旧的CAN控制器上,我还是见过过载帧频繁出现。

3.2 仲裁机制

仲裁是CAN总线最巧妙的设计之一。它用的是CSMA/CA(载波监听多路访问/冲突避免),而不是以太网的CSMA/CD。说白了,就是“谁优先级高谁先走”。

仲裁过程是这样的:

  1. 所有节点监听总线,空闲时才能发送。
  2. 多个节点同时发送时,从ID的最高位开始逐位比较。
  3. 显性位(0)覆盖隐性位(1)。发送隐性位的节点检测到总线是显性,就知道自己输了,立即停止发送。
  4. 赢的节点继续发送完整帧,输的节点自动转为接收。

这就是“非破坏性仲裁”——赢的节点不受任何影响,输的节点也不丢数据,只是等下次再发。我刚开始学CAN时觉得这机制太优雅了,比以太网的碰撞检测高明多了。

经验之谈:ID越小,优先级越高。所以关键信号(比如刹车、转向)要用小ID。我在设计网络拓扑时,习惯把ID按功能安全等级分配:ASIL D的节点用0x001~0x0FF,ASIL B的用0x100~0x3FF,普通信号用0x400以上。

3.3 位填充

位填充是为了保证同步。CAN节点靠电平跳变来同步时钟。如果总线上连续出现太多相同的位,节点就会失去同步。

规则很简单:连续发送5个相同位后,自动插入1个相反位。

举个例子:

原始数据:11111 00000 11111
填充后:  111110 000001 111110

接收端收到后会去掉这些填充位,还原原始数据。位填充的范围是从SOF到CRC序列结束。ACK段、EOF和帧间隔不填充。

我记得有一次调试,发现总线负载率明明不高,但错误帧很多。查了半天,原来是某个节点的数据段里连续出现了十几个0,位填充后帧长度变长,导致位时序裕量不足。后来在应用层做了数据随机化处理,问题就解决了。

注意:位填充会导致实际帧长度不确定。标准数据帧最短47位,最长111位(含填充)。设计网络负载时,建议按最坏情况(每5位插入1位)来估算。

3.4 CRC校验

CRC是CAN总线的最后一道防线。它检测传输过程中有没有比特错误。

CAN用的CRC多项式是:

CRC-15: x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1

计算范围:从SOF到数据段结束。生成15位CRC序列,放在CRC段。

接收节点用同样的多项式计算,如果结果和发送的CRC序列不一致,就说明有错误,节点会发送错误帧。

CRC的检错能力很强:

  • 能检测所有单比特错误。
  • 能检测所有双比特错误。
  • 能检测所有奇数个错误。
  • 能检测所有长度≤15的突发错误。
  • 对长度16的突发错误,检错率99.997%。

说实话,我在实际项目中很少遇到CRC校验失败的情况。但一旦遇到,往往意味着物理层有问题——比如线束接触不良、共模干扰太大。有一次在电磁兼容测试中,CRC错误率突然飙升,后来发现是屏蔽层接地没做好。

避坑指南:我曾经在某个项目中,CRC错误率一直偏高,但示波器看波形又没问题。后来发现是CAN控制器的时钟分频配错了,导致采样点位置不对。所以调试时别光看波形,一定要检查位时序配置。

3.5 本章小结

数据链路层是CAN通信的核心。四种帧各司其职:数据帧传数据,远程帧要数据,错误帧纠错,过载帧流控。仲裁机制保证了多节点无冲突通信,位填充保证了时钟同步,CRC校验保证了数据完整性。

你想想看,一个只有两根线的总线,能实现这么多功能,是不是很巧妙?下一层咱们会讲物理层,到时候你会看到CAN收发器是怎么把这些逻辑信号变成差分信号的。