1. 前言
在UDS(ISO 14229-1)诊断协议中,故障码(DTC,Diagnostic Trouble Code)不仅仅是“有故障”或“无故障”的简单标记。每个DTC都附带一个1字节的状态位(Status Byte),用于精确描述该故障在当前操作循环及历史记录中的生命周期。同时,14服务用于重置这些状态。
理解状态位的跳变逻辑,是开发高质量诊断软件(如ECU固件)或诊断工具(如Tester)的基础。
2. 故障码状态位详解 2.1 状态位定义(Bit 0 - Bit 7)
根据ISO 14229-1定义,DTC状态字节的8个位含义如下:
Bit
名称
描述
核心逻辑
0
TestFailed
当前操作循环中,最近一次测试结果为失败。
瞬时故障标志
1
TestFailedThisOperationCycle
当前操作循环中,曾经检测到故障。
本循环历史
2
PendingDTC
当前循环失败,但尚未达到确认阈值(防抖中)。
待确认故障
3
ConfirmedDTC
故障已确认(达到阈值),存储在NVM中。
固化故障
4
TestNotCompletedSinceLastClear
自上次清除后,该DTC从未被测试过。
测试未运行
5
TestFailedSinceLastClear
自上次清除后,至少一次测试结果为失败。
永久历史标志
6
TestNotCompletedThisOperationCycle
当前操作循环中,该DTC测试未完成。
本循环未测
7
WarningIndicatorRequested
请求点亮警告灯(如MIL灯)。
报警标志
2.2 常用状态位组合(十六进制值)
在实际诊断中,工具通常通过读取状态字节来判断故障类型:
状态位组合 (Bit)
Hex
含义解释
bit0=1
0x01
当前正好有故障(瞬态)
bit3=1
0x08
历史确认故障(存储在内存中)
bit3=1, bit0=1
0x09
当前存在且已确认的硬故障
bit2=1
0x04
待处理故障(防抖中,未确认)
bit5=1
0x20
上次清除后发生过故障(用于二手车检测)
3. 状态位跳变逻辑(核心难点)
状态位并不是随机变化的,而是遵循“测试-防抖-确认-老化”的严格状态机。
3.1 跳变流程图解
下图中,横坐标为操作循环(点火循环)。假设该DTC的确认阈值(Confirmed Threshold)为 2个操作循环。
操作循环 1 操作循环 2
|-----------------------| |-----------------------|
测试结果: 无 合格(Pass) 失败(Fail) 失败 无 失败
| | | | | | | |
bit0(当前):0-->0-->1-->0-->1------------>0?-->1----------->? (车企自定义)
bit1(本循环):0-->0-->1-->1-->1------------>0-->1----------->1
bit3(确认):0-->0-->0-->0-->0------------>0-->0-->1(计数2)->1
bit6(未测):1-->0-->0-->0-->0------------>1-->0----------->0
3.2 关键跳变逻辑详解初始化 :ECU上电或执行14服务后,
bit4=1,bit6=1(表示尚未测试)。**测试通过 (Passed)**:当监控器执行检测且结果为“合格”。
bit0置 0,bit4置 0,bit6置 0。
测试失败 (Failed) - 防抖阶段 :
一旦检测到失败,**
bit0立即置 1**。bit1置 1(本循环发生过失败)。bit2(Pending) 置 1。注意 :此时
bit3(Confirmed) 不一定为1,需要计数器累加。
从 Pending 到 Confirmed :
内部计数器
DTCFailureCounter累加。当
FailureCounter >= ConfirmationThreshold(如 2)时,**bit3置 1**,故障写入NVM。
**跨操作循环 (Power Cycle)**:
下电再上电(操作循环2开始):
bit6被初始化为 1(本循环还未测)。如果故障消失,
bit0变为 0,但bit3维持 1(历史故障)。bit1重置为 0(新的循环开始)。
0x14 服务用于清除ECU中一个或多个DTC的相关信息(状态位、快照、扩展数据、计数器)。
4.1 报文格式
请求 (Request):14 [GroupOfDTC]
14: SID (Service Identifier)GroupOfDTC(3字节): 指定清除范围。最常用的是FF FF FF,代表清除所有DTC。
肯定响应 (Positive Response):54 [GroupOfDTC]
54: 肯定响应SID(14 + 0x40)
否定响应 (Negative Response):7F 14 [NRC]
常见NRC:
13: 报文长度错误。22: 当前条件不满足(例如车辆速度不为0时禁止清除)。31: 请求的DTC组不支持。
ECU收到14服务后,执行流程如下:
1. 验证长度 (len == 4?) -> 否则返回 NRC 13
2. 验证 GroupOfDTC 支持性 -> 不支持返回 NRC 31
3. 检查安全条件 (例如: 是否已解锁SecurityAccess) -> 否返回 NRC 22
4. 遍历DTC列表:
- 清除状态字节 (置为特定值,通常是 0x00 或 0x40/0x80)
- 清除快照数据 (Snapshot)
- 清除扩展数据 (Extended Data)
- 重置 Confirmation Counter 和 Aging Counter
5. 返回肯定响应 0x54
5. C++代码实现示例以下是一个在ECU端模拟DTC状态管理及14服务的C++类实现。
代码输出预期#include
#include
#include
#include
#include
// 模拟DTC结构体
struct DtcInfo {
uint32_t id; // DTC编号 (如 0x123456)
uint8_t status; // 状态位
uint8_t failureCounter; // 确认计数器 (用于防抖)
bool existsInNvm; // 是否已存储在NVM中
DtcInfo(uint32_t dtcId) : id(dtcId), status(0x40), failureCounter(0), existsInNvm(false) {
// 初始状态: TestNotCompletedSinceLastClear (bit4=1)
}
};
class UdsDiagnosticManager {
private:
std::map dtcDatabase; // 存储所有DTC
uint8_t sessionMode; // 当前会话 (默认1-默认会话, 2-编程会话等)
// 模拟NVM读写 (生产环境需调用Flash驱动)
void WriteToNvm(uint32_t dtcId, uint8_t status) {
std::cout << "[NVM] 写入DTC: 0x" << std::hex << dtcId << " 状态: 0x" << (int)status << std::endl;
}
void EraseNvmForDtc(uint32_t dtcId) {
std::cout << "[NVM] 擦除DTC记录: 0x" << std::hex << dtcId << std::endl;
}
// 核心逻辑: 更新DTC状态 (当监控器上报测试结果时调用)
void UpdateDtcStatus(DtcInfo& dtc, bool testResultPassed) {
uint8_t oldStatus = dtc.status;
// 1. 清除 "测试未完成" 标志 (bit4和bit6)
dtc.status &= ~0x50; // 清除 bit4(0x40) 和 bit6(0x10)? 注意掩码: bit4=16 (0x10), bit6=64 (0x40) -> 纠正: bit6=64, bit4=16
// 纠正掩码: bit4=0x10, bit6=0x40, 两者清除: ~0x50
dtc.status &= ~0x50;
if (testResultPassed) {
// 测试合格: 清除当前故障标志
dtc.status &= ~0x01; // bit0 = 0
// 如果当前是 "待确认" 状态,且计数器递减? (实际逻辑更复杂,这里简化)
if (dtc.failureCounter > 0) {
// 防抖递减逻辑: 如果连续合格,计数器减1,但不低于0
// 此处简化为: 如果计数器小于阈值,暂时不清除Confirmed标志
}
std::cout << "诊断结果: 通过 (Pass) -> DTC 0x" << std::hex << dtc.id << std::endl;
}
else {
// 测试失败: 置位当前故障标志
dtc.status |= 0x01; // bit0 = 1 (TestFailed)
dtc.status |= 0x02; // bit1 = 1 (TestFailedThisOpCycle)
dtc.status |= 0x04; // bit2 = 1 (PendingDTC)
dtc.status |= 0x20; // bit5 = 1 (TestFailedSinceLastClear)
// 防抖计数: 增加FailureCounter
dtc.failureCounter++;
std::cout << "诊断结果: 失败 (Fail), 当前计数器=" << (int)dtc.failureCounter << std::endl;
// 检查确认阈值 (假设阈值为2)
if (dtc.failureCounter >= 2) {
if (!(dtc.status & 0x08)) { // bit3 = 0 尚未确认
dtc.status |= 0x08; // bit3 = 1 (ConfirmedDTC)
std::cout << "故障已确认 (Confirmed)! 写入NVM." << std::endl;
// 存储到NVM
WriteToNvm(dtc.id, dtc.status);
dtc.existsInNvm = true;
}
}
}
std::cout << "状态更新: 0x" << std::hex << (int)oldStatus << " -> 0x" << (int)dtc.status << std::endl;
}
public:
UdsDiagnosticManager() : sessionMode(1) {}
// 模拟诊断监控器调用 (周期性执行)
void ReportTestResult(uint32_t dtcId, bool passed) {
auto it = dtcDatabase.find(dtcId);
if (it == dtcDatabase.end()) {
// 动态创建DTC (如果不存在)
dtcDatabase.emplace(dtcId, DtcInfo(dtcId));
it = dtcDatabase.find(dtcId);
}
UpdateDtcStatus(it->second, passed);
}
// 实现14服务 (清除诊断信息)
std::vector HandleService_14(const std::vector& request) {
std::vector response;
// 1. 长度检查: 14服务请求必须为 4字节 (SID + 3字节 GroupOfDTC)
if (request.size() != 4) {
response = {0x7F, 0x14, 0x13}; // NRC 13: incorrectMessageLength
return response;
}
// 提取 GroupOfDTC (例如 0xFFFFFF 表示清除所有)
uint32_t group = (request[1] << 16) | (request[2] << 8) | request[3];
// 2. 会话检查 (某些组需要编程会话)
if (group == 0xFFFF00 && sessionMode != 2) { // 假设清除所有DTC需要编程会话
response = {0x7F, 0x14, 0x22}; // NRC 22: conditionsNotCorrect
return response;
}
// 3. 执行清除操作
std::cout << "\n[14服务] 收到清除请求, Group: 0x" << std::hex << group << std::endl;
if (group == 0xFFFFFF) { // 清除所有DTC
for (auto& pair : dtcDatabase) {
DtcInfo& dtc = pair.second;
// 清除状态位: 重置为 0x40 (TestNotCompletedSinceLastClear)
dtc.status = 0x40;
dtc.failureCounter = 0;
EraseNvmForDtc(dtc.id);
dtc.existsInNvm = false;
std::cout << "清除DTC: 0x" << std::hex << dtc.id << std::endl;
}
// 如果允许CDD抑制,还需清除全局快照数据
}
else if (group == 0x000001) { // 清除特定组: 排放相关
// 遍历过滤逻辑...
}
// 4. 返回肯定响应
response = {0x54, request[1], request[2], request[3]};
return response;
}
// 打印所有DTC当前状态 (用于调试)
void PrintAllDtcStatus() {
std::cout << "\n========== 当前DTC列表 ==========" << std::endl;
for (const auto& pair : dtcDatabase) {
std::cout << "DTC: 0x" << std::hex << pair.first
<< " | Status: 0x" << (int)pair.second.status
<< " | FailCnt: " << std::dec << (int)pair.second.failureCounter
<< std::endl;
}
std::cout << "================================\n" << std::endl;
}
};// 主函数示例
int main() {
UdsDiagnosticManager diag;
// 场景模拟: 模拟一个电压故障 (DTC 0x123456)
uint32_t voltDtc = 0x123456;
std::cout << "=== 操作循环 1 开始 ===" << std::endl;
diag.ReportTestResult(voltDtc, false); // 第一次失败 -> Pending
diag.PrintAllDtcStatus();
diag.ReportTestResult(voltDtc, false); // 第二次连续失败 -> Confirmed (bit3=1)
diag.PrintAllDtcStatus();
std::cout << "\n=== 操作循环 2 开始 (模拟下电上电) ===" << std::endl;
// 注意: 在实际ECU中,跨循环时会重置 bit1=0, bit6=1,此处省略模拟重置函数,仅演示清除逻辑
std::cout << "\n=== 执行 14 服务清除故障 ===" << std::endl;
std::vector clearReq = {0x14, 0xFF, 0xFF, 0xFF};
auto resp = diag.HandleService_14(clearReq);
// 打印响应
if (resp[0] == 0x54) {
std::cout << "14服务肯定响应成功!" << std::endl;
} else if (resp[0] == 0x7F) {
std::cout << "14服务拒绝, NRC: 0x" << std::hex << (int)resp[2] << std::endl;
}
diag.PrintAllDtcStatus(); // 观察状态位被重置为 0x40
return 0;
}
6. 总结=== 操作循环 1 开始 ===
诊断结果: 失败 (Fail), 当前计数器=1
状态更新: 0x40 -> 0x27 (二进制: 0010 0111 -> bit0,1,2,5 = 1)
========== 当前DTC列表 ==========
DTC: 0x123456 | Status: 0x27 | FailCnt: 1
诊断结果: 失败 (Fail), 当前计数器=2
故障已确认 (Confirmed)! 写入NVM.
状态更新: 0x27 -> 0x2F (bit3 变 1)
========== 当前DTC列表 ==========
DTC: 0x123456 | Status: 0x2F | FailCnt: 2
=== 执行 14 服务清除故障 ===
[14服务] 收到清除请求, Group: 0xffffff
[NVM] 擦除DTC记录: 0x123456
清除DTC: 0x123456
14服务肯定响应成功!========== 当前DTC列表 ==========
DTC: 0x123456 | Status: 0x40 | FailCnt: 0
状态位是动态的 :
bit0代表此时此刻,bit3代表历史包袱,bit5代表自上次清零后的“案底”。跳变依赖防抖机制 :从
Pending (bit2)到Confirmed (bit3)需要达到预设的失败计数阈值,防止偶发干扰导致误报。14服务是重置动作 :它不仅清除状态位,还会销毁快照和计数器。执行14服务前通常需要
SecurityAccess(27服务) 解锁,且不同DTC组可能对应不同权限。开发建议 :在ECU代码中,务必使用状态机管理DTC,并严格区分 RAM 中的当前状态和 NVM 中的固化状态。诊断工具侧读取到
0x08(Confirmed DTC) 时,即可认为车辆确实存在历史故障记录。
热门跟贴