☰
UDS车载诊断实战指南:服务格式、时序与否定响应码解析
2026/10/13 1:03:47 网站建设 项目流程

做车载诊断开发的人,几乎绕不开UDS这个名字。无论你是在搞嵌入式ECU软件开发、做整车诊断测试,还是在写售后诊断仪的上位机逻辑,UDS(Unified Diagnostic Services,统一诊断服务)都是需要迈过去的第一道门槛。这篇文章不端着讲,就把我实际接触UDS时的学习路径、踩坑记录和核心理解整理出来,包含服务格式、时序要求、常见否定响应码这些入门必啃的内容。文章后面还整理了一批我自己用过觉得靠谱的学习资源清单,对刚入门的朋友来说,跟着这套思路走,会比直接翻协议文档高效得多。

1. 先搞清楚UDS到底在解决什么问题

1.1 汽车诊断这件事,本质是什么

汽车里现在至少有几十个ECU(电子控制单元):发动机、变速箱、ABS、安全气囊、车机、电池管理……每个ECU都是一个小型计算机,有自己的内存、传感器采集、通讯接口。但问题是,这些ECU分散在车里,平时各干各的,一旦出了问题,你不可能拆开每个控制器拿万用表去量哪里坏了。这时候就需要一套统一的手段,让外部设备(诊断仪、标定工具、产线设备)能和任意一个ECU对话,读取它的运行状态、故障信息、参数配置,甚至临时控制某个执行器动作。

诊断的本质就两句话:往外读和往里写。读,是读故障码、读实时数据、读版本号;写,是写配置参数、刷写固件、执行某个特定动作。UDS这个协议,就是为了把这“读”和“写”的请求、响应格式统一起来,让不同厂商、不同车型、不同ECU之间都遵循一套可交互规则。

1.2 UDS在OSI模型里的位置

UDS本身是ISO 14229标准定义的,它属于应用层协议。也就是说,它只规定了“你说什么话、用什么格式说”,至于这句话怎么通过物理线路传到ECU,那是下面几层的事。在传统车载CAN总线环境里,UDS的传输层主要靠ISO 15765-2(也就是常说的CAN TP)来完成帧的分包和重组。CAN单帧最多只能传8字节数据(经典CAN),一条UDS报文往往不止8字节,所以需要传输层把长报文拆成多帧发送,再在接收端拼回来。理解了这个层次关系,后面调试的时候遇到“报文发不出去”或“响应不完整”就不会一头雾水了。

从逻辑上看,UDS会话有点像日常打电话:你先说“你好,我要进入诊断模式”,对方回“好的,已进入”;你再问“当前故障码有哪些”,对方把故障码列表报给你;你想改某个参数,对方可能要求你先“证明你有权限”,这时候就要走安全访问流程。这套会话、鉴权、读写、控制的框架,就是UDS要规范的事。

1.3 谁在用UDS,哪些场景依赖它

  • 研发阶段的软件刷写:产线或者售后要刷写新的固件,底层Bootloader里跑的就是UDS的会话控制和传输控制服务。
  • 下线检测(EOL):车辆下线时要通过诊断仪配置VIN码、标定参数、校准传感器,这些操作基于UDS写入服务。
  • 售后故障排查:4S店的诊断仪连接车辆后,通过UDS读故障码、清故障码、读取冻结帧数据。
  • 测试验证:很多整车测试规范里对UDS服务都有明确要求,比如必须支持哪些服务ID、默认会话下哪些服务不可用、时序是否满足规定,都需要逐条测试。

我自己当年入行时就是从“读故障码”这个最简单的场景切入的,先搞懂一个服务,再横向扩展,这个思路对新手比较友好。

2. 入门必须理解的四个底层机制

2.1 诊断帧格式:CAN ID和数据域

在实际CAN总线抓包时,UDS诊断报文的CAN ID通常分成两类:一类是功能寻址请求,一类是物理寻址请求。功能寻址是一对多,比如诊断仪发一个请求,总线上所有ECU都会收到并判断是否响应;物理寻址是一对一,CAN ID里明确指定了要和哪个ECU通信,比如0x7E0常见用于请求发动机ECU(依据整车厂定义),ECU则用0x7E8响应。这里需要注意,不同厂商的CAN ID定义可能不同,但同一辆车里诊断请求/响应的ID映射关系是固定的。

