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

好,咱们今天聊聊CAN协议里最核心的东西——帧结构。

说实话,我刚入行那会儿,对着CAN协议手册看了三天,愣是没搞明白数据帧里那些位到底是干嘛的。后来在项目里调了一周的CAN通信,才真正把这些帧结构刻在脑子里。

CAN总线上一共就五种帧:数据帧、远程帧、错误帧、过载帧、帧间隔。你想想看,整个车载网络,成千上万个报文,翻来覆去就是这五种。搞懂了它们,CAN通信你就通了七成。

2.1 CAN数据帧结构

数据帧,这是咱们用得最多的。说白了,就是发数据用的。

CAN 2.0规范里,数据帧分两种:标准帧扩展帧。区别就在ID长度上——标准帧11位ID,扩展帧29位ID。

标准数据帧结构(从SOF到ACK,一共108位):

  • SOF(Start of Frame):1位,显性电平。告诉总线上所有节点:我要开始说话了。
  • 仲裁场:12位。包含11位ID + 1位RTR。RTR=0表示数据帧。
  • 控制场:6位。包含IDE位、保留位r0、DLC(数据长度码,4位)。DLC范围0~8。
  • 数据场:0~64位(0~8字节)。这就是你要传的实际数据。
  • CRC场:16位。15位CRC校验 + 1位CRC界定符(隐性)。
  • ACK场:2位。ACK槽 + ACK界定符。发送节点发隐性,接收节点拉显性表示收到。
  • EOF(End of Frame):7位隐性电平。

扩展帧呢?结构稍微复杂点。仲裁场变成了32位——29位ID + SRR位 + IDE位 + RTR位。控制场里多了一个r1保留位。

我个人的习惯是:能用标准帧就别用扩展帧。为什么?标准帧效率高,总线负载小。除非你的节点实在太多,11位ID不够用了,才去碰扩展帧。

实战小技巧:

我在做车身控制项目时,遇到过一个问题:两个ECU用同一个ID发数据,结果总线直接挂了。后来排查发现,是DBC文件里ID分配冲突了。所以啊,ID分配一定要做文档管理,别靠脑子记。

2.2 CAN远程帧

远程帧,很多人叫它“请求帧”。它的作用是:一个节点主动向另一个节点要数据

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

  • RTR位 = 1(隐性)。数据帧的RTR=0。
  • 没有数据场。DLC虽然写了长度,但实际不发数据。

举个例子:

// 假设节点A想请求节点B的0x123这个ID的数据
// 节点A发送远程帧:ID=0x123, RTR=1, DLC=8
// 节点B收到后,如果它支持这个ID,就会发一个数据帧回来

嗯,这里要注意:远程帧的DLC必须和对应数据帧的DLC一致。我曾经见过一个新手,远程帧DLC写了个0,结果对方节点死活不回数据。查了半天,原来是DLC不匹配。

避坑指南:

我曾经在一个项目里滥用远程帧,结果总线负载直接飙到80%。后来我学乖了——能用周期性发送解决的,就别用远程帧。远程帧适合那种“偶尔需要、不常用”的数据请求。

2.3 CAN错误帧

错误帧,这是CAN总线自己“纠错”的机制。你想想看,车载环境那么恶劣,电磁干扰、电压波动,报文出错是常有的事。

错误帧的结构很简单:

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

CAN协议定义了五种错误类型:

错误类型 检测位置 说明
位错误 发送节点 发送的位和总线上的位不一致
填充错误 所有节点 连续6个相同电平,违反位填充规则
CRC错误 接收节点 CRC校验不通过
形式错误 所有节点 固定格式的位(如EOF、界定符)电平不对
应答错误 发送节点 ACK槽没有被拉显性(没人应答)

我遇到过最头疼的问题就是位错误。有一次,一个节点在发送时,总线上的电平跟它发的不一样,它自己就疯狂发错误帧。结果整个总线都被它拖垮了。后来发现是那个节点的CAN收发器坏了,输出驱动能力不足。

错误状态机:

每个CAN节点都有三个状态:主动错误、被动错误、总线关闭

  • 主动错误:可以正常收发,出错时发主动错误帧(6个显性位)。
  • 被动错误:只能收,发的时候出错只能发被动错误帧(6个隐性位)。
  • 总线关闭:彻底不能参与通信。需要硬件复位或128个11位隐性位才能恢复。

2.4 CAN过载帧

过载帧,这个用得比较少。它的作用是:接收节点告诉发送节点“我忙不过来,你慢点”

过载帧的结构:

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

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

  1. 接收节点内部条件没准备好(比如接收缓冲区满了)。
  2. 在帧间隔期间,检测到显性位(这属于非法情况)。

说实话,我在实际项目中几乎没见过过载帧。现在的MCU处理能力都够强,CAN控制器也都有硬件FIFO,很少会出现“忙不过来”的情况。但你要知道有这么个东西,万一哪天调试时看到总线上冒出过载帧,别慌。

个人经验:

如果你在总线上频繁看到过载帧,大概率不是真的“过载”,而是硬件问题。比如CAN收发器的TXD/RXD引脚虚焊,或者终端电阻没匹配好。先查硬件,再查软件。

2.5 CAN帧间隔

帧间隔,就是两帧之间的“休息时间”。

帧间隔的结构:

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

对于被动错误节点,帧间隔里还会多一个挂起传输场(8个隐性位)。也就是说,被动错误节点在发完一帧后,必须等8个额外的隐性位才能发下一帧。

为什么要这样设计?说白了,就是惩罚那些老出错的节点。你老发错误帧,那就让你少说话,给其他节点让路。

避坑指南:

我曾经调试一个项目,发现某个节点发送间隔特别长。用示波器一抓,发现它每次发完数据帧,都要等11个隐性位才发下一帧。查了手册才知道,这个节点进入了被动错误状态。后来换了CAN收发器,问题解决。

所以啊,如果你发现某个节点发送频率突然变慢,先查它的错误计数器。大概率是它已经“被动”了。

小结

好了,五种帧结构咱们都过了一遍。我总结一下:

  • 数据帧:发数据,最常用。记住标准帧和扩展帧的区别。
  • 远程帧:要数据,RTR=1,无数据场。别滥用。
  • 错误帧:纠错机制。五种错误类型,三种错误状态。
  • 过载帧:忙不过来时用。实际项目中很少见。
  • 帧间隔:帧与帧之间的休息时间。被动错误节点有额外惩罚。

下一章,咱们聊聊CAN的位时序和同步。那个才是真正让人头疼的东西。不过别怕,跟着我一步步来,保证你能搞明白。

课后思考:

如果你在总线上看到连续6个显性位,这属于哪种帧?是错误帧还是过载帧?怎么区分?

提示:看这6个显性位之后跟着的是什么。如果是8个隐性位,那就是错误帧或过载帧。如果是7个隐性位,那就是数据帧的EOF被破坏了。