网易首页 > 网易号 > 正文 申请入驻

UDS(统一诊断服务)详解与C++代码示例

0
分享至


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 :不支持请求的SID

  • 0x11 :不支持子功能

  • 0x12 :子功能在本次会话无效

  • 0x13 :报文长度错误

  • 0x22 :条件不满足

  • 0x31 :请求超出范围

  • 0x33 :安全访问未解锁

3. 重要服务详解(附带场景) 3.1 诊断会话控制(0x10)
  • 作用 :切换ECU的诊断会话模式(默认会话、编程会话、扩展会话)。不同会话下可用的服务集合不同,例如刷写软件通常在编程会话中完成。

  • 请求格式 : [0x10] [子函数] ,子函数如 0x01 =默认会话, 0x02 =编程会话, 0x03 =扩展会话。

  • 响应格式 : [0x50] [子函数] [会话参数] ,会话参数包括P2ServerMax、P2*ServerMax等定时值。

3.2 安全访问(0x27)
  • 作用 :防止未授权操作。步骤:客户端发送“请求种子”子功能(如 0x01 ),ECU返回种子;客户端基于种子计算密钥并发送“发送密钥”子功能(如 0x02 );若匹配则解锁受保护的服务。

  • 子功能 :奇数请求种子,偶数发送密钥。同一组内奇偶对应(0x01/0x02,0x03/0x04…)。

3.3 存活测试(0x3E)
  • 作用 :客户端定期发送此服务告知ECU自己仍在线,ECU则保持当前会话不自动退回默认会话。子功能 0x00 或 0x80 (带标志位)。

3.4 读取数据(0x22)与写入数据(0x2E)
  • 数据标识符(DID) :两个字节,如 0xF190 表示VIN码。请求 [0x22] [DID_H] [DID_L] ,响应 [0x62] [DID_H] [DID_L] [数据…] 。写入类似,携带待写数据。

3.5 例程控制(0x31)
  • 子函数 : 0x01 启动, 0x02 停止, 0x03 请求结果。例程ID两字节,例如 0x0200 表示自检例程。请求 [0x31] [子函数] [例程ID_H] [例程ID_L] [可选参数] 。

3.6 上传下载(0x34/0x35/0x36/0x37)
  • 典型流程 :

    1. 客户端发送 0x34 (请求下载),描述地址、大小等。

    2. 服务器回复 0x74 ,确认最大块长度。

    3. 客户端循环发送 0x36 (传输数据)。

    4. 最后发送 0x37 (请求退出)。

3.7 负响应(0x7F)

当服务器无法执行请求时,返回[0x7F] [请求SID] [NRC]。例如,若未解锁安全访问就尝试写数据,可能返回0x7F 0x2E 0x33。

4. C++代码示例

以下代码演示如何构建常见的UDS请求,解析响应,并模拟一个简单的诊断会话控制服务。为简化,假设使用单帧传输(不超过CAN帧8字节)。

4.1 基础数据结构与工具函数

#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.2 构建常用服务请求

// 诊断会话控制:子函数 (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.3 解析响应

// 解析正响应(检查是否为请求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;
}
4.4 主程序示例

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.

相关推荐
热点推荐
张家大桥这位领导,被15万网友群嘲了……

张家大桥这位领导,被15万网友群嘲了……

麦杰逊
2026-09-29 11:00:09
僵持一年!50人代表团抵京,高市万万没想到,中国给了212吨稀土

僵持一年!50人代表团抵京,高市万万没想到,中国给了212吨稀土

徐云流浪中国
2026-09-29 13:29:03
网红"富婆"人设崩了,镜头前的钻石是塑料的,她承认剧本是编的

网红"富婆"人设崩了,镜头前的钻石是塑料的,她承认剧本是编的

一盅情怀
2026-09-29 12:55:43
教育部:坚决拥护党中央决定

教育部:坚决拥护党中央决定

新京报
2026-09-28 21:37:16
餐车碰到男子手肘,空姐下跪后还被要求踹一脚?涉事航空公司员工:不清楚,已上报此事

餐车碰到男子手肘,空姐下跪后还被要求踹一脚?涉事航空公司员工:不清楚,已上报此事

极目新闻
2026-09-30 01:12:25
举报带来的道德崩坏,在网上炸开了!

举报带来的道德崩坏,在网上炸开了!

新动察
2026-09-29 15:16:40
赵本山落网后屋内搜出20吨黄金,暗中移民海外?私生活谣言太离谱

