我的汽车进步之路-UDS诊断-ISO 14229 通用 UDS 标准和整车真实业务(OTA 升级、产线测试、售后诊断、功能配置)

会话管理(Sessions):诊断权限的入口,对应 UDS 0x10(会话控制服务),定义了 3 种会话等级,等级越高开放权限越大:

服务 ID(SID)0x10,肯定响应 SID:0x50 核心定位:UDS 诊断体系的「权限入口」,用于切换 ECU 的诊断会话等级,控制不同诊断服务、操作权限的开放与关闭,是所有高权限诊断操作(写数据、刷写、例程执行)的前置步骤。

一、服务帧格式

1. 请求帧结构

表格

字节序号 字段 说明
1 SID = 0x10 固定服务标识符
2 子功能字节 低 7 位 = 会话类型;最高位(bit7)= 肯定响应抑制位(SPR)
  • 肯定响应抑制位(SPR):置 1 时 ECU 执行成功后不返回肯定响应,常用于功能寻址群发场景,避免多 ECU 同时回复造成总线拥堵。
    • 示例:10 03 = 请求切换扩展会话,需要 ECU 回复;10 83 = 请求切换扩展会话,不要求肯定响应。
  1. 肯定响应帧结构
字节序号 字段 说明
1 SID = 0x50 固定响应标识符(原 SID+0x40)
2 会话子功能 回应当前切换成功的会话类型
3-4 会话参数 通常为 S3Server 会话超时时间(单位 ms),告知诊断仪保活周期上限

二、ISO 14229 标准定义子功能

标准共定义 3 类基础会话,是所有车企 ECU 通用的基础能力,权限从低到高依次为:

子功能值 官方名称 运行环境 核心权限与用途
0x01 Default Session默认会话 应用层 ECU 上电 / 复位后的默认状态,权限最低:仅开放基础只读操作(读版本、读 VIN、读故障码)无强制超时要求,长期保持所有写操作、刷写、高权限例程全部禁用
0x02 Programming Session编程会话(这是权限最高的) Bootloader 引导层(引导加载程序) 专门用于固件刷写的最高权限会话:・开放 34/35/36/37 数据传输、Flash 擦除校验等刷写服务・ECU 关闭全部应用业务功能,仅保留诊断与刷写逻辑・必须完成最高等级安全访问解锁才能进入
0x03 Extended Diagnostic Session扩展诊断会话 应用层 产线调试、售后维修的主力会话:・开放 2E 写数据、28 通信控制、85 故障码控制、大部分 31 例程・需满足安全访问条件才能执行高权限操作・存在 S3Server 超时(通常 5s),超时自动退回默认会话

常见否定响应与排查

一、完全统一的部分:帧格式 + 通用核心 NRC

1. 否定响应帧格式 全局统一

所有符合 UDS 标准的 ECU,否定响应都遵循固定格式,没有例外:

复制代码
7F + <请求服务SID> + <NRC错误码>

例如切换会话失败返回 7F 10 22,表示:10 服务执行失败,错误原因是条件不满足。这个帧结构是 ISO 强制标准,全车企、全 ECU 一致。

2. 通用类 NRC(全服务通用,标准统一定义)

这部分是所有 UDS 服务都可能返回的基础错误码,含义固定,是排查问题的通用依据,覆盖 90% 的日常场景:

NRC 值 官方名称 典型场景
0x10 generalReject 通用拒绝,无法归类的底层错误
0x11 serviceNotSupported ECU 未实现该服务,比如部分低端 ECU 不支持 23 内存读取
0x12 subFunctionNotSupported 子功能不支持,比如发送了 ECU 未定义的会话子功能
0x21 busyRepeatRequest ECU 正忙,无法响应,需稍后重试
0x22 conditionsNotCorrect 请求条件不满足,比如行驶中刷写、蓄电池电压过低、档位不符
0x31 requestOutOfRange 参数无效,比如 DID / 例程 ID 不存在、内存地址非法、数据长度错误
0x33 securityAccessDenied 安全访问未解锁,无权限执行高权限操作
0x7E subFunctionNotSupportedInActiveSession 当前会话等级不支持该操作,比如默认会话下执行 28 通信控制

二、不统一的部分:专属码、自定义码与触发规则

1. 特定服务的专属 NRC(标准定义,但仅对对应服务有效)

ISO 标准为部分高复杂度服务定义了专属错误码,仅在对应服务的响应中生效,不属于全局通用。相同数值在不同服务下含义可能不同,常见的有:

  • 27 安全访问专属:0x35(密钥无效)、0x36(超过解锁尝试次数)、0x37(解锁冷却时间未到)
  • 31 例程控制专属:0x32(例程已在运行,不可重复启动)
  • 34/36 刷写服务专属:0x70(数据传输挂起)、0x72(Flash 编程失败)
  • 29 认证服务专属:证书验证失败、证书过期等细分错误码

2. 厂商自定义 NRC 区间(完全不通用)

ISO 标准明确规定 0x80 ~ 0xFF 为 OEM 厂商自定义区间,车企可根据业务需求定义专属错误码,不同品牌、不同 ECU 均可能不同。

0x11 服务(ECUReset,ECU 复位服务)

服务 ID(SID)0x11,肯定响应 SID:0x51 核心定位 :UDS 体系中的「重启控制服务」,由诊断仪主动下发指令,控制目标 ECU 执行不同级别的复位重启,是配置生效、刷写收尾、故障恢复的核心操作。你这份文档中明确标注了 11 01 - Hard Reset,是工程中最常用的复位类型。

一、服务帧格式

1. 请求帧结构

字节序号 字段 说明
1 SID = 0x11 固定服务标识符
2 子功能字节 低 7 位 = 复位类型;最高位(bit7)= 肯定响应抑制位(SPR)
  • 肯定响应抑制位(SPR):置 1 时 ECU 不返回肯定响应,复位动作直接执行。功能寻址批量复位时常用,避免总线拥堵。
  • 示例:11 01 = 请求硬复位,需要 ECU 返回响应;11 81 = 请求硬复位,不要求肯定响应。
  1. 肯定响应帧结构
字节序号 字段 说明
1 SID = 0x51 固定响应标识符(原 SID + 0x40)
2 复位子功能 回应当前执行的复位类型

注意:ECU 通常是先返回肯定响应,再执行复位,响应发出后极短时间内 ECU 就会进入重启状态,总线通信短暂中断。

二、ISO 标准定义的子功能

ISO 14229-1 定义了多档复位级别,按复位深度从强到弱排列,工程中 90% 以上的场景只用 0x01 硬复位。

子功能值 官方名称 复位深度 说明
0x01 Hard Reset硬复位 硬件级 最常用、复位最彻底。相当于给 ECU 执行完整的硬件断电重启,CPU、所有外设、存储控制器全部复位,效果等同于整车下电再上电。✅ 本文档使用的就是该类型
0x02 Key On/Off Reset钥匙复位 点火级 模拟整车点火开关关闭再打开的复位效果,仅复位应用层逻辑,部分常电模块不复位,多用于售后场景模拟重启
0x03 Soft Reset软复位 软件级 仅复位应用层软件,硬件外设不复位,重启速度快。一般用于软件异常、逻辑卡死的快速恢复,不影响底层硬件状态
0x04/0x05 快速断电使能 / 禁用 电源管理级 控制 ECU 是否支持快速下电功能,属于电源管理配套子功能,常规诊断场景极少使用

三、核心特性(复位后的状态变化)

  • 会话等级强制归零 复位完成后,ECU 必然回到 默认会话(10 01),无论复位前是扩展会话、OTA 会话还是编程会话,所有高权限状态全部失效。后续要执行高权限操作,必须重新切换会话。

  • 安全访问状态全部失效 所有 27 服务解锁的安全等级、29 服务的认证状态全部清空,复位后需要重新解锁,这是强制的安全设计。

  • 临时配置全部恢复默认 复位前通过 28 服务关闭的通信、85 服务关闭的故障记录、临时修改的运行参数,全部恢复为默认正常状态,不会保留复位前的临时配置。

  • 不保留运行上下文 复位前正在执行的例程、正在传输的数据、定时器状态全部中断,不会断点续跑。这也是为什么刷写必须在复位前完成完整传输,不能传一半复位。

四、典型工程应用场景

1. 固件刷写 / OTA 升级(最核心场景)

这是 11 服务最高频的使用场景,也是 OTA 流程的标准收尾动作:

  • 时机:固件传输完成、校验通过后执行
  • 作用:让 ECU 重启,运行新刷写的固件程序
  • 流程对应:37 结束传输 → 31 校验固件 → 11 01 硬复位 → ECU重启运行新程序

2. 关键配置修改后生效

通过 2E 服务写入的核心配置(如 VIN 码、车辆模式、功能码、网络配置),多数需要执行硬复位后才能正式生效,写入后直接读取可能仍是旧值。

3. 故障与异常恢复

  • ECU 软件跑飞、通信异常、逻辑卡死时,通过 11 服务远程复位,无需物理断电插拔
  • 测试场景中,每条用例执行前执行复位,确保 ECU 处于干净的初始状态,避免前序测试影响结果

4. 模式切换收尾

从 Bootloader 刷写模式切回正常应用模式、退出工厂模式 / OTA 模式时,通过复位完成模式切换,回到正常运行状态。

五、在 CANoe 中的实操表现

结合你正在学习的 CANoe 工具,11 服务的执行有非常直观的现象:

  1. Diagnostic Console 操作 加载 CDD 数据库后,在诊断控制台找到 ECUReset 服务,选择 HardReset 子功能,一键发送即可执行,适合快速验证。

  2. Trace 窗口现象 发送 11 01 后,Trace 中会依次出现:

    • ECU 返回肯定响应 51 01
    • 极短延迟后,目标 ECU 的所有报文全部停止发送(ECU 进入重启)
    • 数百毫秒到数秒后,ECU 重新上电,报文恢复正常发送,此时已处于默认会话状态

CAPL 自动化调用 在 OTA、产线自动化脚本中,11 服务是标准收尾步骤,典型调用逻辑:

cpp 复制代码
// 刷写完成后执行硬复位
diagSendRequest(ecu, "ECUReset_HardReset");
// 等待复位完成,延时后重新建立诊断连接
testWaitForTimeout(2000);

0x27 服务(Security Access,安全访问)

服务 ID(SID)0x27,肯定响应 SID:0x67 核心定位 :UDS 诊断体系的「权限闸门」。所有高风险操作(写配置、刷固件、擦 Flash、产线调试)都必须先通过 27 服务完成身份验证,解锁对应安全等级,ECU 才会允许执行。它和 10 服务(会话控制)配合,构成了 UDS 的完整权限体系:会话决定 "能不能进",安全访问决定 "能不能操作"

一、服务帧格式与核心规则

27 服务采用 **"请求种子 → 发送密钥" 的两步挑战 - 应答机制 **,所有安全等级都遵循同一套交互逻辑。

1. 通用规则

  • 子功能号奇数为 "请求种子",偶数为 "发送密钥";同一安全等级的两个子功能号相邻(如 01 求种子、02 发密钥)。
  • 子功能字节最高位同样支持肯定响应抑制位(SPR),但 27 服务几乎不会开启,因为种子、密钥都需要明确交互。

2. 第一步:请求种子(Request Seed)

诊断仪向 ECU 申请一个随机 "挑战码",用于后续密钥计算。

字节序号 字段 说明
1 SID = 0x27 固定服务标识符
2 子功能(奇数) 安全等级 + 请求种子,如 011141
3+ 安全数据(可选) 部分场景需要诊断仪先发送身份标识,多数 ECU 此段为空

ECU 肯定响应

字节序号 字段 说明
1 SID = 0x67 固定响应标识符
2 子功能号 回应当前请求的安全等级
3+ 种子数据 随机生成的挑战码,长度由厂商定义(通常 2/4/16 字节)

3. 第二步:发送密钥(Send Key)

诊断仪拿到种子后,用双方约定的加密算法计算出密钥,发送给 ECU 校验。

字节序号 字段 说明
1 SID = 0x27 固定服务标识符
2 子功能(偶数) 安全等级 + 发送密钥,如 021242
3+ 密钥数据 诊断仪计算出的应答密钥

ECU 肯定响应

字节序号 字段 说明
1 SID = 0x67 固定响应标识符
2 子功能号 回应当前解锁的安全等级

响应返回即代表解锁成功,此后在会话有效期内,对应等级的高权限操作都可正常执行。

完整交互流程(以 1 级安全为例)

复制代码
诊断仪 → ECU:27 01          // 请求1级安全的种子
ECU → 诊断仪:67 01 12 34    // 返回随机种子 0x1234
诊断仪本地算法:key = f(0x1234)
诊断仪 → ECU:27 02 AB CD    // 发送计算出的密钥 0xABCD
ECU → 诊断仪:67 02          // 校验通过,1级安全解锁成功

三、安全等级体系:标准定义

ISO 14229 标准仅规定了交互机制,安全等级的数量、编号、对应权限完全由厂商自定义

安全等级对照表

子功能对 安全等级 对应会话 解锁后可执行的核心操作
27 01 求种子27 02 发密钥 1 级安全 扩展会话(10 03) 基础写配置操作:- 普通 DID 写入(如车辆模式、产线编号)- 低风险例程执行- 通信控制、故障码控制
27 11 求种子27 12 发密钥 11 级安全 扩展会话(10 03) 高阶调试与配置:- 安全配置写入(如 SecOC 密钥)- 硬件测试例程- 底层参数标定
27 41 求种子27 42 发密钥 41 级安全 OTA 专属会话(10 40) OTA 与刷写最高权限:- Flash 擦除、固件下载- 分区切换、断点续传- 所有 OTA 专属例程

分级设计的核心逻辑

  • 权限隔离:普通售后维修只需解锁 1 级,不会接触到刷写等高危操作;OTA 刷写必须解锁最高的 41 级,最大程度降低误操作风险。
  • 算法分级:等级越高,密钥算法越复杂、种子长度越长,破解难度指数级上升。
  • 通常向下兼容:解锁最高的 41 级后,1 级、11 级的操作也会自动放开,无需重复解锁。

四、核心安全机制(强制设计,所有 ECU 通用)

这是 27 服务的灵魂,也是车载功能安全的强制要求。

1. 临时生效,自动上锁

安全解锁状态不是永久的,出现以下任一情况会自动失效,需要重新解锁:

  • 诊断会话超时(S3Server 超时,通常 5s)
  • 主动切换回默认会话(10 01)
  • ECU 复位、下电重启
  • 点火循环结束

这也是为什么刷写过程中要持续发 3E 服务保活 ------ 一旦会话断了,安全锁自动落下,刷写就会中断。

2. 失败锁定与防暴力破解

连续发送错误密钥会触发锁定机制,防止暴力破解:

  • 通常连续 3 次密钥错误,ECU 会进入锁定状态,一段时间内拒绝所有安全访问请求(常见 10 分钟)。
  • 锁定期间即使发正确的密钥也无法解锁,必须等待冷却时间结束,或 ECU 下电复位后重新计时。

3. 种子随机,防重放攻击

每次请求种子,ECU 都会生成全新的随机数,不会重复。

  • 即使截获了历史上正确的密钥,下次也无法使用,因为种子变了,对应的密钥也会变。
  • 从机制上杜绝了 "录下报文重放" 的攻击方式。

4. 算法保密,闭源实现

密钥生成算法是车企 / ECU 厂商的核心机密,不会对外公开:

  • 工程上通常会封装成 seedkey.dll 动态库,导入 CANoe 后自动完成密钥计算。
  • 不同安全等级、不同 ECU、不同软件版本的算法都可能不同。

五、与会话体系的配合关系