数据域部分,CAN帧的起始字节是PCI(协议控制信息),它告诉接收方这条报文是单帧还是多帧。当发送数据不超过7字节时,直接使用单帧格式,第一字节的高四位为0,低四位表示后续数据长度;当数据超过能单帧容纳的长度时,需要先发首帧(FF),首字节为0x10,接着是16位的数据总长度,然后逐次发送后续帧(CF),后续帧首字节高四位为0x2,低四位为帧序号。理解了单帧、首帧、后续帧的格式,你就已经扫清了UDS入门路上最大的格式障碍。

2.2 服务ID的分类与命名规律

UDS的核心服务ID,简称SID,都是一个字节,范围在0x00-0x7E之间。它们有明确的功能分组:

SID (十六进制)服务名称方向与应用场景
0x10诊断会话控制切换默认/编程/扩展会话
0x11ECU复位软复位、硬复位、键下电复位
0x14清除诊断信息清故障码
0x19读取诊断信息读故障码、冻结帧、快照数据
0x22按标识符读取数据读版本号、电压、温度、VIN等
0x27安全访问种子与密钥交换,解锁受限服务
0x2E按标识符写入数据写VIN、写标定参数、写配置
0x2F输入输出控制控制执行器动作,如开关继电器
0x31例程控制启动/停止/查询例程,如自检例程
0x34请求下载固件刷写前置步骤
0x36数据传输固件刷写时传输数据块
0x37请求传输退出固件刷写收尾
0x3E测试器在线维持会话,防止超时退出
0x83访问时间参数读取/修改定时参数

学习时不建议死记硬背,抓住规律就好:0x2x是读写相关,0x3x是传输和例程相关,0x1x是会话和复位相关,0x19专门负责故障码读取。一次记不熟没关系,实操时反复翻这张表,一个月内自然就烂熟于心了。

2.3 肯定响应和否定响应的格式约定

发送一个UDS请求后,ECU会返回两种结果之一:肯定响应或否定响应。肯定响应的格式很简单:[SID + 0x40] + 具体数据。比如发送0x10 01请求进入默认会话,响应是0x50 01,这个0x50就是0x10 + 0x40的结果。同理,0x22读数据的肯定响应首字节是0x62。这个规律能帮你快速判断一个响应是不是肯定响应。

否定响应的格式是:0x7F + 请求的SID + 否定响应码。比如你发送0x22 F1 90,ECU返回0x7F 22 31,意思是请求的服务ID是0x22,但ECU给出的响应码是0x31(请求超出范围),通常是数据标识符不存在或当前会话不可用。否定响应码是排查问题的重要线索,下面单独列常遇到的几种。

2.4 三种会话模式和安全等级的概念

UDS定义了几种会话模式,最常见的是默认会话(Default Session,0x01)、编程会话(Programming Session,0x02)、扩展会话(Extended Session,0x03)。ECU上电默认在默认会话,默认会话下能用读数据、读故障码这类安全服务,但写数据、刷写固件、例程控制这些高风险操作基本都被禁止。切换到扩展会话或编程会话后,受限制的服务才会开放权限,但很多关键服务还会再叠加一道安全访问(0x27)的门禁。

安全访问机制简单说就是“挑战-应答”:诊断仪发0x27 01请求种子,ECU回一个种子值,诊断仪用密钥算法对种子计算得到密钥,再发0x27 02带上密钥,ECU验证通过后才解锁。密钥算法本身不通过UDS明文传输,只存于ECU和诊断仪的私有配置中,这也是防盗刷的核心手段。理解这个层次后,你在用上位机工具时会很自然地明白为什么“能连上但写不进参数”——大概率是会话没切对或者安全访问没通过。

3. 七个高频服务的报文格式与实操要点

3.1 诊断会话控制(0x10)——一切操作的入场券

