![]()
1. UDS概述
统一诊断服务(Unified Diagnostic Services,UDS)是ISO 14229标准定义的汽车电子诊断通信协议,广泛应用于ECU(电子控制单元)的故障检测、参数配置、软件升级等场景。UDS基于客户端-服务器模型,通常运行在CAN、LIN、以太网等物理总线上。其核心是一组预定义的服务标识符(Service Identifier,SID),每个SID对应一个特定的诊断功能。
UDS报文结构简单:一个字节的SID(请求)或响应SID(通常为SID+0x40),后跟可变长度的参数。若服务器无法处理请求,则返回0x7F(负响应码)加一个否定响应代码(NRC)。
本文将对UDS常用服务进行分类讲解,并给出C++实现的构建与解析示例。
2. UDS服务标识符(SIDs)及分类
下表整理了典型UDS服务的请求SID、响应SID(正响应时使用)及功能描述。响应SID通常由请求SID加上0x40得到,如0x10 -> 0x50,0x27 -> 0x67。
2.1 诊断和通信管理
请求SID
响应SID
服务名称
功能简述
0x10
0x50
诊断会话控制
切换ECU的会话模式(默认、编程、扩展)
0x11
0x51
ECU重置
请求ECU执行复位
0x27
0x67
安全访问
解锁受保护的服务(需种子密钥验证)
0x28
0x68
通信控制
启用/禁用ECU的消息收发
0x29
0x69
身份验证
基于PKI的高级身份验证
0x3E
0x7E
存活测试(Tester Present)
维持当前会话,防止自动退出
0x83
0xC3
访问定时参数
读取/修改通信计时参数
0x84
0xC4
安全数据传输
加密传输诊断数据(ISO 15764)
0x85
0xC5
控制DTC设置
暂时禁用/恢复故障码存储
0x86
0xC6
对事件的响应
ECU在特定事件时主动发送数据
0x87
0xC7
连接控制
设置诊断访问的波特率等参数
2.2 数据传输功能单元
请求SID
响应SID
服务名称
功能简述
0x22
0x62
按标识符读数据
读取由DID标识的数据(如VIN)
0x23
0x63
按地址读取内存
直接读取物理内存
0x24
0x64
按标识符读取标定数据
读取标定/缩放信息
0x2A
0x6A
按标识符定期读取数据
周期广播传感器数据
0x2E
0x6E
按标识符写数据
写入DID对应的数据
0x3D
0x7D
按地址写入内存
直接写物理内存
0x20
0x60
动态定义数据标识符
动态组合多个DID
2.3 存储数据传输功能单元
请求SID
响应SID
服务名称
功能简述
0x14
0x54
清除诊断信息
清除存储的DTC
0x19
0x59
读取DTC信息
读取故障码及其状态
2.4 输入输出控制功能单元
请求SID
响应SID
服务名称
功能简述
0x2F
0x6F
按标识符对输入输出进行控制
控制ECU的模拟/数字输入输出
2.5 例程控制功能单元
请求SID
响应SID
服务名称
功能简述
0x31
0x71
例程控制
启动/停止例程(如自检、擦除)
2.6 上传下载功能单元
请求SID
响应SID
服务名称
功能简述
0x34
0x74
请求下载
准备向ECU写入数据
0x35
0x75
请求上传
准备从ECU读取数据
0x36
0x76
传输数据
实际数据传输
0x37
0x77
请求传输退出
终止上传/下载过程
0x38
0x78
请求文件传输
文件级别的上传/下载
2.7 其他(负响应)
响应SID
含义
用法
0x7F
负响应
服务器无法处理请求,后跟一个字节的NRC
常见否定响应代码(NRC)例如:
0x10:不支持请求的SID0x11:不支持子功能0x12:子功能在本次会话无效0x13:报文长度错误0x22:条件不满足0x31:请求超出范围0x33:安全访问未解锁
作用 :切换ECU的诊断会话模式(默认会话、编程会话、扩展会话)。不同会话下可用的服务集合不同,例如刷写软件通常在编程会话中完成。
请求格式 :
[0x10] [子函数],子函数如0x01=默认会话,0x02=编程会话,0x03=扩展会话。响应格式 :
[0x50] [子函数] [会话参数],会话参数包括P2ServerMax、P2*ServerMax等定时值。
作用 :防止未授权操作。步骤:客户端发送“请求种子”子功能(如
0x01),ECU返回种子;客户端基于种子计算密钥并发送“发送密钥”子功能(如0x02);若匹配则解锁受保护的服务。子功能 :奇数请求种子,偶数发送密钥。同一组内奇偶对应(0x01/0x02,0x03/0x04…)。
作用 :客户端定期发送此服务告知ECU自己仍在线,ECU则保持当前会话不自动退回默认会话。子功能
0x00或0x80(带标志位)。
数据标识符(DID) :两个字节,如
0xF190表示VIN码。请求[0x22] [DID_H] [DID_L],响应[0x62] [DID_H] [DID_L] [数据…]。写入类似,携带待写数据。
子函数 :
0x01启动,0x02停止,0x03请求结果。例程ID两字节,例如0x0200表示自检例程。请求[0x31] [子函数] [例程ID_H] [例程ID_L] [可选参数]。
典型流程 :
客户端发送
0x34(请求下载),描述地址、大小等。服务器回复
0x74,确认最大块长度。客户端循环发送
0x36(传输数据)。最后发送
0x37(请求退出)。
当服务器无法执行请求时,返回[0x7F] [请求SID] [NRC]。例如,若未解锁安全访问就尝试写数据,可能返回0x7F 0x2E 0x33。
4. C++代码示例
以下代码演示如何构建常见的UDS请求,解析响应,并模拟一个简单的诊断会话控制服务。为简化,假设使用单帧传输(不超过CAN帧8字节)。
4.1 基础数据结构与工具函数
4.2 构建常用服务请求#include
#include
#include
#include
// UDS报文结构
struct UdsMessage {
uint8_t sid; // 服务标识符
std::vector data; // 参数(不含SID)
};
// 打印字节数组
void printBytes(const std::vector& bytes, const std::string& prefix) {
std::cout << prefix << ": ";
for (auto b : bytes) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)b << " ";
}
std::cout << std::dec << std::endl;
}
// 构建请求消息(SID + 参数)
UdsMessage buildRequest(uint8_t sid, const std::vector& params) {
return {sid, params};
}// 将UdsMessage序列化为字节流
std::vector serialize(const UdsMessage& msg) {
std::vector result;
result.push_back(msg.sid);
result.insert(result.end(), msg.data.begin(), msg.data.end());
return result;
}
4.3 解析响应// 诊断会话控制:子函数 (0x01默认, 0x02编程, 0x03扩展)
UdsMessage createDiagnosticSessionControl(uint8_t subFunction) {
return buildRequest(0x10, {subFunction});
}
// 安全访问:请求种子 (seedKeyId: 0x01, 0x03等)
UdsMessage createSecurityAccessRequestSeed(uint8_t seedKeyId) {
return buildRequest(0x27, {seedKeyId});
}
// 安全访问:发送密钥
UdsMessage createSecurityAccessSendKey(uint8_t seedKeyId, const std::vector& key) {
std::vector params;
params.push_back(seedKeyId); // 对应请求种子的子函数+1
params.insert(params.end(), key.begin(), key.end());
return buildRequest(0x27, params);
}
// 存活测试
UdsMessage createTesterPresent() {
return buildRequest(0x3E, {0x00}); // 子函数0x00常见
}
// 按标识符读数据 (DID两字节)
UdsMessage createReadDataByIdentifier(uint16_t did) {
return buildRequest(0x22, {static_cast(did >> 8), static_cast(did & 0xFF)});
}
// 例程控制启动 (例程ID两字节)
UdsMessage createRoutineControlStart(uint16_t routineId) {
return buildRequest(0x31, {0x01, static_cast(routineId >> 8), static_cast(routineId & 0xFF)});
}// 请求下载 (address, size)
UdsMessage createRequestDownload(uint32_t address, uint32_t size) {
// 简化:地址长度4字节,大小4字节,不包含内存标识符
std::vector params;
params.push_back(0x00); // 内存标识符,此处简单填0
params.push_back(0x04); // 地址长度4字节
params.push_back(0x04); // 大小长度4字节
for (int i = 24; i >= 0; i -= 8) params.push_back((address >> i) & 0xFF);
for (int i = 24; i >= 0; i -= 8) params.push_back((size >> i) & 0xFF);
return buildRequest(0x34, params);
}
4.4 主程序示例// 解析正响应(检查是否为请求SID+0x40,并提取参数)
bool parsePositiveResponse(const std::vector& response, uint8_t requestSid, std::vector& outData) {
if (response.empty()) return false;
uint8_t expectedRespSid = requestSid + 0x40;
if (response[0] == 0x7F) { // 负响应
std::cout << "Negative response: NRC=" << std::hex << (int)response[2] << std::dec << std::endl;
return false;
}
if (response[0] != expectedRespSid) {
std::cout << "Unexpected response SID: " << std::hex << (int)response[0] << std::endl;
return false;
}
outData.assign(response.begin() + 1, response.end());
return true;
}// 模拟ECU内部处理(演示)
std::vector simulateEcu(const UdsMessage& request) {
// 简单模拟:根据请求SID生成响应
std::vector resp;
switch (request.sid) {
case 0x10: { // 诊断会话控制
if (request.data.empty()) {
resp = {0x7F, 0x10, 0x13}; // 报文长度错误
} else {
uint8_t sub = request.data[0];
// 假设支持默认(0x01)和扩展(0x03)会话
if (sub == 0x01 || sub == 0x03) {
resp.push_back(0x50); // 正响应SID
resp.push_back(sub); // 回显子函数
resp.push_back(0x50); // P2ServerMax = 80ms(例)
resp.push_back(0x64); // P2*ServerMax = 100ms
} else {
resp = {0x7F, 0x10, 0x12}; // NRC: 子功能不支持
}
}
break;
}
case 0x22: { // 读数据
if (request.data.size() == 2) {
uint16_t did = (request.data[0] << 8) | request.data[1];
if (did == 0xF190) { // VIN模拟
resp.push_back(0x62);
resp.push_back(request.data[0]); resp.push_back(request.data[1]);
const char vin[] = "1HGCM82633A123456";
for (size_t i = 0; i < 17; ++i) resp.push_back(vin[i]);
} else {
resp = {0x7F, 0x22, 0x31}; // 请求超出范围
}
} else {
resp = {0x7F, 0x22, 0x13};
}
break;
}
case 0x3E: { // 存活测试
resp = {0x7E, 0x00}; // 正响应
break;
}
default:
resp = {0x7F, request.sid, 0x10}; // 不支持服务
break;
}
return resp;
}
int main() {
// 1. 发送诊断会话控制(扩展会话)
auto req = createDiagnosticSessionControl(0x03);
auto rawReq = serialize(req);
printBytes(rawReq, "Request: 0x10 (Extended Session)");
auto rawResp = simulateEcu(req);
printBytes(rawResp, "Response");
std::vector respData;
if (parsePositiveResponse(rawResp, 0x10, respData)) {
std::cout << "Session control success. SubFunction=0x" << std::hex << (int)respData[0] << std::endl;
}
// 2. 存活测试
req = createTesterPresent();
rawReq = serialize(req);
printBytes(rawReq, "Request: TesterPresent");
rawResp = simulateEcu(req);
printBytes(rawResp, "Response");
parsePositiveResponse(rawResp, 0x3E, respData); // 忽略数据
// 3. 按标识符读VIN
req = createReadDataByIdentifier(0xF190);
rawReq = serialize(req);
printBytes(rawReq, "Request: Read DID 0xF190");
rawResp = simulateEcu(req);
if (parsePositiveResponse(rawResp, 0x22, respData)) {
uint16_t did = (respData[0] << 8) | respData[1];
std::string vin(respData.begin()+2, respData.end());
std::cout << "Read DID 0x" << std::hex << did << " -> VIN: " << vin << std::endl;
}
// 4. 尝试不支持的服务 (0x31 例程控制)
req = createRoutineControlStart(0x0200);
rawReq = serialize(req);
printBytes(rawReq, "Request: Routine 0x0200 start");
rawResp = simulateEcu(req);
parsePositiveResponse(rawResp, 0x31, respData); // 会输出负响应return 0;
}
输出示例:
Request: 0x10 (Extended Session): 10 03
Response: 50 03 50 64
Session control success. SubFunction=0x3
Request: TesterPresent: 3e 00
Response: 7e 00
Request: Read DID 0xF190: 22 f1 90
Response: 62 f1 90 31 48 47 43 4d 38 32 36 33 33 41 31 32 33 34 35 36
Read DID 0xf190 -> VIN: 1HGCM82633A123456
Request: Routine 0x0200 start: 31 01 02 00
Response: 7f 31 10
Negative response: NRC=10
5. 总结UDS为汽车诊断提供了标准化、可扩展的通信框架。本文介绍了核心服务标识符及其分类,详细阐述了诊断会话控制、安全访问、数据传输、上传下载等关键服务的用途和报文格式。通过C++代码示例,展示了如何构建常见UDS请求、模拟ECU响应并解析结果。实际开发中,UDS往往运行在CAN总线上,需要处理多帧传输(ISO 15765-2)、定时参数及安全算法,但本文的基本模式可应用于大多数诊断工具或ECU固件的实现。
掌握UDS服务是理解现代汽车电子架构的基础,开发者可根据ISO 14229标准进一步深入学习每个服务的完整参数和NRC定义。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
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.