这是最容易搞错的点,记住一个固定顺序: 先切会话 → 再解锁安全 → 最后执行业务操作

  1. 会话是 "入场券":不切到对应会话,连 27 服务本身都可能被拒绝(比如默认会话下不允许解锁 41 级安全)。
  2. 安全访问是 "操作权限卡":切了高等级会话,只是进入了房间,柜子还是锁着的,必须用 27 服务拿到钥匙才能开。
  3. 一一对应:高等级安全必须在高等级会话下才能解锁。比如 41 级安全必须在 10 40 OTA 会话下解锁,在扩展会话里发 27 41 会直接报错。

典型 OTA 流程的权限递进顺序:

10 01 默认会话 → 10 03 扩展会话 → 27 01/02 解锁1级安全 → 10 40 OTA会话 → 27 41/42 解锁41级安全 → 执行刷写操作

六、在 CANoe 中的实操

1. 图形化操作(Diagnostic Console)

加载带安全配置的 CDD/ODX 数据库后,选中对应安全等级,点击执行,CANoe 会自动完成 "求种子→算密钥→发密钥" 的完整两步流程,无需手动干预。

  • 前提:工程中正确配置了对应的 seedkey.dll,并绑定了该 ECU 和安全等级。

2. CAPL 自动化调用

在自动化脚本中,通常调用诊断对象接口即可,底层两步交互由 CANoe 协议栈自动处理:

cpp 复制代码
// 解锁41级安全访问
diagSendRequest(CDCU, "SecurityAccess_Level41");
// 等待解锁完成
if(diagWaitForResponse(CDCU, 1000) == 0)
{
  write("41级安全解锁成功");
}

3. Trace 窗口观测

完整解锁过程在 Trace 里会清晰看到两条报文:

复制代码
27 41 → 67 41 xx xx xx xx(请求种子,返回 4 字节种子)
27 42 xx xx xx xx → 67 42(发送密钥,解锁成功)

七、常见否定响应与排查

解锁失败时,ECU 返回格式:7F 27 + NRC错误码,高频原因如下:

NRC 码 含义 常见原因与排查
0x12 子功能不支持 发送了 ECU 未定义的安全等级,或当前会话不支持该等级
0x22 条件不满足 车辆状态不允许解锁,如行驶中、电压异常
0x35 密钥无效 最常见,密钥计算错误,检查 seedkey.dll 是否匹配版本
0x36 超过尝试次数 连续输错密钥,已触发锁定,需等待冷却或 ECU 复位
0x37 要求的延迟时间未到 处于锁定冷却期,暂时无法请求种子
0x7E 当前会话不支持该子功能 会话等级不够,比如默认会话下解锁 41 级安全

0x28 服务(Communication Control,通信控制服务)

服务 ID(SID)0x28,肯定响应 SID:0x68 核心定位 :UDS 诊断体系的「总线带宽开关」。诊断仪主动控制 ECU 的应用报文、网络管理报文的收发状态,核心目的是清空总线带宽、排除业务干扰,是 OTA 刷写、总线故障排查的标配服务。

一、服务帧格式完整拆解

1. 请求帧结构(标准 3 字节基础格式)

字节序号 字段 详细说明
1 SID = 0x28 固定服务标识符
2 子功能字节 低 7 位 = 收发控制模式;最高位 bit7 = 肯定响应抑制位(SPR)
3 通信类型字节 按位标记控制范围,每一位代表一类报文,支持组合

部分多网关场景会在末尾增加「节点标识」可选字节,单 ECU、普通 CAN 总线场景几乎不用,工程上 99% 都是 3 字节请求。

  1. 肯定响应帧结构
字节序号 字段 说明
1 SID = 0x68 固定响应标识符(原 SID + 0x40)
2 子功能值 回应当前执行的控制模式
3 通信类型值 回应当前生效的报文类型

示例:

  • 请求:28 03 03 → 禁用应用 + NM 报文的收发
  • 响应:68 03 03 → 执行成功

二、子功能:4 种标准模式

ISO 标准定义 4 种收发控制模式,权限从全开到全关

子功能值 官方名称 收发状态 实际用途
0x00 enableRxAndTx 接收开启、发送开启 恢复正常通信,退出静默状态
0x01 enableRxAndDisableTx 接收开启、发送禁用 ECU 只监听总线,不主动发报文,极少使用
0x02 disableRxAndEnableTx 接收禁用、发送开启 几乎无实用场景,绝大多数 ECU 不支持
0x03 disableRxAndTx 接收禁用、发送禁用 完全静默,除诊断报文外全部关闭

工程重点:SPR 位的使用

子功能最高位(bit7)置 1 = 开启肯定响应抑制,ECU 执行成功后不回复。

  • 物理寻址单 ECU 控制:不开 SPR,等待响应确认执行成功,比如 28 03 03
  • 功能寻址全车批量控制:必须开 SPR ,比如 28 83 03
    • 原因:总线上十几个 ECU 同时回复 68 03 03,会造成总线瞬间拥堵、甚至丢帧;开 SPR 后所有 ECU 静默执行,避免风暴。
    • OTA 刷写场景 100% 用功能寻址 + SPR 位的组合。

三、通信类型:按位控制,精准覆盖

通信类型字节是按位掩码设计,每一位对应一类报文,置 1 表示该类报文受当前子功能控制,可自由组合。

报文类型 说明
bit0 0x01 普通应用报文 业务信号、控制指令、周期状态报文等所有正常业务帧,占总线负载的主体
bit1 0x02 网络管理(NM)报文 OSEK NM / AUTOSAR NM 唤醒、休眠管理报文
bit2~bit7 - 保留 / 厂商自定义 部分车企会扩展诊断报文以外的其他类别,常规场景不用

四、不可突破的核心底层机制

这是 UDS 标准强制要求的安全底线,所有 ECU 都必须遵守,也是排查 28 服务问题的核心依据。

1. 诊断通信永远不受控

无论选哪种子功能、哪种通信类型,诊断相关的报文(物理寻址、功能寻址的诊断帧)永远正常收发

  • 不会出现 "发了禁用指令后 ECU 失联、再也恢复不了" 的情况,这是标准强制的安全设计。
  • 受控制的只有普通应用报文、NM 报文,诊断帧完全豁免。

2. 临时生效,异常自动恢复

28 服务的控制效果不持久、不固化,出现以下任意一种情况,ECU 会自动恢复正常收发:

  • 诊断会话超时(S3Server 超时,通常 5s)
  • 主动切换回默认会话(10 01)
  • ECU 复位、下电上电、点火循环结束
  • 诊断链路异常断开

这也解释了为什么刷写过程中 3E 服务保活不能断 ------ 一旦会话超时,通信自动恢复,总线负载飙升,刷写大概率会失败。

3. 会话权限绑定

  • 默认会话下绝大多数 ECU 不允许执行 28 服务,返回 0x7E(当前会话不支持)。
  • 至少需要扩展会话(10 03)及以上才能执行,高风险的全车静默通常要求 OTA 会话 + 安全解锁。

4. 点对点生效,不牵连其他节点

  • 物理寻址发给哪个 ECU,就只控制哪个 ECU 的收发。
  • 想要全车静默,必须用功能寻址(如 0x7DF)群发,不能只发一个网关就指望全车都停。

五、两大核心工程场景

场景 1:OTA / ECU 刷写(最高频)

这是 28 服务存在的核心价值,也是你文档中 OTA 流程的标准环节:

  1. 时机:进入 OTA 会话、解锁安全、关闭故障记录之后,固件传输之前
  2. 操作 :功能寻址群发 28 83 03,全车 ECU 关闭应用报文和 NM 报文
  3. 效果:总线负载从正常的 20%~40% 降到 5% 以下,所有带宽留给 36 服务传固件
  4. 收尾 :刷写完成、校验通过后,群发 28 80 03 恢复正常通信

场景 2:总线故障排查

当总线出现错误帧、负载异常、干扰、丢包问题时,28 服务是定位故障源的高效工具:

  1. 逐个 ECU 物理寻址发送 28 03 03,让单个 ECU 静默
  2. 每停一个就观察总线错误是否消失
  3. 停到某个 ECU 后故障消失,即可定位是该 ECU 导致的问题
  • 相比物理拔插头、断电,28 服务毫秒级切换,不影响整车供电,排查效率提升数倍。

六、在 OTA 全流程中的时序位置

28 服务在完整刷写链路中的位置非常固定,完整顺序如下:

复制代码
10 03 扩展会话
  → 27 解锁基础安全
    → 85 02 关闭故障记录
      → 10 40 进入OTA会话
        → 27 解锁41级最高安全
          → 28 03 03 关闭业务+NM报文  ← 28服务在这里
            → 31 启动Flash擦除例程
              → 34 请求下载
                → 36 传输固件(后台持续发3E保活)
                  → 37 结束传输
                    → 31 固件校验
                      → 28 00 03 恢复通信  ← 恢复通信
                        → 85 01 恢复故障记录
                          → 11 01 硬复位

七、CANoe 实操与验证

1. 图形化操作

加载 CDD 数据库后,在 Diagnostic Console 中找到 CommunicationControl 服务,下拉选择子功能和通信类型,一键发送即可;支持物理寻址单节点、功能寻址群发两种模式。

2. Trace 窗口验证效果

发送 28 03 03 后,可直观观察到三个现象:

  1. 目标 ECU 的所有周期应用报文、NM 报文立刻停止发送
  2. Statistics 窗口总线负载率明显下降
  3. 诊断请求 / 响应帧完全正常,不受任何影响

3. CAPL 自动化调用示例

cpp 复制代码
// 关闭目标ECU的应用+NM报文
diagSendRequest(CDCU, "CommunicationControl_DisableAll");
// 等待执行完成
if(diagWaitForResponse(CDCU, 500) == 0)
{
  write("通信关闭成功,总线已静默");
}

八、常见否定响应与踩坑指南

NRC 码 含义 常见原因
0x12 子功能不支持 发送了 01/02 这类 ECU 未实现的模式,多数 ECU 只支持 00 和 03
0x31 请求超出范围 通信类型参数错误,比如写了未定义的 bit 位
0x33 安全访问拒绝 高权限模式需要先解锁对应安全等级
0x7E 当前会话不支持 默认会话下执行 28 服务,需先切扩展 / OTA 会话
0x22 条件不满足 车辆行驶中、特定功能运行时不允许关闭通信

工程高频踩坑点

  1. 群发不开 SPR 位 :功能寻址发 28 03 03 没开最高位,导致总线瞬间拥堵,部分 ECU 收不到指令。
  2. 刷写中断忘记恢复:测试时中途停止测量,ECU 会话超时后会自动恢复,不用怕 "变砖"。
  3. 网关路由场景失效:只给网关发 28 服务,下挂的子网 ECU 不会跟着停,必须单独对子网寻址。

0x22 & 0x2E 服务深度解析

服务定位 :二者是 UDS 体系中最核心的「数据读写服务」,以 2 字节 DID(数据标识符) 为唯一索引,实现对 ECU 内部各类数据的读取与写入。

  • 0x22(ReadDataByIdentifier):按 DID 读数据,只读操作,是日常诊断、状态查询的最高频服务
  • 0x2E(WriteDataByIdentifier):按 DID 写数据,写操作,是产线配置、参数标定、OTA 状态管理的核心服务

一、服务帧格式与基础特性

1. 0x22 读数据服务

  • 服务 ID(SID)0x22,肯定响应 SID:0x62
  • 核心逻辑:传入 1 个或多个 DID 编号,ECU 返回对应 DID 的实际数据值。
请求帧结构
字节序号 字段 说明
1 SID = 0x22 固定服务标识符
2~3 DID 编号 2 字节数据标识符,大端格式,如 F1 90 代表 DID 0xF190
4~N 更多 DID(可选) 标准支持单请求多 DID 批量读取,工程上通常限制单次最多 4~8 个

示例:读取 VIN 码,请求为 22 F1 90

肯定响应帧结构

字节序号 字段 说明
1 SID = 0x62 固定响应标识符
2~3 DID 编号 回应当前读取的 DID,和请求一一对应
4~N 数据内容 该 DID 对应的实际数据,长度由 DID 定义,不同 DID 长度不同

示例:VIN 读取响应 62 F1 90 4C 42 56 4E 41 47 31 32 33 34 35 36 37 38 39 30 31,后续 17 字节就是 VIN 的 ASCII 码。

核心特性
  • 权限门槛低:大部分基础 DID 在默认会话下即可读取,仅高敏感 DID 需要扩展 / OTA 会话 + 安全解锁
  • 无副作用:纯只读操作,不会改变 ECU 任何运行状态,是最安全的诊断服务
  • 数据语义明确:每个 DID 对应固定的数据定义、长度、单位、解析方式,由 CDD/ODX 数据库描述

2. 0x2E 写数据服务

  • 服务 ID(SID)0x2E,肯定响应 SID:0x6E
  • 核心逻辑:传入 DID 编号和目标数据,ECU 将数据写入对应存储位置(RAM/Flash)。
请求帧结构
字节序号 字段 说明
1 SID = 0x2E 固定服务标识符
2~3 DID 编号 2 字节数据标识符,和 22 服务的 DID 编号体系完全一致
4~N 待写入数据 长度、格式必须和该 DID 的定义完全匹配,否则直接报错

示例:写入车辆模式为 0x01,请求为 2E 01 00 01

肯定响应帧结构

字节序号 字段 说明
1 SID = 0x6E 固定响应标识符
2~3 DID 编号 回应当前写入成功的 DID

写入成功仅返回 DID 编号,不返回数据内容;工程上通常写入完成后会再发一次 22 服务回读,确认写入生效。

核心特性
  • 权限门槛高:所有写操作都要求至少扩展会话,且必须解锁对应等级的安全访问
  • 风险分级:普通配置写入对应低安全等级,OTA 参数、安全密钥写入对应最高安全等级
  • 生效机制分两类:部分参数即时生效,核心配置需 ECU 复位后才正式生效

二、核心载体:DID 数据标识符体系

22/2E 服务的本质是「DID 索引体系」,DID 的编号、含义、读写权限都有明确的行业规则;

  1. DID 编号的区间规则
DID 区间 归属 说明
0xF100 ~ 0xF1FF 标准通用 DID ISO 14229 推荐的通用标识类 DID,全行业通用,比如 VIN、软件版本、ECU 序列号等,绝大多数车企都会遵循
0x0000 ~ 0xEFFF 厂商自定义 DID 车企根据自身业务自由定义,覆盖配置、OTA、网络、动态数据等所有场景,不同品牌不通用
  1. DID 分类详解
(1)标准标识类 DID
  • 定位:ECU 身份与版本的基础标识,售后通用,大部分只读,仅产线可写
  • 权限:默认会话可读,写入需扩展会话 + 安全解锁
DID 含义 读写属性
F1 90 VIN 车辆识别码 可读可写
F1 7F ECU 硬件版本号 只读
F1 80 Boot 软件版本号 只读
F1 89 ECU 软件版本号 只读
F1 8C ECU 序列号 只读
F1 86 当前激活的诊断会话 只读
F1 99 编程日期 只读
(2)OTA 专属类 DID
  • 定位:支撑双分区 OTA 升级的状态与参数,是文档中自定义 DID 的核心部分
  • 权限:读取需扩展会话,写入需 OTA 会话 + 41 级最高安全解锁
DID 含义 读写属性
02 15 当前运行应用分区 只读
02 17 重编程计数器 只读
02 18 OTA 应用软件指纹 可读可写
02 19 OTA 安装日期 可读可写
02 57 OTA 断点信息 只读
02 11 RxSWIN 升级包信息 可读可写
(3)配置参数类 DID
  • 定位:车辆功能、工作模式、安全配置的参数开关,产线下发、售后修改
  • 权限:读取默认会话即可,写入需扩展会话 + 对应安全等级(普通配置 1 级、安全配置 11 级)
