2、UDS协议核心:UDS服务分类与SID详解
好,咱们直接进入正题。
UDS协议里最核心的东西是什么?我个人觉得,就是那26个服务。你把它搞明白了,诊断开发就通了八成。剩下的两成,是时间参数和会话管理那些边边角角。
这26个服务,ISO 14229-1把它们分成了6大类。我刚开始学的时候,觉得分类没啥用,背SID就完了。后来踩了坑才发现——分类决定了你代码的模块怎么划分,测试用例怎么组织。嗯,这里很重要。
2.1 六大服务分类概览
先看一张总表,心里有个谱:
| 分类 | 功能描述 | 典型SID |
|---|---|---|
| 诊断与通信管理 | 会话控制、ECU复位、安全访问等 | 0x10, 0x11, 0x27, 0x3E |
| 数据传输 | 读写DID、内存地址 | 0x22, 0x23, 0x2E |
| 存储数据传输 | 读写DTC信息 | 0x14, 0x19 |
| 输入输出控制 | 强制控制引脚/信号 | 0x2F |
| 远程激活 | 启动例程、下载程序 | 0x31, 0x34, 0x36 |
| 上传/下载 | 刷写相关 | 0x34, 0x35, 0x36, 0x37 |
注意,上传下载类在有些文档里被归到远程激活里。我个人习惯按功能分,刷写就是刷写,跟例程激活分开,代码结构更清晰。
2.2 诊断与通信管理类
这是最基础的一类。你想想看,ECU都不知道你在哪个会话模式,怎么响应你的请求?
0x10 - 诊断会话控制
这个服务用来切换会话模式。默认会话、扩展会话、编程会话,就靠它。
子功能参数:
- 0x01:默认会话(Default Session)
- 0x02:编程会话(Programming Session)
- 0x03:扩展会话(Extended Session)
我在项目中遇到过一个问题:ECU从编程会话切回默认会话时,没有正确释放资源,导致下次进编程会话失败。后来加了个状态机清理逻辑才搞定。
0x11 - ECU复位
这个简单,就是让ECU重启。子功能有硬复位、软复位、快速断电再上电等。
我曾经见过一个案例:复位后ECU没有及时发送响应,导致上位机超时报错。其实ECU已经复位成功了,但响应报文被丢掉了。解决方案是在复位前先发一个肯定响应,再执行复位动作。
0x27 - 安全访问
这个服务有点意思。它通过种子和密钥的交换,来验证访问权限。
流程是这样的:
- 客户端发0x27 0x01请求种子
- ECU返回种子(比如4字节随机数)
- 客户端计算密钥,发0x27 0x02带上密钥
- ECU验证,通过则解锁
说白了,就是一把锁和一把钥匙。但要注意,种子和密钥的算法是OEM自己定的,没有统一标准。我见过用AES的,也见过简单的异或算法。
0x3E - 保持激活
这个服务叫Tester Present。它的作用就是告诉ECU:我还活着,别超时退出会话。
子功能只有0x00(请求响应)和0x80(无响应)。我一般用0x80,省带宽。
2.3 数据传输类
这类服务用来读写ECU内部的数据。数据以DID(Data Identifier)来标识。
0x22 - 按标识符读取数据
这是最常用的服务之一。比如读VIN码、读软件版本号、读电压值,都用它。
请求格式:0x22 + DID(2字节)
响应格式:0x62 + DID + 数据
// 示例:读取VIN码,DID = 0xF190
请求:22 F1 90
响应:62 F1 90 4C 56 4B 33 32 33 30 31 32 33 34 35 36 37 38 39
// "LVK3230123456789" 的ASCII码
0x2E - 按标识符写入数据
跟0x22对应,用来写数据。但要注意,不是所有DID都能写。能写的DID需要OEM在规范里定义好。
我遇到过一个问题:写DID时,数据长度不对,ECU直接回了NRC 0x13(长度错误)。后来我养成了习惯,先读一下DID的长度,再拼写请求。
0x23 - 按地址读取内存
这个服务更底层。它直接读写内存地址,不经过DID映射。一般用于调试或刷写。
说实话,这个服务我用的不多。因为0x22已经能满足大部分需求了。只有在做底层调试时,才会用0x23直接看内存。
2.4 存储数据传输类
这类服务专门处理DTC(诊断故障码)。
0x14 - 清除诊断信息
就是清DTC。子功能固定为0xFF。
请求:14 FF FF FF(三个FF表示清除所有DTC)
注意:清DTC前,通常需要先做安全访问解锁。否则ECU会拒绝。
0x19 - 读取DTC信息
这个服务比较复杂,子功能很多。常用的有:
- 0x01:按状态掩码读取DTC数量
- 0x02:按状态掩码读取DTC列表
- 0x04:读取快照数据
- 0x06:读取扩展数据
状态掩码是个8位数据,每一位代表一种状态。比如bit0表示"当前激活",bit1表示"历史记录"。
2.5 输入输出控制类
这个类只有一个服务,但非常实用。
0x2F - 输入输出控制
它可以强制控制ECU的某个输出引脚,或者模拟某个输入信号。比如强制继电器吸合、强制风扇转动。
子功能参数:
- 0x00:返回控制状态
- 0x01:复位为默认值
- 0x02:冻结当前值
- 0x03:强制为指定值
我在做HIL测试时,经常用0x2F来模拟传感器故障。比如强制温度传感器输出-40度,看ECU会不会报相关DTC。
2.6 远程激活类
这类服务用来执行ECU内部的程序或例程。
0x31 - 例程控制
这个服务很灵活。它可以启动、停止、查询某个例程的执行状态。
子功能:
- 0x01:启动例程
- 0x02:停止例程
- 0x03:查询例程结果
例程ID是2字节,由OEM定义。比如0x0202可能是"擦除Flash",0xFF01可能是"自检"。
0x34 - 请求下载
这是刷写流程的第一步。客户端告诉ECU:我要下载数据了,大小是多少,存在哪个地址。
请求格式:0x34 + 数据格式 + 地址 + 大小
ECU会返回一个块大小,告诉客户端每次最多发多少字节。
0x36 - 传输数据
这是刷写流程的第二步。客户端把数据分块发过来。
请求格式:0x36 + 块序号 + 数据
注意:块序号从1开始,每次递增。ECU用这个序号来校验数据是否丢包。
0x37 - 请求退出传输
刷写完成后,用这个服务结束传输。
2.7 我的经验总结
说了这么多,我总结几条实战经验:
- SID别死记硬背。用多了自然就记住了。我刚开始时,桌上贴了一张SID速查表,一个月后就不需要了。
- 子功能要分清。有些服务(如0x19)子功能很多,每个子功能的响应格式都不一样。写代码时一定要对照规范。
- NRC比响应更重要。调试时,先看ECU回了什么NRC。0x22(条件不满足)、0x31(请求超出范围)、0x13(长度错误),这些NRC能帮你快速定位问题。
- 安全访问是很多服务的前置条件。写0x2E、清0x14、做0x31,通常都需要先解锁。别漏了这一步。
好了,这一章就到这里。下一章我们讲UDS的报文格式和时间参数,那才是真正考验协议栈实现的地方。