3. UDS服务分类与格式:服务标识符(SID)定义、请求/响应报文结构、肯定响应与否定响应码(NRC)
好,我们进入第三章。这一章讲的是UDS最基础的东西——服务的分类、报文长什么样、以及怎么判断诊断成功了还是失败了。
说实话,很多工程师做了好几年诊断开发,对SID和NRC的理解还是模棱两可。我见过有人把0x10(诊断会话控制)和0x11(ECU复位)的请求报文格式搞混,结果刷写流程死活跑不通。嗯,咱们今天就把这些基础打扎实。
3.1 服务标识符(SID)的定义与分类
SID就是服务的身份证号。每个UDS服务都有一个唯一的8位ID,范围从0x00到0xFF。但并不是所有值都能用,ISO 14229-1把SID分成了几个功能组。
我个人习惯把SID分成三大类:
- 诊断与通信管理类:0x10~0x3E,负责会话控制、ECU复位、安全访问等
- 数据传输类:0x22~0x2E,读写数据、按标识符读写、动态定义标识符
- 存储数据传输类:0x14~0x1A,清除/读取诊断信息、例程控制
你想想看,这其实跟医院的科室划分很像。挂号(会话控制)、检查(读取数据)、治疗(例程控制)、出院(ECU复位)。
核心要点:SID的高4位决定了服务的功能组。比如0x2X系列全是数据传输类服务,0x3X系列全是输入输出控制类。记住这个规律,查规范的时候能快很多。
我在项目中遇到过一个问题:某供应商的Bootloader把0x31(例程控制)的SID写成了0x30,结果ECU死活不响应。查了两天才发现是SID定义错了。所以,SID的二进制值一定要对着规范逐位核对。
3.2 请求/响应报文结构
UDS的报文结构其实很简单。一个诊断请求由三部分组成:
- SID:服务标识符,1字节
- 子功能(Sub-function):可选,1字节,最高位表示是否需要肯定响应
- 数据参数(Data):可变长度,具体内容由服务定义
举个例子,读取数据标识符服务(0x22)的请求报文:
请求:22 F1 90
- 22:SID,表示读取数据标识符
- F1 90:数据标识符,2字节,这里表示读取VIN码
响应报文分两种:肯定响应和否定响应。
肯定响应报文格式:
响应:62 F1 90 4C 46 56 33 33 30 41 42 43 44 45 46 47 48 49 4A
- 62:SID + 0x40,表示肯定响应
- F1 90:数据标识符
- 4C 46 56...:实际数据,这里是VIN码"LFV330ABCDEFGHIJ"
否定响应报文格式:
响应:7F 22 31
- 7F:固定值,表示否定响应
- 22:请求的SID
- 31:否定响应码,这里表示请求超出范围
这里有个细节:肯定响应的SID = 请求SID + 0x40。比如0x22变成0x62,0x10变成0x50。这个规律在所有UDS服务中都适用。
个人技巧:我习惯在代码里用宏定义来区分请求和响应:#define SID_POSITIVE_RESP(sid) ((sid) | 0x40)。这样写出来的代码可读性高,也不容易出错。
3.3 肯定响应与否定响应码(NRC)
肯定响应好理解,就是ECU说“我收到了,也执行了”。但否定响应才是诊断开发的重头戏。NRC就是ECU告诉你“为什么没执行”。
常用的NRC有这些:
| NRC值 | 名称 | 含义 |
|---|---|---|
| 0x10 | 一般拒绝 | ECU当前无法处理该请求 |
| 0x11 | 不支持该服务 | SID不在ECU支持列表中 |
| 0x12 | 子功能不支持 | SID支持,但子功能不支持 |
| 0x13 | 报文长度错误 | 请求报文长度不对 |
| 0x22 | 条件不满足 | 当前会话/安全等级不允许 |
| 0x31 | 请求超出范围 | 数据参数无效 |
| 0x33 | 安全访问拒绝 | 未解锁或解锁失败 |
| 0x78 | 请求正确但正在处理 | 需要额外时间,请等待 |
我曾经踩过一个坑:在Bootloader里做刷写时,发送了0x34(请求下载)后,ECU返回了0x78。我以为出错了,直接终止了流程。后来才发现,0x78的意思是“我收到了,正在准备,你等会儿再发数据”。
避坑指南:0x78是一个非常特殊的NRC。它表示ECU正在处理,但还没准备好接收后续数据。收到0x78后,诊断仪应该等待一段时间再重新发送相同的请求,而不是直接放弃。我见过很多新手在这里翻车。
3.4 知识结构图:UDS服务与报文格式
下面这张图把本章的核心知识点串起来了。从SID分类到报文结构,再到响应机制,一目了然。
3.5 实战中的注意事项
最后,分享几个我在项目中积累的经验:
- SID的优先级:诊断会话控制(0x10)和ECU复位(0x11)的优先级最高。无论ECU在做什么,这两个服务必须立即响应。我在Bootloader里就把它们放在中断里处理。
- NRC的返回顺序:ECU检查NRC是有顺序的。先检查SID是否支持(0x11),再检查子功能(0x12),然后检查报文长度(0x13),最后检查条件(0x22)和数据(0x31)。这个顺序在ISO 14229-1里有明确规定。
- 0x78的处理:收到0x78后,诊断仪应该等待至少50ms再重试。我见过有些工具等100ms,有些等200ms。建议在代码里做成可配置的,方便调试。
个人习惯:我在设计诊断栈时,会把NRC检查做成一个状态机。每个服务注册自己的检查函数,按优先级顺序执行。这样代码结构清晰,也方便后期维护。