第2章:CAN总线基础

2.1 CAN总线物理层特性

CAN总线,说白了就是汽车电子系统的"神经系统"。我最早接触CAN总线是在2008年做车身控制模块的时候,那时候还不太理解为什么非要搞这么复杂——后来踩的坑多了,才明白它的设计有多精妙。

先说说物理层。CAN总线采用差分信号传输,两条线分别叫CAN_H和CAN_L。平时没数据时,两条线都是2.5V,这叫"隐性"状态,逻辑上代表1。当有节点要发送数据时,会把CAN_H拉到3.5V,CAN_L拉到1.5V,产生1.5V的差分电压,这叫"显性"状态,逻辑上代表0。

关键点:显性位会覆盖隐性位。这个特性是后面仲裁机制的基础,你想想看,如果两个节点同时发数据,谁先发0谁就赢了。

波特率方面,CAN总线最高能到1Mbps,但实际项目中很少用那么高。我做过一个项目,客户非要跑1Mbps,结果线束稍微长一点就各种丢帧。后来老老实实降到500kbps,稳得很。

波特率最大总线长度典型应用场景
125 kbps500米车身控制、舒适系统
250 kbps250米诊断通信、仪表系统
500 kbps100米动力系统、底盘控制
1 Mbps40米测试台架、短距离高速通信

实战经验:我个人习惯在项目初期就定好波特率,因为后期改波特率要动所有节点的配置,特别麻烦。曾经有个项目,因为一个供应商的MCU不支持500kbps,硬生生把整个网络降到了250kbps。

2.2 CAN报文帧结构

CAN总线有四种帧类型:数据帧、远程帧、错误帧、过载帧。平时打交道最多的就是数据帧和远程帧。

2.2.1 数据帧

数据帧是干活的主力。标准格式有11位ID,扩展格式有29位ID。我建议新手先搞懂标准格式,扩展格式无非是多了一些ID位而已。

数据帧的结构是这样的:

SOF + 11位ID + RTR + IDE + r0 + 4位DLC + 0~8字节数据 + 15位CRC + CRC分隔符 + ACK + ACK分隔符 + EOF

嗯,这里要注意几个关键字段:

  • SOF(帧起始):一个显性位,告诉所有节点"我要发数据了"
  • ID(标识符):11位,决定了报文的优先级。数值越小,优先级越高
  • RTR(远程帧标志):数据帧里是0,远程帧里是1
  • DLC(数据长度码):4位,表示数据场有多少字节。范围0~8
  • 数据场:0~8字节,真正要传的数据
  • CRC:15位校验,加上1位分隔符
  • ACK:接收节点会在这一位发显性位,告诉发送方"我收到了"

避坑指南:我曾经遇到过一个诡异的问题,某个节点偶尔会收不到报文。查了两天才发现,是ACK位时序有问题,接收节点回复ACK的时候慢了半拍。所以做CAN驱动时,ACK的响应时间一定要卡死。

2.2.2 远程帧

远程帧说白了就是"请求帧"。一个节点发远程帧,就是告诉另一个节点:"把你的数据发给我"。远程帧没有数据场,DLC表示请求的数据长度。

我实际项目中远程帧用得不多,因为大多数场景都是节点主动发数据。但诊断协议里偶尔会用,比如请求某个节点的版本信息。

2.2.3 错误帧

错误帧是CAN总线的"交警"。当节点检测到总线错误时,会主动发错误帧。错误帧由6个连续的显性位组成,这违反了CAN的位填充规则,所以其他节点也会检测到错误,跟着发错误帧。

错误帧分两种:

  • 主动错误帧:6个显性位 + 8个隐性位
  • 被动错误帧:6个隐性位 + 8个隐性位

节点有个错误计数器,连续出错超过一定次数就会从"主动错误"变成"被动错误",再严重就直接"总线关闭"了。我见过一个项目,因为线束接触不良,某个节点反复发错误帧,最后把自己关掉了。

2.2.4 过载帧

过载帧用得最少。它表示节点"忙不过来了",需要延迟一下。结构跟错误帧类似,但触发条件不同。说实话,我做了十几年CAN,真正遇到过载帧的情况不超过三次。

2.3 CAN总线仲裁机制

仲裁机制是CAN总线最巧妙的设计之一。你想想看,多个节点同时发数据怎么办?CAN的答案是:谁优先级高谁先发。

仲裁过程是这样的:

  1. 所有节点同时发送SOF位,都是显性位,没问题
  2. 然后开始逐位发送ID。如果某个节点发送的是隐性位(1),但总线上检测到显性位(0),说明有更高优先级的节点在发数据
  3. 这个节点立刻停止发送,转为接收模式
  4. 优先级最高的节点继续发送,完全不受影响

说白了,这就是"非破坏性逐位仲裁"。ID数值越小,优先级越高。所以0x000是最高优先级,0x7FF是最低优先级。

设计启示:我在设计网关路由策略时,会把关键报文(比如制动、转向相关的)分配小ID,把非关键报文(比如车窗、座椅)分配大ID。这样即使总线负载很高,关键报文也能优先通过。

举个例子:

节点A发送ID = 0x123(二进制:001 0010 0011)
节点B发送ID = 0x124(二进制:001 0010 0100)

逐位比较:
第1位:0 vs 0 → 相同,继续
第2位:0 vs 0 → 相同,继续
第3位:1 vs 1 → 相同,继续
...
第9位:0 vs 1 → 节点A是0(显性),节点B是1(隐性)
→ 节点B检测到总线是显性,知道自己输了,立刻停止发送
→ 节点A继续发送剩余位

整个过程在微秒级别完成,对应用层完全透明。这就是为什么CAN总线不需要像以太网那样搞CSMA/CD碰撞检测——它根本就不会碰撞。

个人经验:做网关设计时,我建议把网关节点的ID设得小一些。因为网关要转发很多报文,如果它的ID太大,容易被其他节点"欺负",导致转发延迟。我曾经在一个项目中把网关ID设成了0x700,结果发现转发延迟不稳定,后来改到0x100就好了。

嗯,CAN总线的基础就这些。理解物理层特性、帧结构和仲裁机制,是做车载通信网关的前提。下一章我们会讲CAN控制器和收发器的选型,以及实际电路设计中的注意事项。