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

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.

相关推荐
热点推荐
张雪一句“鄙视你”,泰康秒赔8万:他不是突然良心发现,而是突然发现“不要脸的成本”太高了

张雪一句“鄙视你”,泰康秒赔8万:他不是突然良心发现,而是突然发现“不要脸的成本”太高了

迷世书童
2026-09-27 10:52:03
医院老护士含泪揭秘:人临终前的10个残酷真相,很多子女不懂

医院老护士含泪揭秘:人临终前的10个残酷真相,很多子女不懂

蝉吟槐蕊
2026-09-13 18:15:04
25岁女子吐槽河北婆婆做“糊弄饭”,老公不耐烦了:爱吃吃不吃滚

25岁女子吐槽河北婆婆做“糊弄饭”,老公不耐烦了:爱吃吃不吃滚

许三岁
2026-09-28 02:00:08
小布披露绝望输球内幕下周取代吴易昺,王左亚运血案含金量引热议

小布披露绝望输球内幕下周取代吴易昺,王左亚运血案含金量引热议

网球之家
2026-09-28 10:27:54
低价“0公里二手车”连环骗局爆雷,上千车主或面临钱车两空

低价“0公里二手车”连环骗局爆雷,上千车主或面临钱车两空

凤凰网财经
2026-09-27 22:01:48
4次扑救哈兰德+非常规手型!科斯塔9.4分,葡萄牙换代成功了

4次扑救哈兰德+非常规手型!科斯塔9.4分,葡萄牙换代成功了

赵或是个热血青年
2026-09-28 11:28:49
西方不服气,为何中国没发动任何战争,却成为世界一流经济强国

西方不服气,为何中国没发动任何战争,却成为世界一流经济强国

纪史行者
2026-09-28 08:33:10
刘欢临终细节被曝!含泪离世,老婆女儿都守在身旁,母女俩太痛苦

刘欢临终细节被曝!含泪离世,老婆女儿都守在身旁,母女俩太痛苦

观察鉴娱
2026-09-27 10:19:27
张杰谢娜一家五口出游,仨女儿都坐推车不带腿,有专属摄影师摄像

张杰谢娜一家五口出游,仨女儿都坐推车不带腿,有专属摄影师摄像

阿废冷眼观察所
2026-09-28 00:02:06
喜讯!U23国足进入亚运会四强之后,足协就可能跟多位强援完成续约

喜讯!U23国足进入亚运会四强之后,足协就可能跟多位强援完成续约

振刚说足球
2026-09-28 10:05:42
亚运会女单金牌得主诞生!王曼昱4-2险胜孙颖莎,摘得生涯第2冠

亚运会女单金牌得主诞生!王曼昱4-2险胜孙颖莎,摘得生涯第2冠

乒谈
2026-09-27 20:58:07
彻底崩盘!曼联 6000 万水货混日子!名宿怒喷卡里克赶紧清洗

彻底崩盘!曼联 6000 万水货混日子!名宿怒喷卡里克赶紧清洗

澜归序
2026-09-28 09:11:57
人无法共情以前自己买的那些东西。网友:三个垃圾桶1700。

人无法共情以前自己买的那些东西。网友:三个垃圾桶1700。

天气观察站
2026-09-27 16:03:26
气愤!刘翔师弟夺冠后怒斥:日本电视台没把我当回事 日本选手无视我

气愤!刘翔师弟夺冠后怒斥:日本电视台没把我当回事 日本选手无视我

念洲
2026-09-27 20:49:20
李谷一不再隐瞒,揭晓宋祖英走上高位的真相,观众:原来如此!

李谷一不再隐瞒,揭晓宋祖英走上高位的真相,观众:原来如此!

林轻吟
2026-07-02 19:19:19
中秋假期游客在秦皇岛乘坐海上索道被淋成落汤鸡,景区:下雨后暂停了一会索道,淋到雨的游客是在下雨前上去的

中秋假期游客在秦皇岛乘坐海上索道被淋成落汤鸡,景区:下雨后暂停了一会索道,淋到雨的游客是在下雨前上去的

台州交通广播
2026-09-27 19:31:25
一投提前下班!严子怡大幅提升赛会纪录,夺亚运会女子标枪金牌

一投提前下班!严子怡大幅提升赛会纪录,夺亚运会女子标枪金牌

全景体育V
2026-09-27 21:01:43
如果莉莉贝特真长这样!丝毫不输夏洛特公主,皮肤还更白嫩!

如果莉莉贝特真长这样!丝毫不输夏洛特公主,皮肤还更白嫩!

艺利森
2026-09-28 09:07:46
美方同意放行伊朗总统,专机起飞后怕生变故,全程未在第三国停靠

美方同意放行伊朗总统,专机起飞后怕生变故,全程未在第三国停靠

你是我心中最美星空
2026-09-27 20:36:09
案例:湖南女护士被执行死刑,行刑时回头质问武警,让他当场崩溃

案例:湖南女护士被执行死刑,行刑时回头质问武警,让他当场崩溃

星宇共鸣
2025-09-17 17:58:03
2026-09-28 12:08:49
新能源自动驾驶 incentive-icons
新能源自动驾驶
专注于半导体行业资讯
1015文章数 348关注度
往期回顾 全部

科技要闻

智能体惹了多少祸,OpenAI自己没查清!

头条要闻

媒体:俄杜马选举普京获压倒性胜利 俄乌冲突有2大走向

头条要闻

媒体:俄杜马选举普京获压倒性胜利 俄乌冲突有2大走向

体育要闻

114项指控成立,曼城已经完蛋了吗?

娱乐要闻

好汉刘欢:一副好嗓子,一颗赤子心

财经要闻

标价298元核桃油, 实为大豆油冒充

汽车要闻

神龙科技与Momenta达成全球战略合作 2027年起推新车

态度原创

房产
教育
游戏
艺术
公开课

房产要闻

全年霸榜TOP1!南海・叁號院周年官宣:新作即将登场

教育要闻

官宣!成都启动学区制治理改革

外媒为PlayStation提出五项建议!关注旧作与实体版

艺术要闻

致敬,刘欢硬笔书法欣赏

公开课

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

无障碍浏览 进入关怀版