1. 诊断故障码设置控制服务 (ControlDTCSetting, 0x85) 1.1 服务概述
诊断故障码设置控制服务(0x85)用于动态地启用或禁用ECU内部诊断故障码(DTC)的记录功能。该服务通常用于维修或测试场景,避免在特定操作(如刷写、标定)期间因非真实故障产生误报。
根据ISO 14229-1标准,本服务具备以下关键行为:
抑制/恢复记录:通过子函数参数指示ECU停止或重新开始记录DTC。
会话层时序影响:若当前会话模式下抑制了DTC记录,当会话层时序参数(如P2Server_max)超时导致ECU自动返回默认会话(Default Session,0x01),DTC记录应自动恢复。
复位影响:ECU执行任何硬件复位(上电复位、外部复位等)后,DTC记录功能恢复为默认允许状态。
与清除服务联动:当诊断仪发送
ClearDiagnosticInformation(0x14)服务清除DTC时,ECU必须重新开启DTC记录(即使之前处于抑制状态)。
字节序号
参数名称
值(hex)
0
SID
0x85
服务标识符
1
sub-function
0x01–0x02
DTC设置类型:
0x01 = off(停止记录)
0x02 = on(开始记录)
2…n
可选参数(保留)
标准中通常无额外参数,部分OEM扩展使用
注意:子函数字节的高四位为suppressPosRspMsgIndicationBit(0x00表示需要正响应),低四位为实际控制类型。1.2.2 正响应报文
字节序号
参数名称
值(hex)
0
SID + 0x40
0xC5
服务正响应标识符
1
sub-function
0x01或0x02
回显请求中的控制类型
2…n
可选参数
标准无后续数据
1.2.3 负响应报文
字节序号
参数名称
值(hex)
0
0x7F
0x7F
负响应标识符
1
SID
0x85
原始服务标识符
2
NRC
见下表
负响应码(Negative Response Code)
常用NRC:
NRC
含义
0x12
子函数不支持(sub-function无效)
0x13
请求报文长度错误
0x22
条件不满足(例如在非扩展会话下请求)
0x31
请求超出范围(参数错误)
2. 通过标识符读取数据服务 (ReadDataByIdentifier, 0x22) 2.1 服务概述
通过数据标识符(DID,Data Identifier)读取数据服务允许诊断仪从ECU的存储器中读取一个或多个指定DID对应的数据记录。DID为两个字节(0x0000–0xFFFF),由车厂定义。
标准支持单请求多DID,但多数实现仅支持单个DID。下文以单DID为例说明。
2.2 报文格式 2.2.1 请求报文
字节序号
参数名称
值(hex)
0
SID
0x22
服务标识符
1
DID 高字节
0x00–0xFF
数据标识符高位
2
DID 低字节
0x00–0xFF
数据标识符低位
3…n
后续DID(可选)
多DID时重复字节1-2
2.2.2 正响应报文
字节序号
参数名称
值(hex)
0
SID + 0x40
0x62
服务正响应标识符
1
DID 高字节
0x00–0xFF
回显请求中的DID高位
2
DID 低字节
0x00–0xFF
回显请求中的DID低位
3…m
数据记录
可变
该DID对应的数据值
m+1…n
下一个DID+数据
无(多DID时重复)
仅当请求多个DID时存在
2.2.3 负响应报文
字节序号
参数名称
值(hex)
0
0x7F
0x7F
负响应标识符
1
SID
0x22
原始服务标识符
2
NRC
见下表
负响应码
常用NRC:
NRC
含义
0x13
请求报文长度错误
0x14
数据标识符不支持
0x22
条件不满足
0x31
请求超出范围(DID有效但无法读取)
3. C++代码实现示例
以下代码模拟一个简化但符合UDS行为规范的ECU,实现了:
ControlDTCSetting(0x85) 服务。ReadDataByIdentifier(0x22) 服务。与
ClearDiagnosticInformation(0x14) 的联动。会话超时与复位后自动恢复DTC记录的逻辑(通过定时器和复位标志模拟)。
3.1 代码说明#include
#include
#include
#include
#include
#include
#include
// ---------- 辅助工具函数 ----------
// 将字节向量打印为十六进制
void printHex(const std::vector& data, const std::string& prefix = "") {
std::cout << prefix;
for (uint8_t b : data) {
printf("%02X ", b);
}
std::cout << std::endl;
}
// ---------- UDS 服务定义 ----------
const uint8_t SID_CONTROL_DTC_SETTING = 0x85;
const uint8_t SID_READ_DATA_BY_IDENTIFIER = 0x22;
const uint8_t SID_CLEAR_DIAG_INFO = 0x14;
// 子函数定义 (ControlDTCSetting)
const uint8_t DTC_SETTING_OFF = 0x01;
const uint8_t DTC_SETTING_ON = 0x02;
// DID 定义(示例)
const uint16_t DID_VIN = 0xF190;
const uint16_t DID_SW_VERSION = 0xF187;
const uint16_t DID_ECU_SERIAL = 0xF1A0;
// 负响应码 (NRC)
const uint8_t NRC_SUB_FUNCTION_NOT_SUPPORTED = 0x12;
const uint8_t NRC_INCORRECT_MESSAGE_LEN = 0x13;
const uint8_t NRC_REQUEST_OUT_OF_RANGE = 0x31;
const uint8_t NRC_CONDITIONS_NOT_CORRECT = 0x22;
const uint8_t NRC_DATA_IDENTIFIER_NOT_SUPP = 0x14;
// 简单模拟ECU环境
class EcuSimulator {
public:
EcuSimulator() : dtcRecordingEnabled_(true), currentSession_(0x01) // 默认会话
{
// 初始化模拟数据
vin_ = "1HGCM82633A123456";
swVersion_ = "2.1.0";
ecuSerial_ = "ECU_ABC123";
}
// 接收诊断请求(完整CAN帧数据域),返回响应数据
std::vector handleRequest(const std::vector& request) {
if (request.empty()) return buildNegativeResponse(0x00, NRC_INCORRECT_MESSAGE_LEN);
uint8_t sid = request[0];
switch (sid) {
case SID_CONTROL_DTC_SETTING:
return handleControlDTCSetting(request);
case SID_READ_DATA_BY_IDENTIFIER:
return handleReadDataByIdentifier(request);
case SID_CLEAR_DIAG_INFO:
return handleClearDiagnosticInfo(request);
default:
return buildNegativeResponse(sid, NRC_REQUEST_OUT_OF_RANGE);
}
}
// 模拟ECU复位(例如调用此函数后恢复DTC记录)
void performReset() {
std::cout << "[ECU] Performing reset...\n";
dtcRecordingEnabled_ = true; // 复位后恢复DTC记录
// 可选:切换到默认会话
currentSession_ = 0x01;
}
// 模拟会话超时(例如长时间无请求自动调用此函数)
void sessionTimeout() {
if (currentSession_ != 0x01) {
std::cout << "[ECU] Session timeout, switching to default session (0x01)\n";
currentSession_ = 0x01;
dtcRecordingEnabled_ = true; // 根据标准,会话超时返回默认会话后应恢复DTC记录
}
}
// 获取当前DTC记录状态(供外部监控)
bool isDtcRecordingEnabled() const { return dtcRecordingEnabled_; }
private:
bool dtcRecordingEnabled_; // DTC记录使能标志
uint8_t currentSession_; // 当前会话(0x01=默认, 0x02=编程等)
// 模拟数据
std::string vin_;
std::string swVersion_;
std::string ecuSerial_;
// 构建负响应
std::vector buildNegativeResponse(uint8_t sid, uint8_t nrc) const {
return {0x7F, sid, nrc};
}
// 处理 0x85 ControlDTCSetting
std::vector handleControlDTCSetting(const std::vector& req) {
// 最小长度:SID(1) + sub-function(1) = 2
if (req.size() < 2) {
return buildNegativeResponse(SID_CONTROL_DTC_SETTING, NRC_INCORRECT_MESSAGE_LEN);
}
uint8_t subFunc = req[1];
// 低四位为控制类型,高四位为抑制正响应标志(本例忽略抑制标志)
uint8_t controlType = subFunc & 0x0F;
// 可选:检查会话条件(例如只在扩展会话下允许)
if (currentSession_ != 0x02) { // 假设0x02为扩展诊断会话
return buildNegativeResponse(SID_CONTROL_DTC_SETTING, NRC_CONDITIONS_NOT_CORRECT);
}
bool newState;
switch (controlType) {
case DTC_SETTING_OFF:
newState = false;
break;
case DTC_SETTING_ON:
newState = true;
break;
default:
return buildNegativeResponse(SID_CONTROL_DTC_SETTING, NRC_SUB_FUNCTION_NOT_SUPPORTED);
}
dtcRecordingEnabled_ = newState;
std::cout << "[ECU] DTC recording has been " << (dtcRecordingEnabled_ ? "ENABLED" : "DISABLED")
<< " via 0x85 service.\n";
// 正响应:0xC5 + sub-function(原样返回)
return {static_cast(SID_CONTROL_DTC_SETTING + 0x40), subFunc};
}
// 处理 0x22 ReadDataByIdentifier
std::vector handleReadDataByIdentifier(const std::vector& req) {
// 最小长度:SID(1) + DID(2) = 3
if (req.size() < 3) {
return buildNegativeResponse(SID_READ_DATA_BY_IDENTIFIER, NRC_INCORRECT_MESSAGE_LEN);
}
uint16_t did = (static_cast(req[1]) << 8) | req[2];
std::vector data;
// 根据DID获取数据
if (did == DID_VIN) {
data.assign(vin_.begin(), vin_.end());
} else if (did == DID_SW_VERSION) {
data.assign(swVersion_.begin(), swVersion_.end());
} else if (did == DID_ECU_SERIAL) {
data.assign(ecuSerial_.begin(), ecuSerial_.end());
} else {
return buildNegativeResponse(SID_READ_DATA_BY_IDENTIFIER, NRC_DATA_IDENTIFIER_NOT_SUPP);
}
// 正响应:0x62 + DID + 数据
std::vector response;
response.push_back(SID_READ_DATA_BY_IDENTIFIER + 0x40);
response.push_back(req[1]); // DID高字节
response.push_back(req[2]); // DID低字节
response.insert(response.end(), data.begin(), data.end());
return response;
}
// 处理 0x14 ClearDiagnosticInformation
std::vector handleClearDiagnosticInfo(const std::vector& req) {
// 标准中0x14可能携带清除范围参数,此处简化:清除所有DTC并重新开启记录
// 实际实现中应真正清除存储的DTC列表
std::cout << "[ECU] Clearing all DTCs and re-enabling DTC recording.\n";
dtcRecordingEnabled_ = true; // 清除后必须恢复记录
// 正响应:0x54 (SID+0x40)
return {static_cast(SID_CLEAR_DIAG_INFO + 0x40)};
}
};
// ---------- 演示示例 ----------
int main() {
EcuSimulator ecu;
// 初始化:DTC记录默认开启
std::cout << "Initial DTC recording state: " << (ecu.isDtcRecordingEnabled() ? "Enabled" : "Disabled") << "\n\n";
// 模拟诊断仪发送 0x85 请求(停止记录)
std::vector req85_off = {0x85, 0x01}; // sub-function 0x01 = off
std::cout << "Request 0x85 (stop DTC recording): ";
printHex(req85_off);
auto resp85_off = ecu.handleRequest(req85_off);
printHex(resp85_off, "Response: ");
std::cout << "DTC recording state after request: " << (ecu.isDtcRecordingEnabled() ? "Enabled" : "Disabled") << "\n\n";
// 尝试读取一个DID(VIN)
std::vector req22_vin = {0x22, 0xF1, 0x90};
std::cout << "Request 0x22 (read VIN): ";
printHex(req22_vin);
auto resp22_vin = ecu.handleRequest(req22_vin);
printHex(resp22_vin, "Response: ");
std::string vinStr(resp22_vin.begin() + 3, resp22_vin.end());
std::cout << "VIN: " << vinStr << "\n\n";
// 模拟会话超时(应自动恢复DTC记录)
std::cout << "Simulating session timeout...\n";
ecu.sessionTimeout();
std::cout << "DTC recording state after timeout: " << (ecu.isDtcRecordingEnabled() ? "Enabled" : "Disabled") << "\n\n";
// 模拟ECU复位
std::cout << "Simulating ECU reset...\n";
ecu.performReset();
std::cout << "DTC recording state after reset: " << (ecu.isDtcRecordingEnabled() ? "Enabled" : "Disabled") << "\n\n";
// 模拟发送 0x14 清除诊断信息
std::vector req14 = {0x14, 0xFF, 0xFF}; // 简化的清除所有DTC
std::cout << "Request 0x14 (clear DTCs): ";
printHex(req14);
auto resp14 = ecu.handleRequest(req14);
printHex(resp14, "Response: ");
std::cout << "DTC recording state after clear: " << (ecu.isDtcRecordingEnabled() ? "Enabled" : "Disabled") << "\n\n";return 0;
}
EcuSimulator类:
dtcRecordingEnabled_标志位模拟DTC记录状态。handleControlDTCSetting:检查会话条件(要求扩展会话0x02),修改标志位,返回0xC5+子函数。handleReadDataByIdentifier:支持三个示例DID,返回ASCII字符串数据。handleClearDiagnosticInfo:模拟清除所有DTC,并将dtcRecordingEnabled_强制置为true。
会话超时与复位:
sessionTimeout():将当前会话切回默认会话(0x01),同时恢复DTC记录。performReset():模拟硬件复位,恢复DTC记录。
符合标准的行为:
0x85请求中若子函数无效返回NRC 0x12。
非扩展会话下执行0x85返回NRC 0x22(条件不满足)。
0x14服务后自动使能记录。
4. 总结Initial DTC recording state: Enabled
Request 0x85 (stop DTC recording): 85 01
[ECU] DTC recording has been DISABLED via 0x85 service.
Response: C5 01
DTC recording state after request: Disabled
Request 0x22 (read VIN): 22 F1 90
Response: 62 F1 90 31 48 47 43 4D 38 32 36 33 33 41 31 32 33 34 35 36
VIN: 1HGCM82633A123456
Simulating session timeout...
[ECU] Session timeout, switching to default session (0x01)
DTC recording state after timeout: Enabled
Simulating ECU reset...
[ECU] Performing reset...
DTC recording state after reset: EnabledRequest 0x14 (clear DTCs): 14 FF FF
[ECU] Clearing all DTCs and re-enabling DTC recording.
Response: 54
DTC recording state after clear: Enabled
本文详细解析了UDS服务ControlDTCSetting(0x85) 和ReadDataByIdentifier(0x22) 的协议格式、行为约束,并通过C++代码给出了符合ISO 14229-1规范的实现示例。实际量产ECU中还需考虑多DID读取、安全访问、DTC存储管理等额外功能,但上述代码清晰展示了核心流程与标准要求的关键交互行为。
热门跟贴