DID 含义 读写属性
01 00 车辆模式 可读可写
01 10 工厂模式 可读可写
CF 00 车辆功能码 可读可写
12 64 SecOC 功能配置 可读可写
03 08 AVAS 音效配置 可读可写
A5 F5 监控功能开关 可读可写
(4)动态运行数据类 DID
  • 定位:ECU 实时运行状态、传感器数据、健康度信息,纯只读
  • 权限:默认会话全部可读,用于状态监控与故障排查
DID 含义 读写属性
01 01 总里程 只读
01 02 蓄电池电压 只读
03 09 MCU 温度 只读
30 13 eMMC 寿命 只读
02 24 以太网 SQI 信号质量 只读
04 07 GPS 天线状态 只读
(5)网络与外设类 DID
  • 定位:车载网络、外设模块的参数与状态信息
  • 权限:状态类只读,配置类需扩展会话 + 安全解锁写入
DID 含义 读写属性
02 28 MAC 地址 只读
04 02 ICCID 物联网卡号 只读
04 03 IMEI 设备号 只读
13 28 ESK 加密码 仅写

三、核心运行规则(权限 + 生效 + 约束)

1. 权限分级规则:读低写高,双重校验

22/2E 服务的权限和之前讲的「会话等级 + 安全等级」完全绑定,遵循「读松写严」的原则:

  • 读取操作
    • 基础标识、动态数据:默认会话即可读取
    • 敏感配置、OTA 参数:扩展 / OTA 会话下可读,无需安全解锁
  • 写入操作
    • 普通配置(车辆模式、功能码):扩展会话 + 1 级安全解锁
    • 安全配置(SecOC 密钥、加密参数):扩展会话 + 11 级安全解锁
    • OTA 核心参数(软件指纹、分区切换):OTA 会话 + 41 级最高安全解锁

2. 写入生效机制

不是所有参数写入后立刻生效,工程上分为两类:

  • 即时生效:少数运行时开关、临时参数,写入后立刻起作用,比如部分功能使能位
  • 复位生效 :绝大多数核心配置、版本参数、分区信息,写入后必须执行 11 01 硬复位,ECU 重启加载新配置后才正式生效;复位前回读仍是旧值。

3. 数据格式强制约束

同一个 DID 的读、写数据格式必须完全一致:

  • 长度固定:DID 定义了多少字节,读写就必须是多少字节,多一个少一个都会报参数错误
  • 字节序统一:默认大端模式,和总线字节序保持一致
  • 类型匹配:数值、字符串、枚举的编码方式必须严格匹配 DID 定义

四、在 OTA 与产线流程中的典型位置

1. OTA 升级流程中的读写节点

复制代码
升级前:
  22 读当前软件版本、运行分区 → 确认升级目标
  2E 写升级包指纹、断点信息 → 记录升级上下文
升级中:
  22 读刷写计数器、断点状态 → 监控升级进度
升级后:
  22 读新版本号、运行分区 → 验证升级成功
  2E 写安装日期、更新刷写计数器 → 归档升级记录
  1. 产线下发流程

    产线工位:
    10 03 切扩展会话 → 27 解锁安全
    → 2E 批量写入VIN、序列号、功能码、网络配置
    → 11 01 复位生效
    → 22 回读所有配置 → 确认写入无误

五、CANoe 实操与 Trace 观测

1. 图形化操作

加载 CDD/ODX 诊断数据库后,Diagnostic Console 会自动列出所有支持的 DID:

  • 读取:选中 DID 点击「Read」,自动解析出物理值(字符串、数值、枚举),无需手动转码
  • 写入:选中 DID 输入目标值,点击「Write」即可执行,软件自动处理格式转换

2. Trace 窗口报文示例

读取 VIN 的完整交互:

→ 22 F1 90 // 诊断仪请求读VIN ← 62 F1 90 4C 42 56 ... // ECU返回DID+VIN数据

写入车辆模式的完整交互:

→ 2E 01 00 01 // 诊断仪请求写入车辆模式0x01 ← 6E 01 00 // ECU返回写入成功

  1. CAPL 自动化调用示例
cpp 复制代码
// 读取VIN码
readVIN()
{
  diagSendRequest(CDCU, "ReadDataByIdentifier_VIN");
  if(diagWaitForResponse(CDCU, 1000) == 0)
  {
    char vin[18];
    diagGetResponseDataAsString(CDCU, vin, elCount(vin));
    write("读取到VIN: %s", vin);
  }
}

// 写入车辆模式
writeVehicleMode(byte mode)
{
  diagSetRequestDataByte(CDCU, "WriteDataByIdentifier_VehicleMode", 0, mode);
  diagSendRequest(CDCU, "WriteDataByIdentifier_VehicleMode");
}

六、常见否定响应与工程踩坑

1. 22 读服务高频错误

NRC 含义 常见原因
0x31 请求超出范围 最常见,DID 不存在、ECU 不支持该 DID 读取、多 DID 数量超限
0x7E 当前会话不支持 高权限 DID 在默认会话下读取
0x33 安全访问拒绝 极少数敏感 DID 读取也需要解锁安全
0x22 条件不满足 特定工况下 DID 不可读,比如休眠中部分数据无法读取
  1. 2E 写服务高频错误
NRC 含义 常见原因
0x33 安全访问拒绝 最常见,未解锁对应等级的安全访问
0x7E 当前会话不支持 默认会话下执行写操作
0x31 请求超出范围 DID 不支持写、数据长度错误、数值超出合法范围
0x22 条件不满足 行驶中、运行状态不允许写入
0x72 写入失败 Flash 写入错误、校验失败、存储区异常

3. 工程高频踩坑

  1. 写入后不复位:改完配置直接读,发现值没变,误以为写入失败,实际是没复位生效
  2. 数据格式不匹配:小端发大端、长度不对,导致参数错误
  3. 批量读超限:一次塞太多 DID,ECU 只返回前几个或直接报错
  4. 只读 DID 硬写:把状态类 DID 当成配置 DID 写入,直接返回不支持

0x31 服务(RoutineControl,例程控制服务)

服务 ID(SID)0x31,肯定响应 SID:0x71 核心定位:UDS 体系的「函数调用接口」。如果说 22/2E 服务是「读写 ECU 里的变量」,那 31 服务就是「调用 ECU 里封装好的一段程序」,用于执行擦除、校验、自检、功能控制等一系列复杂动作,是 OTA 刷写、产线测试、售后调试的核心执行载体。

一、服务帧格式与核心子功能

1. 请求帧通用结构

所有子功能都遵循同一套基础格式,例程 ID 是唯一的功能标识。

字节序号 字段 说明
1 SID = 0x31 固定服务标识符
2 子功能字节 低 7 位 = 控制类型(启动 / 停止 / 查询);最高位为 SPR 肯定响应抑制位
3~4 Routine ID 2 字节例程标识符,大端格式,唯一对应一段执行逻辑
5~N 入口参数(可选) 启动例程时传入的参数(如擦除分区号、校验偏移量),由具体例程定义

示例:31 01 FF 00 → 启动 ID 为 0xFF00 的例程(擦除内存)

  1. 肯定响应通用结构
字节序号 字段 说明
1 SID = 0x71 固定响应标识符(原 SID + 0x40)
2 子功能值 回应当前操作类型(启动 / 停止 / 查询)
3~4 Routine ID 对应操作的例程 ID
5~N 执行结果 / 状态(可选) 例程的运行状态、返回值、错误码,格式由例程自定义
  1. 三大核心子功能(标准定义)
子功能值 官方名称 作用 使用频率
0x01 startRoutine 启动指定例程 最高
0x02 stopRoutine 停止正在运行的例程 较低,仅长时运行的例程支持
0x03 requestRoutineResults 查询例程执行结果 高,异步例程必须轮询

二、核心运行模式:同步执行 vs 异步执行

这是理解 31 服务最关键的知识点,也是工程上最容易踩坑的地方。

1. 同步例程(简单短耗时动作)

  • 逻辑:诊断仪发启动指令,ECU 立刻执行,执行完成才返回响应,一次交互完成。
  • 特点:耗时极短(毫秒级),比如简单自检、开关切换、状态重置。
  • 交互流程

诊断仪 → 31 01 xx xx(启动) ECU → 71 01 xx xx + 结果(执行完成,返回成功/失败)

2. 异步例程(复杂长耗时动作)

  • 逻辑:诊断仪发启动指令,ECU 先返回 "已接收",后台异步执行;诊断仪需要主动轮询查询结果,直到例程结束。
  • 特点:耗时长(几百毫秒到数秒),比如 Flash 擦除、大文件 MD5 校验、分区切换,是 OTA 场景的绝对主流。
  • 交互流程

诊断仪 → 31 01 FF 00(启动擦除例程) ECU → 71 01 FF 00(已接受,开始后台执行) 诊断仪 → 31 03 FF 00(查询结果) ECU → 71 03 FF 00 + 运行中状态(还在执行) ... 重复轮询 ... 诊断仪 → 31 03 FF 00(再次查询) ECU → 71 03 FF 00 + 成功状态(执行完成)

工程重点:所有刷写相关的例程(擦除、校验)几乎都是异步例程,只发启动不查结果,等于不知道操作是否成功,是自动化测试的高频 bug 来源。

三、例程分类详解(结合真实业务场景)

1. OTA 刷写核心例程(最高优先级)

属于最高风险操作,必须在 OTA 会话 + 41 级安全解锁后才能执行,全部为异步例程。

例程 ID 指令示例 名称 核心作用 在 OTA 中的位置
0x0203 31 01 02 03 刷写前置条件检查 检查电压、车速、档位、温度等整车条件,确认可以升级 OTA 流程第一步,进入 OTA 会话后首先执行
0xFF00 31 01 FF 00 擦除内存 擦除目标 Flash 分区,为写入新固件做准备 固件下载(34 服务)之前必须执行
0x0201 31 01 02 01 文件 MD5 校验 校验传输完成的固件完整性,确认数据无损坏 37 结束传输后执行,验证刷写数据正确性
0xFF02 31 01 FF 02 文件有效性校验 校验固件签名、版本兼容性、硬件匹配度 刷写前校验,防止刷入错误固件
0xFF05 31 01 FF 05 切换运行分区 双分区架构下,将下次启动分区切换为新刷写的分区 校验通过后执行,复位后生效

2. 产线测试例程

工厂下线检测专用,用于硬件功能自检与标定,需扩展会话 + 11 级安全解锁,多为同步例程。

例程 ID 指令示例 名称 核心作用
0xC090 31 01 C0 90 里程复位 产线下线前将总里程清零
0xC092 31 01 C0 92 TFT 五色显示测试 屏幕依次显示红 / 绿 / 蓝 / 白 / 黑,检测屏幕坏点
0xC093 31 01 C0 93 背光测试 调节屏幕背光亮度,检测背光驱动
0xC095 31 01 C0 95 驱动 AVAS 发声 驱动行人警示蜂鸣器发声,检测音频输出
0xC096 31 01 C0 96 点亮屏幕 强制点亮屏幕,跳过正常启动流程

这类例程大多支持停止(02 子功能),比如测试完成后停止屏幕显示、停止蜂鸣。

3. 网络诊断例程

用于车载网络调试、故障定位,需扩展会话 + 1 级 / 11 级安全解锁。

例程 ID 指令示例 名称 核心作用
0xC010 31 01 C0 10 CAN 通道映射到 OBD 将内部 CAN 网段路由到 OBD 接口,方便外接工具直连子网
0xC220 31 01 C2 20 1000BASE-T1 测试模式 以太网 PHY 进入测试模式,用于信号质量校准
0xC222 31 01 C2 22 线缆诊断 检测以太网线缆短路、开路、长度,定位线束故障
0xC225 31 01 C2 25 交换机端口镜像 将指定端口的流量镜像到调试口,用于抓包分析

4. 功能控制例程

用于特殊模式切换、远程功能控制,权限分级覆盖 1 级到 41 级。

例程 ID 指令示例 名称 核心作用
0xC1E0 31 01 C1 E0 OTA 模式控制 进入 / 退出 OTA 专属模式,裁剪非必要功能
0xC1E4 31 01 C1 E4 远程诊断控制 开启 / 关闭远程诊断通道,用于云端远程排障
0xC1FF 31 01 C1 FF 请求 SOC 进入刷写模式 网关向电池管理系统发送请求,让 BMS 进入刷写态
0xC083 31 01 C0 83 蓝牙 MAC 地址测试 蓝牙模块进入测试模式,用于产线校准

四、核心特性与权限规则

1. 与 22/2E 服务的本质区别

对比维度 22/2E 服务 31 服务
本质 读写数据变量 执行一段程序逻辑
操作对象 固定地址的参数 封装好的功能函数
副作用 仅改变参数值 可执行任意硬件 / 软件动作
执行时长 瞬时完成 可短可长,支持异步后台运行
风险等级 中低(仅改参数) 跨度极大,从功能测试到 Flash 擦除
返回结果 固定格式的参数值 自定义格式的执行状态 / 错误码

2. 权限分级规则

和 UDS 整体权限体系完全绑定,遵循「风险越高,权限要求越严」:

  • 低风险例程(如状态查询、简单自检):扩展会话即可,部分默认会话就能执行
  • 中风险例程(产线测试、功能控制):扩展会话 + 1 级 / 11 级安全解锁
  • 高风险例程(擦除、分区切换、刷写相关):OTA 会话 + 41 级最高安全解锁

3. 生命周期与约束

  • 会话绑定:例程状态仅在当前会话内有效,会话超时、切回默认会话、ECU 复位后,所有运行中的例程自动终止,状态清零。
  • 资源独占:高优先级例程(如 Flash 擦除)运行时,ECU 会拒绝其他高耗时操作,避免资源冲突。
  • 不可重入:同一个例程未结束时,重复启动会返回错误,不支持并行执行。

五、在 OTA 全流程中的关键节点

结合之前学习的所有服务,31 服务贯穿了刷写的核心执行阶段,完整时序如下:

10 40 进入OTA会话 → 27 解锁41级安全 → 31 01 02 03 启动前置条件检查 → 轮询结果 → 确认通过 → 28 03 03 关闭业务报文 → 31 01 FF 00 启动Flash擦除 → 轮询结果 → 擦除完成 → 34 请求下载 → 36 传输固件(持续发3E保活) → 37 结束传输 → 31 01 02 01 启动MD5校验 → 轮询结果 → 校验通过 → 31 01 FF 05 启动分区切换 → 28 00 03 恢复通信 → 11 01 硬复位 → 新分区启动

六、CANoe 实操与 Trace 观测

1. 图形化操作

加载 CDD 数据库后,Diagnostic Console 中会按分类列出所有例程:

  • 选择对应例程,点击 Start 即可启动;
  • 异步例程可点击 Results 主动查询执行状态;
  • 支持输入入口参数,软件自动按协议格式组装报文。

2. Trace 窗口报文示例(异步擦除流程)

复制代码
→ 31 01 FF 00                // 启动擦除例程
← 71 01 FF 00 01             // 已接受,状态:运行中
→ 31 03 FF 00                // 查询结果
← 71 03 FF 00 01             // 仍在运行
→ 31 03 FF 00                // 再次查询
← 71 03 FF 00 00             // 执行成功,状态:完成
  1. CAPL 自动化调用示例(异步例程轮询)
cpp 复制代码
// 启动并等待异步例程完成,返回值:0=成功,非0=失败
byte waitRoutineComplete(char ecuName[], char routineName[], int timeoutMs)
{
  byte status;
  int elapsed = 0;
  
  // 启动例程
  diagSendRequest(ecuName, routineName);
  if(diagWaitForResponse(ecuName, 500) != 0)
    return 1; // 启动失败
  
  // 轮询查询结果
  while(elapsed < timeoutMs)
  {
    testWaitForTimeout(100);
    elapsed += 100;
    
    diagSendRequest(ecuName, routineName "_Query");
    if(diagWaitForResponse(ecuName, 500) == 0)
    {
      status = diagGetResponseDataByte(ecuName, 0);
      if(status == 0)
        return 0; // 执行成功
    }
  }
  
  return 2; // 超时
}

