1. UDS协议基础:OSI模型与汽车诊断、UDS协议栈概述、诊断会话控制(0x10)、ECU复位(0x11)

1.1 OSI模型与汽车诊断——别被七层吓到

很多刚入行的朋友,一听到OSI七层模型就头大。说实话,我当年也是。但做汽车诊断这行,你不需要背下每一层,关键是理解数据怎么从你的上位机,一路跑到ECU里。

OSI模型分七层:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。在UDS里,我们最关心的是下面几层:

  • 物理层:说白了就是线束和接口。CAN总线用双绞线,DoIP用网线。我在项目里遇到过,某次台架测试死活连不上ECU,最后发现是CAN收发器的地线没接好——物理层出问题,上层再牛也白搭。
  • 数据链路层:负责把数据打包成帧。CAN协议里就是CAN帧,带ID、数据长度、CRC校验这些。嗯,这里要注意,CAN ID的分配经常是整车厂的机密,你拿到的诊断文档里一般会写。
  • 网络层:在UDS里,这一层叫ISO 15765-2(CAN总线)或者ISO 13400(以太网)。它负责把长报文拆包、重组。比如你要发一个超过8字节的诊断请求,网络层会自动帮你分包发送。
  • 应用层:这就是UDS协议本身了。0x10、0x11这些诊断服务,都在这一层定义。

我的经验:做上位机开发,你至少要把应用层和网络层搞透。物理层和数据链路层,交给硬件工程师和底层驱动去操心。但如果你能懂一点,排查问题时会快很多。

1.2 UDS协议栈概述——从请求到响应的旅程

UDS协议栈,说白了就是一套规则。它规定了:

  • 上位机怎么发请求(Request)
  • ECU怎么回响应(Response)
  • 出错时怎么报错(Negative Response)

一个典型的UDS请求长这样:

请求:02 10 03 00 00 00 00 00
响应:06 50 03 00 32 01 F4 00

我来拆解一下:

  • 02:数据长度,表示后面有2个有效字节
  • 10:服务ID,0x10就是诊断会话控制
  • 03:子功能,0x03表示扩展诊断会话
  • 后面补的00是填充字节,CAN报文固定8字节

响应里的50,是0x10 + 0x40的结果。UDS规定,肯定响应(Positive Response)的服务ID = 请求ID + 0x40。这是规矩,记住就行。

小技巧:我习惯在代码里把服务ID和响应ID定义成宏,比如:

#define UDS_SID_DIAG_SESSION_CONTROL    0x10
#define UDS_RES_SID_DIAG_SESSION_CONTROL 0x50

这样写代码时一眼就能看出是请求还是响应,不容易搞混。

1.3 诊断会话控制(0x10)——ECU的"工作模式"开关

0x10服务,是用来切换ECU工作模式的。ECU平时在跑车,你不能随便动它的内部参数,否则出事了谁负责?所以UDS定义了三种会话:

子功能 会话名称 说明
0x01 默认会话 (Default Session) 上电后的初始状态,只能做基本诊断
0x02 编程会话 (Programming Session) 用于刷写ECU固件,权限最高
0x03 扩展诊断会话 (Extended Diagnostic Session) 可以读写参数、执行例程,日常诊断最常用

你想想看,为什么要有这三种?

我举个例子。有一次我在做产线终检工具,需要读取ECU的VIN码。默认会话下,ECU只允许你读几个基本ID。想读VIN?必须切到扩展会话。我当时没注意,直接发了0x22读VIN,结果ECU回了否定响应——0x7F 22 0x7F(服务不支持)。查了半天才发现,原来是会话没切。

避坑指南:我曾经在量产项目上犯过一个低级错误——切到编程会话后忘了切回默认会话。结果ECU一直处于可刷写状态,整车下电后静态电流超标。后来我养成了习惯:每次诊断操作结束后,主动发一条0x10 0x01切回默认会话。

0x10服务的响应里,还带两个重要参数:

  • P2_Server_max:ECU处理单个请求的最大时间(单位ms)。比如0x0032就是50ms。
  • P2*_Server_max:当ECU需要额外时间处理时的扩展超时时间。比如0x01F4就是500ms。

这两个参数,直接决定了你的上位机超时设置。我一般会把超时设成P2_Server_max的2倍,留点余量。

1.4 ECU复位(0x11)——让ECU"重启一下"

0x11服务,就是让ECU复位。听起来简单,但这里面的门道不少。

子功能有三种:

子功能 复位类型 说明
0x01 硬复位 (Hard Reset) 相当于断电重启,ECU所有RAM数据丢失
0x02 钥匙通断复位 (Key Off/On Reset) 模拟点火开关通断,部分ECU会保留一些数据
0x03 软复位 (Soft Reset) 只复位应用层,底层驱动不动,速度最快

我个人习惯,在开发调试阶段用软复位最多。为什么呢?因为硬复位后ECU要重新初始化CAN控制器,有时候会丢几帧报文,上位机那边容易报超时。软复位快,而且稳定。

但要注意,量产测试时一定要用硬复位。为什么?因为软复位可能残留一些诊断状态,影响下一轮测试的准确性。我曾经吃过这个亏——某次产线测试,用软复位后ECU还保留着之前的诊断会话,导致下一台车的测试用例跑错了分支。

关键点:ECU复位后,会自动切回默认会话。所以如果你复位后还想做诊断操作,记得重新发0x10切会话。

0x11的响应比较简单,就是0x51 + 子功能。但有个细节:ECU在复位前,会先发响应,然后再执行复位。所以你的上位机收到响应后,不要立刻发下一条指令,要等ECU重启完成。等多久?一般建议等500ms以上,具体看ECU的启动时间。

嗯,说到这我想起一个坑。有一次我写上位机,收到0x11的响应后立刻发了0x10切会话,结果ECU还在重启中,CAN控制器都没初始化好,报文直接丢了。后来我在代码里加了个延时:

# 发送复位请求
send_uds_request(0x11, 0x01)
# 等待响应
response = recv_uds_response()
if response[0] == 0x51:
    # 关键:等ECU重启完成
    time.sleep(1.0)  # 等1秒,保守一点
    # 再切会话
    send_uds_request(0x10, 0x03)

这个1秒的延时,看起来笨,但稳。你想想看,ECU启动慢一点的车,500ms可能不够。我宁愿多等一会儿,也不愿意报文丢了重试。

小结

这一章我们聊了:

  • OSI模型在汽车诊断里怎么用——重点在应用层和网络层
  • UDS协议栈的基本结构——请求、响应、否定响应
  • 0x10诊断会话控制——三种会话的切换,以及P2超时参数
  • 0x11ECU复位——三种复位方式的区别,以及复位后的注意事项

下一章,我们会聊安全访问(0x27)和通信控制(0x28)。这两个服务,一个管权限,一个管通信,都是实战中的高频服务。到时候我会分享一些我在安全访问"种子-密钥"算法上踩过的坑,敬请期待。