简介:这份PDF面向汽车电子、嵌入式及诊断测试方向的学习者与工程师,围绕ISO 14229定义的UDS统一诊断服务展开,帮助读者建立从诊断会话管理到各类服务调用的整体认识,适合入门梳理与项目开发时查阅。文档按功能单元组织内容,涵盖0x10诊断会话控制、0x11 ECU重置、0x27安全访问、0x22与0x2E按标识符读写数据、0x19读取故障码、0x2F输入输出控制、0x31例程控制以及0x34~0x37上传下载等服务的请求与响应格式,并结合NRC负响应码、正负响应Service ID规则加以说明,同时讲清诊断仪与ECU之间“问—答”的交互模式及诊断描述文件的数据转化思路。资源包共1个PDF文件,约10.49MB,单文件即完整承载上述笔记,便于离线阅读与打印。目前已有1932人学习,适合对照实车或台架调试时快速定位服务定义与报文结构。
1. 一份 UDS 笔记的整体结构:先看清“问—答”这条主干
UDS(Unified Diagnostic Services,ISO 14229)看起来像几十个服务码的堆叠,真正的骨架只有一条:诊断仪发请求,ECU 回响应,正响应把服务号加 0x40 回显,负响应固定以 7F 开头并附一个 NRC 码。整份笔记正是按这条主干把 25 个服务分成六类写入文件:诊断和通信管理、数据传输、已存储数据传输、输入输出控制、例程控制、上传下载。
它适合谁?写过 CAPL、调过 CANoe 诊断工程,却总在会话切换、安全解锁、NRC 判断上卡住的人;也适合正在准备诊断相关面试、需要一张能按图索骥的复习地图的人。下面按“地图 → 模块要点 → 易错点 → 查漏清单”推进。
2. 诊断与通信管理单元:会话是地基,安全访问是常见的难点
这十一个服务(0x10、0x11、0x27、0x29、0x28、0x3E、0x83、0x84、0x85、0x86、0x87)是 UDS 里最容易被低估的一类。它们本身几乎不搬运业务数据,却决定了后面的读写、刷写能不能开展。把这一单元吃透,后面五个单元就是纯粹的格式记忆。
2.1 0x10 会话控制:所有诊断命令的起点
0x10 的作用只有一个——让服务器进入某个诊断会话。ISO 把会话分成两大类:DefaultSession 和 OtherSession。项目里常见的 10 01、10 02、10 03 就是在这个框架下由主机厂定义的。
请求格式是10 xx,xx 取 00~FF。切换会话之所以必要,是因为 DefaultSession 下有一部分服务直接不支持,比如安全访问、例程控制、上传下载这类涉及写入或读取内存的操作,必须先切到扩展会话或编程会话才有资格发。
| 参数位置 | 名称 | 说明 |
|---|---|---|
| byte 0 | Service ID | 固定 0x10 |
| byte 1 | diagnosticSessionType | 子功能,回显到正响应 |
| byte 2~ | sessionParameterRecord | 会话参数记录,随项目而定 |
正响应的判断依据是服务号加 0x40,所以 0x10 的正响应是 0x50,后面跟 diagnosticSessionType 回显和 sessionParameterRecord。负响应则是三字节结构:
7F 10 22 | | └── NRC:conditionsNotCorrect,当前条件不满足 | └───── 请求的服务 ID 原样返回 └──────── 固定 7F,负响应标志这三段是读日志时最有用的信息:第一个字节确认是负响应,第二个字节确认是哪条服务出了问题,第三个字节才是真正的原因。很多人只盯 NRC 码而忽略中间字节,多服务并发时就会把责任归到错误的模块上。
2.2 0x11 复位与 0x3E 保活:自动化里最容易翻车的两个点
0x11 让客户端强制服务器复位,请求里带一个 resetType 子功能。这里有一条必须记住的行为约定:复位成功执行后,服务器会激活 DefaultSession。也就是说,前一秒还在扩展会话里顺畅地跑用例,发了 0x11 之后会话状态直接归零,后续所有依赖非默认会话的服务全部失效。这一点在写自动化序列时格外要命,用例之间不做会话重建,就会出现“上一条过了、下一条莫名报 NRC”的玄学现象。
正响应里有个 powerDownTime 参数,只在子功能为 0x04(enableRapidPowerShutDown)时才有效,取值范围 00~FE,FF 表示出错或时间不可用。这个细节在核对响应长度时经常被漏掉。
0x3E TesterPresent 是维持会话不掉线的保活帧,通常周期性发送,子功能 0x80 表示抑制正响应,避免刷屏。
2.3 0x27 安全访问:seed 与 key 的配对规则
0x27 是全篇最值得反复看的一节。它的流程四步:客户端请求 seed、服务器回 seed、客户端用 seed 算出 key 并发送、服务器校验 key 后解锁。
子功能有个硬规则:奇数是请求 seed,偶数是发送 key。所以 27 01 请求 seed,对应发 key 用 27 02;27 03 对应 27 04。原文给出的理解是“seed 请求对应的 key 是相邻且大于 seed 的最小整数”,这条对应关系必须背下来,否则自动化脚本里子功能错一位,现象就是一直返回无效 key。
27 01 → 请求 Level1 seed 67 01 <seed...> → 正响应,带 seed 27 02 <key...> → 用 seed 算出的 key 发回 67 02 → 解锁成功,正响应只有 SID 和子功能注意正响应的差异:seed 请求的正响应带 securitySeed 参数,而 key 请求的正响应只有 Service ID 和 securityAccessType 两段。如果解析函数对这两种响应一视同仁地取 seed 字节,key 响应就会读越界。
NRC 侧重点看 0x35、0x36、0x37 这一组:invalidKey、exceedNumberOfAttempts、requiredTimeDelayNotExpired。原文的实测节奏是第一次错 key 报 0x35,等 5 秒第二次错报 0x36,再等 5 秒第三次错报 0x37。这个延时不是随意定的,脚本里不加重试间隔,用例会连续拿到同一类 NRC,看起来像功能坏了。
2.4 0x29 身份验证:比 0x27 更重的一套体系
0x29 解决的是同一类需求——证明“我是合法客户端”,但手段升级了。它支持两条技术路线:一条是基于非对称加密的 PKI 证书交换,证书格式可用符合 ISO 7816-8 的 CVC,或 X.509;另一条是不带证书的挑战—应答,可用非对称也可用对称加密。
两条路线内部又各有单向和双向之分。单向认证只让服务器验客户端,客户端无法确认对面是不是正确的服务器;双向认证则双方互验,服务器也要出具自己的证书和所有权证明。原文列出的子功能包括 deAuthenticate、verifyCertificateUnidirectional、verifyCertificateBidirectional、proofOfOwnership、transmitCertificate、requestChallengeForAuthentication、verifyProofOfOwnershipUnidirectional、verifyProofOfOwnershipBidirectional、authenticationConfiguration。
复习这一节不必背流程编号,抓住三件事即可:一是认证和 0x27 解决的是同类问题,区别在强度与是否引入证书体系;二是失败尝试的次数上限和延迟由主机厂自定,不是协议写死的;三是客户端在服务器已授权后若发现验证异常,可以主动发 deAuthenticate 让服务器退出已验证状态。
2.5 这一单元里其余服务的定位
0x28 通信控制用于开关某类报文的收发,常见于刷写前静默总线;0x85 ControlDTCSetting 控制 DTC 记录开关,刷写或标定时临时关掉故障码记录;0x83 访问时序参数、0x84 安全数据传输、0x86 事件响应、0x87 链路控制属于低频服务,多数项目里不实现或用不到,但读规范时要知道它们属于哪个功能单元,别在排查时误以为协议缺项。
3. 数据读写与 DTC:日常诊断里出现频率最高的三个服务
这一块是诊断仪每天真正在跑的部分。0x22 读数据、0x2E 写数据、0x19 读故障码,三者撑起了售后诊断和产线检测的大部分场景。0x14 清故障码、0x2F 输入输出控制、0x31 例程控制则是配套动作。
3.1 0x22 与 0x2E:按 DID 读写,一问一答的基本盘
0x22 ReadDataByIdentifier 用两字节的 DID 去读数据。DID 是数据标识符,主机厂按自己的规范分配,比如某个 DID 对应车速、某个对应软件版本号。
22 F1 90 → 请求读 DID 0xF190(常见为车辆识别信息) 62 F1 90 <data...> → 正响应,0x22 + 0x40 = 0x62,后跟 DID 回显和数据0x2E 写入的结构对称:
2E F1 90 <data...> → 写入 DID 0xF190 的数据 6E F1 90 → 正响应,只回显 SID 和 DID,不带数据提示:0x2E 的正响应不回显写入的数据,只回显 DID。做响应长度断言时按“SID + 2 字节 DID”来算,别按请求长度对齐。
0x22 的请求可以一次带多个 DID,响应按顺序拼接,但解析时必须依据每个 DID 的长度定义切分,硬编码偏移量在换项目后一定崩。这是读诊断日志最容易踩的坑之一。
同单元里 0x23 按地址读内存、0x3D 按地址写内存,粒度比 DID 粗,风险也高,一般只在开发或刷写场景开放;0x24 读缩放数据、0x2A 周期读数据、0x2C 动态定义 DID 属于优化手段,用于减少请求次数或减少解析工作,多数量产项目不用,但规范里有。
3.2 0x19 读 DTC:状态掩码是理解的关键
0x19 的子功能很多,最常用的是 0x02(按状态掩码读 DTC)。请求里的 statusMask 是一个按位解释的字节,每一位代表一种故障状态,比如测试失败、本循环测试失败、待定、已确认等。
19 02 FF → 读取所有状态位满足掩码条件的 DTC 59 02 FF <DTC1><status1><DTC2><status2> ...三个要点:第一,DTC 通常三字节,后跟一字节状态,解析要按四字节步进;第二,返回的 DTC 数量往往在响应头里给出,做长度校验时先信这个计数再信报文长度;第三,0x19 各子功能的参数差异很大,原文专门另开一篇讲 0x19 的参数,说明这个服务的子功能分化程度值得单独复习。
0x14 清除诊断信息按 groupOfDTC 分组清除,常见请求14 FF FF FF清全部。清完之后一般要重新读一次确认,因为部分 DTC 会在下一个驾驶循环立即复现,这是判断“真修好了还是暂时消掉”的常用手法。
3.3 0x2F 与 0x31:控制类和例程类的共同点
0x2F InputOutputControlByIdentifier 用 DID 定位一个可控制的输入输出对象,再用控制参数指定是临时替代、短期调节还是恢复默认。它的价值在于执行器测试,比如强制驱动某个风扇或某个灯,观察硬件响应。
0x31 RoutineControl 用两字节例程 ID 加一个子功能(启动、停止、查结果)来执行一段 ECU 内置逻辑,常见用途是擦除、校验、自检。这两个服务的共同点是:它们都依赖非默认会话,0x31 在多数项目里还要求先过 0x27 解锁。
| 服务 | 定位键 | 典型子功能 | 会话/安全要求 |
|---|---|---|---|
| 0x2F | DID | 短替代、长替代、恢复默认 | 通常需扩展会话 |
| 0x31 | routineIdentifier | start / stop / requestResults | 常需解锁 |
3.4 上传下载四件套:0x34 到 0x37
刷写流程是这四个服务按序串起来的:0x34 RequestDownload 告诉 ECU 我要写、写多大数据、数据格式如何;0x35 RequestUpload 是反方向读;0x36 TransferData 搬数据块;0x37 RequestTransferExit 收尾。
34 00 44 <addrAndLength...> → 请求下载,dataFormatIdentifier 含压缩/加密信息 74 20 <maxNumberOfBlockLength> → 正响应返回单块最大长度 36 01 <data...> → 传第一块 76 01 → 正响应回显块序号 37 → 请求退出传输 77 → 退出成功这里有两个必须注意的地方:一是 0x36 的块序号从 1 开始递增,之后每次加一,序号错乱会导致 ECU 拒绝接收;二是 RequestDownload 正响应里的 maxNumberOfBlockLength 决定了后续每块数据的最大尺寸,脚本按固定长度发块而不读这个参数,换一个 ECU 就可能超长。原文把上传下载单列为一个功能单元,正因为它是刷写类工具的主战场,和普通读数据的容错要求完全不是一个量级。
4. 易错点集中梳理:五个最容易翻车的地方
这一章按“易漏点 → 为什么漏 → 怎么补”来写,全部来自上面各节的边界情况。
4.1 易漏点一:把负响应中间字节当成 NRC
为什么漏:负响应三字节里,7F 是固定标志,第二个字节是原样返回的请求服务号,第三个才是 NRC。读日志时视线容易直奔最后一个字节,尤其在一条日志里连续出现多条负响应时,会把 0x10 的 NRC 记到 0x27 头上。
怎么补:解析函数里显式拆成三段,打印时把服务名一并输出,例如“0x27 返回 NRC 0x35”,不做裸十六进制堆叠。
4.2 易漏点二:忘记 0x11 复位后会话回到 Default
为什么漏:0x11 的正响应看起来很“正常”,用例认为这一步通过就该继续下一步,而会话状态是隐式的,报文里不体现。
怎么补:把“复位后重建会话”写进用例模板,凡是 0x11 正响应之后,强制先发 0x10 切回目标会话,再发后续请求。
4.3 易漏点三:0x27 子功能奇偶搞反,或 key 响应仍去取 seed
为什么漏:请求 seed 与发送 key 只差一个子功能值的奇偶,肉眼扫代码很难发现;同时 key 请求的正响应确实不带 seed,但解析代码往往是复制粘贴过来的。
怎么补:子功能用常量而非字面量,seed 与 key 用两个独立的解析分支,正响应参数个数做长度校验,0x67 02 这类两字节响应直接判定为异常。
4.4 易漏点四:错误 key 的连续重试没有间隔
为什么漏:脚本追求速度会连续发帧,而 ECU 对失败尝试有次数上限与延时要求,连续发只会让 NRC 从 0x35 迅速走到 0x36、0x37,看起来像“功能坏了”。
怎么补:错误 key 的负向用例之间显式等待,按项目要求留足延时;同时把 0x36、0x37 当作可预期结果写进预期表,而不是判失败。
4.5 易漏点五:0x36 块序号与 maxNumberOfBlockLength 被写死
为什么漏:示例里常写 36 01,脚本照抄后块序号不再递增;块长度也按经验值固定。
怎么补:块序号做成自增变量,每次发帧前加一;块长度从 0x34 正响应里解析出来,作为后续分块的上限,不读参数不发送。
5. 复习清单与查漏补缺:把笔记变成能跑的自检流程
复习这份笔记,我的建议是按“服务分组 → 报文结构 → NRC → 会话与安全约束”四层过一遍,而不是从头背到尾。具体可以照下面这张单子自检,每一条都要能不看资料说出结论。
| 检查项 | 要能答出的内容 |
|---|---|
| 正负响应判别 | 正响应 SID 加 0x40;负响应 7F + 请求 SID + NRC |
| 六个功能单元 | 通信管理 11 个、数据传输 7 个、已存储数据 2 个、IO 控制 1 个、例程 1 个、上传下载 4 个 |
| 0x10 | 请求格式 10 xx、正响应 50 xx、会话切换的必要性 |
| 0x11 | 复位后回 DefaultSession、powerDownTime 仅 0x04 子功能有效 |
| 0x27 | 奇数请求 seed、偶数发 key、对应关系、35/36/37 的节奏 |
| 0x29 | 两条技术路线、单向与双向的区别、deAuthenticate 的作用 |
| 0x22/0x2E | DID 两字节、0x2E 正响应不回数据、多 DID 按定义切分 |
| 0x19/0x14 | 状态掩码按位解释、DTC 四字节步进、清后复读 |
| 0x34~0x37 | 块序号从 1 递增、maxNumberOfBlockLength 决定分块 |
查漏补缺的做法:找一份真实的诊断日志,遮住服务名,只看十六进制,逐条说出服务、方向、结果;说不出来的就是漏洞所在,回到对应小节重读。第二遍再反过来,拿服务名去默写请求格式和正响应格式,尤其注意正响应里哪些参数会被回显、哪些不会。
最容易忽略的一步是 NRC 的归属整理。协议里每个服务支持的 NRC 集合是有限的,把常用七八个服务的 NRC 列成一张对照表,会突然发现很多“偶发失败”其实是有规律的条件不满足,比如会话不对、安全未解锁、长度不符、条件不满足。
血泪经验是:我最早写自动化时把用例串成一条长链,中间任何一个 0x11 或会话切换都会让后续全崩,日志里全是 NRC,排查了两天才发现是状态没重置。从那以后我每次搭诊断用例都强制走一遍“复位 → 建会话 → 解锁 → 执行 → 收尾”的骨架,中间任何一步失败就地终止并打印三段式响应,不再让错误往下传。这份按六个功能单元组织的笔记正好可以当骨架的参照,遇到记不清的服务码翻一下对应单元,比从头搜规范快得多。希望帮到你。
本文还有配套的精品资源,点击获取