赵本山落网后屋内搜出20吨黄金,暗中移民海外?私生活谣言太离谱

秋姐居
2026-09-29 11:31:44
黄仁勋回应中国实验室蒸馏:他们每天都在拆我的产品

黄仁勋回应中国实验室蒸馏:他们每天都在拆我的产品

我是一个养虾人
2026-09-29 06:07:01
利曼数百俄军集体投降照引爆俄网,俄军顿巴斯假捷报遭事实清算

利曼数百俄军集体投降照引爆俄网,俄军顿巴斯假捷报遭事实清算

火星宏观
2026-09-29 10:38:10
40岁纽约时报总监遭7旬华裔岳父母枪杀,案发原因疑为不满女儿外孙遭受家暴,老夫妇轮流开枪,杀人后平静离开现场

40岁纽约时报总监遭7旬华裔岳父母枪杀,案发原因疑为不满女儿外孙遭受家暴,老夫妇轮流开枪,杀人后平静离开现场

大风新闻
2026-09-29 15:50:25
坚决反对删除“敌国条款”,中国代表两问日方

坚决反对删除“敌国条款”,中国代表两问日方

澎湃新闻
2026-09-29 15:16:59
陈妤颉霸气和汶颂兑子,成史上最年轻亚洲短跑女皇,剑指亚运MVP

陈妤颉霸气和汶颂兑子,成史上最年轻亚洲短跑女皇,剑指亚运MVP

杨华评论
2026-09-29 21:17:32
伊朗革命卫队发言人:美国若想结束战争则必须承认战败

伊朗革命卫队发言人:美国若想结束战争则必须承认战败

新京报
2026-09-29 17:30:08
台上唱到一半裙子化了,奥利维亚这身造型藏了3天秘密

台上唱到一半裙子化了,奥利维亚这身造型藏了3天秘密

娱圈观察员
2026-09-29 01:08:53
中国男排3-2力克韩国,3连胜头名晋级亚运男排8强,将战泰国男排

中国男排3-2力克韩国,3连胜头名晋级亚运男排8强,将战泰国男排

侧身凌空斩
2026-09-29 21:39:32
多所高校发文“过紧日子”,北大明确禁赴黄山、九寨沟、三亚海滨等21个景区开会

多所高校发文“过紧日子”,北大明确禁赴黄山、九寨沟、三亚海滨等21个景区开会

山西晚报
2026-09-29 10:48:20
中国第二轮“严打”来了,这次更严格,查到的一个都跑不了!

中国第二轮“严打”来了,这次更严格,查到的一个都跑不了!

职场资深秘书
2026-09-29 15:09:05
北理工裴轶被解聘,她老公换马甲挣外快的事更该严查

北理工裴轶被解聘,她老公换马甲挣外快的事更该严查

历史总在押韵
2026-09-29 21:45:04
广西政协原副主席彭晓春一审被判死缓:受贿2.63亿,已认罪悔罪

广西政协原副主席彭晓春一审被判死缓:受贿2.63亿,已认罪悔罪

界面新闻
2026-09-29 17:13:10
随着中国男排3-2韩国,诞生5个不可思议,第五个最让人开心

随着中国男排3-2韩国,诞生5个不可思议,第五个最让人开心

南海浪花
2026-09-30 00:37:45
2026-09-30 03:00:49
新能源自动驾驶 incentive-icons
新能源自动驾驶
专注于半导体行业资讯
1016文章数 348关注度
往期回顾 全部

科技要闻

82亿美元收购!李飞飞将与苏姿丰并肩做AI

头条要闻

空姐双膝下跪“还被要求踹一脚” 现场画面流出

头条要闻

空姐双膝下跪“还被要求踹一脚” 现场画面流出

体育要闻

石雨豪:亚运金牌与背后的十年

娱乐要闻

赌王四房婚礼,何超琼带三房成员现身

财经要闻

央行调整完善若干货币政策工具

汽车要闻

搭华为乾崑ADS 5/设计风格换新 2027款纵横G700售30.49万起

态度原创

家居
亲子
数码
房产
公开课

家居要闻

2026建博会(广州) 公装联探展交流活动

亲子要闻

小朋友的语言体系真的能把人萌翻!网友:我都不知道咋接话了!

数码要闻

苹果iPhone 18 Pro系列体验

房产要闻

三亚楼市下半场,被一次交付和一座艺术馆讲透了

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版