3. UDS协议基础:核心服务精讲

UDS协议,说白了就是ECU之间的“官方语言”。你想想看,一个车上几十个ECU,要是各说各话,那不乱套了?UDS就是ISO 14229定义的那套标准对话规则。我做了这么多年Bootloader,跟UDS打交道最多。今天咱们就挑几个最核心的服务聊透。

本章核心脉络:UDS服务模型 → 0x10诊断会话控制 → 0x27安全访问 → 0x28通信控制。这四个服务,是刷写流程的“开门三件事”。

3.1 UDS服务模型速览

UDS的服务,本质上就是“请求-响应”模式。诊断仪发一个请求,ECU回一个响应。就这么简单。

但这里有个坑——响应分两种:肯定响应和否定响应。肯定响应格式固定:SID + 0x40。比如你发0x10,肯定响应就是0x50。否定响应呢?格式是0x7F + 请求SID + NRC。NRC就是错误码,告诉你为啥不行。

报文类型 格式 示例
请求 SID + 子功能 + 参数 02 10 02
肯定响应 SID + 0x40 + 参数 06 50 02 00 32 01 F4
否定响应 0x7F + 请求SID + NRC 03 7F 10 12

我的习惯:调试阶段,我会先抓CAN报文,过滤出0x7F开头的帧。NRC能告诉你90%的问题。比如0x12(子功能不支持)和0x22(条件不满足),这两个最常见。

3.2 诊断会话控制 (0x10)

ECU上电后,默认处于默认会话(Default Session)。这个会话下,很多服务是禁用的。你想刷写?必须先切到扩展会话或编程会话。

0x10服务的子功能就三个:

  • 0x01:默认会话。上电自动进入。
  • 0x02:编程会话。刷写专用。
  • 0x03:扩展会话。诊断、配置用。

我记得有一次,客户反馈说刷写总失败。我一看日志,ECU根本没进编程会话。原因是他们发了0x10 02,但ECU回了0x7F 10 22——条件不满足。查了半天,原来是车速信号没清零。ECU检测到车速不为0,拒绝切会话。

避坑指南:我曾经遇到过,切会话后ECU会启动一个会话超时定时器(S3定时器)。默认一般是5秒。如果5秒内没有新的请求,ECU自动退回默认会话。所以刷写时,要定期发个0x3E(Tester Present)保活。

会话切换的响应里,会带两个关键参数:P2_Server_max和P2*_Server_max。前者是正常响应超时,后者是扩展响应超时。我建议你解析这两个值,动态调整你的超时策略。

3.3 安全访问 (0x27)

刷写Bootloader,说白了就是在改写ECU的“灵魂”。这么敏感的操作,肯定不能谁都能干。0x27服务就是那把锁。

流程分三步:

  1. 请求种子:诊断仪发0x27 01(请求种子),ECU返回一串随机数(种子)。
  2. 计算密钥:诊断仪用种子和约定的算法,算出一个密钥。
  3. 发送密钥:诊断仪发0x27 02(发送密钥),ECU校验。对了就解锁,错了就锁死。

安全等级:0x27服务支持多个安全等级。比如0x01/0x02是一级,0x03/0x04是二级。刷写通常用一级,但有些ECU会要求先过一级再过二级。具体看OEM的规范。

这里有个细节——密钥算法。有的用简单的XOR,有的用AES,还有的用自定义算法。我见过最坑的,是把种子和密钥算法写在源码注释里。嗯,那安全性基本等于零。

我的经验:调试安全访问时,我会在ECU端打印种子和计算出的密钥。两边一对比,问题出在算法还是传输,一目了然。另外,注意安全访问失败计数器。连续失败几次,ECU会锁死一段时间。我建议你测试时先把这个计数器清零。

否定响应码里,跟安全访问相关的有:

  • 0x12:子功能不支持。你发了0x27 05,但ECU只支持0x01/0x02。
  • 0x22:条件不满足。比如没切到编程会话就请求安全访问。
  • 0x35:密钥错误。最常见。
  • 0x36:超过尝试次数。锁死了。
  • 0x37:需要时间。你发得太快了,ECU还没准备好。

3.4 通信控制 (0x28)

刷写时,ECU会往外发一堆报文——周期报文、事件报文、诊断报文。这些报文会干扰刷写过程,甚至导致刷写失败。0x28服务就是用来“关掉”这些报文的。

0x28有三个子功能:

子功能 含义 典型场景
0x00 使能Rx/Tx 刷写完成后,恢复通信
0x01 使能Rx,禁用Tx ECU只收不发,减少总线负载
0x02 禁用Rx,使能Tx 很少用,一般不用
0x03 禁用Rx/Tx 刷写期间,完全静默

你可能会问:为什么要禁用通信?我举个例子。刷写时,ECU如果还在发周期报文,CAN总线负载会很高。万一刷写报文被延迟或丢失,那可就麻烦了。所以刷写前,我习惯先发0x28 01,让ECU闭嘴。

注意:0x28服务只控制应用报文,不影响诊断报文。也就是说,你关了Tx,ECU照样能回你的诊断请求。另外,有些ECU在切到编程会话后,会自动禁用应用报文。但为了保险,我建议你显式调用一次0x28。

通信控制还有一个参数——通信类型。它用bitmask表示,比如bit0是“正常通信”,bit1是“网络管理报文”。刷写时,我一般只关掉正常通信,保留网络管理报文,防止ECU因为收不到网络管理帧而进入休眠。

3.5 知识结构图:UDS核心服务关系

下面这张图,把四个服务的关系画清楚了。你一看就明白刷写流程的“开门三件事”是啥。

UDS核心服务关系图 UDS核心服务关系图 诊断仪 (Tester) ECU (服务器) 0x10 会话控制 默认/编程/扩展 0x27 安全访问 种子→密钥→解锁 0x28 通信控制 使能/禁用Tx/Rx 0x3E 保活 防止会话超时 刷写流程:开门三件事 ① 0x10 02 → 切到编程会话 ② 0x27 01/02 → 安全解锁 ③ 0x28 01 → 关闭应用报文

3.6 实战要点总结

这四个服务,是UDS刷写的基石。我总结几个实战要点:

  • 会话管理:刷写前必须切到编程会话。切完后,记得用0x3E保活。
  • 安全访问:种子和密钥算法要保密。调试时,注意失败计数器。
  • 通信控制:刷写期间关掉应用报文,减少总线干扰。刷完再恢复。
  • 超时处理:每个服务都有超时时间。我建议你统一用P2*_Server_max的值。

我的习惯:写代码时,我会把每个服务的请求和响应都封装成独立函数。比如UDS_SessionControl(sessionType)、UDS_SecurityAccess(level, data)。这样上层逻辑清晰,调试也方便。