2、CAN总线基础:CAN协议帧结构详解
各位同学,今天我们聊聊CAN总线最核心的东西——帧结构。说实话,我刚入行那会儿,觉得CAN协议挺简单的,不就是发数据嘛。直到有一次调试一个刹车系统,莫名其妙丢帧,我才意识到,不把帧结构吃透,你连问题出在哪都找不到。
2.1 四种帧类型:各司其职
CAN总线上一共就四种帧:数据帧、远程帧、错误帧、过载帧。我习惯把它们分成两类——干活的和喊救命的。
数据帧,顾名思义,就是用来传数据的。车上90%的通信都是数据帧。比如发动机转速、车速、油门开度,全是通过数据帧在总线上跑来跑去。
远程帧,这个有意思。它本身不带数据,就是一个请求。比如某个节点想知道当前车速,它就发一个远程帧,让车速传感器节点把数据发回来。我在做BCM项目时,就经常用远程帧来轮询各个子节点的状态。
错误帧,嗯,这个大家都不喜欢见到。一旦某个节点检测到总线错误,它就会发错误帧。我记得有一次,一个客户的车在颠簸路上总报故障,查了半天,发现是线束接触不良导致错误帧满天飞。
过载帧,这个比较少见。说白了就是节点忙不过来,说“等等,我处理一下”。我做了这么多年,真正遇到过载帧的场景不超过三次。
2.2 数据帧结构:逐位拆解
数据帧是CAN协议里最复杂的帧,我们重点讲。标准帧有11位ID,扩展帧有29位ID。我个人建议,能用标准帧就别用扩展帧,省带宽。
一个标准数据帧长这样:
SOF | 11位ID | RTR | IDE | r0 | DLC | 数据段(0-8字节) | CRC | ACK | EOF
咱们逐段看:
- SOF(起始帧):1位,显性电平。告诉所有节点“我要开始说话了”。
- ID(标识符):11位,决定优先级。数值越小,优先级越高。0x000是最高优先级。
- RTR(远程帧标志):数据帧里是显性,远程帧里是隐性。这个位决定了你是发数据还是要数据。
- IDE(扩展标志):标准帧里是显性,扩展帧里是隐性。说白了就是告诉别人“我是标准帧”。
- r0(保留位):显性,留给以后用。现在基本没用。
- DLC(数据长度码):4位,表示数据段有多少字节。范围0-8。注意,DLC=0不代表没数据,远程帧的DLC就是0。
- 数据段:0-8字节,真正要传的数据。我建议能用4字节就别用8字节,够用就行。
- CRC(循环冗余校验):15位CRC码+1位CRC界定符。用来检查数据有没有传错。
- ACK(应答):2位。发送节点发隐性,接收节点如果收到正确的帧,就拉成显性。这是CAN总线的一个巧妙设计——接收方告诉发送方“我收到了”。
- EOF(帧结束):7位隐性。告诉所有节点“我说完了”。
重点记住:ID越小优先级越高。这是CAN仲裁的基础。
2.3 位填充机制:为什么要有这个?
你想想看,如果总线上连续出现5个以上的隐性位,接收节点怎么知道这是数据还是空闲?这就是位填充的由来。
规则很简单:发送节点在连续发送5个相同电平后,自动插入一个相反电平。比如你发了5个显性位,第6位就强制发一个隐性位。
我曾经遇到过一个坑:有个同事自己写CAN驱动,忘了实现位填充,结果总线上一跑就报错。嗯,这个坑我踩过,你们别踩。
位填充的范围包括:SOF、ID、RTR、IDE、r0、DLC、数据段、CRC。注意,CRC界定符、ACK、EOF这些不参与位填充。
小技巧:如果你在调试时发现总线错误率特别高,先检查位填充有没有实现正确。我遇到过三次类似问题,两次是位填充的锅。
2.4 仲裁机制:谁说了算?
CAN总线是广播式的,所有节点都能发。那问题来了:如果两个节点同时发,听谁的?
答案就是仲裁。CAN的仲裁机制,说白了就是“谁优先级高谁先发”。
具体怎么仲裁?看ID。所有节点同时发送SOF后,开始逐位比较ID。如果某个节点发送的是隐性位(1),但总线上是显性位(0),那这个节点就知道自己输了,立刻停止发送,转为接收模式。
举个例子:
节点A:ID = 0x123(二进制:0001 0010 0011)
节点B:ID = 0x456(二进制:0100 0101 0110)
第1位:A发0,B发0 → 都是显性,继续
第2位:A发0,B发1 → A是显性,B是隐性
总线是显性(0),B发现自己输了,退出
节点A赢得仲裁,继续发送
你看,ID小的节点A赢了。这就是为什么我说ID越小优先级越高。
注意:仲裁只在数据帧和远程帧之间发生。错误帧和过载帧的优先级高于数据帧和远程帧。也就是说,一旦有节点发错误帧,所有正常通信都得停。
我做过一个项目,两个ECU的ID设成了相同的值,结果一上电就冲突。查了三天才发现是ID重复了。所以,设计阶段一定要做好ID分配表,别偷懒。
2.5 避坑指南:实战经验
最后,分享几个我踩过的坑:
- CRC计算别搞错:CAN的CRC算法是固定的,但不同厂家的工具算出来可能不一样。我建议用标准CAN控制器,别自己用软件模拟。
- ACK位要注意:如果总线上只有一个节点,它发的帧没人应答,它会一直重发。这在单节点测试时经常遇到。
- 位填充影响带宽:理论上数据帧最多有8字节数据,但加上位填充后,实际长度会变。设计带宽时,要留20%的余量。
- 远程帧的DLC:远程帧的DLC表示请求的数据长度,不是它自己带的数据长度。这个很多人搞混。
好了,CAN帧结构就讲到这里。下一章我们讲CAN的物理层,到时候聊聊为什么CAN要用差分信号,以及终端电阻到底该放哪。