2. CAN协议详解:从物理层到仲裁机制
大家好,我是你们的车载通信架构讲师。今天我们来啃一块硬骨头——CAN协议。说实话,CAN协议在汽车圈摸爬滚打了几十年,至今仍是车载网络的中流砥柱。我入行那会儿,第一个项目就是调试CAN总线,当时被各种帧结构搞得晕头转向。今天咱们就把CAN协议彻底讲透。
2.1 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通信频繁丢帧。排查了半天,发现是终端电阻的温漂导致信号反射。嗯,这里要注意,CAN总线的终端电阻(120Ω)一定要选低温漂的型号,别为了省几毛钱给自己挖坑。
2.2 CAN数据链路层:帧结构与位填充
数据链路层,我习惯把它拆成两部分看:帧结构和位填充规则。咱们先看帧结构。
2.2.1 数据帧(Data Frame)
数据帧是咱们用得最多的。它负责把数据从发送节点传到接收节点。标准帧有11位ID,扩展帧有29位ID。我个人建议,新项目直接用扩展帧,别给自己留后患。
数据帧的结构如下:
| 字段 | 位数 | 说明 |
|---|---|---|
| SOF | 1 | 帧起始,显性位 |
| 仲裁场 | 12或32 | ID + RTR + IDE + r0 |
| 控制场 | 6 | DLC + r1/r0 |
| 数据场 | 0~64 | 实际数据,最多8字节 |
| CRC场 | 16 | CRC序列 + CRC界定符 |
| ACK场 | 2 | ACK槽 + ACK界定符 |
| EOF | 7 | 帧结束,隐性位 |
避坑指南:我曾经在项目中遇到DLC(数据长度码)设置错误的问题。DLC可以设置0~8,但有些节点只支持特定长度。如果你设了DLC=5,但实际只发了3个字节,接收方可能会读到垃圾数据。记住:DLC必须与实际数据长度一致。
2.2.2 远程帧(Remote Frame)
远程帧,说白了就是“请求帧”。一个节点发远程帧,另一个节点收到后,会发一个同ID的数据帧作为响应。远程帧没有数据场,RTR位为隐性(1)。
我刚开始做CAN开发时,总觉得远程帧很鸡肋。后来在诊断协议(UDS)中频繁用到它,才明白它的价值。比如ECU请求另一个ECU上报故障码,用远程帧就很优雅。
2.2.3 错误帧(Error Frame)
错误帧是CAN总线的“纠错机制”。当节点检测到总线错误时,会主动发送错误帧。错误帧由两部分组成:
- 错误标志:6个连续显性位(主动错误)或6个连续隐性位(被动错误)
- 错误界定符:8个隐性位
这里有个细节:主动错误节点发送6个显性位,会破坏当前帧的位填充规则,迫使其他节点也检测到错误。这就是所谓的“错误扩散”。
警告:如果一个节点频繁发送错误帧,它可能会进入“总线关闭”状态。我曾经见过一个案例,某个ECU的CAN收发器坏了,不停地发错误帧,把整条总线都拖垮了。排查时发现总线利用率只有30%,但错误帧占了80%。所以,监控错误计数器是很有必要的。
2.2.4 过载帧(Overload Frame)
过载帧,用于节点在接收数据时请求延迟。它和错误帧结构类似,但触发条件不同。过载帧在实际项目中用得不多,但你要知道它的存在。我记得有一次调试,发现某个节点总是发过载帧,后来发现是它的接收缓冲区太小,处理不过来。嗯,硬件资源规划要留余量。
2.3 CAN总线仲裁机制:谁先说话?
CAN总线的仲裁机制,是我觉得最巧妙的设计之一。它基于“线与”特性:显性位(0)会覆盖隐性位(1)。
仲裁过程是这样的:
- 多个节点同时发送数据时,它们会逐位比较ID
- 如果某个节点发送了隐性位(1),但总线上是显性位(0),说明有更高优先级的节点在发送
- 该节点立即停止发送,转为接收模式
- 优先级最高的节点(ID最小)赢得仲裁,继续发送
你想想看,这个过程完全是无损的。赢得仲裁的节点继续发它的数据,输了的节点下次再试。没有冲突,没有重传,效率极高。
核心要点:CAN总线的优先级由ID决定。ID越小,优先级越高。所以,关键信号(如刹车、转向)要用小ID,非关键信号(如车窗、座椅)用大ID。
我在设计车载网络时,习惯把ID分配做成一张表,按优先级排序。比如:
| 信号类型 | ID范围 | 优先级 |
|---|---|---|
| 安全相关(刹车、气囊) | 0x000~0x0FF | 最高 |
| 动力相关(发动机、变速箱) | 0x100~0x2FF | 高 |
| 车身相关(灯光、雨刮) | 0x300~0x4FF | 中 |
| 舒适相关(空调、座椅) | 0x500~0x6FF | 低 |
| 诊断相关 | 0x700~0x7FF | 最低 |
曾经有个项目,工程师把刹车信号的ID设成了0x500,结果在总线负载高时,刹车指令被延迟了。嗯,这种错误一旦出现,后果不堪设想。所以,ID分配一定要慎之又慎。
2.4 位填充与同步机制
CAN总线还有一个重要机制:位填充。规则很简单:连续发送5个相同位后,自动插入一个相反位。目的是保证时钟同步。
为什么会这样?因为CAN总线没有单独的时钟线,接收方靠检测信号边沿来同步时钟。如果连续发送太多相同位,接收方可能会失步。位填充确保了总线上有足够的跳变沿。
我记得有一次调试,发现某个节点在发送大量0x00数据时,总线波形异常。后来发现是位填充机制导致的。嗯,这个细节在分析总线波形时很有用。
2.5 小结
CAN协议虽然老,但设计得非常精巧。物理层的差分信号保证了抗干扰能力,数据链路层的帧结构提供了完整的通信机制,仲裁机制实现了无损冲突解决。我个人觉得,理解CAN协议是进入车载通信领域的第一步,也是最重要的一步。
下一章,我们会讲CAN FD协议。它是在CAN基础上做的扩展,速度更快,数据更长。但核心思想是一样的。先把CAN吃透,后面就轻松了。