请求格式:10 [子功能]

常用子功能:

  • 0x01:切换到默认会话
  • 0x02:切换到编程会话
  • 0x03:切换到扩展会话

肯定响应:50 [子功能] [P2*定时参数] [P3*定时参数](后两个参数可选)

实操经验:很多ECU在扩展会话里会自动降低某些报文周期,方便诊断通信;切换会话后,ECU往往会立刻结束当前正在进行的其他服务。所以写配置项之前,先切到扩展会话(0x10 03),确认响应里的定时参数,再继续下一步,这会减少很多莫名奇妙的通信问题。

3.2 安全访问(0x27)——受限服务的通行证

请求种子:27 [子功能]请求密钥:27 [子功能 + 1] [密钥数据]

子功能通常是成对出现的,奇数请求种子,偶数发送密钥。比如27 01请求种子,27 02发送密钥;27 03请求种子,27 04发送密钥。子功能每+1就是一轮新的安全解锁,实际厂商可能定义多级解锁,比如先解锁级别1再解锁级别2。

一个容易踩的坑:安全访问有次数限制,连续输错若干次密钥之后,ECU会在一段时间内不再响应新的种子请求。所以调试阶段不要猛点“发送密钥”,要等响应、确认种子值逻辑无误后再发。

3.3 读取数据(0x22)与读取故障码(0x19)——最常见的两个服务

0x22的请求格式是:22 [数据标识符高字节] [数据标识符低字节]

举例:读取ECU软件版本号,数据标识符假设是0xF190,请求发22 F1 90,肯定响应是62 F1 90 [版本数据]。数据标识符的定义通常写在厂商的诊断规范里,比如0xF187是VIN、0xF18C是ECU硬件号,不同厂商差异很大。

0x19读取故障码,比很多人想象中复杂,因为它有不同的子功能:

  • 19 02:按状态掩码读故障码,响应里带DTCHighByte、DTC中字节、DTC低字节和状态位
  • 19 04:读快照数据(冻结帧),响应里带数量、DTC和时间参数
  • 19 0A:读最近一次出现且当前状态存在的故障码

我最初用0x19时总习惯只发19 02,但整车厂诊断规范里往往要求用19 02 FF读取所有故障码状态。掩码0xFF表示所有状态都读,非常实用。

3.4 写入数据(0x2E)——改参数前必看会话与安全状态

请求格式:2E [DID高字节] [DID低字节] [数据]

举例:写入VIN,数据标识符0xF190,数据为17字节ASCII码,请求就是2E F1 90 [ASCII字符串],肯定响应是6E F1 90。

写入服务涉及对ECU存储器的修改,绝大多数情况下要求ECU处于扩展会话或编程会话,且通过安全访问。另外,不同DID的写入条件不一样:有的DID只能在未锁定状态写一次,有的DID要求先用例程控制做解锁。遇到“写失败+否定响应码0x22”这类情况时,先查会话模式和安全状态,通常十有八九是这两兄弟的门槛没过。

3.5 例程控制(0x31)和输入输出控制(0x2F)——主动让ECU干活的入口

例程控制的请求格式是:31 [子功能] [例程ID高字节] [例程ID低字节] [可选参数]

子功能定义:

  • 0x01:启动例程
  • 0x02:停止例程
  • 0x03:查询例程状态

例程ID常见的场景有:删除DTC信息(0xFF00在部分规范里用于清码)、自检例程、传感器标定例程、Bootloader擦除Flash的例程。例程控制非常灵活,不同ECU可执行的例程列表必须查规范,不能凭经验。

输入输出控制0x2F则是直接控制某个执行器,请求格式:2F [DID高字节] [DID低字节] [控制选项] [控制值]。控制选项一般是:

  • 0x00:强制为指定值
  • 0x01:是而非,短暂置为指定值
  • 0x02:释放控制

这个服务在测试执行器动作时极其方便,比如强制打开继电器、驱动风扇测试、让水泵全速运转。用的时候一定要记得结束控制,不然执行器会一直保持强制状态,这在台架测试时容易引发安全风险。