七、常见否定响应与工程踩坑

高频 NRC 对照表

NRC 码 含义 典型场景
0x31 请求超出范围 最常见,例程 ID 不存在、入口参数错误
0x24 请求顺序错误 未启动就停止 / 查询结果,或例程状态不匹配
0x33 安全访问拒绝 未解锁对应安全等级
0x7E 当前会话不支持 会话等级不足,如默认会话下执行擦除
0x22 条件不满足 工况不允许,如行驶中执行擦除
0x32 例程已在运行 重复启动同一个正在运行的异步例程

工程高频踩坑点

  1. 只启动不查询结果:误以为发了启动指令就成功,实际例程可能后台报错失败。
  2. 轮询间隔 / 超时设置不合理:擦除大分区需要几秒到十几秒,超时设太短会误判失败。
  3. 复位前未等待例程结束:例程还在跑就发复位,可能导致 Flash 损坏、ECU 变砖。
  4. 忽略入口参数:部分例程必须传分区号、长度等参数,不传会直接报参数错误。

0x29 服务(Authentication,身份认证服务)

服务 ID(SID)0x29,肯定响应 SID:0x69 核心定位:UDS 体系的「数字身份闸门」,基于 PKI 公钥基础设施、数字证书体系实现双向身份认证,是 ISO 14229-1:2020 版新增的标准服务,也是智能网联汽车满足信息安全法规(UNECE R155、ISO/SAE 21434)的强制要求。

和你之前学的 27 服务(对称密钥挑战应答)不同,29 服务用非对称加密 + 数字证书做身份校验,不需要共享密钥,安全性高一个量级,专门适配远程 OTA、云端诊断、无线刷写等开放网络场景。

一、先搞懂:29 服务 vs 27 服务,本质区别是什么

很多人会把这两个搞混,二者都是 "验身份、开锁",但安全层级、技术原理、适用场景完全不同:

对比维度 27 安全访问服务 29 身份认证服务
技术原理 对称加密,挑战 - 应答,双方共享同一套密钥算法 非对称加密,数字证书体系,CA 根证书背书,无需共享密钥
安全等级 中低,密钥泄露即可破解,适合本地封闭场景 高,证书体系可溯源、抗伪造,适合远程开放网络
核心作用 解锁 ECU 的操作权限(能不能写数据、刷固件) 验证通信双方的身份合法性(对面是不是真的 ECU / 真的诊断端)
适用场景 本地有线诊断、产线调试、售后维修 远程 OTA、云端诊断、无线刷写、车云安全交互
标准定位 经典 UDS 基础服务,所有 ECU 必备 2020 版标准新增,智能网联 ECU 标配

一句话总结:27 是 "开门锁",29 是 "验身份"。远程场景下,必须先通过 29 确认对方是合法身份,才能继续走 27 解锁操作权限,是双重安全保障。

二、服务帧格式与核心子功能

1. 通用帧结构

29 服务的请求格式随子功能不同而变化,核心结构为:

字节序号 字段 说明
1 SID = 0x29 固定服务标识符
2 子功能字节 低 7 位 = 认证动作类型;最高位为 SPR 肯定响应抑制位
3~N 认证参数 随子功能变化,如证书类型、证书数据、签名数据等

肯定响应格式:

字节序号 字段 说明
1 SID = 0x69 固定响应标识符(原 SID + 0x40)
2 子功能值 回应当前执行的认证动作
3~N 响应参数 随子功能返回证书、验证结果、签名挑战等
  1. 对应 4 大核心子功能详解
指令示例 子功能 官方名称 核心作用
29 01 00 0x01 Verify Certificate Unidirectional单向证书验证 诊断端验证 ECU 的身份合法性,确认连接的是正品 ECU,不是伪造节点
29 03 0x03 Proof Of Ownership所有权证明 双向认证第二步,ECU 验证诊断端的身份,确认对方持有合法私钥,有权操作 ECU
29 00 0x00 DeAuthentication注销认证 主动退出认证状态,清空所有认证权限,安全收尾
29 08 0x08 Authentication Configuration认证配置 读取 / 修改 ECU 的认证相关配置参数

三、完整认证流程拆解

1. 单向认证:验证 ECU 身份(对应 29 01 00)

目标:诊断端 / 云端确认 "我连的 ECU 是原厂正品,没有被篡改、伪造",防止接入恶意节点。 完整交互流程:

  1. 证书交互:诊断端向 ECU 请求数字证书,ECU 返回自身的设备证书
  2. 证书校验:诊断端用预置的 CA 根证书,验证 ECU 证书的签名、有效期、颁发机构,确认证书合法有效
  3. 验证确认 :诊断端发送 29 01 00 提交验证结果,ECU 确认后单向认证完成

这一步是远程连接的第一道防线:如果 ECU 证书是伪造的、过期的,直接终止连接,避免后续交互。

2. 双向认证:所有权证明(对应 29 03)

目标:ECU 反过来验证诊断端 / 云端的身份,确认 "对方是合法的控制端,不是黑客",是高安全操作的前置要求。 完整交互流程:

  1. ECU 生成挑战:诊断端发送所有权证明请求后,ECU 生成一段随机挑战数据,返回给诊断端
  2. 私钥签名:诊断端用自己的私钥对挑战数据签名,将签名结果发回 ECU
  3. 公钥验签:ECU 用合法证书的公钥验证签名,确认对方持有对应私钥,身份合法
  4. 认证通过:ECU 返回肯定响应,双向认证完成

这一步之后,双方身份都已确认,才可以继续执行会话切换、安全解锁、刷写等高权限操作。

3. 注销认证(对应 29 00)

主动清空认证状态,所有依赖 29 认证的权限全部失效。和 27 安全解锁一样,认证状态是临时的,会话超时、ECU 复位也会自动失效,注销是主动安全收尾动作。

域控认证实现

1. 配套认证配置 DID

文档数据区标注:22 11 31 - UDS Service 29 Authentication Configuration Read

  • 作用:通过 22 服务读取 29 服务的配置信息,比如支持的证书类型、当前认证状态、协议版本号
  • 对应写操作:可通过 2E 服务或 29 08 子功能修改配置,通常仅产线可写,售后只读

2. 权限绑定规则

和 UDS 整体权限体系深度绑定:

  • 会话要求:至少扩展会话(10 03)以上才能执行 29 服务,默认会话下直接拒绝
  • 安全等级联动:双向认证通过后,才能解锁最高等级的 27 安全访问(比如文档中的 41 级刷写权限)
  • 远程场景强制:远程诊断、远程 OTA 必须先完成 29 双向认证,才能继续后续操作;本地有线诊断可根据配置豁免

3. 和 OTA 流程的结合

在远程 OTA 场景中,29 认证是整个流程的第一步,比会话切换还要靠前:

车云建立诊断连接 → 29 双向证书认证(先验双方身份) → 10 40 进入OTA会话 → 27 解锁41级安全访问 → 后续刷写全流程

四、核心安全特性

  1. 证书可溯源,抗伪造 所有证书由车企 CA 根证书签发,每台 ECU 的证书唯一,可追溯到具体生产批次;无法通过篡改、重放伪造合法身份,从机制上杜绝了假冒 ECU、假冒诊断端的风险。

  2. 临时生效,自动失效 认证状态仅在当前诊断会话内有效,出现以下情况自动清空:

    • 诊断会话超时、切换回默认会话
    • ECU 复位、下电上电
    • 主动发送 29 00 注销认证 和 27 安全访问一样,不会永久保持高权限状态。
  3. 算法可升级,适配安全要求 支持国密 SM2、RSA、ECC 等多种非对称加密算法,可根据安全等级要求升级;而 27 服务的对称算法通常是固定的,升级难度大。

  4. 符合法规强制要求 是 UNECE R155 汽车网络安全法规、ISO/SAE 21434 网络安全工程标准的强制要求,所有具备远程升级、远程诊断功能的车辆必须具备证书级身份认证能力。

五、CANoe 实操与 Trace 观测

1. 图形化操作

加载带认证配置的 CDD/ODX 数据库后,Diagnostic Console 中会有独立的 Authentication 模块:

  • 导入合法的客户端证书和私钥,一键执行双向认证
  • 支持查看 ECU 证书详情、验证结果、当前认证状态
  • 产线调试场景可直接调用预设的认证脚本
  1. Trace 窗口报文示例(双向认证简化流程)
cpp 复制代码
→ 29 01 00                // 请求单向证书验证
← 69 01 00 + ECU证书数据   // ECU返回自身证书
→ 29 03                   // 请求所有权证明
← 69 03 + 随机挑战数据     // ECU返回挑战码
→ 29 03 + 签名数据         // 诊断端返回私钥签名结果
← 69 03                   // 验签通过,双向认证完成

3. CAPL 自动化调用

CANoe 的诊断协议栈支持自动处理证书验签,只需调用封装好的认证接口,底层多步交互自动完成:

cpp 复制代码
// 执行双向身份认证
byte doAuthentication()
{
  diagSendRequest(CDCU, "Authentication_Bidirectional");
  if(diagWaitForResponse(CDCU, 5000) == 0)
  {
    write("双向认证通过");
    return 0;
  }
  write("认证失败");
  return 1;
}

六、常见否定响应与排查

NRC 码 含义 典型原因
0x22 条件不满足 证书已过期、ECU 安全状态异常
0x31 请求超出范围 证书类型不支持、签名数据长度错误
0x33 安全访问拒绝 证书验证失败、签名无效、私钥不匹配
0x35 密钥无效 最常见,证书签名校验不通过,证书非法或已吊销
0x7E 当前会话不支持 默认会话下执行认证操作
0x36 超过尝试次数 连续认证失败,触发锁定冷却

工程高频踩坑点

  1. 证书时间不匹配:ECU 时钟异常,导致证书有效期校验失败
  2. 根证书不匹配:诊断端导入的 CA 根证书和 ECU 的证书签发机构不一致
  3. 私钥不对应:用了错误的私钥做签名,验签失败
  4. 忽略证书链:只校验了 ECU 实体证书,没校验中间 CA 证书链

0x3E 服务(TesterPresent,测试仪在线)

服务 ID(SID)0x3E,肯定响应 SID:0x7E 核心定位 :UDS 体系的「会话心跳包」,唯一作用是周期性重置 ECU 的会话超时定时器,维持当前高权限会话不自动退回默认会话。它是所有长时诊断操作(刷写、长时例程、产线调试)的后台刚需服务,本身不执行任何业务功能,却决定了整个高权限流程能不能跑通。

3E 00- Tester Present Send 是标准基础指令,而工程实操中 99% 的场景都会使用带 SPR 位的 3E 80,这也是新手最容易混淆的细节。

一、服务帧格式:极简但有门道

3E 是所有 UDS 服务里格式最简单的之一,但SPR 肯定响应抑制位是它的灵魂。

1. 请求帧结构

字节序号 字段 详细说明
1 SID = 0x3E 固定服务标识符
2 子功能字节 低 7 位:仅0x00为标准通用子功能;最高位 bit7:SPR 肯定响应抑制位
  1. 肯定响应结构
字节序号 字段 说明
1 SID = 0x7E 固定响应标识符(原 SID + 0x40)
2 子功能值 固定返回0x00,和请求对应

3. 重点:3E 00 vs 3E 80,90% 的人都用错

这是工程实操最核心的细节,文档里写的是基础指令,真实量产场景几乎都用后者:

指令 SPR 位状态 效果 使用场景
3E 00 关闭 ECU 必须返回7E 00响应 单次验证、调试场景,确认保活功能正常
3E 80 开启 ECU 收到后静默执行,不返回任何响应 周期保活的标准用法,量产、自动化测试、刷写全用这个

为什么必须开 SPR: 3E 是周期性高频发送的服务(通常 3~4 秒一次),如果每次都要 ECU 回复,一条总线上十几个 ECU 就会产生大量无用的响应帧,白白占用总线带宽。开 SPR 后用 1 帧请求完成保活,总线开销降到最低。

二、存在的本质:S3Server 会话超时机制

3E 服务不是凭空设计的,它完全是为了配合 S3Server 会话超时定时器 存在的 ------ 理解了这个定时器,就彻底理解了 3E 的价值。

1. 什么是 S3Server 定时器

ISO 14229 规定:所有非默认会话(扩展会话、编程会话、厂商自定义 OTA 会话)都必须设置超时定时器。

  • ECU 每次收到有效的诊断请求,都会把这个定时器清零重新计时;
  • 如果超过设定时间没有收到任何诊断请求,定时器超时,ECU自动退回默认会话,同时清空所有安全解锁状态、终止所有运行中的例程。

2. 为什么要设计超时(安全底层逻辑)

这是强制的功能安全设计: 如果诊断仪异常断开、线缆被拔掉、上位机死机,ECU 不能永远停在高权限的刷写 / 调试状态,否则会有极大的安全风险。超时自动退回,就是兜底的安全机制。

3. 行业通用参数与发送周期

  • 超时时间 :标准未强制,行业通用值为 5000ms(5 秒),部分高安全 ECU 会设为 3 秒;
  • 发送周期 :工程上统一取超时时间的 70%,比如 5 秒超时就设为 3500ms 发一次;
  • 为什么留 30% 余量:应对总线延迟、操作系统调度抖动、多节点优先级抢占,避免因为几毫秒的延迟意外超时。

关键补充:所有有效的诊断请求都会重置 S3 定时器,不只是 3E。 比如发 22 读 DID、2E 写数据、31 启动例程,都会顺便给会话 "续命"。3E 的价值,是在没有业务请求的空闲期、长时异步操作的等待期,用最小的开销维持会话。

三、核心特性:边界非常清晰

3E 功能单一,但边界很明确,新手很容易搞错它的能力范围。

1. 只能 "维持",不能 "升级"

3E 只能保住当前的会话等级,绝对不能用来提升会话权限

  • 默认会话发再多 3E,也不会自动变成扩展会话;
  • 扩展会话发 3E,永远停在扩展会话,不会升到 OTA / 编程会话。 想要升级会话,只能靠 10 服务主动切换。

2. 保活的不只是会话,还有全部高权限状态

会话超时是 "连根拔起" 的,一旦超时,以下状态全部失效:

  • 所有 27 服务解锁的安全等级
  • 所有 29 服务的认证状态
  • 所有正在运行的 31 异步例程
  • 28 服务设置的通信静默状态
  • 85 服务设置的故障记录关闭状态

所以说,3E 保的不只是会话,是整个高权限运行环境。

3. 默认会话无需保活

默认会话(10 01)没有强制的 S3 超时机制,ECU 正常运行时永远停在默认会话,不需要发 3E 续命。给默认会话发 3E 属于无效操作,多数 ECU 会忽略,少数会正常响应但没有实际意义。

4. 纯无副作用,不改变任何业务状态

3E 是真正的 "纯心跳":它不会修改任何参数、不会触发任何动作、不会影响正在运行的程序,只会重置会话超时定时器。这也是它可以高频后台发送的前提。

四、三大核心工程应用场景

场景 1:OTA / 固件刷写(最高频、最核心)

这是 3E 服务的第一大使用场景,也是你之前学习的 OTA 流程里的隐形支柱。

刷写流程中,有两个阶段必须靠 3E 保活:

  1. 长时异步例程等待期:比如 Flash 擦除、固件校验,可能需要几秒到十几秒,中间没有业务诊断请求,不发 3E 就会会话超时,擦除直接中断。
  2. 固件传输间隙:36 服务传数据包时,每个包都会重置定时器,但如果传输出现暂停、流控等待,就需要 3E 补位兜底,避免传输中途会话掉线。

