第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 kbps | 500米 | 车身控制、舒适系统 |
| 250 kbps | 250米 | 诊断通信、仪表系统 |
| 500 kbps | 100米 | 动力系统、底盘控制 |
| 1 Mbps | 40米 | 测试台架、短距离高速通信 |
实战经验:我个人习惯在项目初期就定好波特率,因为后期改波特率要动所有节点的配置,特别麻烦。曾经有个项目,因为一个供应商的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的答案是:谁优先级高谁先发。
仲裁过程是这样的:
- 所有节点同时发送SOF位,都是显性位,没问题
- 然后开始逐位发送ID。如果某个节点发送的是隐性位(1),但总线上检测到显性位(0),说明有更高优先级的节点在发数据
- 这个节点立刻停止发送,转为接收模式
- 优先级最高的节点继续发送,完全不受影响
说白了,这就是"非破坏性逐位仲裁"。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控制器和收发器的选型,以及实际电路设计中的注意事项。