4. 诊断通信基础

各位工程师,今天我们聊一个诊断开发中绕不开的话题——0x19 服务。说白了,它就是读取故障码的“总开关”。你想想看,ECU 出了毛病,第一件事肯定是问它:“你哪里不舒服?”0x19 就是干这个的。

我个人习惯把 0x19 称为“诊断界的瑞士军刀”。它功能多,子功能有 20 多个,但实际项目中常用的就那么几个。今天咱们重点讲三个:DTC 格式、读取当前故障码(0x02)、读取历史故障码(0x0A),最后再手撕一把快照数据。

核心要点:0x19 服务是所有诊断会话的“入口”,掌握它的子功能选择逻辑,是读懂 ECU 故障状态的第一步。

0x19 读取故障码服务知识体系 0x19 读取故障码服务知识体系 0x19 服务 DTC 格式 子功能 0x02 子功能 0x0A 3字节结构 P/B/C/S 组 当前激活状态 实时故障列表 历史故障记录 老化/确认状态 DTC 快照数据解析 说明:0x19 服务通过不同子功能区分“当前故障”与“历史故障”,快照数据是故障发生时的“黑匣子”

4.1 DTC 格式:3 字节里藏着什么?

DTC,全称 Diagnostic Trouble Code。别被名字吓到,它就是个 3 字节的编码。嗯,这里要注意——这 3 个字节不是随便填的,它有严格的位域定义。

字节位置 名称 位域说明 示例(P0101)
Byte 0(高字节) 故障组 + 系统类型 Bit 7-6: 组别(P/B/C/S);Bit 5-0: 系统类型 0x01 → P 组(动力系统)
Byte 1(中字节) 子系统 + 故障类型 Bit 7-4: 子系统编号;Bit 3-0: 故障类型 0x01 → 燃油/空气系统
Byte 2(低字节) 具体故障编号 0x01-0xFF 具体故障码 0x01 → 进气压力/流量故障

举个例子,P0101 这个码:
- 组别是 P(动力系统),对应 Byte0 高两位为 00。
- 系统类型是 01(燃油/空气系统)。
- 具体故障是 01(进气压力/流量故障)。
所以 ECU 里存的就是 0x01 0x01 0x01 三个字节。

我的经验: 有一次客户反馈某个 DTC 读出来是 0x81 0x12 0x34,我一看 Byte0 高两位是 10,就知道这是 B 组(车身系统)的故障。如果你不熟悉位域,很容易把组别搞混。建议手边常备一张 DTC 组别对照表。

4.2 读取当前故障码(子功能 0x02)

子功能 0x02,顾名思义,就是问 ECU:“你现在有什么故障?”ECU 会返回当前所有激活的故障码。

什么叫“激活”?就是故障条件当前满足,ECU 正在报故障。举个例子,氧传感器信号持续异常,ECU 检测到后立即置位该 DTC 的状态位,这时候用 0x02 就能读到它。

请求格式很简单:

请求:02 19 02 [DTCStatusMask] [DTCGroup] [DTCSeverity]
响应:02 59 02 [DTCStatusAvailabilityMask] [DTCAndStatusRecord]...

这里有个关键参数——DTCStatusMask。它是一个字节,用来过滤你要读哪些状态的 DTC。比如你想只读“当前激活且未确认”的故障,就把 mask 设为 0x01。

Bit 位 状态含义 说明
Bit 0 testFailed 当前测试失败(激活)
Bit 1 testFailedThisOperationCycle 本操作循环内测试失败
Bit 2 pendingDTC 待确认故障
Bit 3 confirmedDTC 已确认故障
Bit 4 testNotCompletedSinceLastClear 上次清除后未完成测试
Bit 5 testFailedSinceLastClear 上次清除后测试失败
Bit 6 testNotCompletedThisOperationCycle 本操作循环内未完成测试
Bit 7 warningIndicatorRequested 请求点亮警告灯

注意: 0x02 只返回当前激活的故障。如果一个故障曾经出现过但现在已经恢复,用 0x02 是读不到的。这时候你需要用 0x0A。

4.3 读取历史故障码(子功能 0x0A)