工程现状:成熟的刷写脚本都会开一个后台定时器,只要进入高权限会话就持续周期发3E 80,直到刷写完成切回默认会话,全程不用关心业务间隙。

场景 2:长时异步例程运行

比如产线的屏幕老化测试、硬件长时自检、以太网线缆诊断,这些例程要跑几十秒到几分钟,后台必须同步发 3E,否则例程跑到一半会话超时,直接被强制终止。

场景 3:产线 / 售后长时调试

工程师接 CANoe 连车调试,长时间停在扩展会话看数据、改参数,如果不开自动保活,每隔 5 秒就会退回默认会话,每次都要重新切会话、解锁安全,非常影响效率。

五、CANoe 实操细节

1. 自带自动保活,90% 的场景不用自己写

CANoe 的诊断协议栈原生支持 TesterPresent 自动保活,不用手写定时器脚本:

  • 在诊断配置界面,勾选对应 ECU 的「Automatic Tester Present」;
  • 设置发送周期(默认 3500ms,匹配 5 秒超时);
  • 勾选「Suppress Positive Response」(开启 SPR 位,即发送3E 80);
  • 配置后,只要进入非默认会话,CANoe 就会自动后台发心跳,切回默认会话自动停止。

这是标准操作,新手最容易踩的坑就是不知道有这个功能,自己手写定时器写不准,还容易忘关 SPR 位。

2. CAPL 手动控制

特殊场景需要手动控制保活时,调用也非常简单:

cpp 复制代码
// 开启自动保活
diagSetTesterPresentActive(CDCU, 1);
// 设置保活周期3500ms,开启SPR
diagSetTesterPresentCycleTime(CDCU, 3500, 1);

// 关闭自动保活
diagSetTesterPresentActive(CDCU, 0);

3. Trace 窗口观测

开了 SPR 的保活,Trace 里只会周期性出现 3E 80 请求帧,没有对应的响应帧; 如果没开 SPR,会成对出现 3E 007E 00,总线负载明显更高。

六、常见踩坑与排错

3E 本身简单,但因为是后台隐形服务,出问题很难第一时间想到它,是很多 "莫名其妙掉线" 的元凶。

1. 最常见:周期设置太长

超时设 5 秒,保活周期设成 4.5 秒,稍微有点总线抖动就超时了。 ✅ 规范:永远按超时时间的 70% 设置,留足余量。

2. 最隐蔽:刷写中途停了保活

脚本里切会话、解锁的时候开了保活,刷写完成后提前关了,但后面还有校验、复位操作,导致后半段会话超时。 ✅ 规范:保活的生命周期和高权限会话完全绑定,切回默认会话再关保活。

3. 最新手:忘了开 SPR 位

3E 00周期发送,总线上飘满无用的7E 00响应,占用带宽还容易引发总线冲突。 ✅ 规范:周期保活一律用3E 80

4. 最容易混淆:会话超时 ≠ 业务超时

3E 只能管会话级的超时,管不了业务逻辑自己的超时。比如某个例程自己有 3 秒执行超时,你发再多 3E 也没用,例程到时间还是会自己终止。

5. 常见否定响应

3E 本身很少失败,高频错误只有两个:

  • 0x12 子功能不支持:发送了非 0x00 的自定义子功能,ECU 不识别
  • 0x7E 当前会话不支持:极少数 ECU 在默认会话下会拒绝 3E 服务,属于正常设计

结合你已经学过的服务,完整的高权限状态维持逻辑是:

cpp 复制代码
10 03 切扩展会话 → 27 解锁安全 → 开启3E后台保活
        ↓
(所有业务操作期间,3E持续后台运行,维持会话+安全状态)
        ↓
业务完成 → 关闭3E保活 → 10 01 切回默认会话

0x85 服务(ControlDTCSetting,故障码记录控制)

服务 ID(SID)0x85,肯定响应 SID:0xC5 核心定位:UDS 体系的「故障记录暂停开关」,用于临时开启 / 关闭 ECU 的故障检测与记录逻辑,避免诊断、刷写、测试过程中产生大量虚假故障码,干扰真实故障排查。

一、服务帧格式

1. 请求帧结构

字节序号 字段 详细说明
1 SID = 0x85 固定服务标识符
2 子功能字节 低 7 位 = 控制模式;最高位 bit7 = SPR 肯定响应抑制位
3~N DTC 组掩码(可选) 指定控制的故障范围,1 字节组号或 3 字节 DTC 掩码;工程上绝大多数场景省略,默认控制全部故障
  1. 肯定响应帧结构
字节序号 字段 说明
1 SID = 0xC5 固定响应标识符(原 SID + 0x40)
2 子功能值 回应当前执行的控制模式

3. 工程常用指令

和 28 服务一样,功能寻址群发时必须开启 SPR 位,避免多 ECU 同时回复造成总线拥堵。

指令 含义 使用场景
85 02 关闭故障记录,需要 ECU 回复 单 ECU 物理寻址,调试场景
85 82 关闭故障记录,抑制肯定响应 全车群发标准用法,OTA、产线批量操作通用
85 01 开启故障记录,需要 ECU 回复 单节点恢复调试
85 81 开启故障记录,抑制肯定响应 全车批量恢复

二、核心子功能与作用边界

1. 两个标准子功能

子功能值 官方名称 核心作用
0x01 On 恢复正常故障检测与记录,ECU 按正常逻辑监控、存储故障
0x02 Off 暂停故障检测与记录,期间即使故障条件满足,也不更新故障状态、不存储故障、不触发冻结帧

2. 必须明确的作用边界(新手最容易搞错)

很多人会把 85 服务和 14 服务(清故障码)搞混,二者本质完全不同:

对比项 85 服务(控制故障记录) 14 服务(清除故障信息)
核心作用 暂停 / 恢复新故障的检测与记录 删除已经存储的历史故障
对已有故障的影响 无任何影响,已存的故障码还在 清空所有故障码、冻结帧、扩展数据
效果持续时间 临时,会话结束自动恢复 永久,除非故障再次触发
风险等级 低,只是暂停记录 中,会删除故障证据

一句话总结:85 是 "暂停记账",14 是 "删掉旧账"。关闭故障记录期间,ECU 相当于 "失明",看不到也不记新故障,但之前的旧账还在。

3. 关闭期间到底关了什么

执行 85 02 后,ECU 会:

  1. 停止所有故障检测逻辑,不再判断故障条件是否满足
  2. 不更新任何 DTC 的状态位(不会从待确认变成已确认)
  3. 不存储新故障,不生成冻结帧、扩展数据
  4. 不会触发表盘故障灯点亮

不会做的事:

  • 不会删除已有的历史故障码
  • 不会熄灭已经点亮的故障灯
  • 不会影响 ECU 的正常功能逻辑,只是不记录故障

三、核心特性:安全兜底设计

和 28 服务、27 服务一样,85 服务严格遵循 UDS 的「临时生效、异常自动恢复」安全原则,绝不会因为诊断仪异常断开导致故障记录永久关闭。

1. 会话绑定,超时自动恢复

故障记录关闭的状态仅在当前高权限会话内有效,出现以下任意情况会自动恢复开启:

  • 诊断会话超时(S3Server 定时器超时,通常 5s)
  • 主动切换回默认会话(10 01)
  • ECU 复位、下电上电、点火循环结束

这是强制的功能安全设计:哪怕诊断仪中途掉线、线缆被拔掉,ECU 也会自动恢复故障检测,不会漏掉真实故障。

2. 权限门槛

  • 默认会话下不允许执行,返回 0x7E(当前会话不支持)
  • 至少需要扩展会话(10 03)及以上权限
  • 部分 ECU 要求解锁 1 级安全访问才能执行

3. 不影响诊断通信

关闭故障记录不会影响诊断报文本身的收发,所有诊断服务都可以正常执行。

四、三大核心工程场景

场景 1:OTA / 固件刷写(最高频)

这是 85 服务存在的核心价值,也是你文档中 OTA 流程的标准前置步骤。 为什么刷写前必须关故障记录: 刷写流程中会执行 28 03 关闭业务通信、ECU 复位跳转 Bootloader,这些操作在正常工况下都会被判定为 "通信丢失故障"、"异常复位故障"。如果不关故障记录,一次刷写会产生十几个虚假故障码,污染故障记忆,售后无法区分真假故障。

标准执行顺序:

cpp 复制代码
进入扩展会话 → 解锁安全 → 85 关闭故障记录 → 28 关闭业务通信 → 执行刷写
刷写完成 → 28 恢复通信 → 85 恢复故障记录 → 14 清除过程中产生的零星故障 → 19 读故障确认无异常

场景 2:产线功能测试

产线下线时会做大量执行器强制驱动、硬件自检、通信测试,这些非正常工况都会触发故障码。

  • 测试前发 85 02 关闭故障记录
  • 测试完成后恢复,再清除测试过程中可能产生的零星故障
  • 确保出厂车辆的故障记忆干净,没有测试残留故障

场景 3:故障注入与调试

开发、测试阶段做故障注入测试、边界工况验证时,为了避免测试故障污染真实故障库,测试前关闭故障记录,测试结束后恢复,保证历史故障的真实性。

五、在完整 OTA 流程中的时序位置

结合你之前学习的所有服务,85 服务在刷写链路中的位置非常固定,完整顺序如下:

cpp 复制代码
10 03 扩展会话
  → 27 解锁1级安全
    → 85 02 关闭故障记录  ← 85服务在这里
      → 10 40 进入OTA会话
        → 27 解锁41级安全
          → 28 03 关闭业务通信
            → 31 Flash擦除
              → 34/36/37 固件传输
                → 31 固件校验
                  → 28 00 恢复通信
                    → 85 01 恢复故障记录  ← 恢复在这里
                      → 14 清除过程故障
                        → 19 读故障确认
                          → 11 01 硬复位

关键原则:先关故障,再关通信;先开通信,再开故障,最大限度避免误报通信类故障。

六、CANoe 实操与 Trace 观测

1. 图形化操作

加载 CDD 数据库后,在 Diagnostic Console 中找到 ControlDTCSetting 服务,下拉选择 On/Off 模式,一键发送即可;支持物理寻址单节点、功能寻址全车群发。

2. Trace 窗口验证效果

发送 85 82 后:

  • 总线无肯定响应(SPR 位生效)
  • 后续人为制造故障条件,也不会出现 DTC 状态更新的相关报文
  • 切回默认会话后,故障记录自动恢复,故障检测恢复正常

3. CAPL 自动化调用示例

cpp 复制代码
// 关闭全车故障记录(功能寻址+SPR)
void disableDTCSetting()
{
  diagSendRequest(Gateway, "ControlDTCSetting_Off_SPR");
  diagWaitForResponse(Gateway, 500);
  write("已关闭故障记录");
}

// 恢复全车故障记录
void enableDTCSetting()
{
  diagSendRequest(Gateway, "ControlDTCSetting_On_SPR");
  diagWaitForResponse(Gateway, 500);
  write("已恢复故障记录");
}

七、常见否定响应与工程踩坑

高频 NRC 对照表

NRC 码 含义 典型原因
0x7E 当前会话不支持 最常见,默认会话下执行 85 服务
0x33 安全访问拒绝 未解锁对应安全等级
0x12 子功能不支持 发送了 ECU 未实现的子功能,多数 ECU 仅支持 01 和 02
0x31 请求超出范围 DTC 组掩码参数错误、指定了不存在的故障组
0x22 条件不满足 特定工况下不允许关闭,如车辆高速行驶中

工程高频踩坑点

  1. 关了忘记开:虽然会话超时会自动恢复,但正常流程必须主动恢复,避免 ECU 在一段时间内漏记真实故障。
  2. 顺序搞反:先关通信再关故障记录,导致关通信的瞬间已经触发了通信故障,等于白关。
  3. 以为能清故障:误以为关故障记录会清除已有故障,实际没有任何清除效果,清故障必须用 14 服务。
  4. 群发不开 SPR :功能寻址发 85 02 不带 SPR 位,十几个 ECU 同时回复,总线瞬间拥堵。
  5. 休眠场景失效:ECU 进入休眠后,故障记录状态自动重置,休眠唤醒后默认开启,不需要额外操作。

0x19 & 0x14 服务(故障信息读写)

核心定位:二者构成 UDS 完整的故障管理体系,是售后诊断、产线下线、刷写收尾的核心服务。

  • 0x19(ReadDTCInformation):读取故障码详情,是读故障、查根因的核心入口,也是所有诊断仪 "读故障" 功能的底层实现
  • 0x14(ClearDiagnosticInformation):清除已存储的故障数据,是维修、测试后清理故障记忆的唯一标准方式

一、0x19 读取故障信息服务

服务 ID(SID)0x19,肯定响应 SID:0x59 它不是单一功能,而是通过不同子功能覆盖故障数量、故障列表、冻结帧、扩展数据、全量故障等多个维度的查询,是 UDS 中子功能最丰富的服务之一。

1. 通用帧格式

请求帧结构
字节序号 字段 说明
1 SID = 0x19 固定服务标识符
2 子功能字节 低 7 位 = 查询类型;最高位为 SPR 肯定响应抑制位(极少用)
3~N 查询参数 随子功能变化,如状态掩码、DTC 编号、冻结帧编号等

肯定响应结构

字节序号 字段 说明
1 SID = 0x59 固定响应标识符
2 子功能值 回应当前查询类型
3~N 数据内容 随子功能返回故障数量、DTC 列表、状态码、冻结帧数据等
  1. 文档对应 5 大核心子功能详解
子功能值 文档对应名称 核心作用 使用频率
0x01 Fault Memory Read (Number) 读取符合状态掩码的 DTC 数量 中,用于分页展示、快速判断有无故障
0x02 Fault Memory Read (all identified) 读取符合状态掩码的 DTC 完整列表 + 状态 最高,日常读故障、售后排查的主力功能
0x04 Fault Memory Read- DTC Snapshot Record 读取指定 DTC 的冻结帧数据 高,分析故障触发时的工况,定位根因
0x06 Fault Memory Read-extended data record 读取指定 DTC 的扩展数据 中,查看故障发生次数、老化次数、发生里程等统计信息
0x0A Fault Memory Read (supported errors) 读取 ECU 支持的全部 DTC 列表 低,用于确认 ECU 故障配置完整性
(1)子功能 0x02:读故障列表(最核心)

这是日常最常用的子功能,通过状态掩码过滤出目标类型的故障,返回每个匹配故障的 3 字节 DTC 编号 + 1 字节状态码。

  • 请求格式:19 02 + 1字节状态掩码
  • 示例:19 02 09 = 读取所有 "当前失败 + 已确认" 的故障
  • 响应格式:59 02 + 状态掩码 + [DTC1 3字节 + 状态1 1字节] + [DTC2 3字节 + 状态2 1字节]...
(2)子功能 0x04:读冻结帧数据

故障触发瞬间,ECU 会自动拍下一张 "工况快照",记录车速、转速、电压、温度等关键信号,这就是冻结帧,是排查偶发故障的核心依据。

  • 请求格式:19 04 + 3字节DTC编号 + 冻结帧记录号
  • 响应内容:DTC 编号 + 记录号 + 一组实时信号值
(3)子功能 0x06:读扩展数据

每个故障码附带的统计信息,常见内容包括:

  • 故障发生总次数、老化次数
  • 首次发生里程、最近一次发生里程
  • 故障持续时间
  • 当前老化循环计数

3. 核心概念:DTC 状态位(1 字节)

这是理解 19 服务的基石。每个故障码都对应 1 字节的状态位,每一位代表故障的一个阶段属性,0x01/0x02 子功能的 "状态掩码" 就是通过位过滤来筛选目标故障。

