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服务就是那把锁。
流程分三步:
- 请求种子:诊断仪发0x27 01(请求种子),ECU返回一串随机数(种子)。
- 计算密钥:诊断仪用种子和约定的算法,算出一个密钥。
- 发送密钥:诊断仪发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核心服务关系
下面这张图,把四个服务的关系画清楚了。你一看就明白刷写流程的“开门三件事”是啥。
3.6 实战要点总结
这四个服务,是UDS刷写的基石。我总结几个实战要点:
- 会话管理:刷写前必须切到编程会话。切完后,记得用0x3E保活。
- 安全访问:种子和密钥算法要保密。调试时,注意失败计数器。
- 通信控制:刷写期间关掉应用报文,减少总线干扰。刷完再恢复。
- 超时处理:每个服务都有超时时间。我建议你统一用P2*_Server_max的值。
我的习惯:写代码时,我会把每个服务的请求和响应都封装成独立函数。比如UDS_SessionControl(sessionType)、UDS_SecurityAccess(level, data)。这样上层逻辑清晰,调试也方便。