2. 车载网络基础
各位同学,今天我们来聊聊CAN总线的基础。说实话,CAN总线在车载网络里已经存在了几十年,但直到今天,它依然是车载通信的基石。我刚开始做嵌入式时,总觉得CAN很简单——不就是两根线嘛。后来踩了不少坑,才发现里面的门道真不少。
CAN 2.0A与CAN 2.0B:标准帧与扩展帧
CAN协议最早由Bosch在1986年提出,后来演变成了两个主要版本:CAN 2.0A和CAN 2.0B。它们的核心区别,说白了就是标识符(ID)的长度。
| 特性 | CAN 2.0A(标准帧) | CAN 2.0B(扩展帧) |
|---|---|---|
| ID长度 | 11位 | 29位 |
| 最大节点数 | 约2032个 | 约5.36亿个 |
| 帧格式标识 | IDE位=0 | IDE位=1 |
| 兼容性 | CAN 2.0B节点可接收 | CAN 2.0A节点无法接收 |
你可能会问:为什么需要29位ID?我在做某款高端车型的网关项目时,发现标准帧的11位ID根本不够用。一个现代汽车可能有上百个ECU,每个ECU又发送多条消息,11位ID很快就用完了。扩展帧的29位ID,给了我们足够的地址空间。
CAN帧结构:四种帧类型
CAN总线定义了四种帧类型。嗯,这里要注意,不是所有帧都用来传数据的。
1. 数据帧
数据帧是干活的主力。它把数据从发送节点送到接收节点。结构如下:
SOF + 仲裁场 + 控制场 + 数据场 + CRC场 + ACK场 + EOF
其中数据场最多8字节。为什么是8字节?我记得当年看规范时也疑惑过。其实这是为了满足实时性——数据量小,传输快,延迟低。在车载环境里,一个传感器读数通常也就几个字节,8字节够用了。
2. 远程帧
远程帧有点特别。它不携带数据,而是用来请求数据。比如某个节点想获取发动机转速,就发一个远程帧,目标节点收到后回复数据帧。
3. 错误帧
错误帧是CAN总线的自我修复机制。当节点检测到总线错误时,会主动发送错误帧,通知其他节点丢弃当前数据。错误帧由错误标志和错误界定符组成。
错误帧有主动和被动两种。主动错误帧发送6个显性位,被动错误帧发送6个隐性位。我在调试一个总线抖动问题时,发现某个节点频繁发送被动错误帧,最后定位到是它的晶振偏差太大。
4. 过载帧
过载帧用于流量控制。当接收节点来不及处理数据时,就发过载帧,让发送节点等一等。过载帧的结构和错误帧类似,但触发条件不同。
| 帧类型 | 用途 | 数据场 | 触发条件 |
|---|---|---|---|
| 数据帧 | 发送数据 | 0-8字节 | 节点主动发送 |
| 远程帧 | 请求数据 | 无 | 节点主动请求 |
| 错误帧 | 错误通知 | 无 | 检测到总线错误 |
| 过载帧 | 流量控制 | 无 | 接收节点忙 |
CAN总线仲裁机制
这是CAN总线最巧妙的设计之一。多个节点同时发送时,谁优先级高谁先发。怎么判断优先级?看ID。
CAN总线是线与逻辑:显性位(0)会覆盖隐性位(1)。仲裁时,每个节点逐位发送自己的ID,同时监听总线。如果某个节点发送了隐性位,但总线上是显性位,它就知道自己输了,立刻停止发送。
举个例子:节点A发送ID=0x123,节点B发送ID=0x124。从最高位开始比较:
- 第1位:A发0,B发0,总线=0,继续
- 第2位:A发0,B发0,总线=0,继续
- 第3位:A发1,B发1,总线=1,继续
- 第4位:A发0,B发1,总线=0(A的显性位覆盖了B的隐性位)
- B检测到冲突,停止发送,A继续
你看,整个过程不需要中央调度器,完全靠硬件自动完成。这就是CAN总线的高明之处。
我在做网关路由时,经常利用这个特性来设计消息优先级。比如制动相关的消息用低ID,车窗控制用高ID。这样即使总线负载高,关键消息也能优先通过。
这张图展示了仲裁的完整流程。节点A和B同时发送,在位4时,A发送0(显性),B发送1(隐性),总线被拉低为0。B检测到冲突,立刻退出。整个过程在几个微秒内完成。