工程上最常用的 4 个状态位:

掩码值 名称 含义
bit0 0x01 testFailed 当前测试失败,故障当前真实存在
bit2 0x04 pendingDTC 待确认故障,本点火循环出现过,但还没满足确认阈值
bit3 0x08 confirmedDTC 已确认故障,历史上正式记录的故障,会点亮仪表故障灯
bit7 0x80 warningIndicatorRequested 故障灯请求位,该故障是否触发仪表亮灯

常用掩码组合

掩码值 作用 场景
0x01 只读取当前存在的故障 排查实时故障
0x08 读取所有历史已确认故障 售后常规读故障
0x09 读取当前存在 + 历史已确认的所有故障 全面排查
0xFF 读取所有有记录的故障,无论当前状态 完整故障记忆导出

一句话理解:bit0 代表 "现在坏没坏",bit3 代表 "曾经坏过没",这两个位是判断故障状态的核心依据。

4. 核心特性

  • 权限门槛低:绝大多数 ECU 在默认会话下即可读取故障信息,不需要扩展会话和安全解锁
  • 只读无副作用:读取操作不会修改、清除任何故障数据,不会影响 ECU 运行
  • DTC 格式统一:3 字节 DTC 编号遵循 ISO 15031-6 标准,P 开头(动力)、B 开头(车身)、U 开头(网络)、C 开头(底盘),全行业通用

二、0x14 清除故障信息服务

服务 ID(SID)0x14,肯定响应 SID:0x54 核心作用:一次性清除 ECU 内所有与故障相关的存储数据,是故障维修、测试收尾的标准操作。

1. 帧格式

请求帧结构
字节序号 字段 说明
1 SID = 0x14 固定服务标识符
2~4 DTC 组掩码 3 字节,指定清除范围;FF FF FF = 清除所有故障码
5~N 可选参数 部分厂商支持指定清除冻结帧、扩展数据,常规场景省略
  • 最常用指令:14 FF FF FF → 清除所有故障信息
  • 肯定响应仅单字节 54,无额外数据。

2. 清除的完整范围

14 服务不是只删故障码编号,而是清除一整套故障相关数据:

  • 当前故障、历史故障的状态标记
  • 所有冻结帧快照数据
  • 所有故障扩展统计数据(次数、里程、时间)
  • 故障老化计数、待确认标记

简单说:执行完 14 全清后,ECU 的故障记忆会回到 "全新出厂" 的空白状态。

3. 必须分清的边界:14 服务 vs 85 服务

这两个服务最容易混淆,本质完全不同:

对比项 14 清除故障信息 85 控制故障记录
核心作用 删除已经存储的历史故障 暂停 / 恢复新故障的检测与记录
对已有故障 全部清空 无任何影响,旧故障还在
对后续故障 不影响,后续故障照常记录 暂停记录,期间故障不会被存储
效果性质 永久删除,除非故障再次触发 临时生效,会话结束自动恢复
类比 删掉账本上的所有旧账 暂停记账,账本不动

4. 核心特性

  • 权限门槛高:通常需要扩展会话(10 03)+ 1 级安全解锁,默认会话下会直接拒绝
  • 支持批量操作:可通过功能寻址群发,全车 ECU 批量清故障,需开启 SPR 位避免总线拥堵
  • 条件约束:行驶中、高压上电等工况下通常禁止清故障,返回条件不满足
  • 不可逆:清除后故障数据无法恢复,是高风险操作

三、标准故障诊断流程(二者配合)

售后维修、产线下线、刷写收尾的标准操作顺序,完全遵循「先读后清再验证」的逻辑:

cpp 复制代码
1. 19 02 09  读取所有当前+历史故障,确认故障情况
2. 19 04     读取目标故障的冻结帧,分析根因
3. 执行维修/测试操作
4. 14 FF FF FF  清除所有故障记忆
5. 19 02 01  读取当前故障,确认故障已清除、不再复现

在 OTA 刷写流程中的位置

故障管理的执行节点如下:

cpp 复制代码
刷写前:85 关闭故障记录 → 避免刷写过程产生虚假故障
刷写后:
  28 恢复通信
  85 恢复故障记录
  14 FF FF FF  清除刷写过程中产生的零星故障
  19 02 08  读取历史故障,确认无异常残留
  11 01 复位收尾

四、CANoe 实操与 Trace 观测

1. 图形化操作

加载 CDD/ODX 数据库后,Diagnostic Console 有独立的故障管理界面:

  • 读取故障:一键获取 DTC 列表、状态位、冻结帧、扩展数据,软件自动解析为文字描述,不用手动查表
  • 清除故障:选择清除范围(全部 / 指定 DTC),一键执行
  • 支持自动刷新,实时监控故障状态变化

2. Trace 窗口报文示例

读故障列表
cpp 复制代码
→ 19 02 09                // 读取当前+已确认的所有故障
← 59 02 09 00 01 23 09    // 返回1个故障:DTC 0x000123,状态0x09(当前失败+已确认)

清除所有故障

cpp 复制代码
→ 14 FF FF FF             // 请求清除所有故障
← 54                      // 清除成功
  1. CAPL 自动化调用示例
cpp 复制代码
// 读取并打印所有当前故障
void readCurrentFaults()
{
  diagSendRequest(CDCU, "ReadDTCInformation_StatusMask_01");
  if(diagWaitForResponse(CDCU, 1000) == 0)
  {
    int count = diagGetNumberOfDTC(CDCU);
    write("当前故障数量: %d", count);
  }
}

// 清除所有故障
void clearAllFaults()
{
  diagSendRequest(CDCU, "ClearDiagnosticInformation_All");
  if(diagWaitForResponse(CDCU, 1000) == 0)
    write("故障清除成功");
}

五、常见否定响应与工程踩坑

1. 19 服务高频错误

NRC 码 含义 典型原因
0x12 子功能不支持 发送了 ECU 未实现的子功能
0x31 请求超出范围 DTC 编号不存在、冻结帧记录号无效
0x22 条件不满足 ECU 休眠、未完成初始化时无法读取
  1. 14 服务高频错误
NRC 码 含义 典型原因
0x33 安全访问拒绝 最常见,未解锁安全访问就清故障
0x7E 当前会话不支持 默认会话下执行清除操作
0x22 条件不满足 车辆行驶中、高压状态下禁止清故障
0x31 请求超出范围 DTC 掩码格式错误、指定了不存在的故障组

工程高频踩坑点

  1. 清完不验证:发了 14 就以为清干净了,实际可能因为条件不满足清除失败,必须用 19 回读确认
  2. 状态掩码用错:想读历史故障却用了 0x01 掩码,导致漏读已确认的历史故障
  3. 功能寻址清故障不开 SPR:全车 ECU 同时回复 54,造成总线瞬间拥堵
  4. 混淆 85 和 14:以为关了故障记录就会清除已有故障,或者清完故障就不会再记录新故障

0x23 服务(ReadMemoryByAddress,按地址读取内存)

服务 ID(SID)0x23,肯定响应 SID:0x63 核心定位 :UDS 体系的「底层内存调试接口」。它绕过了 DID 封装,直接通过物理内存地址读取 ECU 内部 Flash/RAM 的原始二进制数据,是开发调试、底层故障排查、标定验证的高阶工具,量产阶段基本不对外开放,属于高权限调试服务。

一、先搞懂本质:23 服务 vs 22 服务,核心区别是什么

你已经学过 22 服务(按 DID 读数据),二者最本质的区别是「是否经过封装」------22 是标准化的封装接口,23 是裸的内存直读。

对比维度 0x22 按 DID 读数据 0x23 按地址读内存
访问方式 通过预定义的 DID 编号访问,调用者不需要知道数据存在哪里 通过物理内存地址直接访问,必须知道数据的具体存储地址
数据语义 标准化参数,含义明确(如 VIN、版本号、车速),CDD 自动解析 原始二进制字节流,必须对照软件 map 文件 / 符号表才能解析含义
权限要求 低,默认 / 扩展会话即可读取绝大多数 DID 极高,需扩展 / 编程会话 + 最高等级安全访问解锁,量产常禁用
通用性 强,同平台 ECU 的 DID 定义基本一致,软件版本升级也不变 极弱,地址随软件版本、编译选项变化,换版本地址就失效
灵活度 固定,只能读厂商预留的 DID 极强,合法地址范围内的任意内存都能读
适用场景 量产诊断、售后维修、常规数据读取 开发调试、底层问题排查、标定验证、Bootloader 调试

一句话总结:22 是给售后、量产用的 "安全接口",23 是给开发工程师用的 "调试后门"

二、服务帧格式详解:格式字节是灵魂

23 服务最容易搞错的就是第 2 字节的「格式字节」,它同时定义了地址长度和数据长度,是解析报文的关键。

1. 请求帧结构

字节序号 字段 详细说明
1 SID = 0x23 固定服务标识符
2 格式字节(sizeAndAddressLength) 高 4 位 = 地址长度(字节数)低 4 位 = 数据长度(字节数)
3~N 起始内存地址 长度由格式字节高 4 位决定,大端格式
N+1~M 内存长度(可选) 当低 4 位为 0 时,后续单独字段表示读取长度
格式字节计算(重点)

格式字节是 1 个字节,拆成高 4 位和低 4 位分别定义长度,单位都是字节。 举几个工程最常用的例子:

格式字节 高 4 位 地址长度 低 4 位 数据长度 适用场景
0x24 2 2 字节地址 4 4 字节数据 16 位 MCU,读取单个 32 位变量
0x44 4 4 字节地址 4 4 字节数据 32 位 MCU,读取单个 32 位变量(最通用)
0x41 4 4 字节地址 1 1 字节数据 读取单个寄存器 / 标志位
0x40 4 4 字节地址 0 长度单独放在后续字段 读取长数据块,长度不固定

工程惯例:车载 32 位域控制器,地址几乎都是 4 字节,格式字节固定用 0x44 读单变量、0x40 读长数据块。

  1. 肯定响应帧结构
字节序号 字段 说明
1 SID = 0x63 固定响应标识符(原 SID + 0x40)
2~N 原始数据 从指定地址读取的连续内存原始数据,长度和请求一致,大端顺序

3. 真实报文示例(32 位 MCU 场景)

需求 :从地址 0x0800 1000 开始,读取 4 字节数据。

  • 请求:23 44 08 00 10 00
    • 23:服务 ID
    • 44:4 字节地址,4 字节数据
    • 08 00 10 00:起始地址 0x08001000
  • 响应:63 12 34 56 78
    • 地址 0x08001000 处的数据为 0x12345678

三、核心特性:高权限、高灵活、高风险

1. 权限门槛极高,安全管控严格

  • 会话要求:默认会话绝对不支持,至少需要扩展会话,多数 ECU 要求编程 / OTA 会话才能调用
  • 安全要求:必须解锁最高等级的安全访问(对应你文档中的 41 级),部分 ECU 还需要先通过 29 证书认证
  • 量产禁用:量产交付的车辆,绝大多数会直接关闭 23 服务(返回服务不支持),避免固件、密钥被读取泄露

2. 地址强依赖软件版本,无通用性

  • 同一个变量,软件版本一编译,地址就可能变化,必须对照对应版本的 map 文件、符号表才能正确读取
  • 不同 ECU、不同项目的地址完全不通用,没有像 DID 那样的标准定义
  • 这也是为什么量产诊断不用 23:不可能给售后每个版本都更新地址表

3. 可访问范围极广

合法地址范围内,所有内存都能读:

  • RAM 区:全局变量、堆栈、运行时状态、缓存数据
  • Flash 区:程序代码、常量数据、标定参数、固件镜像
  • 外设寄存器:硬件外设的控制寄存器、状态寄存器(部分 MCU 支持)

4. 纯只读,无副作用(正常地址下)

  • 合法地址读取不会修改内存内容,不会影响 ECU 运行,属于安全的调试操作
  • 但如果读取了非法地址、外设敏感寄存器,可能触发异常、总线错误,甚至导致 ECU 复位

四、四大核心工程应用场景

场景 1:开发阶段软件调试(最核心用途)

这是 23 服务存在的主要价值。开发工程师排查软件 bug 时,不需要为每个变量都做一个 DID,直接通过 23 服务读取 RAM 里的全局变量、结构体、数组,实时查看运行状态:

  • 排查逻辑错误:读取变量值,确认程序执行路径是否符合预期
  • 排查内存问题:读取堆栈、内存池状态,定位内存溢出、野指针问题
  • 无需重新烧录调试版软件,通过诊断口就能读内部状态,大幅提升调试效率

场景 2:底层故障定位

ECU 出现死机、跑飞、异常复位等严重问题时,通过 23 服务读取关键信息:

  • 读取异常复位寄存器、错误中断状态,定位复位原因
  • 读取死机前的堆栈快照、程序计数器 PC 值,还原死机现场
  • 读取 RAM 中保留的故障日志,分析偶发死机根因

场景 3:标定参数验证

标定工程师写入标定参数后,通过 23 服务直接读取 Flash/RAM 中的标定参数原始值,验证写入是否正确,绕过 DID 封装的中间层,确保数据真实写入到了目标地址。

场景 4:Bootloader 与刷写调试

刷写开发阶段,通过 23 服务直接读取 Flash 分区的原始数据,验证固件烧录是否正确、校验值是否匹配,排查刷写失败的底层原因。

五、CANoe 实操与 Trace 观测

1. 图形化操作

  • 23 服务属于原始内存操作,CDD 数据库一般不会封装每个地址,通常用 Raw Diagnostics(原始诊断) 功能手动组装报文发送
  • 输入服务 ID、格式字节、地址,即可发送请求,返回的原始十六进制数据需要自己对照符号表解析

2. Trace 窗口报文示例

读取 4 字节地址 0x2000 0000(RAM 区)的数据:

cpp 复制代码
→ 23 44 20 00 00 00
← 63 00 00 00 00

表示该地址当前值为 0。

  1. CAPL 自动化调用示例
cpp 复制代码
// 读取指定地址的4字节数据
dword readMemoryDword(dword address)
{
  byte req[6];
  req[0] = 0x23;
  req[1] = 0x44; // 4字节地址,4字节数据
  req[2] = (byte)(address >> 24);
  req[3] = (byte)(address >> 16);
  req[4] = (byte)(address >> 8);
  req[5] = (byte)(address);
  
  diagSendRawRequest(CDCU, req, elCount(req));
  if(diagWaitForResponse(CDCU, 1000) == 0)
  {
    dword value;
    diagGetResponseRawData(CDCU, 1, value); // 跳过响应SID,从第2字节开始读
    return value;
  }
  return 0;
}

六、常见否定响应与工程踩坑

高频 NRC 对照表

NRC 码 含义 典型原因
0x11 服务不支持 最常见,量产 ECU 直接禁用 23 服务,返回服务不支持
0x33 安全访问拒绝 未解锁最高等级安全访问
0x7E 当前会话不支持 默认 / 扩展会话下调用,需切换到更高权限会话
0x31 请求超出范围 格式字节错误、地址非法、读取长度超出合法范围
0x22 条件不满足 ECU 处于非正常运行状态,无法读取内存

工程高频踩坑点

  1. 地址错误:软件版本更新后地址变了,还用旧地址读取,得到无意义数据,误以为是 ECU 问题
  2. 字节序搞反:读取多字节数据时,大小端解析错误,导致数值完全不对
  3. 越界读取:读取长度超过合法内存范围,触发 ECU 内存异常,甚至复位
  4. 量产环境调用:量产车默认关闭 23 服务,反复调用只会报错,浪费排查时间
  5. 格式字节算错:高 4 位低 4 位搞反,导致地址和长度解析全部错误

0x3D 服务(WriteMemoryByAddress,按地址写入内存)

