3、CAN总线基础:CAN总线工作原理,CAN报文结构(ID、DLC、Data),CAN与UDS的关系
好,咱们进入第三讲。说实话,CAN总线是UDS协议的“腿”。没有CAN,UDS哪也去不了。很多刚入行的朋友一上来就啃UDS的规范,结果发现报文发不出去,或者收不到响应,卡在那里半天。我当年也干过这事儿,后来才明白,先把CAN总线搞透,UDS学起来就是水到渠成的事。
3.1 CAN总线工作原理——它到底是怎么“说话”的?
CAN总线,全称是Controller Area Network。它最早是博世公司为汽车开发的。你想想看,一辆车里有几十个ECU,发动机、变速箱、ABS、气囊……它们之间要互相通信。如果每个ECU都拉一根线到另一个ECU,那线束比车还重。CAN总线的出现,就是为了解决这个问题——用两根线,把所有节点挂上去。
这两根线,一根叫CAN_H,一根叫CAN_L。它们传输的是差分信号。什么意思?就是逻辑“1”和“0”不是靠一根线的电压高低来判断,而是看两根线的电压差。这样做的好处是抗干扰能力强。我在做EMC测试时深有体会,别的总线被干扰得乱七八糟,CAN总线依然稳如老狗。
CAN总线的工作方式,我总结为三个关键词:多主、广播、仲裁。
- 多主:总线上任何一个节点都可以主动发消息,没有“老大”来分配时间片。谁想说话,谁就开口。
- 广播:一个节点发消息,所有节点都能收到。但节点会判断这条消息是不是“我该听的”,不是就忽略。
- 仲裁:如果两个节点同时发消息,怎么办?CAN总线通过ID来仲裁。ID越小,优先级越高。说白了,就是“谁的数字小,谁先走”。
核心要点:CAN总线的仲裁机制是非破坏性的。高优先级的节点在发送时,低优先级的节点会自动退出发送,转为接收。整个过程不会损坏任何数据。这一点非常巧妙。
我曾经在调试一个多节点系统时,发现某个节点总是发不出消息。查了半天,原来是它的ID设得太大了,每次都被别的节点“挤”下去。嗯,这里要注意,ID分配是个技术活,不是随便写的。
3.2 CAN报文结构——ID、DLC、Data,一个都不能少
CAN报文,说白了就是一串数据。但这一串数据里,每个字段都有它的意义。咱们以最常用的标准帧(11位ID)为例,来看看它的结构。
| 字段 | 长度 | 说明 |
|---|---|---|
| SOF | 1 bit | 帧起始,标志报文开始 |
| ID | 11 bits | 标识符,决定优先级 |
| RTR | 1 bit | 远程帧标志,0为数据帧 |
| IDE | 1 bit | 扩展帧标志,0为标准帧 |
| DLC | 4 bits | 数据长度,0~8字节 |
| Data | 0~64 bits | 实际数据,最多8字节 |
| CRC | 15 bits | 校验码,保证数据完整性 |
| ACK | 2 bits | 应答位,接收节点确认 |
| EOF | 7 bits | 帧结束 |
咱们重点看三个字段:ID、DLC、Data。因为UDS协议主要跟它们打交道。
3.2.1 ID——报文的“身份证”
ID决定了这条报文在总线上的优先级。ID越小,优先级越高。在UDS中,ID还用来区分是“请求”还是“响应”。比如,诊断请求的ID通常是0x7DF,而响应的ID是0x7E8。我习惯把ID看作“门牌号”,你要找哪个ECU,就发对应的ID。
3.2.2 DLC——数据长度
DLC告诉接收方,Data字段里有多少个字节。范围是0到8。注意,DLC可以小于8,但Data字段里剩下的字节会被忽略。我在项目中遇到过一个问题:发送方DLC设成了8,但实际只用了3个字节,接收方却把后面5个字节的垃圾数据也解析了,结果出了bug。所以,DLC一定要和实际数据长度一致。
3.2.3 Data——真正的“干货”
Data字段最多8个字节。UDS的请求和响应,就塞在这8个字节里。比如,你要读取ECU的版本号,UDS请求可能是“22 F1 90”,这3个字节就放在Data里。如果数据超过8个字节,就需要用多帧传输了。这个后面会讲。
个人经验:刚开始学CAN时,我总记不住DLC和Data的区别。后来我这样理解:DLC是“信封上写的页数”,Data是“信封里装的纸”。页数写错了,纸就数不对。
3.3 CAN与UDS的关系——它们是怎么“合作”的?
这个问题,我经常被问到。说白了,CAN是“运输工具”,UDS是“运输的货物”。CAN负责把数据从一个ECU搬到另一个ECU,而UDS定义了这些数据是什么含义、该怎么处理。
你可以这样理解:
- CAN:相当于快递员。他只知道把包裹从A送到B,至于包裹里是什么,他不关心。
- UDS:相当于包裹里的物品清单和操作手册。它告诉你,这个包裹是“读取故障码”还是“写入配置参数”。
在实际的汽车诊断中,UDS的请求和响应,都是通过CAN报文来传输的。具体来说:
- 诊断仪(Tester)发送一个CAN报文,ID是诊断请求ID,Data字段里放着UDS请求。
- ECU收到后,解析Data字段里的UDS请求,执行相应的操作。
- ECU再发送一个CAN报文,ID是诊断响应ID,Data字段里放着UDS响应。
举个例子,读取ECU的软件版本号:
// 诊断仪发送的CAN报文
ID: 0x7DF
DLC: 0x03
Data: 0x22 0xF1 0x90 // 这是UDS的“读取数据”请求
// ECU响应的CAN报文
ID: 0x7E8
DLC: 0x06
Data: 0x62 0xF1 0x90 0x01 0x02 0x03 // 这是UDS的响应,版本号是1.2.3
避坑指南:我曾经在调试时,发现ECU一直不响应。查了半天,原来是CAN报文的ID搞反了。诊断仪发的是0x7DF,但ECU监听的是0x7E8。嗯,这里要注意,ID必须一一对应,否则就是“鸡同鸭讲”。
所以,学UDS之前,一定要先把CAN总线的基础打牢。你不需要成为CAN专家,但至少要能看懂CAN报文,知道ID、DLC、Data是什么意思。否则,后面学UDS的会话控制、安全访问、故障码读取,都会觉得云里雾里。
我个人习惯,在开始一个新项目时,先用CAN工具抓一下总线上的报文,看看ID是怎么分配的,DLC是不是正确。这花不了几分钟,但能避免很多低级错误。你想想看,如果连CAN报文都发不对,UDS学得再好又有什么用呢?
好,这一讲就到这里。下一讲,咱们正式进入UDS协议的核心——服务标识符(SID)。