4. UDS应用层核心概念:服务标识符(SID)、子功能(Sub-function)、诊断请求与响应格式、否定响应码(NRC)
好,我们进入UDS协议最核心的部分。说白了,UDS就是一套“问与答”的规则。你发一个请求,ECU给你一个响应。但怎么问、怎么答、答不了怎么办,这些都有严格定义。今天我们就把这套规则拆开看。
4.1 服务标识符(SID)—— 诊断服务的“身份证”
每个UDS服务都有一个唯一的ID,叫SID(Service Identifier)。它是一个1字节的值,范围0x00-0xFF。我个人习惯把SID分成三类:
- 诊断与通信管理类:0x10(诊断会话控制)、0x11(ECU复位)、0x28(通信控制)等
- 数据读取类:0x22(读取数据)、0x23(读取内存)、0x24(读取缩放数据)等
- 写入与编程类:0x2E(写入数据)、0x31(例程控制)、0x34(请求下载)等
你想想看,ECU收到一个请求,第一件事就是看SID。如果SID不在它支持的服务列表里,直接回一个否定响应。我在项目中遇到过,有个供应商把0x22和0x23搞混了,结果读取数据时一直报错,查了两天才发现是SID写错了。
关键点:SID决定了“做什么”,但不决定“怎么做”。具体怎么做,要看子功能。
4.2 子功能(Sub-function)—— 细化服务行为
很多服务支持子功能。子功能占请求字节的第2个字节,但只用低6位(bit0-bit5),最高位(bit7)是抑制正响应指示位(SPR)。
举个例子,0x10服务(诊断会话控制)有三个子功能:
| 子功能值 | 含义 | 典型应用场景 |
|---|---|---|
| 0x01 | 默认会话 | 上电后自动进入,功能受限 |
| 0x02 | 编程会话 | 刷写ECU固件时使用 |
| 0x03 | 扩展会话 | 需要解锁高级诊断功能时使用 |
这里有个小技巧:SPR位。如果SPR=1,ECU不会发正响应,但否定响应照常发。我建议在刷写流程中大量使用SPR,减少总线负载。曾经有个项目,刷写时每发一个请求都等正响应,结果超时一堆。后来我把SPR置1,只处理否定响应,速度提升明显。
个人经验:子功能的定义不是随便来的。ISO 14229-1对每个服务的子功能都有明确说明。别自己发明子功能,除非你写的是OEM私有服务。
4.3 诊断请求与响应格式
UDS的请求和响应格式非常规整。我画了一张图,帮你理清结构。
从图上可以清楚看到:
- 请求:SID + 子功能(可选) + 数据参数(可选)
- 正响应:SID+0x40 + 子功能(可选) + 数据参数(可选)
- 否定响应:0x7F + 请求的SID + NRC
正响应的SID为什么加0x40?因为0x40是第6位,置1表示“这是响应”。比如请求SID=0x22,正响应SID=0x62。这个设计很巧妙,一眼就能区分请求和响应。
注意:否定响应的第一个字节固定是0x7F,不是SID+0xC0。我见过有人搞混,结果ECU解析出错。记住:0x7F是否定响应的标志。
4.4 否定响应码(NRC)—— 诊断失败的“原因码”
NRC是1字节的值,范围0x00-0xFF。ECU用NRC告诉你“为什么不行”。常见的NRC有:
| NRC值 | 含义 | 常见原因 |
|---|---|---|
| 0x10 | 一般拒绝 | 服务不支持或条件不满足 |
| 0x11 | 服务不支持 | SID不在支持列表中 |
| 0x12 | 子功能不支持 | 子功能值无效 |
| 0x13 | 消息长度错误 | 请求字节数不对 |
| 0x22 | 条件不满足 | 安全访问未解锁、会话不对等 |
| 0x31 | 请求超出范围 | 数据ID或内存地址无效 |
| 0x33 | 安全访问拒绝 | 未通过安全解锁 |
| 0x78 | 请求正确但响应待定 | ECU正在处理,稍后重试 |
这里有个坑:NRC 0x78。它表示“我收到请求了,但还没处理完,你先等着”。我在项目中遇到过,刷写时ECU返回0x78,测试工具直接报失败。后来我加了重试逻辑,等ECU处理完再发下一个请求,问题解决。
避坑指南:我曾经在一个项目中,ECU对0x22服务返回0x13(消息长度错误)。查了半天,发现是请求里多了一个字节。UDS对长度非常敏感,多一个少一个都不行。建议在开发初期就用CANoe或PCAN的UDS库做一致性测试。
4.5 实战:一个完整的诊断交互流程
我们看一个实际例子。假设你要读取ECU的VIN码(数据ID 0xF190)。
步骤1:发送请求
请求:22 F1 90
解释:
0x22 - 读取数据服务SID
0xF1 - 数据ID高字节
0x90 - 数据ID低字节
步骤2:ECU处理
- 检查SID 0x22是否支持 → 支持
- 检查数据ID 0xF190是否有效 → 有效
- 检查当前会话是否允许读取 → 允许
- 读取VIN数据
步骤3:发送正响应
正响应:62 F1 90 4C 46 56 33 32 33 34 35 36 37 38 39 30 31 32
解释:
0x62 - 正响应SID(0x22+0x40)
0xF1 0x90 - 数据ID
0x4C...0x32 - VIN码 "LFV323456789012"
如果出错呢?
否定响应:7F 22 31
解释:
0x7F - 否定响应标志
0x22 - 请求的SID
0x31 - NRC:请求超出范围(数据ID无效)
核心总结:UDS应用层就四个东西——SID告诉ECU做什么,子功能细化怎么做,请求响应格式规定怎么包装,NRC告诉你怎么错了。把这四个搞明白,UDS就算入门了。