服务 ID(SID)0x3D,肯定响应 SID:0x7D 核心定位:与 23 服务成对出现的「底层内存写入接口」,绕过 DID 封装,直接通过物理内存地址向 ECU 的 RAM/Flash 写入原始二进制数据。它是 UDS 体系中风险最高的调试服务之一,仅在开发、产线调试阶段开放,量产阶段几乎全部禁用,属于最高权限的底层调试能力。

一、本质对比:3D 服务 vs 2E 服务

和 23/22 的对应关系完全一致,核心差异还是「是否经过封装」,写操作的风险远高于读操作,因此权限管控也更严格。

对比维度 0x2E 按 DID 写数据 0x3D 按地址写内存
写入方式 通过预定义 DID 写入,调用者无需关心数据存储地址 通过物理内存地址直接写入,必须精准知道目标地址
数据校验 有参数合法性校验、范围检查,写入安全 无业务层校验,原始数据直接写入内存,完全由调用者负责
权限要求 中,扩展会话 + 对应安全等级即可 极高,需编程 / OTA 会话 + 最高等级安全解锁,量产默认禁用
通用性 强,同平台 DID 定义稳定,版本升级不影响 极弱,地址随软件版本、编译选项变化,无通用性
灵活度 固定,只能写厂商开放的 DID 参数 极强,合法地址范围内任意内存都可写
风险等级 中低,写错参数最多功能异常 极高,写错 Flash 固件会直接导致 ECU 变砖
适用场景 量产配置写入、售后参数修改 开发调试、底层调参、Bootloader 开发、产线底层烧录

一句话总结:2E 是给量产用的 "安全写入接口",3D 是给开发工程师用的 "底层调试后门"

二、服务帧格式详解

3D 服务的帧结构和 23 服务高度镜像,格式字节的定义、地址规则完全一致,只是把 "读取数据" 换成了 "写入数据"。

1. 请求帧结构

字节序号 字段 详细说明
1 SID = 0x3D 固定服务标识符
2 格式字节(ALFID) 高 4 位 = 地址长度(字节数)低 4 位 = 数据长度(字节数,工程简化实现)
3~N 起始内存地址 长度由格式字节高 4 位决定,大端格式
N+1~M 待写入原始数据 长度与格式字节低 4 位对应,大端格式

补充标准完整格式:ISO 标准中低 4 位定义为「长度字段的字节数」,即地址后会跟一个独立的长度字段。但工程上绝大多数 ECU 做了简化,短数据读写时低 4 位直接表示数据字节数,和 23 服务的简化规则完全一致。

  1. 肯定响应帧结构
字节序号 字段 说明
1 SID = 0x7D 固定响应标识符(原 SID + 0x40)
2 格式字节 回应当前写入的地址与长度格式
3~N 起始内存地址 回应当前写入的目标地址,用于确认写入位置正确

3. 真实报文示例(32 位 MCU 场景)

需求 :向 RAM 地址 0x2000 1000 写入 4 字节数据 0x12345678

  • 请求:3D 44 20 00 10 00 12 34 56 78
    • 3D:服务 ID
    • 44:4 字节地址,4 字节数据
    • 20 00 10 00:目标地址 0x20001000
    • 12 34 56 78:待写入的 4 字节数据
  • 响应:7D 44 20 00 10 00
    • 确认在该地址执行了写入操作

三、核心运行机制:RAM 写入 vs Flash 写入

这是理解 3D 服务最关键的知识点,二者的写入逻辑、生效方式、风险等级天差地别,也是新手最高频的踩坑点。

1. RAM 写入:即时生效,掉电丢失

  • 底层逻辑:RAM 是随机读写内存,可任意改写 0 和 1,不需要前置操作,写入后立刻生效。
  • 生效时机:写入完成的瞬间,变量值就已改变,程序下一次读取就会用到新值。
  • 持久性:完全临时,ECU 复位、下电后数据全部丢失,回到原始值。
  • 速度:极快,微秒级完成。
  • 典型用途:在线调试修改变量、临时改标定参数、模拟故障注入。
  • 风险:低,写错最多导致程序逻辑异常,复位就恢复。

2. Flash 写入:先擦后写,持久固化

Flash 的物理特性决定了它不能像 RAM 一样随便写,这是硬件层面的硬性规则:

Flash 只能把 1 改成 0,不能把 0 改成 1;擦除操作会把整扇区全部变成 1,之后才能写入新数据。

因此 Flash 写入必须严格遵循「先擦除 → 再写入」的两步流程:

  1. 擦除阶段 :调用 31 服务的0xFF00擦除例程,把目标 Flash 扇区全部擦成 0xFF(全 1)
  2. 写入阶段:用 3D 服务逐字节写入数据,把对应位置的 1 改成 0
Flash 写入的核心特性
  • 生效时机:写入完成即固化,掉电、复位都不会丢失。
  • 速度:很慢,擦除按扇区算需要几毫秒到几十毫秒,写入也比 RAM 慢几个数量级。
  • 风险:极高,写错固件分区、擦错扇区会直接导致 ECU 无法启动,只能通过 Bootloader 救砖。
  • 次数限制:Flash 有擦写寿命(通常几万到几十万次),不能频繁擦写。

工程红线:没有擦除就直接写 Flash,100% 会出现数据错误,不是写入失败就是值不对,这是底层调试最常见的低级错误。

四、核心特性与权限管控

1. 权限等级拉满,管控最严格

  • 会话要求:默认会话、普通扩展会话一律不支持,必须进入 OTA 会话 / 编程会话才能调用
  • 安全要求:必须解锁最高等级安全访问(对应你文档中的 41 级),部分 ECU 还要求 29 证书双向认证通过
  • 量产默认关闭 :量产交付的车辆,99% 会直接屏蔽 3D 服务(返回0x11服务不支持),从根源杜绝风险
  • 比 23 服务的管控更严格:很多 ECU 会开放 23 读内存调试,但完全关闭 3D 写内存能力

2. 强依赖软件版本,无通用性

和 23 服务完全一致:同一个变量,软件版本一变,地址就可能偏移,必须对照对应版本的 map 文件、符号表才能确定正确地址。没有任何跨版本、跨车型的通用写入地址。

3. 无业务层校验,责任全在调用方

2E 服务写入时,ECU 会做参数范围、合法性校验,超出范围会直接报错; 3D 服务是纯内存写入,ECU 不做任何业务逻辑校验,写错了就是直接写进去了,后果由调用者承担。

五、四大核心工程应用场景

场景 1:开发阶段在线调试(最核心用途)

这是 3D 服务存在的主要价值:

  • 调试软件逻辑时,直接修改 RAM 中的全局变量、标志位,快速验证不同分支的逻辑,不用重新编译、烧录固件,调试效率提升数倍
  • 故障注入测试:直接修改内存变量模拟异常工况,验证故障处理逻辑是否正确
  • 不需要给每个调试变量都做 DID,大幅减少开发工作量

场景 2:标定参数在线调优

标定工程师在实车调试时,通过 3D 服务直接修改 RAM 中的标定参数(如阈值、增益、补偿系数),实时观察效果,快速迭代最优参数,定稿后再固化到 Flash 里。

场景 3:Bootloader 与刷写开发

刷写底层开发阶段,3D 服务是调试 Flash 读写的核心工具:

  • 验证 Flash 擦除、写入驱动是否正常
  • 直接写入固件片段,验证刷写逻辑
  • 排查刷写失败的底层原因,对比写入前后的 Flash 数据

注:量产刷写用的是标准的 34/36/37 数据传输服务,不会直接用 3D 服务;3D 仅用于开发调试阶段。

场景 4:产线底层数据烧录

产线末端工位,通过 3D 服务直接写入每台 ECU 的唯一数据(如硬件序列号、加密密钥、出厂校准值),写入 Flash 永久固化,比 2E 服务更底层、效率更高。

六、结合你这份文档的落地说明

  1. 文档未标注 3D 服务,不代表 ECU 不支持,而是它属于开发 / 产线内部调试接口,不会出现在量产售后的公开诊断规范中。
  2. 权限对应文档中的最高等级:必须在10 40 OTA 会话下,解锁27 41最高级安全访问后才能调用,普通扩展会话直接拒绝。
  3. 和 23 服务严格配套:开发调试时永远是「23 读出来 → 修改数值 → 3D 写进去 → 23 回读验证」的标准流程。
  4. 配套 31 擦除例程:写 Flash 前必须调用31 01 FF 00擦除对应扇区,否则写入必然失败。

七、CANoe 实操与 CAPL 示例

1. 图形化操作

3D 属于原始内存操作,CDD 数据库一般不会封装,通常用 Raw Diagnostics 原始诊断 功能手动组装报文发送;写入后配合 23 服务回读验证。

2. Trace 窗口报文示例

向 RAM 地址0x20000000写入 4 字节0x00000001

cpp 复制代码
→ 3D 44 20 00 00 00 00 00 00 01
← 7D 44 20 00 00 00
  1. CAPL 自动化写入示例
cpp 复制代码
// 向指定地址写入4字节数据
byte writeMemoryDword(dword address, dword value)
{
  byte req[10];
  req[0] = 0x3D;
  req[1] = 0x44; // 4字节地址,4字节数据
  // 写入目标地址(大端)
  req[2] = (byte)(address >> 24);
  req[3] = (byte)(address >> 16);
  req[4] = (byte)(address >> 8);
  req[5] = (byte)(address);
  // 写入目标数据(大端)
  req[6] = (byte)(value >> 24);
  req[7] = (byte)(value >> 16);
  req[8] = (byte)(value >> 8);
  req[9] = (byte)(value);
  
  diagSendRawRequest(CDCU, req, elCount(req));
  if(diagWaitForResponse(CDCU, 1000) == 0)
  {
    write("写入成功,地址: 0x%08X, 值: 0x%08X", address, value);
    return 0;
  }
  write("写入失败");
  return 1;
}

八、常见否定响应与工程踩坑

高频 NRC 对照表

NRC 码 含义 典型原因
0x11 服务不支持 最常见,量产 ECU 直接禁用 3D 服务
0x33 安全访问拒绝 未解锁最高等级安全访问
0x7E 当前会话不支持 普通扩展会话下调用,需切换到 OTA / 编程会话
0x31 请求超出范围 地址非法、长度越界、格式字节错误
0x22 条件不满足 写 Flash 前未擦除、Flash 忙、蓄电池电压过低
0x72 写入失败 Flash 硬件错误、校验失败、写入超时

工程高频踩坑点

  1. 写 Flash 不先擦除:最经典的低级错误,Flash 硬件特性决定了必须先擦后写,否则数据必然错误
  2. 混淆 RAM 和 Flash:往 RAM 里写了参数,以为复位后还能保留,结果下电就丢了
  3. 地址偏移错误:软件版本更新后地址变了,还用旧地址写入,导致覆盖了其他变量,引发莫名奇妙的异常
  4. 大小端搞反:多字节数据写入时字节序错误,写入的值和预期完全不同
  5. 越界写入:写入长度超过合法范围,覆盖了后面的程序 / 数据,导致 ECU 跑飞、死机
  6. 量产环境调用:量产车默认关闭 3D 服务,反复调用只会报错,浪费排查时间

0x34/35/36/37 数据传输服务组

服务组定位 :这是 UDS 体系中专门用于大数据块传输的标准协议组,也是整车 OTA、ECU 固件刷写的核心传输通道。如果说之前学的 31 服务是 "刷写的动作执行器",那这组服务就是 "刷写的数据搬运工"------ 所有固件、标定文件、大段日志的传输,都通过这组服务完成,是量产刷写的唯一官方标准路径。

Data Transfer 分类下的 4 条指令,正好对应这四个标准服务:

  • 34:RequestDownload(请求下载,诊断仪→ECU 下发数据)
  • 35:RequestUpload(请求上传,ECU→诊断仪上传数据)
  • 36:Transmit Data(数据传输,逐包搬运数据)
  • 37:Transfer Stop(结束传输,收尾校验)

这组服务学完,你就掌握了 UDS 刷写的完整底层逻辑,之前学的会话、安全、通信控制、例程、故障管理,最终都会落地到这组服务上,形成完整的 OTA 刷写闭环。

一、先搞懂核心定位:和 23/3D 服务的本质区别

很多人会混淆 "按地址写内存" 和 "数据传输刷写",二者层级、用途、安全性完全不同:

对比维度 34/36/37 标准数据传输 3D 按地址写内存
协议层级 标准应用层传输协议,带流控、校验、块序号 底层调试接口,原始内存直写,无传输管控
适用场景 量产正式刷写、OTA 升级、标定文件下载 开发调试、底层参数修改、故障注入
流控机制 有,通过 34 协商最大包长,ECU 端缓冲区保护 无,完全由调用者控制,极易溢出
顺序保障 有块计数器,严格保证数据顺序,支持丢包重传 无,写乱了就是写乱了
权限等级 高,需刷写专属会话 + 最高安全等级 极高,仅开发阶段开放,量产禁用
稳定性 高,标准协议栈实现,出错有明确错误码 低,写错直接变砖,无兜底
通用性 强,所有车企刷写都用这套标准 弱,地址随版本变化,无通用性

一句话总结:3D 是开发调试的 "后门",34/36/37 是量产刷写的 "正门"。正式 OTA、产线刷写 100% 用这组标准服务,不会用 3D 直写。

二、逐个服务拆解:帧格式与核心作用

1. 0x34 服务:RequestDownload(请求下载)

  • 服务 ID0x34,肯定响应 SID:0x74
  • 核心作用 :刷写前的 "握手协商"。诊断仪告诉 ECU"我要往指定地址发数据,总长度是多少",ECU 检查地址、长度合法性,分配接收缓冲区,返回每个数据包最大允许长度,完成传输参数协商。
请求帧结构
字节序号 字段 详细说明
1 SID = 0x34 固定服务标识符
2 格式字节 和 23/3D 服务完全一致:高 4 位 = 地址长度(字节数)低 4 位 = 数据总长度字段的字节数
3~N 起始内存地址 长度由格式字节高 4 位决定,大端格式,即固件要写入的 Flash 起始地址
N+1~M 数据总长度 整个固件的大小,长度由格式字节低 4 位决定

肯定响应结构(最关键参数在这里)

字节序号 字段 说明
1 SID = 0x74 固定响应标识符
2 格式字节(长度格式) 最大块长度字段的字节数,通常为 1~2 字节
3~N maxNumberOfBlockLength 核心参数:每个 36 数据包最多能带的数据字节数,由 ECU 缓冲区大小决定

工程重点:这个最大块长是 ECU 说了算,诊断仪必须严格遵守。如果发的 36 包数据超过这个长度,ECU 会直接拒绝,甚至缓冲区溢出导致传输中断。

典型报文示例(32 位 MCU,4 字节地址,4 字节长度)
  • 请求:34 44 08 00 10 00 00 02 00 00
    • 含义:请求向地址 0x08001000 下载 128KB(0x20000 字节)数据
  • 响应:74 01 80
    • 含义:同意下载,每个数据包最大 128 字节(0x80)

2. 0x35 服务:RequestUpload(请求上传)

  • 服务 ID0x35,肯定响应 SID:0x75
  • 核心作用:和 34 服务完全镜像,方向反过来 ------ 诊断仪请求从 ECU 指定地址读取大数据块,用于读取固件、导出日志、回读标定参数,工程上使用频率远低于 34。

帧格式、协商逻辑和 34 完全一致,只是数据方向从 "下发" 变成 "上传",ECU 返回最大块长后,通过 36 服务逐包回传数据。

3. 0x36 服务:TransferData(数据传输)

  • 服务 ID0x36,肯定响应 SID:0x76
  • 核心作用:真正的 "搬砖" 服务,按协商好的最大块长,逐包传输数据,带块序号保证顺序,是整个传输过程中发送量最大的服务。