3.6 ECU复位(0x11)——最容易被忽略却影响全局的操作

请求格式:11 [子功能]

常见子功能:

  • 0x01:硬复位
  • 0x02:键下电复位(OFF/ON)
  • 0x03:软复位

ECU复位后,会话会回到默认会话,已经解锁的安全状态也被清空。所以刷完固件要做复位验证时,记得复位之后重新切会话、重新做安全访问,不要用旧会话往下推。

4. 从零开始的一次完整诊断实操流程

4.1 软硬件准备与工具链选择

做UDS诊断入门,不需要一开始就上全套整车环境。入门阶段准备好这几样:

  • 一块CAN分析仪或者支持CAN的调试设备(常见的有USBCAN类设备、周立功CAN卡、PCAN,以及一些国产集成式诊断盒子)
  • 一个PC端CAN调试工具,支持收发CAN报文、解析DBC/CANdb或ODX/CDD文件最好
  • 一个被测ECU或台架模拟环境,开发阶段常见的是直接用支持CAN接口的桌面测试板卡加载UDS协议栈
  • 如果手头有整车/台架资源,直接用诊断仪也能做,但学习效果不如逐步用原始CAN报文交互来得深

入门阶段不建议一上来就追求自动化测试工具链。先手动发一条10 03,看ECU怎么回应,建立报文的体感,后面再用自动化工具批量执行回归测试,才会有判断力。

4.2 一个标准示例:从默认会话成功读到故障码

假设CAN ID定义:请求ID 0x7E0,响应ID 0x7E8,波特率500kbps,ECU供电正常。下面是一次完整的交互流程:

  1. 发送会话切换请求:02 10 03

    这是单帧格式,第一字节0x02表示数据长度2字节,后面跟2字节数据10 03。如果ECU响应02 50 03,说明已进入扩展会话。

  2. 请求读取DTC状态:02 19 02 FF

    这里单帧数据长度2字节,服务ID是0x19,子功能0x02,参数0xFF。

    如果DTC为空,会收到类似04 59 02 00 00的响应:0x04是数据长度,0x59是0x19+0x40的肯定响应,0x02是子功能,0x00表示后续DTC状态计数0,0x00表示状态可用性掩码。

    如果有DTC,响应可能是多帧格式,比如首帧10 14 59 02 01 00 ...,表示总长度20字节,后续跟多个后续帧直到数据完整。

  3. 清除故障码:01 14

    0x14服务不需要子功能参数,直接发一个字节的SID即可。响应是01 54。

    注意:清除故障码前通常要求条件满足(比如发动机熄火、车速为零),否则ECU会回否定响应。

我在实际操作中遇到过一种情况:发送02 10 03后,ECU迟迟不回响应,但直接发19 02 FF却能收到响应。原因是目标ECU并不支持0x03扩展会话,只支持0x02编程会话(部分Bootloader阶段才支持扩展会话)。这提醒我:拿到一个新ECU时,先发10 03试探,再发10 02试探,根据否定响应码判断当前支持什么会话,比翻规范更快建立直观认识。

4.3 报文解析实操:抓包还原一段完整对话

用CAN工具抓到的原始报文大概长这样(假设是PCAN或USBCAN工具里的数据):

ID=0x07E0 DATA: 02 10 03 00 00 00 00 00 ID=0x07E8 DATA: 03 50 03 32 F4 00 00 00

第一帧是请求:单帧、长度2、数据是10 03,请求切换扩展会话。第二帧是响应:单帧、长度3、数据是50 03 32 F4,0x50是肯定响应,0x03表示已切换,0x32 0xF4表示P2*定时参数为0x32F4个毫秒,大约13ms。这里的32 F4不是随便的数值,解读时要把两个字节组合成16位整型数。

不建议一开始就依赖工具的自动解码功能,自己手动拆解几组原始数据后再用工具的DBC协议解析对照,能非常有效地建立“数据在链路层长什么样”的感觉。我见过不少新人,工具解得出报文就万事大吉,一旦遇到非标格式就完全束手无策,这就是底子没打牢。

