3、UDS服务基础:服务标识符(SID)概念、请求与响应机制、肯定响应与否定响应码(NRC)基础
好,我们正式开始聊UDS协议的核心——服务。你想想看,诊断仪和ECU之间怎么对话?总得有个规矩吧。这个规矩就是“请求-响应”机制,而服务标识符(SID)就是每条指令的“身份证号”。
我刚开始接触UDS时,总觉得这玩意儿就是发一串十六进制数,没啥技术含量。后来踩了坑才明白——SID选不对、响应格式写错、NRC没处理好,ECU直接就不理你了。咱们今天就把这些基础打牢。
3.1 服务标识符(SID)是什么?
SID,全称Service Identifier,占用1个字节。范围从0x00到0xFF,但UDS协议只定义了其中一部分。说白了,它就是诊断请求的“操作码”。
举个例子:你想读取ECU的故障码,那就发0x19;你想读写数据,那就发0x22或0x2E。每个SID背后都对应一套固定的子功能(Sub-function)和参数结构。
关键点:SID的高层分类很清晰——
- 0x10 - 0x3E:诊断与通信管理类(如会话控制、安全访问)
- 0x22 - 0x2E:数据传输类(如读取数据、写入数据)
- 0x14 - 0x19:故障存储类(如清除故障码、读取故障码)
- 0x31 - 0x3D:输入输出控制类(如例程控制、IO控制)
- 0x34 - 0x36:上传下载类(如请求下载、传输数据)
我个人习惯把SID表打印出来贴在工位上。你想想看,调试时查手册多浪费时间?背下来几个常用的,效率翻倍。
3.2 请求与响应机制
UDS的通信模型很简单:诊断仪发请求 → ECU回响应。但这里有个细节——响应分两种:肯定响应和否定响应。
3.2.1 请求格式
一个标准的UDS请求长这样:
| SID (1字节) | Sub-function (1字节, 可选) | 数据参数 (N字节) |
举个例子,读取ECU的VIN码(数据标识符0xF190):
请求:22 F1 90
→ 0x22 = 读取数据服务
→ 0xF190 = 数据标识符
3.2.2 肯定响应格式
ECU处理成功时,会返回肯定响应。格式是:SID + 0x40,再加上具体数据。
肯定响应:62 F1 90 4C 46 56 33 30 30 30 30 30 30 30 30 30 30 30
→ 0x62 = 0x22 + 0x40
→ 0xF190 = 数据标识符
→ 后续字节 = VIN码 "LFV30000000000000"
嗯,这里要注意:肯定响应的第一个字节永远是请求SID加上0x40。这是协议硬性规定,别搞错了。
3.2.3 否定响应格式
如果ECU处理失败,它会返回否定响应。格式固定为三字节:
否定响应:7F [请求SID] [NRC]
→ 0x7F = 否定响应标识
→ 请求SID = 你刚才发的SID
→ NRC = 否定响应码,告诉你为什么失败
举个例子,你发了个0x22请求,但ECU当前会话不支持:
请求:22 F1 90
响应:7F 22 7F
→ 0x7F = 否定响应
→ 0x22 = 请求的SID
→ 0x7F = NRC "服务不支持当前会话"
我的经验:调试时看到0x7F开头,别慌。先看第三个字节的NRC,那才是关键。我曾经花了一下午查一个0x22请求失败的问题,最后发现是NRC 0x78(请求正确接收-响应待定)——ECU只是忙,等一会儿重试就好了。
3.3 否定响应码(NRC)基础
NRC是UDS协议里最实用的部分。它告诉你ECU为什么拒绝你的请求。常见的NRC就那么十几种,我列个表:
| NRC (十六进制) | 名称 | 含义 |
|---|---|---|
| 0x10 | 一般拒绝 | 请求格式正确,但ECU无法处理(比如正在执行其他任务) |
| 0x11 | 服务不支持 | 你发的SID,ECU根本不认识 |
| 0x12 | 子功能不支持 | SID认识,但子功能号不对 |
| 0x13 | 报文长度错误 | 你发的数据长度不对,多一个字节或少一个字节 |
| 0x22 | 条件不满足 | ECU当前状态不允许执行该服务(比如没上电) |
| 0x31 | 请求超出范围 | 参数值非法(比如数据标识符不存在) |
| 0x33 | 安全访问拒绝 | 需要解锁才能执行该服务 |
| 0x35 | 密钥无效 | 安全访问时密钥不对 |
| 0x36 | 超过最大重试次数 | 安全访问尝试次数超限 |
| 0x37 | 所需时间未到 | 安全访问需要等待一段时间 |
| 0x78 | 请求正确接收-响应待定 | ECU正在处理,稍后重试 |
| 0x7E | 服务不支持当前会话 | 当前会话模式不支持该服务 |
| 0x7F | 服务不支持当前会话(同0x7E) | 同上,部分协议版本用0x7F |
避坑指南:我曾经在OTA升级项目中遇到一个诡异问题——ECU总是返回0x78。查了半天,发现是ECU在处理大数据写入时,内部缓冲区满了,但协议栈没正确处理流控。解决方案是:客户端收到0x78后,等待100ms再重试,最多重试3次。如果还不行,就报超时错误。
3.4 请求-响应流程的完整示例
咱们用一个实际场景串一下:诊断仪想读取ECU的软件版本号(数据标识符0xF1A0)。
- 诊断仪发送请求:
22 F1 A0 - ECU收到请求:
- 检查SID 0x22是否支持 → 是
- 检查数据标识符0xF1A0是否存在 → 是
- 检查当前会话是否允许读取 → 是
- 检查安全访问是否要求解锁 → 否(读取数据通常不需要解锁)
- ECU返回肯定响应:
62 F1 A0 56 31 2E 30 2E 30- 0x62 = 肯定响应标识
- 0xF1A0 = 数据标识符
- 56 31 2E 30 2E 30 = ASCII码 "V1.0.0"
如果ECU当前处于编程会话(Programming Session),但该会话不允许读取数据标识符0xF1A0,它会返回:7F 22 7E。
3.5 知识结构图:UDS请求-响应核心流程
下面这张图帮你理清整个逻辑:
3.6 实战中的几个坑
- 坑一:SID+0x40溢出——如果SID是0xF0,加0x40后变成0x130,超过1字节范围。协议规定:只取低8位,即0x30。我见过新手直接写死0x40+SID,结果0xF0的服务永远收不到肯定响应。
- 坑二:NRC优先级——ECU检查条件有顺序。比如先检查SID是否支持(NRC 0x11),再检查子功能(NRC 0x12),最后检查安全访问(NRC 0x33)。你发一个不支持的SID,ECU不会告诉你“安全访问拒绝”,而是直接报“服务不支持”。
- 坑三:0x78的误用——有些工程师把0x78当成“ECU忙,别烦我”,但实际上0x78的意思是“请求已正确接收,正在处理,请稍后重试”。两者区别很大:前者是拒绝,后者是延迟响应。
我的习惯:在项目初期,我会写一个UDS请求-响应的状态机,把每个SID的肯定响应和否定响应都列出来。调试时对照着看,一眼就能发现问题。你想想看,如果ECU返回0x7F 0x22 0x31,你马上就知道是数据标识符超出范围,而不是SID不支持。