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

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-28 09:06:08
洛夫顿离开上海得不偿失!发文开启勿扰模式,重返大鲨鱼障碍重重

洛夫顿离开上海得不偿失!发文开启勿扰模式,重返大鲨鱼障碍重重

体育见习官
2026-09-28 07:58:50
时隔两年半冲突视频曝光!斯图尔特挥拳尤班克斯 曾因此被禁赛3场

时隔两年半冲突视频曝光!斯图尔特挥拳尤班克斯 曾因此被禁赛3场

罗说NBA
2026-09-28 06:21:57
曼联无意让卡里克下课,重用拉什福德起作用!聘拉爵心仪主帅需1500万

曼联无意让卡里克下课,重用拉什福德起作用!聘拉爵心仪主帅需1500万

罗米的曼联博客
2026-09-28 10:14:03
CBA速递,上海大鲨鱼锁定麦基,六十万美元非常平价,三剑客浮现

CBA速递,上海大鲨鱼锁定麦基,六十万美元非常平价,三剑客浮现

体坛小快灵
2026-09-28 08:43:41
“升糖直接爆了”,家长晒儿子上学早餐,网友:吃完了倒头就睡

“升糖直接爆了”,家长晒儿子上学早餐,网友:吃完了倒头就睡

泽泽先生
2026-09-21 13:37:01
刘欢病逝2天,别墅、版权收入等资产曝光,遗产分配成焦点

刘欢病逝2天,别墅、版权收入等资产曝光,遗产分配成焦点

往史过眼云烟
2026-09-27 16:40:27
儿子作文里写:“我爸爸妈妈从来不吵架,但他们也不怎么说话”

儿子作文里写:“我爸爸妈妈从来不吵架,但他们也不怎么说话”

自由心向往之
2026-09-25 08:30:13
拒绝网红脸!央视秋晚四美火出圈,观众:这才是真审美

拒绝网红脸!央视秋晚四美火出圈,观众:这才是真审美

阿废冷眼观察所
2026-09-27 12:56:52
国家统计局:2026年1—8月份全国规模以上工业企业利润增长15.7%

国家统计局:2026年1—8月份全国规模以上工业企业利润增长15.7%

界面新闻
2026-09-28 09:31:49
台湾提出和统条件:一国三制,“外交权”和拥有军队?是痴心妄想

台湾提出和统条件:一国三制,“外交权”和拥有军队?是痴心妄想

老头的传奇色彩
2026-08-06 17:59:31
广州一工人误踩8层自建房楼顶绿色帆布坠入电梯井,连破两道安全网跌至一楼,调查报告披露:承包人无资质冒用他人名义,未设警示标志

广州一工人误踩8层自建房楼顶绿色帆布坠入电梯井,连破两道安全网跌至一楼,调查报告披露:承包人无资质冒用他人名义,未设警示标志

扬子晚报
2026-09-28 10:35:00
红薯正大量上市!研究发现:糖尿病吃一次红薯,等于给血糖添堵?

红薯正大量上市!研究发现:糖尿病吃一次红薯,等于给血糖添堵?

老李讲健康
2026-09-28 07:30:07
“磨膝大户”被揪出,是跑步的20倍,医生:不想软骨磨光,早扔掉

“磨膝大户”被揪出,是跑步的20倍,医生:不想软骨磨光,早扔掉

看世界的人
2026-09-20 22:04:47
王思聪坐香奈儿VIP贵宾室,懒懒旁边试包,消费降级传闻又被打脸

王思聪坐香奈儿VIP贵宾室,懒懒旁边试包,消费降级传闻又被打脸

梦醉为红颜一笑
2026-09-27 14:27:39
骁龙8EE6+11000mAh!荣耀新机官宣,9月28日发布

骁龙8EE6+11000mAh!荣耀新机官宣,9月28日发布

科技堡垒
2026-09-28 10:19:38
“抱歉,我们不喜欢肥头大耳的”,女孩公开追求体制内男,面相输了

“抱歉,我们不喜欢肥头大耳的”,女孩公开追求体制内男,面相输了

世界圈
2026-08-15 10:29:14
新央企首任董事长,未公布被调查就直接“双开”

新央企首任董事长,未公布被调查就直接“双开”

评论员岳连
2026-09-27 20:54:46
一个国家能蠢到什么程度?看看法国就明白了:法国黑人接近800万,巴黎新生儿里70%是黑人

一个国家能蠢到什么程度?看看法国就明白了:法国黑人接近800万,巴黎新生儿里70%是黑人

怪味历史连连看
2026-09-08 01:55:32
越老越妖,梅西30岁后罚进43粒直接任意球,比30岁前多10粒

越老越妖,梅西30岁后罚进43粒直接任意球,比30岁前多10粒

懂球帝
2026-09-28 10:05:05
2026-09-28 11:35:00
新能源自动驾驶 incentive-icons
新能源自动驾驶
专注于半导体行业资讯
1015文章数 348关注度
往期回顾 全部

科技要闻

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

头条要闻

牛弹琴:内塔尼亚胡有大麻烦了 不是命硬就能扛过去的

头条要闻

牛弹琴:内塔尼亚胡有大麻烦了 不是命硬就能扛过去的

体育要闻

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

娱乐要闻

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

财经要闻

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

汽车要闻

MAZDA EZ-60激光版上市 焕新全系11.39万元起

态度原创

健康
手机
旅游
艺术
公开课

鼻子三角区的痘,千万别乱挤!

手机要闻

涨价了照样买 iPhone 18 Pro系列国内销量上涨

旅游要闻

一组AI动态海报,解锁四川文旅融合鲜活场景|2026四川文旅大会特别策划

艺术要闻

致敬,刘欢硬笔书法欣赏

公开课

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

无障碍浏览 进入关怀版