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

好,咱们今天聊聊CAN总线最核心的部分——数据链路层。说白了,这一层就是定义了“数据长什么样、怎么发、怎么收”。你想想看,一堆节点挂在同一条总线上,如果没有统一的规矩,那不乱套了?

我个人习惯把数据链路层比作“快递系统”。帧结构就是快递单的格式,数据帧是普通包裹,远程帧是“我要查单”的请求,错误帧是包裹破损的报警,过载帧是快递站忙不过来了。嗯,这么一比喻,是不是好理解多了?

3.1 CAN帧结构:快递单的格式

CAN总线上一共定义了5种帧,分别是:

  • 数据帧:发送节点把数据传给接收节点
  • 远程帧:节点请求其他节点发送数据
  • 错误帧:检测到总线错误时发送
  • 过载帧:接收节点忙不过来时发送
  • 帧间隔:帧与帧之间的“喘息时间”

每种帧都有自己的结构,但都遵循一个基本格式。我刚开始学CAN的时候,最头疼的就是记这些位域。后来发现,其实只要抓住几个关键点就行。

3.2 数据帧格式:最常用的帧

数据帧分两种:标准帧(11位ID)和扩展帧(29位ID)。我在项目中遇到过不少新手,上来就用扩展帧,其实很多场景下标准帧完全够用。

数据帧的结构如下:

位域 位数 说明
帧起始(SOF) 1 显性位,同步所有节点
仲裁场 12或32 包含ID和RTR位
控制场 6 包含IDE、DLC等
数据场 0-64 实际数据,最多8字节
CRC场 16 15位CRC + 1位界定符
应答场 2 ACK槽 + ACK界定符
帧结束(EOF) 7 隐性位

关键点:数据场最多8字节。为什么是8?因为CAN总线设计之初,主要面向汽车控制,8字节足够传递发动机转速、车速、温度等关键参数。多了反而影响实时性。

我曾经在一个项目中,客户非要传10字节的数据。我说不行,CAN帧最多8字节。最后我们拆成两帧发,接收端再拼起来。嗯,这也是个办法,但要注意帧之间的时序关系。

3.3 远程帧:我要数据,你给我发

远程帧的结构和数据帧几乎一样,区别在于:

  • RTR位为隐性(数据帧是显性)
  • 没有数据场

你想想看,远程帧就像你对着对讲机喊:“老王,把温度数据发过来!”老王听到后,就会发一个数据帧给你。

避坑指南:我曾经犯过一个错误——远程帧的DLC必须和对应数据帧的DLC一致。比如你请求一个4字节的数据,远程帧的DLC就要设成4。否则,接收节点可能不知道要发多少数据。

3.4 错误帧:出问题了,大家注意

错误帧由两部分组成:

  • 错误标志:6个连续显性位
  • 错误界定符:8个连续隐性位

为什么会这样?因为CAN总线采用“错误主动”和“错误被动”两种机制。当节点检测到错误时,会发送错误标志。其他节点收到后,也会发送错误标志。这样一来,总线上就会出现6-12个显性位。

我记得有一次调试,总线上老是出现错误帧。查了半天,发现是一个节点的终端电阻没焊好。嗯,硬件问题往往比软件问题更难排查。

注意:错误帧不是用来“通知”的,而是用来“破坏”的。它通过破坏当前帧的CRC或填充规则,让所有节点都意识到出错了。然后大家一起重发。

3.5 过载帧:我忙,等等

过载帧的结构和错误帧很像,也是6个显性位 + 8个隐性位。但触发条件不同:

  • 接收节点还没准备好接收下一帧
  • 检测到位填充错误(但还没到发送错误帧的程度)

说白了,过载帧就是节点在说:“慢点慢点,我处理不过来了!”

我在实际项目中很少见到过载帧。因为现在的CAN控制器处理速度都很快,除非你设计得特别不合理——比如中断优先级设得太低,导致CAN接收中断被长时间屏蔽。

3.6 帧间隔:喘口气

帧间隔是帧与帧之间的空闲时间。它由三部分组成:

  • 间歇场:3个隐性位
  • 总线空闲:任意长度的隐性位
  • 暂停发送:仅错误被动节点需要,8个隐性位

你想想看,如果没有帧间隔,节点之间怎么区分“这一帧结束”和“下一帧开始”?帧间隔就是那个“句号”。

重要:帧间隔不是可选的,是强制存在的。任何节点在发送完一帧后,都必须等待至少3个位时间的间歇场,才能发送下一帧。这是CAN协议的基本规则。

嗯,到这里,CAN数据链路层的五种帧就讲完了。我个人觉得,理解这些帧的关键不在于死记硬背位域,而在于理解它们为什么存在。数据帧是干活的主力,远程帧是请求,错误帧和过载帧是异常处理,帧间隔是规矩。

下一章,咱们聊聊CAN的位时序和同步。那才是真正考验功底的地方。