2、CAN总线基础回顾:CAN协议帧结构、ID与仲裁、位填充与CRC校验、物理层

各位同学,咱们今天聊点硬核的。在开始动手搭建仿真节点之前,我建议咱们先把CAN总线的基础再过一遍。你想想看,如果连报文长什么样都不清楚,那后面调试起来肯定抓瞎。我在项目里见过太多人,上来就拖个CAN报文发出去,结果总线上一片混乱,最后发现连帧类型都选错了。

好,咱们直接进入正题。

2.1 CAN协议帧结构:四种帧类型

CAN总线上跑的数据,说白了就四种格式。我习惯把它们分成两类:一类是干活的(数据帧和远程帧),另一类是管秩序的(错误帧和过载帧)。

2.1.1 数据帧

这是最常用的帧。它负责把数据从一个节点发到另一个节点。数据帧的结构我建议你死记硬背下来,因为后面用CANalyzer抓报文时,你看到的每一段都对应着这里的一个字段。

字段 长度(标准帧) 说明
SOF 1 bit 帧起始,显性电平,告诉总线“我要发数据了”
仲裁场 12 bits 包含11位ID + RTR位(远程帧请求位)
控制场 6 bits IDE位、保留位、DLC(数据长度码)
数据场 0~64 bits 实际要传输的数据,最多8字节
CRC场 16 bits 15位CRC校验码 + 1位CRC界定符
ACK场 2 bits 接收节点确认收到,拉低总线电平
EOF 7 bits 帧结束,隐性电平

嗯,这里要注意:数据场最多8个字节。为什么是8?因为CAN总线最早是为汽车设计的,当时觉得控制信号8个字节足够了。现在虽然有些应用觉得不够用,但CAN FD(灵活数据速率)已经扩展到了64字节。不过咱们课程主要讲经典CAN,8字节够用。

2.1.2 远程帧

远程帧这东西,说白了就是“请求数据”。节点A发一个远程帧,告诉节点B:“嘿,把你那个ID为0x123的数据发给我”。

远程帧和数据帧长得几乎一样,唯一的区别是RTR位。数据帧的RTR是显性(0),远程帧的RTR是隐性(1)。

我的经验: 远程帧在实际项目中用得不多。我曾经在一个项目中看到有人用远程帧做轮询,结果总线负载率直接飙到80%。后来我建议改成事件触发,负载率降到了20%。所以,远程帧能用,但别滥用。

2.1.3 错误帧

错误帧是CAN总线自愈能力的体现。当任何一个节点检测到总线错误时,它会主动发送错误帧。错误帧由两部分组成:

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

为什么会这样设计?你想想看,6个连续的显性位在CAN总线协议里是违法的(因为位填充规则不允许连续5个相同电平)。所以一旦出现6个显性位,所有节点都知道“出事了”。

2.1.4 过载帧

过载帧用得很少。它表示节点“忙不过来,请稍等”。过载帧的结构和错误帧类似,但触发条件不同。我在实际项目中几乎没见过过载帧,因为现在的CAN控制器处理速度都很快。不过如果你在做一些老旧的ECU仿真,可能会遇到。

2.2 CAN ID与仲裁机制

这是CAN总线最精彩的部分。为什么多个节点同时发数据不会冲突?因为CAN总线用ID来决定谁有发言权。

仲裁规则很简单:ID越小,优先级越高。

具体怎么仲裁?总线电平是“线与”逻辑。显性电平(0)会覆盖隐性电平(1)。当两个节点同时发送时,它们逐位比较ID。谁先发出隐性位(1)而对方发出显性位(0),谁就输了,自动转为接收模式。

举个例子:

节点A发送ID = 0x100(二进制:001 0000 0000)

节点B发送ID = 0x200(二进制:010 0000 0000)

从高位开始比较:

  • 第1位:A发0,B发0 → 平局
  • 第2位:A发0,B发1 → A赢了,B退出

最终节点A的数据成功发送。

我记得有一次调试,客户说他的节点总是发不出数据。我一看,好家伙,他把自己的ID设成了0x7FF(标准帧最大ID)。这相当于在高速公路上开着一辆拖拉机,谁都比你快。后来改成0x100,问题解决。

2.3 位填充与CRC校验

2.3.1 位填充

位填充是CAN总线保证同步的机制。规则很简单:连续发送5个相同电平后,必须插入一个相反电平。

为什么要这么做?因为CAN节点靠电平跳变来同步时钟。如果长时间没有跳变,节点之间的时钟误差会累积,导致采样错误。

举个例子:

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

注意:填充位是自动插入的,接收端会自动移除。你在CANalyzer里看到的报文数据是不包含填充位的。

避坑指南: 我曾经遇到过一个奇怪的问题,总线偶尔出现错误帧。查了半天,发现是某个节点的晶振精度不够,导致位填充后的同步误差累积。后来换了高精度晶振,问题消失。所以,做仿真节点时,时钟精度一定要选对。

2.3.2 CRC校验

CRC(循环冗余校验)是CAN总线保证数据完整性的最后一道防线。CAN协议使用15位CRC,生成多项式是:

x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1

计算范围是从SOF到数据场结束。接收节点会重新计算CRC,如果和发送的CRC不一致,就发送错误帧。

CRC的误检率极低。我在项目中几乎没见过CRC校验失败的情况——除非硬件坏了。但话说回来,如果你在CANalyzer里看到CRC错误,别急着怀疑硬件,先检查一下你的仿真节点是不是把DLC设错了。

2.4 CAN总线物理层

物理层决定了信号怎么在线上传输。CAN总线使用差分信号,两条线:CAN_H和CAN_L。

状态 CAN_H电压 CAN_L电压 差分电压
显性(0) 3.5V 1.5V 2.0V
隐性(1) 2.5V 2.5V 0V

差分信号的好处是抗干扰能力强。你想想看,如果外界有电磁干扰,两条线上受到的干扰基本一样,差分电压不变。这就是为什么CAN总线能在汽车这种恶劣环境中稳定工作。

另外,总线两端需要各接一个120欧姆的终端电阻。为什么是120欧姆?因为CAN总线的特性阻抗大约是120欧姆,匹配电阻可以防止信号反射。我见过有人忘了接终端电阻,结果总线波形乱七八糟,通信时好时坏。

小技巧: 在CANalyzer里,你可以用“CAN Stress”工具模拟物理层问题,比如短路、断路、阻抗不匹配。我经常用这个工具来测试ECU的容错能力。建议你也试试。

好了,CAN总线的基础就回顾到这里。这些内容虽然基础,但非常重要。后面我们搭建仿真节点时,会反复用到这些知识。下一章,我们开始动手——在CANalyzer里创建第一个仿真节点。