一、服务概述
在UDS(Unified Diagnostic Services)协议中,诊断故障码(DTC)的管理是核心功能之一。本文介绍两个关键服务:
服务
SID
功能描述
ReadDTCInformation
0x19
从ECU读取DTC及其详细信息(状态、快照、扩展记录)
ClearDiagnosticInformation
0x14
清除ECU中存储的DTC及相关诊断信息
这两个服务互为逆过程,共同完成故障信息的读取与清除功能。
二、DTC数据结构详解 2.1 三字节DTC组成
UDS中的DTC采用3字节存储,结构如下:
+------------------+------------------+------------------+
| Byte 1 | Byte 2 | Byte 3 |
| (Root DTC高) | (Root DTC低) | FTB |
+------------------+------------------+------------------+
2.2 Root DTC解析(Byte1-2)Byte1的位域含义:
Bit位
含义
编码规则
bit7-6
故障所属系统
00=P(动力), 01=C(底盘), 10=B(车身), 11=U(网络)
bit5-4
故障码类型
00=ISO/SAE标准, 01=制造商自定义, 10=ISO/SAE保留
bit3-0
故障子系统
见详细定义表
示例解析:DTC = 0x0123
Byte1: 0x01 = 0000 0001
├─ bit7-6 = 00 → P (动力系统)
├─ bit5-4 = 00 → 标准故障码
└─ bit3-0 = 0001 → 燃油或空气系统
Byte2: 0x23 → 节气门/踏板位置传感器电路
结果: P0123 - 节气门/踏板位置传感器电路A高输入
2.3 FTB(Failure Type Byte)解析(Byte3)FTB用于精确描述故障的具体模式:
FTB值
故障类型
FTB值
故障类型
0x01
信号偏低
0x11
更新错误
0x02
信号偏高
0x12
编码错误
0x03
信号不稳定
0x13
校准错误
0x04
信号中断
0x17
电压低于阈值
0x05
信号短路
0x18
电压高于阈值
0x06
接地短路
0x21
信号卡滞
完整示例:DTC = 0x012317
三、DTC状态掩码(DTCStatusMask)三字节DTC: 0x01 0x23 0x17
├─ Byte1(0x01): P(动力系统) + 标准故障码 + 燃油/空气系统
├─ Byte2(0x23): 节气门/踏板位置传感器电路
└─ Byte3(0x17): 电压低于阈值完整解读:P0123 - 节气门/踏板位置传感器电路A输入高,故障模式为电压低于阈值
状态掩码用于筛选特定状态的DTC,占用1字节,每个bit代表一种故障状态:
+-------+-------+-------+-------+-------+-------+-------+-------+
| Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 |
| test | test | test | test | conf- | pending| mat- | test |
|Failed |Failed |Failed |Failed |irmed |DTC |uration|Failed |
|ThisOp |ThisOp |ThisOp |ThisOp |DTC | | | |
|Cycle |Cycle |Cycle |Cycle | | | | |
+-------+-------+-------+-------+-------+-------+-------+-------+
常用掩码组合掩码值
含义
应用场景
0x09 (bit0+bit3)
当前故障(确认且当前存在)
读取当前活动故障
0x08 (bit3)
历史故障(已确认的故障)
读取历史故障记录
0xFF
所有故障
读取全部故障
四、0x19服务(读取DTC信息) 4.1 服务格式概述
请求: 0x19 + SubFunction + [参数]
响应: 0x59 + SubFunction + [响应数据]
4.2 子功能01H - 获取DTC数量功能:根据掩码统计符合条件的DTC数量
请求格式: 19 01 [DTCStatusMask]
响应格式: 59 01 [DTCStatusAvailabilityMask] [DTCFormatIdentifier] [DTCCount(高字节)] [DTCCount(低字节)]
字段说明:
DTCStatusAvailabilityMask:ECU支持的DTC状态位
DTCFormatIdentifier:DTC格式标识符(0x00=SAE_J2012, 0x01=ISO_14229-1, 0x02=SAE_J1939)
DTCCount:符合条件的DTC数量(2字节)
示例:
请求: 19 01 09 // 统计当前活动故障数量
响应: 59 01 09 01 00 03 // 3个当前故障
4.3 子功能02H - 读取DTC列表及状态功能:读取符合条件的完整DTC列表及其状态
请求格式: 19 02 [DTCStatusMask]
响应格式: 59 02 [DTCStatusAvailabilityMask] [DTC1_H] [DTC1_M] [DTC1_L] [Status1] [DTC2...] ...
示例:
请求: 19 02 09 // 读取所有当前故障
响应: 59 02 09
01 23 45 09 // DTC1=0x012345, Status=0x09
01 23 46 09 // DTC2=0x012346, Status=0x09
4.4 子功能03H/04H - 读取快照数据快照数据(冻结帧)是故障发生时刻ECU记录的环境数据。
步骤1 - 03H:获取快照记录编号(SRN)
请求: 19 03 [DTC_H] [DTC_M] [DTC_L]
响应: 59 03 [DTC_H] [DTC_M] [DTC_L] [SRN1] [SRN2] ...
步骤2 - 04H:读取快照数据
请求: 19 04 [DTC_H] [DTC_M] [DTC_L] [SRN]
响应: 59 04 [DTC_H] [DTC_M] [DTC_L] [Status] [SRN] [DID1] [Data1] [DID2] [Data2]...
示例:
4.5 子功能0AH - 读取所有支持的DTC// 步骤1:获取0x012345的快照记录编号
请求: 19 03 01 23 45
响应: 59 03 01 23 45 01 02 // SRN=0x01, 0x02// 步骤2:读取SRN=0x01的快照数据
请求: 19 04 01 23 45 01
响应: 59 04 01 23 45 09 01 0x1000 0xABCD 0x1001 0x00C8
// 解读:状态0x09, SRN=0x01, DID=0x1000数据=0xABCD, DID=0x1001数据=0x00C8(车速200km/h)
请求格式: 19 0A
响应格式: 59 0A [DTCStatusAvailabilityMask] [DTC1] [Status1] [DTC2] [Status2]...
五、0x14服务(清除DTC) 5.1 服务格式请求格式: 14 [FF] [FF] [FF] // 清除所有DTC
响应格式: 54 // 肯定响应
5.2 清除范围清除0x14服务时,ECU应清除:
所有已存储的DTC
所有快照数据(冻结帧)
所有扩展记录数据
其他与DTC相关的诊断信息
NRC
含义
触发条件
0x13
请求报文长度错误
报文长度与协议不匹配
0x31
请求超出范围
请求了非0xFFFFFF之外的DTC范围
0x33
安全访问未解锁
ECU需要解锁才能清除DTC
六、C++代码实现 6.1 DTC数据结构定义
6.2 DTC管理器类实现#include
#include
#include
#include
#include
#include
// DTC状态位枚举
enum class DTCStatus : uint8_t {
TestFailed = 0x01, // bit0: 最近测试失败
TestFailedThisOperationCycle = 0x02, // bit1: 本次操作周期测试失败
PendingDTC = 0x04, // bit2: 待确认DTC
ConfirmedDTC = 0x08, // bit3: 已确认DTC
TestNotCompletedSinceLastClear = 0x10, // bit4: 自上次清除后测试未完成
TestFailedSinceLastClear = 0x20, // bit5: 自上次清除后测试失败
TestNotCompletedThisOperationCycle = 0x40, // bit6: 本次操作周期测试未完成
WarningIndicatorRequested = 0x80 // bit7: 警告指示请求
};
inline DTCStatus operator|(DTCStatus a, DTCStatus b) {
return static_cast ( static_cast(a) | static_cast(b));
}
inline bool operator&(DTCStatus a, DTCStatus b) {
return (static_cast(a) & static_cast(b)) != 0;
}
// DTC结构体
struct DTC {
uint8_t byte1; // 高字节(系统+类型+子系统)
uint8_t byte2; // 中间字节(具体故障位置)
uint8_t ftb; // 故障类型字节
uint8_t status; // DTC状态
DTC() : byte1(0), byte2(0), ftb(0), status(0) {}
DTC(uint8_t b1, uint8_t b2, uint8_t f, uint8_t s = 0)
: byte1(b1), byte2(b2), ftb(f), status(s) {}
// 获取完整DTC码(3字节)
uint32_t getCode() const {
return (static_cast(byte1) << 16) |
(static_cast(byte2) << 8) |
static_cast(ftb);
}
// 获取Root DTC(前2字节)
uint16_t getRootDTC() const {
return (static_cast(byte1) << 8) | static_cast(byte2);
}
// 解析故障系统(P/C/B/U)
char getSystem() const {
uint8_t sys = (byte1 >> 6) & 0x03;
switch(sys) {
case 0: return 'P'; // Powertrain
case 1: return 'C'; // Chassis
case 2: return 'B'; // Body
case 3: return 'U'; // Network
default: return '?';
}
}
// 是否为标准故障码
bool isStandard() const {
return ((byte1 >> 4) & 0x03) == 0;
}
// 获取故障子系统
uint8_t getSubsystem() const {
return byte1 & 0x0F;
}
// 获取故障模式描述
std::string getFailureMode() const {
static const std::map modes = {
{0x01, "信号偏低"},
{0x02, "信号偏高"},
{0x03, "信号不稳定"},
{0x04, "信号中断"},
{0x05, "信号短路"},
{0x06, "接地短路"},
{0x11, "更新错误"},
{0x12, "编码错误"},
{0x13, "校准错误"},
{0x17, "电压低于阈值"},
{0x18, "电压高于阈值"},
{0x21, "信号卡滞"}
};
auto it = modes.find(ftb);
return it != modes.end() ? it->second : "未知故障模式";
}
std::string toString() const {
std::stringstream ss;
ss << getSystem() << std::hex << std::setw(3) << std::setfill('0')
<< (getRootDTC() & 0xFFF) << " - " << getFailureMode()
<< " [Status: 0x" << std::hex << static_cast(status) << "]";
return ss.str();
}
};
// 快照数据项
struct SnapshotData {
uint16_t did; // 数据标识符
std::vector data; // 数据值
};// 冻结帧数据
struct FreezeFrame {
uint8_t srn; // 快照记录编号
uint8_t dtcStatus; // DTC状态
std::map> snapshotData; // DID->数据
};
class DTCEcuSimulator {
private:
std::vector
storedDTCs;
// 已存储的DTC列表
std::map> snapshotRecords; // DTC码->SRN列表
std::map> freezeFrames; // DTC->SRN->冻结帧
bool securityAccessGranted; // 安全访问状态
public:
DTCEcuSimulator() : securityAccessGranted(false) {}
// 获取ECU支持的DTC状态位掩码
uint8_t getSupportedStatusMask() const {
return 0xFF; // 支持所有状态位
}
// 检查DTC是否匹配给定的状态掩码
bool isDTCStatusMatch(uint8_t dtcStatus, uint8_t statusMask) const {
return (dtcStatus & statusMask) != 0;
}
/**
* 0x19 01 - 获取DTC数量
* @param statusMask 状态掩码
* @return 响应数据
*/
std::vector handleReadDTCCount(uint8_t statusMask) {
std::vector response;
response.push_back(0x59); // 肯定响应SID
response.push_back(0x01); // 子功能
// DTC状态可用性掩码
response.push_back(getSupportedStatusMask());
// DTC格式标识符 (ISO 14229-1格式)
response.push_back(0x01);
// 统计符合条件的DTC数量
uint16_t count = 0;
for (const auto& dtc : storedDTCs) {
if (isDTCStatusMatch(dtc.status, statusMask)) {
count++;
}
}
response.push_back(static_cast((count >> 8) & 0xFF));
response.push_back(static_cast(count & 0xFF));
return response;
}
/**
* 0x19 02 - 读取DTC列表
* @param statusMask 状态掩码
* @return 响应数据
*/
std::vector handleReadDTCList(uint8_t statusMask) {
std::vector response;
response.push_back(0x59);
response.push_back(0x02);
response.push_back(getSupportedStatusMask());
for (const auto& dtc : storedDTCs) {
if (isDTCStatusMatch(dtc.status, statusMask)) {
response.push_back(dtc.byte1);
response.push_back(dtc.byte2);
response.push_back(dtc.ftb);
response.push_back(dtc.status);
}
}
return response;
}
/**
* 0x19 03 - 获取快照记录编号
* @param dtcCode 三字节DTC码
* @return 响应或否定响应码
*/
std::vector handleGetSnapshotRecordNumbers(uint32_t dtcCode) {
std::vector response;
auto it = snapshotRecords.find(dtcCode);
if (it == snapshotRecords.end()) {
// DTC不存在,返回NRC
response.push_back(0x7F);
response.push_back(0x19);
response.push_back(0x31); // 请求超出范围
return response;
}
response.push_back(0x59);
response.push_back(0x03);
// 输出DTC(3字节)
response.push_back(static_cast((dtcCode >> 16) & 0xFF));
response.push_back(static_cast((dtcCode >> 8) & 0xFF));
response.push_back(static_cast(dtcCode & 0xFF));
// 返回所有SRN
for (uint8_t srn : it->second) {
response.push_back(srn);
}
return response;
}
/**
* 0x19 04 - 读取快照数据
* @param dtcCode DTC码
* @param srn 快照记录编号
* @return 响应数据
*/
std::vector handleReadSnapshotData(uint32_t dtcCode, uint8_t srn) {
std::vector response;
// 检查DTC是否存在
auto dtcIt = freezeFrames.find(dtcCode);
if (dtcIt == freezeFrames.end()) {
response.push_back(0x7F);
response.push_back(0x19);
response.push_back(0x31);
return response;
}
// 检查SRN是否存在
auto srnIt = dtcIt->second.find(srn);
if (srnIt == dtcIt->second.end()) {
response.push_back(0x7F);
response.push_back(0x19);
response.push_back(0x31);
return response;
}
const FreezeFrame& ff = srnIt->second;
response.push_back(0x59);
response.push_back(0x04);
// 输出DTC
response.push_back(static_cast((dtcCode >> 16) & 0xFF));
response.push_back(static_cast((dtcCode >> 8) & 0xFF));
response.push_back(static_cast(dtcCode & 0xFF));
// 输出状态和SRN
response.push_back(ff.dtcStatus);
response.push_back(srn);
// 输出快照数据(DID + 数据)
for (const auto& [did, data] : ff.snapshotData) {
response.push_back(static_cast((did >> 8) & 0xFF));
response.push_back(static_cast(did & 0xFF));
response.insert(response.end(), data.begin(), data.end());
}
return response;
}
/**
* 0x19 0A - 读取所有支持的DTC
* @return 响应数据
*/
std::vector handleReadSupportedDTCs() {
std::vector response;
response.push_back(0x59);
response.push_back(0x0A);
response.push_back(getSupportedStatusMask());
// 这里返回所有可能的DTC(包括未发生的)
// 实际应用中会从DTC数据库读取
for (const auto& dtc : storedDTCs) {
response.push_back(dtc.byte1);
response.push_back(dtc.byte2);
response.push_back(dtc.ftb);
response.push_back(dtc.status);
}
return response;
}
/**
* 0x14 - 清除诊断信息
* @param data 请求数据(应包含0xFF FF FF表示清除所有)
* @return 响应或否定响应码
*/
std::vector handleClearDiagnosticInfo(const std::vector& data) {
std::vector response;
// 检查安全访问
if (!securityAccessGranted) {
response.push_back(0x7F);
response.push_back(0x14);
response.push_back(0x33); // 需要安全访问
return response;
}
// 检查请求数据长度
if (data.size() != 3 || data[0] != 0xFF || data[1] != 0xFF || data[2] != 0xFF) {
response.push_back(0x7F);
response.push_back(0x14);
response.push_back(0x13); // 报文长度错误
return response;
}
// 清除所有诊断信息
storedDTCs.clear();
snapshotRecords.clear();
freezeFrames.clear();
// 肯定响应
response.push_back(0x54);
return response;
}
// 模拟DTC发生(用于测试)
void simulateDTC(const DTC& dtc, uint16_t engineSpeed = 0, uint16_t vehicleSpeed = 0) {
// 检查是否已存在
for (auto& existing : storedDTCs) {
if (existing.byte1 == dtc.byte1 && existing.byte2 == dtc.byte2 && existing.ftb == dtc.ftb) {
// 更新状态,添加Confirmed标志
existing.status |= static_cast(DTCStatus::ConfirmedDTC);
return;
}
}
// 存储新的DTC
DTC newDTC = dtc;
newDTC.status = static_cast(DTCStatus::ConfirmedDTC) |
static_cast(DTCStatus::TestFailed);
storedDTCs.push_back(newDTC);
uint32_t code = newDTC.getCode();
// 创建快照数据
uint8_t srn = 1;
snapshotRecords[code].push_back(srn);
FreezeFrame ff;
ff.srn = srn;
ff.dtcStatus = newDTC.status;
// 存储快照数据(DID对应参数)
if (engineSpeed > 0) {
ff.snapshotData[0x1000] = {
static_cast((engineSpeed >> 8) & 0xFF),
static_cast(engineSpeed & 0xFF)
};
}
if (vehicleSpeed > 0) {
ff.snapshotData[0x1001] = {
static_cast((vehicleSpeed >> 8) & 0xFF),
static_cast(vehicleSpeed & 0xFF)
};
}
// 添加时间戳
ff.snapshotData[0x1002] = {0x00, 0x00, 0x00, 0x01}; // 虚拟时间戳
freezeFrames[code][srn] = ff;
}
// 安全访问授权
void grantSecurityAccess() {
securityAccessGranted = true;
}
// 显示当前所有DTC
void displayDTCs() const {
std::cout << "=== 当前存储的DTC列表 ===" << std::endl;
if (storedDTCs.empty()) {
std::cout << "无存储的DTC" << std::endl;
}
for (const auto& dtc : storedDTCs) {
std::cout << "DTC: 0x" << std::hex << dtc.getCode()
<< " -> " << dtc.toString() << std::endl;
}
std::cout << "=========================" << std::endl;
}
};
6.3 主函数与测试示例int main() {
DTCEcuSimulator ecu;
std::cout << "========== UDS DTC服务测试 ==========" << std::endl;
// 测试1:模拟DTC发生
std::cout << "\n【测试1】模拟故障发生" << std::endl;
DTC dtc1(0x01, 0x23, 0x17); // P0123 - 节气门位置传感器电压低
DTC dtc2(0x01, 0x34, 0x02); // P0134 - 氧传感器信号偏高
DTC dtc3(0x02, 0x45, 0x04); // C0245 - 轮速传感器信号中断
ecu.simulateDTC(dtc1, 2500, 80); // 发动机2500rpm, 车速80km/h
ecu.simulateDTC(dtc2, 2800, 65);
ecu.simulateDTC(dtc3, 0, 45); // 轮速传感器故障时的数据
ecu.displayDTCs();
// 测试2:0x19 01 - 获取DTC数量
std::cout << "\n【测试2】0x19 01 - 统计DTC数量" << std::endl;
std::vector countResp = ecu.handleReadDTCCount(0x09); // 当前活动故障
std::cout << "请求: 19 01 09" << std::endl;
std::cout << "响应: ";
for (auto b : countResp) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)b << " ";
}
std::cout << std::endl;
// 测试3:0x19 02 - 读取DTC列表
std::cout << "\n【测试3】0x19 02 - 读取DTC列表" << std::endl;
std::vector listResp = ecu.handleReadDTCList(0x09);
std::cout << "请求: 19 02 09" << std::endl;
std::cout << "响应: ";
for (auto b : listResp) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)b << " ";
}
std::cout << std::endl;
// 测试4:0x19 03 - 获取快照记录编号
std::cout << "\n【测试4】0x19 03 - 获取快照记录编号" << std::endl;
uint32_t dtcCode = dtc1.getCode();
std::vector srnResp = ecu.handleGetSnapshotRecordNumbers(dtcCode);
std::cout << "请求: 19 03 " << std::hex << ((dtcCode >> 16) & 0xFF) << " "
<< ((dtcCode >> 8) & 0xFF) << " " << (dtcCode & 0xFF) << std::endl;
std::cout << "响应: ";
for (auto b : srnResp) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)b << " ";
}
std::cout << std::endl;
// 测试5:0x19 04 - 读取快照数据
std::cout << "\n【测试5】0x19 04 - 读取快照数据" << std::endl;
std::vector snapResp = ecu.handleReadSnapshotData(dtcCode, 0x01);
std::cout << "请求: 19 04 " << std::hex << ((dtcCode >> 16) & 0xFF) << " "
<< ((dtcCode >> 8) & 0xFF) << " " << (dtcCode & 0xFF) << " 01" << std::endl;
std::cout << "响应: ";
for (auto b : snapResp) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)b << " ";
}
std::cout << std::endl;
// 测试6:0x14 - 清除DTC(需要安全访问)
std::cout << "\n【测试6】0x14 - 清除诊断信息" << std::endl;
std::vector clearReq = {0xFF, 0xFF, 0xFF};
// 未授权时清除
std::vector clearResp1 = ecu.handleClearDiagnosticInfo(clearReq);
std::cout << "未授权清除请求响应: ";
for (auto b : clearResp1) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)b << " ";
}
std::cout << " (NRC 0x33: 需要安全访问)" << std::endl;
// 授权后清除
ecu.grantSecurityAccess();
std::vector clearResp2 = ecu.handleClearDiagnosticInfo(clearReq);
std::cout << "授权后清除请求响应: ";
for (auto b : clearResp2) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)b << " ";
}
std::cout << " (肯定响应0x54)" << std::endl;
// 验证清除结果
ecu.displayDTCs();
// 测试7:0x19 0A - 读取所有支持的DTC
std::cout << "\n【测试7】0x19 0A - 读取所有支持的DTC(清除后)" << std::endl;
std::vector supportedResp = ecu.handleReadSupportedDTCs();
std::cout << "请求: 19 0A" << std::endl;
std::cout << "响应: ";
for (auto b : supportedResp) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)b << " ";
}
std::cout << std::endl;
return 0;
}
6.4 编译与运行说明# 编译命令
g++ -std=c++11 -o uds_dtc uds_dtc.cpp# 运行
./uds_dtc
预期输出示例:
七、总结========== UDS DTC服务测试 ==========
【测试1】模拟故障发生
=== 当前存储的DTC列表 ===
DTC: 0x12317 -> P0123 - 电压低于阈值 [Status: 0x09]
DTC: 0x13402 -> P0134 - 信号偏高 [Status: 0x09]
DTC: 0x24504 -> C0245 - 信号中断 [Status: 0x09]
=========================
【测试2】0x19 01 - 统计DTC数量
请求: 19 01 09
响应: 59 01 ff 01 00 03
【测试3】0x19 02 - 读取DTC列表
请求: 19 02 09
响应: 59 02 ff 01 23 17 09 01 34 02 09 02 45 04 09
【测试4】0x19 03 - 获取快照记录编号
请求: 19 03 01 23 17
响应: 59 03 01 23 17 01
【测试5】0x19 04 - 读取快照数据
请求: 19 04 01 23 17 01
响应: 59 04 01 23 17 09 01 10 00 09 C4 10 01 00 50 10 02 00 00 00 01
【测试6】0x14 - 清除诊断信息
未授权清除请求响应: 7f 14 33 (NRC 0x33: 需要安全访问)
授权后清除请求响应: 54 (肯定响应0x54)
=== 当前存储的DTC列表 ===
无存储的DTC
=========================【测试7】0x19 0A - 读取所有支持的DTC(清除后)
请求: 19 0A
响应: 59 0a ff
本文对UDS协议中的DTC相关服务进行了详细分析:
服务
核心功能
关键子功能
0x19
读取DTC信息
01H(计数)、02H(列表)、03H/04H(快照)、0AH(所有DTC)
0x14
清除诊断信息
0xFFFFFF(清除所有)
关键技术点:
DTC结构 :3字节编码,前2字节标识故障位置,第3字节(FTB)描述故障模式
状态掩码 :8位状态机,支持精确筛选故障类型
快照机制 :故障发生时自动记录环境数据,便于故障复现与分析
安全机制 :清除操作通常需要安全访问权限
实际开发中,ECU应确保DTC存储的持久性(如EEPROM),避免掉电丢失诊断数据。
热门跟贴