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个,否则信号会衰减)。

关键指标速查

参数经典CANCAN FD
最大速率1 Mbps8 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诊断通信上。你保存下来,以后调试诊断通信时,可以对照着看。

UDS诊断通信CAN层知识体系 UDS诊断通信CAN层知识体系 CAN总线基础 CAN帧结构(数据帧) CAN-ID分配策略(29位扩展帧) 物理寻址(点对点) 请求: 0x18DAxxF1 功能寻址(广播) 请求: 0x18DBFFxx UDS诊断通信(ISO 15765) 核心原则:物理寻址用于数据交互,功能寻址用于命令广播 CAN-ID决定了优先级和过滤规则,分配错误会导致通信失败

2.6 实战建议:如何配置CAN-ID?

最后,给你几个我在项目中踩过坑之后总结的配置建议:

  1. 统一使用扩展帧(29位ID):标准帧只有11位,地址空间太小,容易冲突。现在主流OEM(主机厂)都要求扩展帧。
  2. 物理寻址的请求ID和响应ID要成对出现:比如请求是0x18DA10F1,响应必须是0x18DFF110。不要自己发明规则,否则诊断仪和ECU对不上。
  3. 功能寻址的响应要加“抑制”机制:如果多个ECU同时响应,总线会冲突。可以在诊断请求里加一个“抑制响应位”(Supress Positive Response),让ECU只执行不回复。
  4. CAN-ID的优先级要规划好:诊断报文的ID通常设得比较小(优先级高),但不要比动力系统的实时控制报文还高。否则诊断报文会抢了发动机控制报文的带宽,导致车辆抖动。

小技巧

调试CAN通信时,我习惯用CANalyzer或PCAN-View先抓一帧看看。如果看到诊断仪发了请求,ECU没回,先检查CAN-ID对不对,再检查DLC是不是0。很多时候,问题就出在这两个地方。