2、CAN协议基础:从物理层到仲裁机制

大家好,我是你们的车载网络讲师。今天咱们来聊聊CAN协议的基础。说实话,CAN协议在汽车圈里摸爬滚打了几十年,至今仍是车载网络的中流砥柱。我个人习惯把CAN协议比作一个“老练的交通警察”——它不花哨,但绝对可靠。

咱们这一章,重点攻克四个核心:物理层特性、报文帧格式、还有那个让CAN脱颖而出的仲裁机制。嗯,内容有点多,但都是硬货。

2.1 CAN总线物理层特性:为什么它能抗干扰?

先说说物理层。你想想看,汽车发动机舱里电磁干扰那么严重,CAN凭什么能稳定通信?

CAN总线用的是差分信号传输。说白了,就是两根线——CAN_H和CAN_L。信号不是靠一根线的电压高低,而是靠两根线的电压差来传递。

关键点: CAN总线有两种逻辑状态:

  • 显性(Dominant): CAN_H ≈ 3.5V,CAN_L ≈ 1.5V,电压差约2V,逻辑“0”
  • 隐性(Recessive): CAN_H ≈ 2.5V,CAN_L ≈ 2.5V,电压差约0V,逻辑“1”

我在项目中遇到过一件事:有次台架测试,CAN通信老是丢帧。排查了半天,发现是CAN_H和CAN_L线束绞合得太松了。双绞线绞合不紧密,共模抑制能力就会下降。从那以后,我要求团队必须严格按规范绞合,每米至少绞30圈。

避坑指南: 我曾经见过有人把CAN_H和CAN_L接反了。结果呢?总线完全无法通信。记住:CAN_H接CAN_H,CAN_L接CAN_L,别搞混。另外,终端电阻(120Ω)一定要加在总线两端,少一个都不行。

CAN总线的通信速率和总线长度成反比。速率越高,允许的总线长度越短。比如125kbps时,总线可以到500米;但到了1Mbps,总线长度就不能超过40米。这个在整车布线时一定要算清楚。

2.2 CAN报文帧格式:四种帧,各司其职

CAN协议定义了四种帧:数据帧、远程帧、错误帧、过载帧。咱们一个一个说。

2.2.1 数据帧:最常用的“快递员”

数据帧是干活最多的那个。它负责把数据从一个节点送到另一个节点。标准格式的数据帧长这样:

SOF | 11位ID | RTR | IDE | r0 | DLC | 数据段(0-8字节) | CRC | ACK | EOF

我解释一下关键字段:

  • SOF(Start of Frame): 1位显性位,告诉所有节点“我要发数据了”
  • ID(标识符): 标准帧11位,扩展帧29位。ID越小,优先级越高
  • RTR(Remote Transmission Request): 数据帧里是显性(0),远程帧里是隐性(1)
  • DLC(Data Length Code): 4位,表示数据段有多少字节。范围0-8
  • 数据段: 0到8字节,这就是真正的“货物”
  • CRC: 15位校验码,加上1位定界符
  • ACK: 2位。发送节点发隐性位,接收节点如果正确接收,就拉成显性位
  • EOF(End of Frame): 7位隐性位,表示帧结束

注意: 数据段最多8字节。为什么是8?因为CAN设计之初,考虑到实时性要求,一帧数据必须在短时间内发完。8字节在当时的应用场景下足够了。现在虽然有些应用觉得8字节不够用,但CAN FD(灵活数据速率)已经把这个限制扩展到了64字节。

2.2.2 远程帧:请求数据的“问路人”

远程帧很有意思。它本身不带数据,作用是请求其他节点发送数据。比如一个传感器节点,平时不主动发数据,但ECU可以通过远程帧问它:“嘿,把你的温度数据发给我。”

远程帧和数据帧的结构几乎一样,唯一的区别是RTR位是隐性(1)。而且远程帧没有数据段,DLC字段表示请求的数据长度。

我记得有一次调试,一个节点怎么都不响应远程帧。查了半天,发现是那个节点的软件里没实现远程帧处理逻辑。嗯,很多初学者会忽略这个细节。

2.2.3 错误帧:总线的“纠错员”

错误帧是CAN协议健壮性的体现。任何节点检测到总线错误,都会立即发送错误帧。错误帧由两部分组成:

  • 错误标志: 6个连续相同位(显性或隐性)
  • 错误定界符: 8个隐性位

CAN协议有五种错误检测机制:位错误、填充错误、CRC错误、格式错误、ACK错误。我建议你重点记住前三个,项目中最常见。

警告: 如果一个节点频繁发送错误帧,它可能会进入“总线关闭”状态。这时候节点会彻底断开与总线的连接。我曾经在实车上遇到过,一个故障的ABS控制器把整个CAN总线都拉垮了,最后只能换控制器。

2.2.4 过载帧:总线的“慢下来”信号

过载帧用得比较少。它表示一个节点还没准备好接收下一帧数据,需要总线“等一下”。过载帧的结构和错误帧类似,也是6个显性位加8个隐性位。

说实话,我在实际项目中几乎没见过过载帧。现在的MCU处理速度都很快,很少出现来不及处理的情况。但作为协议的一部分,你得知道它的存在。

2.3 CAN总线仲裁机制:为什么不会“撞车”?

这是CAN协议最巧妙的地方。多个节点同时发送数据时,总线怎么决定谁先发?

答案是:基于ID的逐位仲裁。

过程是这样的:

  1. 所有想发送的节点同时发送SOF位(显性),同步时钟
  2. 然后从ID的最高位开始,每个节点逐位发送自己的ID
  3. 如果一个节点发送了隐性位(1),但总线上检测到显性位(0),它就立刻退出竞争
  4. 最后,ID最小的节点赢得仲裁,继续发送剩余数据

你想想看,这个过程完全是无损的。输掉仲裁的节点不会丢失数据,它会在总线空闲后自动重发。这就是CAN的“非破坏性逐位仲裁”。

核心结论: ID值越小,优先级越高。0x000是最高优先级,0x7FF是最低优先级(标准帧)。所以设计CAN网络时,要把关键信号(比如刹车、油门)分配小ID,非关键信号(比如车窗、空调)分配大ID。

我在设计一个混合动力系统的CAN网络时,就把电机控制器的ID设成了0x001,电池管理系统的ID设成了0x002,而空调控制器的ID设成了0x100。为什么?因为电机控制器的实时性要求最高,必须优先响应。

个人经验: 我曾经遇到过一个仲裁相关的bug。两个节点ID不同,但其中一个节点在发送ID时,由于时钟漂移,导致位时序错位,仲裁结果不稳定。后来在软件里加了同步机制才解决。所以,CAN节点的时钟精度很重要,建议使用晶振而不是内部RC振荡器。

好了,这一章的内容就到这里。CAN协议的基础打牢了,后面学CAN FD、CANopen、J1939就会轻松很多。下一章咱们聊聊CAN的位时序和同步机制,那才是真正考验功底的地方。