请求帧结构
字节序号 字段 详细说明
1 SID = 0x36 固定服务标识符
2 块序号计数器 blockSequenceCounter,从0x01开始递增,0xFF之后回到0x01循环
3~N 数据段 当前包的有效数据,长度≤34 协商的最大块长

肯定响应结构

字节序号 字段 说明
1 SID = 0x76 固定响应标识符
2 块序号计数器 回应当前已正确接收的包序号
块序号核心规则
  1. 首包序号必须是0x01,不能从 0 开始;
  2. 每发一包序号加 1,到0xFF后下一包回到0x01
  3. ECU 会严格校验序号,跳号、错序、重复包都会直接报错,要求重传对应序号的包;
  4. 这是传输可靠性的核心保障,防止丢包、乱序导致固件损坏。
典型报文示例(续上例,第一包数据)
  • 请求:36 01 00 01 02 03 ... 7F(128 字节数据)
  • 响应:76 01(确认第 1 包接收成功)

4. 0x37 服务:RequestTransferExit(结束传输)

  • 服务 ID0x37,肯定响应 SID:0x77
  • 核心作用:所有数据传完后,通知 ECU 传输结束,ECU 做收尾校验、关闭缓冲区、完成写入收尾,是传输流程的正式收尾。
请求帧结构
字节序号 字段 说明
1 SID = 0x37 固定服务标识符
2~N 可选校验参数 部分实现会在这里带 CRC / 哈希值,让 ECU 做完整性校验

肯定响应结构

字节序号 字段 说明
1 SID = 0x77 固定响应标识符
2~N 可选结果数据 返回校验结果、传输统计等自定义信息

工程红线:传输完成必须发 37 收尾。只发 36 不发 37,ECU 可能认为传输异常中断,不会正式固化数据,导致刷写无效。

三、完整下载(刷写)传输流程

以固件刷写为例,一次完整的 34/36/37 传输时序如下:

cpp 复制代码
1. 诊断仪 → 34 请求下载(带起始地址、总长度)
2. ECU → 74 同意下载(返回最大包长)
   ↓
3. 诊断仪 → 36 01 + 第1包数据
4. ECU → 76 01 确认
5. 诊断仪 → 36 02 + 第2包数据
6. ECU → 76 02 确认
   ... 循环N包 ...
7. 诊断仪 → 36 XX + 最后一包数据(长度可小于最大包长)
8. ECU → 76 XX 确认
   ↓
9. 诊断仪 → 37 结束传输
10. ECU → 77 传输完成,返回校验结果

传输过程中的两个关键配套动作

  1. 3E 服务后台保活 刷写传输通常需要几秒到几分钟,全程必须持续周期发送3E 80保活,一旦会话超时,传输直接中断,前功尽弃。

  2. 28 服务静默总线 传输前已经通过 28 服务关闭了所有业务报文,总线 100% 带宽留给 36 数据包,最大化传输效率,避免业务帧抢占导致超时。

四、结合你这份文档的落地说明

  1. 文档中这 4 个服务是 OTA 刷写的核心传输层,和 31 服务(擦除、校验)配合,完成完整的固件烧录流程。
  2. 权限对应最高等级:必须在10 40 OTA 会话下,解锁27 41最高级安全访问后才能调用,普通扩展会话直接拒绝。
  3. 标准流程严格遵循:先 31 擦除 Flash → 34 请求下载 → 36 循环传数据 → 37 结束传输 → 31 固件校验,是行业通用的标准刷写时序。
  4. 35 服务(上传)主要用于产线出厂校验、售后回读固件版本验证,日常 OTA 几乎不用,属于配套能力。

五、CANoe 实操细节

1. 图形化全自动刷写

CANoe 原生支持固件刷写,完全不用手动分包:

  • 在 Diagnostic 模块中导入 Hex/S19/Motorola 格式的固件文件
  • 配置好目标地址、刷写参数,一键启动
  • 软件自动完成:34 协商 → 自动拆分固件 → 36 逐包传输 → 37 收尾 的全流程
  • 自动处理块序号、超时重传、保活等底层逻辑,无需人工干预

2. CAPL 自动化调用

可以调用 CANoe 封装好的刷写接口,也可以手动控制传输流程,典型简化示例:

cpp 复制代码
// 启动固件刷写
byte startFlashProgramming(char filePath[])
{
  // 1. 调用34请求下载,自动协商参数
  diagFlashStartDownload(CDCU, 0x08001000, filePath);
  if(diagWaitForResponse(CDCU, 2000) != 0)
    return 1; // 下载请求失败

  // 2. 自动传输固件,内部处理36分包、序号、重传
  diagFlashTransferData(CDCU);
  // 等待传输完成,根据固件大小设置超时
  if(testWaitForDiagServiceDone(CDCU, 30000) != 0)
    return 2; // 传输失败

  // 3. 37结束传输
  diagFlashStopTransfer(CDCU);
  if(diagWaitForResponse(CDCU, 2000) != 0)
    return 3; // 收尾失败

  write("固件传输完成");
  return 0;
}

3. Trace 窗口观测

刷写时 Trace 里会呈现非常有规律的现象:

  • 开头一组 34/74 握手
  • 中间是大量连续的 36 xx76 xx 成对报文,序号依次递增
  • 结尾一组 37/77 收尾
  • 后台周期性穿插3E 80保活报文

六、常见否定响应与工程踩坑

高频 NRC 对照表

服务 NRC 码 含义 典型原因
34 0x31 请求超出范围 地址非法、长度超出现有分区大小
34 0x33 安全访问拒绝 未解锁最高等级刷写权限
34 0x22 条件不满足 Flash 未擦除、电压不足、缓冲区不足
36 0x24 请求顺序错误 块序号错误,跳号 / 重复 / 序号不对
36 0x31 请求超出范围 数据长度超过协商的最大块长
36 0x70 传输挂起 ECU 缓冲区满,暂停发送,稍后重试
37 0x24 请求顺序错误 未开始传输就发结束指令
37 0x72 通用编程错误 数据校验失败、Flash 写入失败

工程最高频踩坑点

  1. 最大块长搞错:不看 34 返回的最大长度,自己硬编码包长,导致 ECU 缓冲区溢出,传输中断
  2. 块序号错误:手动写脚本时序号算错,首包从 0 开始、跳号、循环逻辑错误
  3. 忘了保活:传输时间长,中途会话超时,刷写到一半断了
  4. 没擦 Flash 就传数据:跳过 31 擦除步骤,直接发 34 下载,写入必然失败
  5. 传完不收尾:最后一包发完就以为结束了,没发 37,ECU 不固化数据
  6. 总线负载太高:没关业务报文,36 包被业务帧挤占,频繁超时重传

七、终极收尾:完整 OTA 刷写全流程大串联

到这里,所有核心 UDS 服务就全部学完了。把它们按真实 OTA 刷写的时序串起来,就是一套完整的、工业级的 ECU 刷写流程,也是你文档中 OTA 功能的完整底层逻辑:

cpp 复制代码
【前置准备阶段】
1.  10 03 切换扩展会话
2.  27 01/02 解锁1级安全访问
3.  85 82 关闭故障记录(功能寻址+SPR)
4.  10 40 切换OTA专属会话
5.  27 41/42 解锁41级最高刷写权限
6.  28 83 03 关闭业务+NM报文(功能寻址+SPR)
7.  开启3E 80 后台周期保活

【刷写执行阶段】
8.  31 01 FF 00 启动Flash擦除例程
9.  31 03 FF 00 轮询擦除结果 → 擦除完成
10. 34 请求下载(起始地址+固件长度)
11. 36 循环传输固件数据包(自动分包、块序号递增)
12. 37 结束传输
13. 31 01 02 01 启动MD5固件校验例程
14. 31 03 02 01 轮询校验结果 → 校验通过
15. 31 01 FF 05 启动分区切换例程

【收尾恢复阶段】
16. 28 80 03 恢复业务+NM报文
17. 85 81 恢复故障记录
18. 14 FF FF FF 清除刷写过程故障
19. 19 02 08 回读故障,确认无异常
20. 关闭3E保活
21. 11 01 硬复位ECU → 新固件启动

学习收尾总结

从最基础的 10 会话控制,到 27 安全解锁,再到通信、故障、数据读写、例程、认证、内存调试,最后到这组刷写核心传输服务,整个 UDS 诊断体系的核心框架就完整了。

  • 日常售后、产线测试:用 10+22/2E+19+14 这套基础组合
  • 功能调试、产线测试:加上 28+31+85 这套控制组合
  • 固件刷写、OTA 升级:走完整的 10+27+31+34/36/37 全链路
  • 信息安全、远程场景:补充 29 证书认证
  • 开发底层调试:用 23/3D 内存直读直写

补充:

真正的 Flash 固件烧录,确实必须在标准的 10 02 编程会话里执行 ;本质是因为现代智能域控的 OTA 是「应用层准备 + Bootloader 烧录」的两阶段架构 ------10 40 跑在应用层做准备,10 02 藏在 Bootloader 烧录阶段;

一、先讲透:为什么不直接进 10 02 编程会话

传统低端 ECU 的刷写逻辑很简单:切10 02 → 进 Bootloader → 擦除 → 写固件 → 复位。但这种模式有两个致命问题:

  1. 刷写期间 ECU 完全瘫痪:进了 Bootloader 就停掉所有业务功能,刷写多久车就失效多久,OTA 体验极差。
  2. 变砖风险极高:刷写中途断电、中断,ECU 就彻底启动不了,只能返厂救砖。

新势力的域控 OTA,用的是 A/B 双分区架构,把「固件下载校验」和「真正烧录生效」拆开了:

  • 90% 的工作在应用层完成:后台静默把新固件下载到备用分区、做完整校验、准备好所有环境,这时候 ECU 正常运行,所有功能都能用,用户完全无感知。
  • 只有最后 10% 的烧录动作在 Bootloader 完成:确认固件没问题了,才重启进 Bootloader,把备用分区设为启动分区,几秒就完成,变砖风险几乎为零。

10 40 OTA 专属会话,就是应用层用来做「刷写准备工作」的高权限会话 ;而10 02 只在跳转 Bootloader 后的真正烧录阶段才会出现。

二、完整两阶段时序:10 4010 02 分别在哪里

把 Bootloader 阶段补进去,完整 OTA 流程是这样的,你就能清晰看到两个会话的分工:

第一阶段:应用层(你文档里的内容,全程在 App 里运行)

cpp 复制代码
10 03 扩展会话
  → 解锁基础安全
  → 关故障记录、关业务报文
  → 10 40 进入OTA专属会话  ← 你看到的自定义会话
    → 解锁41级最高安全
    → 前置条件检查
    → 擦除备用分区
    → 34/36/37 把新固件下载到备用分区
    → MD5校验、签名校验、版本校验
    → 设置下次启动从备用分区启动

✅ 这一阶段全程 ECU 跑着主应用,所有刷写准备都在后台完成,不影响正常功能。

第二阶段:Bootloader 层(文档里没写,单独的 Bootloader 规范)

应用层准备完成后,执行11 01硬复位,ECU 重启,跳转到 Bootloader 引导程序 ,这时候才会出现标准10 02

cpp 复制代码
ECU复位 → 进入Bootloader
  → 建立新的诊断连接
  → 10 02 进入标准编程会话  ← 真正的编程会话在这里
    → 解锁Bootloader专属安全等级
    → 执行最终的分区切换/固件固化
    → 校验启动分区完整性
      → 校验通过:复位回到应用层,运行新固件
      → 校验失败:自动回滚旧分区,保证车能正常启动
会话 运行环境 核心工作
10 40 OTA 会话 应用层 App 固件下载、校验、分区准备、断点管理
10 02 编程会话 Bootloader 引导层 最终 Flash 固化、分区切换、启动校验

三、什么时候会直接用 10 02

也不是所有刷写都走10 40两阶段,以下场景会直接进10 02编程会话:

  1. 产线首次烧录 :ECU 刚生产出来,里面没有应用程序,只能直接进 Bootloader 用10 02烧录固件。
  2. 售后救砖 :应用层损坏、ECU 无法正常启动,只能通过 Bootloader 模式强制刷写,用10 02
  3. 低端 ECU 刷写 :车身小模块、传感器这类没有双分区的简单 ECU,刷写直接进10 02,没有应用层 OTA 会话。

而正常的整车远程 OTA,为了可靠性和用户体验,都是应用层10 40做准备,最后短暂进 Bootloader 用10 02收尾。

物理寻址与功能寻址:本质是「一对一私聊」和「一对多群发」

这是 CAN 总线上 UDS 诊断的两种基础通信方式,决定了诊断请求发给谁、谁会回复,所有诊断服务都要基于其中一种寻址方式发送。

1. 核心定义
  • 物理寻址(Physical Addressing) :点对点专属通信,一个请求只发给一个指定 ECU,只有目标 ECU 会处理并响应,相当于 "私聊"。
  • 功能寻址(Functional Addressing) :一对多广播通信,一个请求发给总线上所有 / 一组 ECU,所有符合条件的 ECU 都会接收并执行指令,相当于 "群公告"。
  1. 核心区别对照表
对比维度 物理寻址 功能寻址
通信模式 一对一单播 一对多广播
CAN 标识 每个 ECU 有唯一物理诊断 ID(如 0x701、0x702) 统一的广播功能 ID(如标准 OBD 的 0x7DF)
响应规则 目标 ECU 必须返回肯定 / 否定响应 必须开启 SPR 肯定响应抑制,禁止批量回复,否则引发总线风暴
支持服务 所有诊断服务都支持 仅支持简单控制类服务(10、11、28、3E、85、14 等)
典型场景 单个 ECU 读写 DID、刷写固件、读故障详情、安全解锁 全车关闭故障记录、全车关闭通信、全车清故障码、批量复位
总线负载 可控,一对一交互无冲突 控制不当会引发报文冲突、总线拥堵
3. 工程实操关键规则
(1)物理寻址:精准交互的主力

所有需要读取返回值、大数据交互、精准控制单个 ECU 的操作,必须用物理寻址。

  • 例子:读取 CDCU 的软件版本、写入配置参数、执行单个 ECU 固件刷写、解锁安全访问,都用物理寻址。
  • 不需要考虑响应冲突,ECU 的响应只会发给诊断仪,报文交互稳定可靠。
(2)功能寻址:批量控制的利器,SPR 位是灵魂

只能用于简单的、不需要返回详细数据的控制类指令,目的是一次性给所有 ECU 发相同指令,不用逐个发送,提升效率。

  • 铁则:功能寻址必须开启 SPR 肯定响应抑制位(子功能最高位置 1)
    • 原因:如果不开 SPR,总线上十几个 ECU 同时回复响应,CAN 总线会瞬间拥堵,报文冲突、丢包,大部分响应都收不到,甚至影响总线正常通信。
    • 例子:全车关闭故障记录,发85 82(SPR 位开启),所有 ECU 静默执行不回复;如果发85 02,就会触发总线风暴。
  • 不是所有服务都支持功能寻址,34/36 刷写、19 读冻结帧这类需要大数据交互的服务,只能用物理寻址逐个执行。
4. 和之前所学服务的对应关系
  • 支持功能寻址 + SPR 批量操作:10 会话切换、11 复位、28 通信控制、3E 保活、85 故障控制、14 清故障码
  • 仅支持物理寻址:22/2E 数据读写、27 安全访问、29 认证、31 例程、19 读故障详情、23/3D 内存读写、34/35/36/37 数据传输
5. 新手高频踩坑

用功能寻址发指令忘了开 SPR 位,导致总线上大量响应冲突,误以为 ECU 不支持指令,实际是通信已经乱了。记住:功能寻址 + 批量操作 = 必开 SPR 位