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的请求和响应格式非常规整。我画了一张图,帮你理清结构。

UDS请求与响应格式 诊断请求 SID 子功能(可选) 数据参数(可选) 正响应 SID + 0x40 子功能(可选) 数据参数(可选) 否定响应 0x7F 请求的SID NRC(否定响应码)

从图上可以清楚看到:

  • 请求: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就算入门了。