会话管理(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 | 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 | 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 服务的执行有非常直观的现象:
-
Diagnostic Console 操作 加载 CDD 数据库后,在诊断控制台找到
ECUReset服务,选择 HardReset 子功能,一键发送即可执行,适合快速验证。 -
Trace 窗口现象 发送
11 01后,Trace 中会依次出现:- ECU 返回肯定响应
51 01 - 极短延迟后,目标 ECU 的所有报文全部停止发送(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 | 子功能(奇数) | 安全等级 + 请求种子,如 01、11、41 |
| 3+ | 安全数据(可选) | 部分场景需要诊断仪先发送身份标识,多数 ECU 此段为空 |
ECU 肯定响应:
| 字节序号 | 字段 | 说明 |
|---|---|---|
| 1 | SID = 0x67 |
固定响应标识符 |
| 2 | 子功能号 | 回应当前请求的安全等级 |
| 3+ | 种子数据 | 随机生成的挑战码,长度由厂商定义(通常 2/4/16 字节) |
3. 第二步:发送密钥(Send Key)
诊断仪拿到种子后,用双方约定的加密算法计算出密钥,发送给 ECU 校验。
| 字节序号 | 字段 | 说明 |
|---|---|---|
| 1 | SID = 0x27 |
固定服务标识符 |
| 2 | 子功能(偶数) | 安全等级 + 发送密钥,如 02、12、42 |
| 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、不同软件版本的算法都可能不同。
五、与会话体系的配合关系
这是最容易搞错的点,记住一个固定顺序: 先切会话 → 再解锁安全 → 最后执行业务操作
- 会话是 "入场券":不切到对应会话,连 27 服务本身都可能被拒绝(比如默认会话下不允许解锁 41 级安全)。
- 安全访问是 "操作权限卡":切了高等级会话,只是进入了房间,柜子还是锁着的,必须用 27 服务拿到钥匙才能开。
- 一一对应:高等级安全必须在高等级会话下才能解锁。比如 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 | 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 位的组合。
- 原因:总线上十几个 ECU 同时回复
三、通信类型:按位控制,精准覆盖
通信类型字节是按位掩码设计,每一位对应一类报文,置 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 流程的标准环节:
- 时机:进入 OTA 会话、解锁安全、关闭故障记录之后,固件传输之前
- 操作 :功能寻址群发
28 83 03,全车 ECU 关闭应用报文和 NM 报文 - 效果:总线负载从正常的 20%~40% 降到 5% 以下,所有带宽留给 36 服务传固件
- 收尾 :刷写完成、校验通过后,群发
28 80 03恢复正常通信
场景 2:总线故障排查
当总线出现错误帧、负载异常、干扰、丢包问题时,28 服务是定位故障源的高效工具:
- 逐个 ECU 物理寻址发送
28 03 03,让单个 ECU 静默 - 每停一个就观察总线错误是否消失
- 停到某个 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 后,可直观观察到三个现象:
- 目标 ECU 的所有周期应用报文、NM 报文立刻停止发送
- Statistics 窗口总线负载率明显下降
- 诊断请求 / 响应帧完全正常,不受任何影响
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 |
条件不满足 | 车辆行驶中、特定功能运行时不允许关闭通信 |
工程高频踩坑点
- 群发不开 SPR 位 :功能寻址发
28 03 03没开最高位,导致总线瞬间拥堵,部分 ECU 收不到指令。 - 刷写中断忘记恢复:测试时中途停止测量,ECU 会话超时后会自动恢复,不用怕 "变砖"。
- 网关路由场景失效:只给网关发 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 的编号、含义、读写权限都有明确的行业规则;
- DID 编号的区间规则
| DID 区间 | 归属 | 说明 |
|---|---|---|
0xF100 ~ 0xF1FF |
标准通用 DID | ISO 14229 推荐的通用标识类 DID,全行业通用,比如 VIN、软件版本、ECU 序列号等,绝大多数车企都会遵循 |
0x0000 ~ 0xEFFF |
厂商自定义 DID | 车企根据自身业务自由定义,覆盖配置、OTA、网络、动态数据等所有场景,不同品牌不通用 |
- 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 写安装日期、更新刷写计数器 → 归档升级记录
-
产线下发流程
产线工位:
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返回写入成功
- 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 不可读,比如休眠中部分数据无法读取 |
- 2E 写服务高频错误
| NRC | 含义 | 常见原因 |
|---|---|---|
0x33 |
安全访问拒绝 | 最常见,未解锁对应等级的安全访问 |
0x7E |
当前会话不支持 | 默认会话下执行写操作 |
0x31 |
请求超出范围 | DID 不支持写、数据长度错误、数值超出合法范围 |
0x22 |
条件不满足 | 行驶中、运行状态不允许写入 |
0x72 |
写入失败 | Flash 写入错误、校验失败、存储区异常 |
3. 工程高频踩坑
- 写入后不复位:改完配置直接读,发现值没变,误以为写入失败,实际是没复位生效
- 数据格式不匹配:小端发大端、长度不对,导致参数错误
- 批量读超限:一次塞太多 DID,ECU 只返回前几个或直接报错
- 只读 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 | SID = 0x71 |
固定响应标识符(原 SID + 0x40) |
| 2 | 子功能值 | 回应当前操作类型(启动 / 停止 / 查询) |
| 3~4 | Routine ID | 对应操作的例程 ID |
| 5~N | 执行结果 / 状态(可选) | 例程的运行状态、返回值、错误码,格式由例程自定义 |
- 三大核心子功能(标准定义)
| 子功能值 | 官方名称 | 作用 | 使用频率 |
|---|---|---|---|
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 // 执行成功,状态:完成
- 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 |
例程已在运行 | 重复启动同一个正在运行的异步例程 |
工程高频踩坑点
- 只启动不查询结果:误以为发了启动指令就成功,实际例程可能后台报错失败。
- 轮询间隔 / 超时设置不合理:擦除大分区需要几秒到十几秒,超时设太短会误判失败。
- 复位前未等待例程结束:例程还在跑就发复位,可能导致 Flash 损坏、ECU 变砖。
- 忽略入口参数:部分例程必须传分区号、长度等参数,不传会直接报参数错误。
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 | 响应参数 | 随子功能返回证书、验证结果、签名挑战等 |
- 对应 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 是原厂正品,没有被篡改、伪造",防止接入恶意节点。 完整交互流程:
- 证书交互:诊断端向 ECU 请求数字证书,ECU 返回自身的设备证书
- 证书校验:诊断端用预置的 CA 根证书,验证 ECU 证书的签名、有效期、颁发机构,确认证书合法有效
- 验证确认 :诊断端发送
29 01 00提交验证结果,ECU 确认后单向认证完成
这一步是远程连接的第一道防线:如果 ECU 证书是伪造的、过期的,直接终止连接,避免后续交互。
2. 双向认证:所有权证明(对应 29 03)
目标:ECU 反过来验证诊断端 / 云端的身份,确认 "对方是合法的控制端,不是黑客",是高安全操作的前置要求。 完整交互流程:
- ECU 生成挑战:诊断端发送所有权证明请求后,ECU 生成一段随机挑战数据,返回给诊断端
- 私钥签名:诊断端用自己的私钥对挑战数据签名,将签名结果发回 ECU
- 公钥验签:ECU 用合法证书的公钥验证签名,确认对方持有对应私钥,身份合法
- 认证通过: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级安全访问 → 后续刷写全流程
四、核心安全特性
-
证书可溯源,抗伪造 所有证书由车企 CA 根证书签发,每台 ECU 的证书唯一,可追溯到具体生产批次;无法通过篡改、重放伪造合法身份,从机制上杜绝了假冒 ECU、假冒诊断端的风险。
-
临时生效,自动失效 认证状态仅在当前诊断会话内有效,出现以下情况自动清空:
- 诊断会话超时、切换回默认会话
- ECU 复位、下电上电
- 主动发送 29 00 注销认证 和 27 安全访问一样,不会永久保持高权限状态。
-
算法可升级,适配安全要求 支持国密 SM2、RSA、ECC 等多种非对称加密算法,可根据安全等级要求升级;而 27 服务的对称算法通常是固定的,升级难度大。
-
符合法规强制要求 是 UNECE R155 汽车网络安全法规、ISO/SAE 21434 网络安全工程标准的强制要求,所有具备远程升级、远程诊断功能的车辆必须具备证书级身份认证能力。
五、CANoe 实操与 Trace 观测
1. 图形化操作
加载带认证配置的 CDD/ODX 数据库后,Diagnostic Console 中会有独立的 Authentication 模块:
- 导入合法的客户端证书和私钥,一键执行双向认证
- 支持查看 ECU 证书详情、验证结果、当前认证状态
- 产线调试场景可直接调用预设的认证脚本
- 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 |
超过尝试次数 | 连续认证失败,触发锁定冷却 |
工程高频踩坑点
- 证书时间不匹配:ECU 时钟异常,导致证书有效期校验失败
- 根证书不匹配:诊断端导入的 CA 根证书和 ECU 的证书签发机构不一致
- 私钥不对应:用了错误的私钥做签名,验签失败
- 忽略证书链:只校验了 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 | 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 保活:
- 长时异步例程等待期:比如 Flash 擦除、固件校验,可能需要几秒到十几秒,中间没有业务诊断请求,不发 3E 就会会话超时,擦除直接中断。
- 固件传输间隙: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 00 → 7E 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 | 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 会:
- 停止所有故障检测逻辑,不再判断故障条件是否满足
- 不更新任何 DTC 的状态位(不会从待确认变成已确认)
- 不存储新故障,不生成冻结帧、扩展数据
- 不会触发表盘故障灯点亮
不会做的事:
- 不会删除已有的历史故障码
- 不会熄灭已经点亮的故障灯
- 不会影响 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 |
条件不满足 | 特定工况下不允许关闭,如车辆高速行驶中 |
工程高频踩坑点
- 关了忘记开:虽然会话超时会自动恢复,但正常流程必须主动恢复,避免 ECU 在一段时间内漏记真实故障。
- 顺序搞反:先关通信再关故障记录,导致关通信的瞬间已经触发了通信故障,等于白关。
- 以为能清故障:误以为关故障记录会清除已有故障,实际没有任何清除效果,清故障必须用 14 服务。
- 群发不开 SPR :功能寻址发
85 02不带 SPR 位,十几个 ECU 同时回复,总线瞬间拥堵。 - 休眠场景失效: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 列表、状态码、冻结帧数据等 |
- 文档对应 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 // 清除成功
- 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 休眠、未完成初始化时无法读取 |
- 14 服务高频错误
| NRC 码 | 含义 | 典型原因 |
|---|---|---|
0x33 |
安全访问拒绝 | 最常见,未解锁安全访问就清故障 |
0x7E |
当前会话不支持 | 默认会话下执行清除操作 |
0x22 |
条件不满足 | 车辆行驶中、高压状态下禁止清故障 |
0x31 |
请求超出范围 | DTC 掩码格式错误、指定了不存在的故障组 |
工程高频踩坑点
- 清完不验证:发了 14 就以为清干净了,实际可能因为条件不满足清除失败,必须用 19 回读确认
- 状态掩码用错:想读历史故障却用了 0x01 掩码,导致漏读已确认的历史故障
- 功能寻址清故障不开 SPR:全车 ECU 同时回复 54,造成总线瞬间拥堵
- 混淆 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 | SID = 0x63 |
固定响应标识符(原 SID + 0x40) |
| 2~N | 原始数据 | 从指定地址读取的连续内存原始数据,长度和请求一致,大端顺序 |
3. 真实报文示例(32 位 MCU 场景)
需求 :从地址 0x0800 1000 开始,读取 4 字节数据。
- 请求:
23 44 08 00 10 0023:服务 ID44:4 字节地址,4 字节数据08 00 10 00:起始地址 0x08001000
- 响应:
63 12 34 56 78- 地址 0x08001000 处的数据为
0x12345678
- 地址 0x08001000 处的数据为
三、核心特性:高权限、高灵活、高风险
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。
- 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 处于非正常运行状态,无法读取内存 |
工程高频踩坑点
- 地址错误:软件版本更新后地址变了,还用旧地址读取,得到无意义数据,误以为是 ECU 问题
- 字节序搞反:读取多字节数据时,大小端解析错误,导致数值完全不对
- 越界读取:读取长度超过合法内存范围,触发 ECU 内存异常,甚至复位
- 量产环境调用:量产车默认关闭 23 服务,反复调用只会报错,浪费排查时间
- 格式字节算错:高 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 | SID = 0x7D |
固定响应标识符(原 SID + 0x40) |
| 2 | 格式字节 | 回应当前写入的地址与长度格式 |
| 3~N | 起始内存地址 | 回应当前写入的目标地址,用于确认写入位置正确 |
3. 真实报文示例(32 位 MCU 场景)
需求 :向 RAM 地址 0x2000 1000 写入 4 字节数据 0x12345678
- 请求:
3D 44 20 00 10 00 12 34 56 783D:服务 ID44:4 字节地址,4 字节数据20 00 10 00:目标地址 0x2000100012 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 写入必须严格遵循「先擦除 → 再写入」的两步流程:
- 擦除阶段 :调用 31 服务的
0xFF00擦除例程,把目标 Flash 扇区全部擦成 0xFF(全 1) - 写入阶段:用 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 服务更底层、效率更高。
六、结合你这份文档的落地说明
- 文档未标注 3D 服务,不代表 ECU 不支持,而是它属于开发 / 产线内部调试接口,不会出现在量产售后的公开诊断规范中。
- 权限对应文档中的最高等级:必须在
10 40OTA 会话下,解锁27 41最高级安全访问后才能调用,普通扩展会话直接拒绝。 - 和 23 服务严格配套:开发调试时永远是「23 读出来 → 修改数值 → 3D 写进去 → 23 回读验证」的标准流程。
- 配套 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
- 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 硬件错误、校验失败、写入超时 |
工程高频踩坑点
- 写 Flash 不先擦除:最经典的低级错误,Flash 硬件特性决定了必须先擦后写,否则数据必然错误
- 混淆 RAM 和 Flash:往 RAM 里写了参数,以为复位后还能保留,结果下电就丢了
- 地址偏移错误:软件版本更新后地址变了,还用旧地址写入,导致覆盖了其他变量,引发莫名奇妙的异常
- 大小端搞反:多字节数据写入时字节序错误,写入的值和预期完全不同
- 越界写入:写入长度超过合法范围,覆盖了后面的程序 / 数据,导致 ECU 跑飞、死机
- 量产环境调用:量产车默认关闭 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(请求下载)
- 服务 ID :
0x34,肯定响应 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(请求上传)
- 服务 ID :
0x35,肯定响应 SID:0x75 - 核心作用:和 34 服务完全镜像,方向反过来 ------ 诊断仪请求从 ECU 指定地址读取大数据块,用于读取固件、导出日志、回读标定参数,工程上使用频率远低于 34。
帧格式、协商逻辑和 34 完全一致,只是数据方向从 "下发" 变成 "上传",ECU 返回最大块长后,通过 36 服务逐包回传数据。
3. 0x36 服务:TransferData(数据传输)
- 服务 ID :
0x36,肯定响应 SID:0x76 - 核心作用:真正的 "搬砖" 服务,按协商好的最大块长,逐包传输数据,带块序号保证顺序,是整个传输过程中发送量最大的服务。
请求帧结构
| 字节序号 | 字段 | 详细说明 |
|---|---|---|
| 1 | SID = 0x36 |
固定服务标识符 |
| 2 | 块序号计数器 | blockSequenceCounter,从0x01开始递增,0xFF之后回到0x01循环 |
| 3~N | 数据段 | 当前包的有效数据,长度≤34 协商的最大块长 |
肯定响应结构
| 字节序号 | 字段 | 说明 |
|---|---|---|
| 1 | SID = 0x76 |
固定响应标识符 |
| 2 | 块序号计数器 | 回应当前已正确接收的包序号 |
块序号核心规则
- 首包序号必须是
0x01,不能从 0 开始; - 每发一包序号加 1,到
0xFF后下一包回到0x01; - ECU 会严格校验序号,跳号、错序、重复包都会直接报错,要求重传对应序号的包;
- 这是传输可靠性的核心保障,防止丢包、乱序导致固件损坏。
典型报文示例(续上例,第一包数据)
- 请求:
36 01 00 01 02 03 ... 7F(128 字节数据) - 响应:
76 01(确认第 1 包接收成功)
4. 0x37 服务:RequestTransferExit(结束传输)
- 服务 ID :
0x37,肯定响应 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 传输完成,返回校验结果
传输过程中的两个关键配套动作
-
3E 服务后台保活 刷写传输通常需要几秒到几分钟,全程必须持续周期发送
3E 80保活,一旦会话超时,传输直接中断,前功尽弃。 -
28 服务静默总线 传输前已经通过 28 服务关闭了所有业务报文,总线 100% 带宽留给 36 数据包,最大化传输效率,避免业务帧抢占导致超时。
四、结合你这份文档的落地说明
- 文档中这 4 个服务是 OTA 刷写的核心传输层,和 31 服务(擦除、校验)配合,完成完整的固件烧录流程。
- 权限对应最高等级:必须在
10 40OTA 会话下,解锁27 41最高级安全访问后才能调用,普通扩展会话直接拒绝。 - 标准流程严格遵循:先 31 擦除 Flash → 34 请求下载 → 36 循环传数据 → 37 结束传输 → 31 固件校验,是行业通用的标准刷写时序。
- 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 xx→76 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 写入失败 |
工程最高频踩坑点
- 最大块长搞错:不看 34 返回的最大长度,自己硬编码包长,导致 ECU 缓冲区溢出,传输中断
- 块序号错误:手动写脚本时序号算错,首包从 0 开始、跳号、循环逻辑错误
- 忘了保活:传输时间长,中途会话超时,刷写到一半断了
- 没擦 Flash 就传数据:跳过 31 擦除步骤,直接发 34 下载,写入必然失败
- 传完不收尾:最后一包发完就以为结束了,没发 37,ECU 不固化数据
- 总线负载太高:没关业务报文,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 → 擦除 → 写固件 → 复位。但这种模式有两个致命问题:
- 刷写期间 ECU 完全瘫痪:进了 Bootloader 就停掉所有业务功能,刷写多久车就失效多久,OTA 体验极差。
- 变砖风险极高:刷写中途断电、中断,ECU 就彻底启动不了,只能返厂救砖。
新势力的域控 OTA,用的是 A/B 双分区架构,把「固件下载校验」和「真正烧录生效」拆开了:
- 90% 的工作在应用层完成:后台静默把新固件下载到备用分区、做完整校验、准备好所有环境,这时候 ECU 正常运行,所有功能都能用,用户完全无感知。
- 只有最后 10% 的烧录动作在 Bootloader 完成:确认固件没问题了,才重启进 Bootloader,把备用分区设为启动分区,几秒就完成,变砖风险几乎为零。
10 40 OTA 专属会话,就是应用层用来做「刷写准备工作」的高权限会话 ;而10 02 只在跳转 Bootloader 后的真正烧录阶段才会出现。
二、完整两阶段时序:10 40 和 10 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编程会话:
- 产线首次烧录 :ECU 刚生产出来,里面没有应用程序,只能直接进 Bootloader 用
10 02烧录固件。 - 售后救砖 :应用层损坏、ECU 无法正常启动,只能通过 Bootloader 模式强制刷写,用
10 02。 - 低端 ECU 刷写 :车身小模块、传感器这类没有双分区的简单 ECU,刷写直接进
10 02,没有应用层 OTA 会话。
而正常的整车远程 OTA,为了可靠性和用户体验,都是应用层10 40做准备,最后短暂进 Bootloader 用10 02收尾。
物理寻址与功能寻址:本质是「一对一私聊」和「一对多群发」
这是 CAN 总线上 UDS 诊断的两种基础通信方式,决定了诊断请求发给谁、谁会回复,所有诊断服务都要基于其中一种寻址方式发送。
1. 核心定义
- 物理寻址(Physical Addressing) :点对点专属通信,一个请求只发给一个指定 ECU,只有目标 ECU 会处理并响应,相当于 "私聊"。
- 功能寻址(Functional Addressing) :一对多广播通信,一个请求发给总线上所有 / 一组 ECU,所有符合条件的 ECU 都会接收并执行指令,相当于 "群公告"。
- 核心区别对照表
| 对比维度 | 物理寻址 | 功能寻址 |
|---|---|---|
| 通信模式 | 一对一单播 | 一对多广播 |
| 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 位。