2、UDS物理层与数据链路层:CAN总线基础、CAN帧结构、CAN-ID分配策略、诊断物理寻址与功能寻址
聊UDS诊断,绕不开CAN总线。说白了,UDS就是坐在CAN这辆车上的一台“对讲机”。你连物理层和数据链路层都没搞明白,那诊断报文怎么发出去的、怎么收回来的,就全是黑盒。
我个人习惯,讲诊断之前,一定先把CAN总线的基础夯结实。因为很多诊断通信的“坑”,其实都埋在CAN的帧结构和ID分配里。
2.1 CAN总线基础:为什么是它?
CAN(Controller Area Network)总线,上世纪80年代由Bosch发明,初衷是为了让汽车里的各种电子控制单元(ECU)能“聊起来”。
为什么汽车行业选了CAN,而不是别的总线?
- 差分信号,抗干扰强:CAN_H和CAN_L两根线,电压差传数据。发动机舱里电磁干扰那么严重,它照样稳如老狗。
- 多主通信,谁都能抢:总线上任何一个节点都可以主动发消息,不需要主机轮询。实时性有保障。
- 错误处理机制完善:5种错误检测机制,加上自动重发。我在项目里见过,线束被老鼠咬了一口,CAN总线居然还能靠错误恢复继续跑,只是丢了几帧。
- 成本低,节点多:一根双绞线,最多能挂110个节点(实际建议不超过32个,否则信号会衰减)。
关键指标速查
| 参数 | 经典CAN | CAN FD |
|---|---|---|
| 最大速率 | 1 Mbps | 8 Mbps(数据段) |
| 数据长度 | 8字节 | 64字节 |
| 仲裁机制 | CSMA/CR(载波监听多路访问/冲突解决) | 同左 |
| 典型应用 | 动力、车身、诊断 | 刷写、大数据量诊断 |
嗯,这里要注意:UDS over CAN,目前主流还是经典CAN。CAN FD虽然能传64字节,但很多老旧的诊断工具和ECU还不支持。你如果做刷写(Bootloader)开发,CAN FD能大幅缩短时间,但兼容性测试一定要做足。
2.2 CAN帧结构:报文长什么样?
CAN总线上跑的数据,叫“帧”。诊断通信主要用的是数据帧。我刚开始学的时候,总觉得帧结构很复杂,其实你只需要记住几个关键字段就行。
一个标准CAN数据帧(11位ID)长这样:
SOF | 11位ID | RTR | IDE | r0 | DLC | 0~8字节数据 | CRC | ACK | EOF
咱们拆开看:
- SOF(Start of Frame):1个显性位,告诉所有节点“我要发消息了”。
- ID(Identifier):11位(标准帧)或29位(扩展帧)。这玩意儿决定了优先级——ID越小,优先级越高。诊断报文通常用扩展帧(29位),因为地址空间更大。
- RTR(Remote Transmission Request):区分是数据帧还是远程帧。诊断基本用数据帧,RTR=0。
- DLC(Data Length Code):4位,表示数据段有几个字节。范围0~8。注意:DLC可以写0,但诊断报文最少也得有1字节(就是那个服务ID)。
- Data Field:0~8字节。UDS诊断报文就塞在这里面。经典CAN一次最多传8字节,所以UDS的多帧传输(超过8字节)要靠传输层协议(ISO 15765-2)来分包。
- CRC、ACK、EOF:校验、应答、帧结束。这些硬件自动处理,你写软件时基本不用操心。
避坑指南
我曾经在一个项目里,发现诊断仪发请求,ECU就是不响应。查了两天,最后发现是CAN控制器配置错了——把帧格式配成了标准帧(11位ID),而ECU只认扩展帧(29位ID)。两边ID对不上,ECU直接过滤掉了。所以,确认CAN帧格式是标准帧还是扩展帧,这是调试的第一步。
2.3 CAN-ID分配策略:谁是谁的“门牌号”
CAN总线上每个报文都有一个ID。这个ID不只是标识,它还决定了谁能收到、谁不能收到。诊断通信的ID分配,核心就是解决一个问题:诊断仪怎么找到指定的ECU?
ISO 15765-4(即UDS on CAN的诊断规范)给出了两种寻址方式:
| 寻址方式 | CAN-ID范围(29位扩展帧) | 特点 |
|---|---|---|
| 物理寻址(点对点) | 请求:0x18DAxxF1 响应:0x18DFF1xx | 一对一的,诊断仪指定找某个ECU |
| 功能寻址(广播) | 请求:0x18DBFFxx 响应:一般不响应或按需响应 | 一对多的,所有ECU都能收到 |
这里“xx”是ECU的物理地址(比如0x01、0x10)。F1是诊断仪的逻辑地址(通常固定为0xF1)。
举个例子:
- 你想读取发动机ECU(地址0x10)的故障码,诊断仪发:0x18DA10F1(请求ID)。发动机ECU收到后,回复:0x18DFF110(响应ID)。
- 你想让所有ECU都进入扩展诊断会话,诊断仪发:0x18DBFFF1(功能寻址ID)。所有支持该服务的ECU都会收到,但通常只有被请求的ECU才回复,否则总线会冲突。
注意:功能寻址的“坑”
功能寻址虽然方便,但千万不要在功能寻址的响应里发大量数据。我曾经见过一个项目,诊断仪发功能寻址请求“读取所有ECU的软件版本”,结果十几个ECU同时回复,CAN总线直接爆了——仲裁失败、错误帧满天飞。正确的做法是:功能寻址只用来做“广播命令”(比如复位、进入会话),响应要么不发,要么用物理寻址单独回复。
2.4 诊断物理寻址与功能寻址:怎么选?
这两个概念,说白了就是“打电话”和“喊话”的区别。
- 物理寻址:你给某个ECU打电话,号码是唯一的。它接电话,你们俩私聊。适合:读取故障码、读写数据、刷写固件。
- 功能寻址:你在走廊里喊一嗓子“所有ECU注意!”,谁听到了谁响应。适合:广播复位、广播进入扩展会话、广播停止通信。
我个人习惯,在项目初期定义诊断规范时,会先列一个表:哪些服务用物理寻址,哪些用功能寻址。比如:
| 服务 | 推荐寻址方式 | 原因 |
|---|---|---|
| 0x10(诊断会话控制) | 物理寻址 + 功能寻址 | 广播进入扩展会话,方便批量操作 |
| 0x19(读取故障码) | 物理寻址 | 每个ECU的故障码不同,必须单独读 |
| 0x22(读取数据) | 物理寻址 | 数据是ECU私有的,不能广播 |
| 0x11(ECU复位) | 物理寻址 + 功能寻址 | 广播复位常用于产线或维修站 |
| 0x28(停止通信) | 物理寻址 | 功能寻址可能导致总线静默,危险 |
核心原则
功能寻址只用于“命令类”服务,且响应要谨慎。物理寻址用于“数据类”服务,确保一对一通信不冲突。
2.5 知识体系总览:一张图看懂
下面这张SVG图,把本章的核心逻辑串起来了。从CAN总线物理层,到帧结构,再到ID分配和寻址方式,最后落到UDS诊断通信上。你保存下来,以后调试诊断通信时,可以对照着看。
2.6 实战建议:如何配置CAN-ID?
最后,给你几个我在项目中踩过坑之后总结的配置建议:
- 统一使用扩展帧(29位ID):标准帧只有11位,地址空间太小,容易冲突。现在主流OEM(主机厂)都要求扩展帧。
- 物理寻址的请求ID和响应ID要成对出现:比如请求是0x18DA10F1,响应必须是0x18DFF110。不要自己发明规则,否则诊断仪和ECU对不上。
- 功能寻址的响应要加“抑制”机制:如果多个ECU同时响应,总线会冲突。可以在诊断请求里加一个“抑制响应位”(Supress Positive Response),让ECU只执行不回复。
- CAN-ID的优先级要规划好:诊断报文的ID通常设得比较小(优先级高),但不要比动力系统的实时控制报文还高。否则诊断报文会抢了发动机控制报文的带宽,导致车辆抖动。
小技巧
调试CAN通信时,我习惯用CANalyzer或PCAN-View先抓一帧看看。如果看到诊断仪发了请求,ECU没回,先检查CAN-ID对不对,再检查DLC是不是0。很多时候,问题就出在这两个地方。