![]()
UDS(统一诊断服务)详解及 C++ 代码示例 一、UDS 概述
UDS(Unified Diagnostic Services,统一诊断服务)是 ISO 14229 标准定义的汽车诊断协议,广泛应用于 ECU(电子控制单元)的诊断、刷写、标定等场景。它运行在 CAN、CAN-FD、Ethernet 等底层传输协议之上(如 ISO 15765-2 即 DoCAN)。
UDS 的核心思想是:客户端(诊断仪)发送服务请求,服务端(ECU)执行并返回响应。所有交互都通过SID(Service Identifier,服务标识符)来区分功能。
1.1 请求/响应模型
+-----------+ Request (SID + Data) +-----------+
| Tester | ----------------------------> | ECU |
| (Client) | <---------------------------- | (Server) |
+-----------+ Response (SID+0x40 + Data)+-----------+
关键规则:
肯定响应:
响应 SID = 请求 SID + 0x40例如请求
0x22(ReadDataByIdentifier),肯定响应为0x62。
否定响应:固定为
0x7F,格式为7FNRC(Negative Response Code)表示失败原因,如
0x13(报文长度错误)、0x31(请求超出范围)、0x22(条件不满足)等。
SID
服务名
功能
肯定响应
0x10
DiagnosticSessionControl
切换诊断会话(默认/编程/扩展)
0x50
0x11
ECUReset
ECU 复位
0x51
0x14
ClearDiagnosticInformation
清除故障码
0x54
0x19
ReadDTCInformation
读取故障码
0x59
0x22
ReadDataByIdentifier
按 DID 读数据
0x62
0x27
SecurityAccess
安全访问(种子/密钥)
0x67
0x2E
WriteDataByIdentifier
按 DID 写数据
0x6E
0x31
RoutineControl
例程控制
0x71
0x3E
TesterPresent
保持会话
0x7E
0x34/0x36/0x37
刷写相关
下载/传输/退出
0x74/0x76/0x77
1.3 常见 NRC
NRC
含义
0x10
General reject
0x11
Service not supported
0x12
Sub-function not supported
0x13
Incorrect message length
0x22
Conditions not correct
0x31
Request out of range
0x33
Security access denied
0x78
Response pending(用于长耗时操作)
二、C++ 代码示例
下面实现一个简化但结构完整的 UDS 服务端,模拟 ECU 处理请求与生成响应。代码演示了:
请求/响应帧结构
服务分发(dispatch)
肯定响应 SID + 0x40
否定响应 0x7F + NRC
DID 读写、DTC 读取、清除故障码
2.1 编译与运行#include
#include
#include
#include
#include
#include
using Byte = uint8_t;
using Frame = std::vector ;
// ---------------- UDS 常量定义 ----------------
namespace UDS {
// 服务 SID
constexpr Byte SID_DiagnosticSessionControl = 0x10;
constexpr Byte SID_ECUReset = 0x11;
constexpr Byte SID_ClearDiagnosticInformation = 0x14;
constexpr Byte SID_ReadDTCInformation = 0x19;
constexpr Byte SID_ReadDataByIdentifier = 0x22;
constexpr Byte SID_WriteDataByIdentifier = 0x2E;
constexpr Byte SID_TesterPresent = 0x3E;
// 肯定响应偏移
constexpr Byte POS_RESP_OFFSET = 0x40;
// 否定响应
constexpr Byte NEG_RESP_SID = 0x7F;
// NRC
constexpr Byte NRC_GeneralReject = 0x10;
constexpr Byte NRC_ServiceNotSupported = 0x11;
constexpr Byte NRC_SubFunctionNotSupport = 0x12;
constexpr Byte NRC_IncorrectLength = 0x13;
constexpr Byte NRC_ConditionsNotCorrect = 0x22;
constexpr Byte NRC_RequestOutOfRange = 0x31;
}
// ---------------- 打印工具 ----------------
void printFrame(const std::string& tag, const Frame& f) {
std::cout << tag << ": ";
for (Byte b : f)
std::cout << std::hex << std::uppercase
<< std::setw(2) << std::setfill('0') << (int)b << " ";
std::cout << std::dec << "\n";
}
// ---------------- 模拟 ECU ----------------
class UdsServer {
public:
UdsServer() {
// 初始化一些 DID 数据
didTable_[0xF190] = {'V','I','N','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
didTable_[0xF187] = {'P','A','R','T','-','0','0','1'};
// 初始化 DTC
dtcList_ = {0x00A001, 0x00B002};
}
// 核心入口:接收请求,返回响应
Frame handleRequest(const Frame& request) {
if (request.empty()) return negativeResponse(0x00, UDS::NRC_IncorrectLength);
Byte sid = request[0];
std::cout << "\n[ECU] 收到请求 SID=0x" << std::hex << (int)sid << std::dec << "\n";
switch (sid) {
case UDS::SID_DiagnosticSessionControl: return handleSessionControl(request);
case UDS::SID_ECUReset: return handleEcuReset(request);
case UDS::SID_ClearDiagnosticInformation: return handleClearDtc(request);
case UDS::SID_ReadDTCInformation: return handleReadDtc(request);
case UDS::SID_ReadDataByIdentifier: return handleReadDid(request);
case UDS::SID_WriteDataByIdentifier: return handleWriteDid(request);
case UDS::SID_TesterPresent: return handleTesterPresent(request);
default: return negativeResponse(sid, UDS::NRC_ServiceNotSupported);
}
}
private:
std::map didTable_; // DID -> 数据
std::vector dtcList_; // 故障码列表
Byte currentSession_ = 0x01; // 默认会话
// ---------- 构造响应 ----------
Frame positiveResponse(Byte sid, const Frame& data = {}) {
Frame resp;
resp.push_back(sid + UDS::POS_RESP_OFFSET);
resp.insert(resp.end(), data.begin(), data.end());
return resp;
}
Frame negativeResponse(Byte sid, Byte nrc) {
return { UDS::NEG_RESP_SID, sid, nrc };
}
// ---------- 各服务处理 ----------
// 0x10 会话控制:请求 [10, subFunc]
Frame handleSessionControl(const Frame& req) {
if (req.size() != 2)
return negativeResponse(req[0], UDS::NRC_IncorrectLength);
Byte sub = req[1] & 0x7F;
if (sub != 0x01 && sub != 0x02 && sub != 0x03)
return negativeResponse(req[0], UDS::NRC_SubFunctionNotSupport);
currentSession_ = sub;
// 响应:50
Frame data = { sub, 0x00, 0x32 };
return positiveResponse(req[0], data);
}
// 0x11 ECU 复位:请求 [11, subFunc]
Frame handleEcuReset(const Frame& req) {
if (req.size() != 2)
return negativeResponse(req[0], UDS::NRC_IncorrectLength);
Byte sub = req[1] & 0x7F;
// 响应:51
return positiveResponse(req[0], { sub });
}
// 0x14 清除 DTC:请求 [14, groupHigh, groupMid, groupLow]
Frame handleClearDtc(const Frame& req) {
if (req.size() != 4)
return negativeResponse(req[0], UDS::NRC_IncorrectLength);
dtcList_.clear();
std::cout << "[ECU] 故障码已清除\n";
// 肯定响应只有 SID
return positiveResponse(req[0]);
}
// 0x19 读取 DTC:请求 [19, subFunc=0x02, statusMask]
Frame handleReadDtc(const Frame& req) {
if (req.size() != 3)
return negativeResponse(req[0], UDS::NRC_IncorrectLength);
Byte sub = req[1];
if (sub != 0x02) // 只支持 reportDTCByStatusMask
return negativeResponse(req[0], UDS::NRC_SubFunctionNotSupport);
// 响应:59 02 [DTC(3B) status(1B)]...
Frame data = { sub, 0xFF };
for (uint32_t dtc : dtcList_) {
data.push_back((dtc >> 16) & 0xFF);
data.push_back((dtc >> 8) & 0xFF);
data.push_back(dtc & 0xFF);
data.push_back(0x09); // 状态:confirmedDTC
}
return positiveResponse(req[0], data);
}
// 0x22 读数据:请求 [22, DID_H, DID_L, ...]
Frame handleReadDid(const Frame& req) {
if (req.size() < 3 || (req.size() % 2) == 0)
return negativeResponse(req[0], UDS::NRC_IncorrectLength);
Frame data;
for (size_t i = 1; i + 1 < req.size(); i += 2) {
uint16_t did = (req[i] << 8) | req[i + 1];
auto it = didTable_.find(did);
if (it == didTable_.end())
return negativeResponse(req[0], UDS::NRC_RequestOutOfRange);
data.push_back(req[i]);
data.push_back(req[i + 1]);
data.insert(data.end(), it->second.begin(), it->second.end());
}
return positiveResponse(req[0], data);
}
// 0x2E 写数据:请求 [2E, DID_H, DID_L, data...]
Frame handleWriteDid(const Frame& req) {
if (req.size() < 4)
return negativeResponse(req[0], UDS::NRC_IncorrectLength);
uint16_t did = (req[1] << 8) | req[2];
Frame value(req.begin() + 3, req.end());
didTable_[did] = value;
// 响应:6E DID_H DID_L
return positiveResponse(req[0], { req[1], req[2] });
}
// 0x3E TesterPresent:请求 [3E, 00]
Frame handleTesterPresent(const Frame& req) {
if (req.size() != 2)
return negativeResponse(req[0], UDS::NRC_IncorrectLength);
Byte sub = req[1] & 0x7F;
if (sub != 0x00)
return negativeResponse(req[0], UDS::NRC_SubFunctionNotSupport);
return positiveResponse(req[0], { 0x00 });
}
};
// ---------------- 主函数:模拟诊断仪 ----------------
int main() {
UdsServer ecu;
// 构造若干诊断请求
std::vector requests = {
{ 0x10, 0x03 }, // 进入扩展会话
{ 0x22, 0xF1, 0x90 }, // 读 VIN
{ 0x22, 0xF1, 0x87 }, // 读零件号
{ 0x19, 0x02, 0xFF }, // 读取 DTC
{ 0x14, 0xFF, 0xFF, 0xFF }, // 清除 DTC
{ 0x19, 0x02, 0xFF }, // 再次读取 DTC
{ 0x2E, 0xF1, 0x87, 'N','E','W' }, // 写零件号
{ 0x22, 0xF1, 0x87 }, // 验证写入
{ 0x22, 0x12, 0x34 }, // 读不存在的 DID -> NRC 0x31
{ 0x99, 0x00 }, // 不支持的服务 -> NRC 0x11
};for (const auto& req : requests) {
printFrame("[Tester] 请求", req);
Frame resp = ecu.handleRequest(req);
printFrame("[ECU] 响应", resp);
}
return 0;
}
g++ -std=c++17 -Wall -O2 uds_demo.cpp -o uds_demo
./uds_demo
2.2 预期输出(节选)三、代码要点解析[Tester] 请求: 10 03
[ECU] 收到请求 SID=0x10
[ECU] 响应: 50 03 00 32
[Tester] 请求: 22 F1 90
[ECU] 收到请求 SID=0x22
[ECU] 响应: 62 F1 90 56 49 4E 30 30 30 30 30 30 30 30 30 30 30 30 30
[Tester] 请求: 19 02 FF
[ECU] 收到请求 SID=0x19
[ECU] 响应: 59 02 FF 00 A0 01 09 00 B0 02 09
[Tester] 请求: 14 FF FF FF
[ECU] 收到请求 SID=0x14
[ECU] 故障码已清除
[ECU] 响应: 54
[Tester] 请求: 22 12 34
[ECU] 响应: 7F 22 31 ← NRC 0x31 RequestOutOfRange[Tester] 请求: 99 00
[ECU] 响应: 7F 99 11 ← NRC 0x11 ServiceNotSupported
服务分发机制
handleRequest()用switch按 SID 分发,这正是真实 ECU 中 UDS 栈的典型结构。肯定响应 SID 计算
resp[0] = sid + 0x40,如0x22 → 0x62、0x10 → 0x50。否定响应格式统一统一封装
negativeResponse(sid, nrc)返回{0x7F, sid, nrc},便于所有服务复用。参数长度校验每个服务都先校验报文长度,不符合则回
NRC 0x13(IncorrectMessageLength)。DID 数据表用
std::map模拟 ECU 内部的数据存储;写数据后立即读可验证。可扩展性增加新服务只需:
定义 SID 常量
在
switch中加case实现对应的
handleXxx()返回positiveResponse或negativeResponse
方向
多帧传输
ISO 15765-2 的 SF/FF/CF/FC 机制,处理 >7 字节(CAN)的数据
会话与安全联动
某些服务(如 0x2E、0x31)需要在扩展会话 + 安全解锁后才能执行
定时器 S3
超时未收到TesterPresent自动回默认会话
P2/P2* 管理
长耗时服务先回NRC 0x78,再发最终响应
DTC 状态位
按 ISO 14229-1 的 8 位状态位(testFailed、confirmedDTC 等)解析
这份代码是一个可运行的骨架,把握住了 UDS 的核心:SID 分发 → 参数校验 → 肯定/否定响应。在此基础上加入多帧、会话管理、安全访问,就能构造出接近量产的 ECU 诊断栈。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.