5. 常见问题与排查技巧实录

5.1 请求发出去了,ECU完全不响应

这种现象常见的原因有四种:

  1. 物理层通信不通。先检查CAN高/低线路是否正确连接、终端电阻是否匹配(目标网络两端各120欧姆)、总线是否有干扰导致Bus Off。用CAN工具查看总线负载率和错误帧数量,能直接判断链路健康状态。
  2. 请求ID不对。诊断请求的CAN ID没有对准目标ECU的物理寻址ID。整车网络里同一个功能寻址ID可能同时被多个ECU监听,但物理寻址ID是唯一对应的。如果请求ID错误,ECU自然不会应答。
  3. 会话模式把服务关掉了。比如在默认会话请求执行例程控制31 01 XX XX,某些ECU规范要求只有编程会话才支持,默认会话下发这条请求,ECU可能直接丢弃而非回否定响应。
  4. 波特率不匹配。CAN波特率必须和ECU侧一致,否则根本收不到有效数据。

排查思路:先看错误帧数量,再看总线负载,再确认发送ID、波特率,最后逐个会话模式试探。不要上来就怀疑ECU坏了,绝大多数“无响应”是配置问题。

5.2 响应是0x7F,否定响应码到底怎么读

否定响应本身是最有价值的信息来源,常见响应码列表如下:

响应码 (十六进制)含义常见触发场景
0x10一般拒绝请求格式不符合当前条件
0x11不支持请求服务ID不存在/不识别
0x12子功能不支持服务ID支持但子功能不支持
0x13报文长度错误数据长度不对,比如缺参数或多字节
0x22条件不满足会话不对、安全未解锁、条件未满足
0x24请求超出范围DID不存在或参数值非法
0x31请求超出范围,但已尝试执行例程ID范围不对
0x33安全访问被拒绝密钥错误或超过尝试次数
0x36超过最大字节数数据传输时数据块超限
0x37传输时间超时数据传输间隔超时,需重新请求下载
0x72正确数据但存储失败写入Flash校验失败

0x7F 22 31一条响应,拆开看:0x7F是否定响应标志,0x22是原请求的服务ID,0x31是具体失败原因。新手最容易忽略的是中间那个“原请求SID”,它常被误当成失败码本身,结果排查方向完全跑偏。

我的习惯是:每次收到否定响应,先记录完整的三个字节,再对照响应码表逐条筛选,最后结合当前会话模式判断,通常能快速定位问题。

5.3 安全访问的种子和密钥,绕不开的算法逻辑

种子是ECU每次动态生成的随机值或伪随机值,不同ECU的种子长度可能不同,常见的是2字节或4字节。密钥算法有几种典型形态:比如把种子字节按位取反、字节交换、加固定偏移后循环左移,甚至用查表方式。密钥算法不会通过CAN报文暴露,厂商内部会把算法封装在诊断仪配置文件或私有DLL里。

实际调试中,如果拿不到厂商算法,有两个合法途径:一是研究已知开源诊断协议栈对种子算法的支持方式,二是通过逆向诊断仪软件分析其算法逻辑,但这往往牵扯授权问题,无论哪种都要注意知识产权约束。初学者不要在这个环节卡太久,先学会“解析种子、计算密钥、发送密钥、验证解锁”这个流程本身,算法实现细节可以后续项目再深度钻研。

5.4 时序参数P2和P3是怎么回事

UDS规范里有一条重要时序要求:ECU收到请求后,需要在P2时间内给出响应。P2默认一般是50ms,如果ECU因为内部处理忙,不能在这个时间内给出响应,它会先回复一个0x7F 0x78 0x02这样的“待定响应”(responsePending),告诉诊断仪“我正在处理,别急”,然后在大约P2*的时间内给出最终响应。

这里有个容易出问题的地方:如果诊断仪工具那边的响应超时时间设置小于ECU实际需要的处理时间,就会出现“ECU明明处理完成了,但诊断仪认为超时”的假象。所以调试时要把CAN工具的响应超时时间调大,比如设置5000ms,同时观察是否有待定响应,别直接下“ECU不响应”的结论。