子功能 0x0A 用来读取所有已存储的故障码,包括当前激活的和历史记录的。说白了,它就像 ECU 的“病历本”,记录着所有曾经发生过的故障。

我建议你在做下线检测(EOL)时,先用 0x0A 扫一遍,看看 ECU 有没有“前科”。如果发现某个 DTC 的状态位显示“testFailedSinceLastClear”为 1,说明这个故障在产线上出现过,需要排查。

请求格式和 0x02 几乎一样,只是子功能号不同:

请求:02 19 0A [DTCStatusMask] [DTCGroup] [DTCSeverity]
响应:02 59 0A [DTCStatusAvailabilityMask] [DTCAndStatusRecord]...

这里有个坑——历史故障码的状态位可能已经变化。比如一个故障曾经激活过,但后来恢复了,它的 testFailed 位会变成 0,但 confirmedDTC 位可能还是 1。我曾经在项目里遇到一个情况:客户说某个故障灯偶尔亮,但用 0x02 读不到任何故障。我改用 0x0A 一读,发现一个历史故障的 confirmedDTC 位是 1,但 testFailed 是 0。这说明故障间歇性出现,ECU 已经确认了但当前不激活。后来我们通过快照数据找到了根因。

实战建议: 排查间歇性故障时,优先用 0x0A 而不是 0x02。0x02 只能告诉你“现在有没有病”,0x0A 能告诉你“得过什么病”。

4.4 实战:解析 DTC 快照数据

快照数据,英文叫 Snapshot Data。它是 ECU 在故障发生瞬间“拍下”的一张照片,记录了当时的各种参数值,比如发动机转速、车速、水温、电压等。

为什么需要快照?你想想看,一个故障可能只持续了几毫秒,等你用诊断仪去读的时候,故障已经消失了。没有快照,你根本不知道故障发生时发生了什么。

快照数据通过 0x19 子功能 0x04(读取快照数据)来获取。请求格式如下:

请求:03 19 04 [DTC高字节] [DTC中字节] [DTC低字节] [SnapshotRecordNumber]
响应:03 59 04 [DTC高字节] [DTC中字节] [DTC低字节] [SnapshotRecordNumber] [数据字节]...

举个例子,假设我们要读取 P0101 的快照数据:

请求:02 19 04 01 01 01 01
响应:02 59 04 01 01 01 01 0C 10 20 30 40 50 60

响应里的 0C 表示后面有 12 个字节的快照数据。这 12 个字节怎么解析?需要查 ECU 的 DTC 快照数据定义表。通常每个 DTC 会关联一组固定的 DID(数据标识符),比如:

字节偏移 关联 DID 参数名称 示例值
0-1 0xF190 发动机转速 0x10 0x20 → 4130 rpm
2-3 0xF191 车速 0x30 0x40 → 123.52 km/h
4-5 0xF192 冷却液温度 0x50 0x60 → 90.5 °C

我的习惯: 拿到快照数据后,我通常会先看发动机转速和车速。如果转速很高但车速很低,可能是变速箱或离合器的问题。如果转速和车速都正常但水温异常,那可能是冷却系统的问题。快照数据就像侦探的线索,串联起来就能还原故障现场。

最后,给你一个解析快照数据的伪代码思路:

1. 发送 0x19 0x04 请求,指定 DTC 和快照记录号
2. 接收响应,提取快照数据字节
3. 根据 ECU 的 DID 映射表,将字节拆分为具体参数
4. 将原始值转换为物理值(如转速 = 原始值 * 0.125 rpm)
5. 输出解析结果

避坑指南: 我曾经遇到一个 ECU,它的快照数据字节顺序和文档写的不一致。文档说 Byte0-1 是转速,实际 Byte0-1 是车速。后来发现是 ECU 固件版本和文档版本不匹配。所以,拿到快照数据后,一定要先和 ECU 供应商确认 DID 映射表,不要盲目相信文档。

好了,关于 0x19 服务的 DTC 格式、当前故障码、历史故障码和快照数据,今天就聊到这里。这些内容在实际项目中几乎天天用到,希望你能动手试试,用诊断仪连上 ECU 亲自读一读,感受一下数据在手里解析出来的成就感。