3. UDS服务基础:服务ID(SID)分类、请求与响应机制、子功能与数据参数
好,咱们进入第三章。这一章是整个UDS诊断协议的基石,说白了就是「怎么跟ECU说话,ECU又怎么回你」。我见过不少新手,上来就背SID列表,结果真到项目里连个请求报文都拼不对。别急,咱们一步步来。
3.1 服务ID(SID)是什么?
服务ID,全称Service Identifier,就是诊断请求的第一个字节。ECU看到这个字节,就知道你要干嘛。
举个例子:你发 10 01,ECU就知道你要切换会话模式。这里的 0x10 就是SID。我个人习惯把SID理解成「指令码」,跟单片机里的指令集一个道理。
UDS标准(ISO 14229-1)把SID分成了六大类,我整理了一张表,你存着,以后查起来方便:
| SID范围 | 服务类别 | 典型服务 |
|---|---|---|
| 0x10 - 0x3E | 诊断和通信管理 | 10(诊断会话控制)、11(ECU复位)、3E(Tester Present) |
| 0x22 - 0x2E | 数据传输 | 22(按ID读取数据)、2E(按ID写入数据) |
| 0x14 - 0x19 | 存储数据传输 | 14(清除诊断信息)、19(读取DTC信息) |
| 0x27 - 0x27 | 输入输出控制 | 2F(输入输出控制) |
| 0x28 - 0x3D | 远程激活 | 31(例程控制)、34(请求下载) |
| 0x84 - 0x85 | 安全 | 27(安全访问) |
3.2 请求与响应机制
UDS的通信模式很简单,就是「一问一答」。你发一个请求,ECU必须回一个响应。但这里有个坑——响应分两种:肯定响应和否定响应。
3.2.1 肯定响应
ECU成功执行了你的请求,就会回肯定响应。格式是:SID + 0x40 + 数据。
比如你发 22 F1 90(读取VIN码),ECU回 62 F1 90 4C 53 56 00 ...。这里的 0x62 就是 0x22 + 0x40,表示「你的读数据请求我收到了,这是你要的数据」。
嗯,这里要注意:肯定响应的第一个字节永远是 请求SID + 0x40。这是UDS协议的铁律,别搞错了。
3.2.2 否定响应
ECU执行失败了,就会回否定响应。格式是:0x7F + 请求SID + 否定响应码(NRC)。
举个例子:你发 22 F1 90,但当前会话模式不允许读数据,ECU就回 7F 22 7F。这里的 0x7F 是固定前缀,0x22 是请求的SID,0x7F 是NRC,表示「服务不支持」。
- 肯定响应:SID + 0x40
- 否定响应:0x7F + SID + NRC
SID + 0x7F。结果解析出来的数据全是乱的,排查了半天才发现是格式搞错了。记住,否定响应的第一个字节永远是 0x7F,不是别的。
3.3 子功能与数据参数
很多UDS服务都支持子功能。子功能就是SID后面的第二个字节,用来细分这个服务要干什么。
3.3.1 子功能的结构
子功能字节的高位(bit 7)有个特殊含义:
- bit 7 = 0:表示这是一个请求,ECU需要执行并回复
- bit 7 = 1:表示这是一个响应,ECU回复时会把bit 7置1
你想想看,这个设计是不是很巧妙?ECU收到请求后,直接把子功能的bit 7置1,就变成了响应。省去了额外的判断逻辑。
举个例子:10 01 是请求进入默认会话。ECU回复 50 01,这里的 0x50 = 0x10 + 0x40,而子功能 0x01 的bit 7被置1,变成了 0x81?不对,这里要注意:子功能bit 7置1只适用于某些服务,比如0x10、0x11、0x27等。对于0x22这种没有子功能的服务,就不适用。
我个人习惯把子功能理解成「菜单选项」。比如0x10服务(诊断会话控制)的子功能:
01:默认会话02:编程会话03:扩展会话
你选哪个,ECU就进哪个模式。
3.3.2 数据参数
数据参数就是SID和子功能后面的字节。它们提供了执行服务所需的额外信息。
比如0x22服务(按ID读取数据):
请求:22 F1 90
^^ ^^ ^^
|| || +-- 数据标识符(高字节)
|| +---- 数据标识符(低字节)
+------- SID
这里的 F1 90 就是数据参数,告诉ECU你要读哪个数据。ECU根据这个ID去查内部的数据表,找到对应的值返回给你。
再比如0x2E服务(按ID写入数据):
请求:2E F1 90 01 02 03 04
^^ ^^ ^^ ^^^^^^^^^^^^
|| || || +-- 要写入的数据
|| || +---- 数据标识符(低字节)
|| +------- 数据标识符(高字节)
+---------- SID
这里的数据参数就复杂了,既有数据标识符,又有要写入的数据值。
3.4 实战:拼一个完整的诊断请求
咱们来拼一个真实的诊断请求。假设我们要读取ECU的VIN码(数据标识符0xF190):
- 确定SID:读数据用0x22
- 确定数据参数:VIN码的ID是0xF190,所以数据参数是
F1 90 - 拼请求:
22 F1 90
ECU收到后,如果成功,会回复:
62 F1 90 4C 53 56 00 00 00 00 00 00 00 00 00 00 00
^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|| || || +-- VIN码数据(ASCII编码)
|| || +---- 数据标识符(低字节)
|| +------- 数据标识符(高字节)
+---------- 肯定响应SID(0x22 + 0x40)
如果失败,比如当前会话不允许读数据,ECU会回复:
7F 22 7F
^^ ^^ ^^
|| || +-- NRC(服务不支持)
|| +---- 请求的SID
+------- 否定响应前缀
- SID是诊断请求的「指令码」,ECU靠它识别你要干嘛
- 肯定响应 = SID + 0x40,否定响应 = 0x7F + SID + NRC
- 子功能是SID的「菜单选项」,bit 7用于区分请求和响应
- 数据参数是SID和子功能后面的字节,提供执行服务所需的额外信息
这一章的内容就到这里。下一章咱们会深入讲一个最常用的服务——0x10诊断会话控制。到时候我会结合项目中的实际案例,告诉你不同会话模式到底该怎么用。咱们下章见。