每次切换会话后,ECU会在肯定响应里返回当前P2/P3定时参数,如果响应里包含了这些值,那说明ECU希望诊断仪用这个新值更新本地超时配置,忽略这些返回值可能导致后续长操作反复判错。

文章后面附的资源清单里,我按类别给了一些我作为过来人用过且觉得有价值的资料,从协议规范、开源实现到视频课程,都有覆盖。

5.5 一个完整的Bootloader刷写流程印象

虽然UDS刷写不在“入门必须全部掌握”的范围内,但提前理解整体流程对搭建知识体系极有帮助。典型流程是:

  1. 进入编程会话(0x10 02)
  2. 做安全访问解锁(0x27)
  3. 请求下载(0x34),携带内存地址和大小,ECU返回块长度
  4. 数据传输(0x36),按块大小循环发送固件数据
  5. 请求传输退出(0x37),确认传输结束
  6. 检查编程完整性(例程控制0x31启动校验例程,比如CRC检查)
  7. ECU复位(0x11)进入应用模式

刷写过程最常翻车的环节是传输超时和块大小不匹配。0x36每次最多传多少数据,取决于0x34响应里的块长度,不要自己随便定。另外,在0x34和0x36阶段千万不要随意插入其他UDS服务,很多ECU在这个状态下只接受数据传输相关报文,多插一条无关请求可能导致刷写中断,整包固件作废。

6. 我建议的学习路径与资源清单

UDS资料其实不少,但能直接让新人从零建体系的没那么好找。我整理了一张按“入门→进阶”排序的资源清单,每类都挑了质量过关的,读者可以根据自己的基础按次序使用:

  • 协议标准阅读顺序:ISO 14229-1(UDS规范)为主线,ISO 15765-2(CAN传输层)为辅线。先读后面提到的服务部分,再读协议状态机,不必第一遍就逐页啃完。
  • 行业实践类资料:很多整车厂内部诊断规范虽然不外发,但网络上公开的OEM诊断手册、第三方诊断培训课件(Word/PDF格式)、供应商提供的DBC和CDD示例文件,都是很好的对照案例。
  • 开源项目参考:开源的UDS协议栈实现有不少,建议找一个精简版,结合本文章节的报文流程逐行读源码,能把“协议文字”和“代码行为”对应起来,理解度会有明显提升。
  • 视频与培训课程:部分主流培训机构的公开课片段、行业会议技术分享(非商业推广性质),对于理解整车厂实际项目里UDS的应用方式有帮助。

我的建议是不要贪多,先把这篇里列的服务ID表、否定响应码表和一次完整交互流程吃透,然后再去啃标准原文。如果手头有开发板和CAN工具,最好把常用服务都手动发一遍,记录每个请求的响应,这个过程比看十遍文档都管用。

7. 写在最后:一条少有人提的实操心法

很多人学UDS卡住,不是因为不努力,而是因为太早钻进ISO协议正文里抠细节。真实项目里UDS和DBC、CDD、ODX这些文件密不可分,光懂UDS格式而不会用诊断数据库文件,你面对一台真实车辆时依然发不出正确的请求。反过来,如果你先把常用服务、会话切换、肯定/否定响应的交互节奏玩明白,再去碰诊断数据库工具,就会觉得非常自然。

我个人在实际调试中最受益的习惯是:每接触一个新ECU,先制作一张“会话模式×服务可用性”的矩阵表,把支持的服务、允许的会话、安全等级、响应时序全部录进去,后续所有测试都围着这张表展开。这样不仅能快速定位问题,还能在团队协作时让对方三分钟理解这个ECU的诊断能力边界。

最后再分享一个小技巧:调试UDS时,把你的CAN工具配置成“自动发送单条请求”模式,用键盘快捷键逐条发送请求、逐条观察响应,比把一堆报文倒进脚本里盲跑更能建立直觉。保持手工发报文的习惯,哪怕后面用上了自动化测试,这个基本功依然能在疑难问题排查时帮你抓住突破口。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询