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

UDS统一诊断服务详解:核心机制、DoCAN传输与C++代码实现

0
分享至


一、UDS 概述与定位 1.1 什么是UDS?

UDS(Unified Diagnostic Services)是ISO 14229定义的应用层诊断协议,用于汽车电子控制单元(ECU)的故障读取、数据通信、例程控制、软件升级等。

1.2 UDS 与 OBD 的区别

项目

OBD

UDS

目的

排放相关强制诊断

全车ECU增强诊断

范围

有限(发动机、排放)

所有ECU(BCM、GW、BMS等)

扩展性

固定服务

支持自定义服务

网络层

ISO 15765-4

ISO 15765-2(DoCAN)


1.3 DoCAN 协议栈

  • 应用层:UDS(ISO 14229)

  • 网络层:ISO 15765-2(多帧传输)

  • 数据链路层:CAN(ISO 11898)

二、核心通信机制 2.1 请求与响应模型
  • 请求 :SID + SubFunction + 参数

  • 肯定响应 :SID + 0x40 + 参数

  • 否定响应 :0x7F + SID + NRC

示例:
  • 请求读取DID 0xF190 : 22 F1 90

  • 肯定响应: 62 F1 90 01 02 03

  • 否定响应: 7F 22 13 (无效长度)

三、C++ 实现示例(DoCAN + UDS 核心服务) 3.1 数据结构定义

#include  
          
#include
#include
#include

using Byte = uint8_t;
using Bytes = std::vector ;

// UDS 服务 ID(部分)
enum class UDS_SID : Byte {
DIAG_SESSION_CTRL = 0x10,
ECU_RESET = 0x11,
READ_DATA = 0x22,
WRITE_DATA = 0x2E,
SECURITY_ACCESS = 0x27,
TESTER_PRESENT = 0x3E,
ROUTINE_CTRL = 0x31,
READ_DTC = 0x19,
CLEAR_DTC = 0x14
};

// NRC
enum class NRC : Byte {
OK = 0x00,
GENERAL_REJECT = 0x10,
SERVICE_NOT_SUPPORTED = 0x11,
INVALID_FORMAT = 0x13,
CONDITIONS_NOT_CORRECT = 0x22,
SECURITY_ACCESS_DENIED = 0x33,
INVALID_KEY = 0x35
};
3.2 ISO 15765-2 网络层简版封装

// 帧类型
enum class N_PCI_TYPE : Byte {
SINGLE = 0x00,
FIRST = 0x10,
CONSECUTIVE = 0x20,
FLOW_CTRL = 0x30
};

// 单帧/多帧处理(简单示例)
class DoCANTransport {
public:
static Bytes packRequest(const Bytes& udsReq) {
if (udsReq.size() <= 7) {
// 单帧
Byte pci = static_cast (N_PCI_TYPE::SINGLE) | (Byte)udsReq.size();
Bytes frame = {pci};
frame.insert(frame.end(), udsReq.begin(), udsReq.end());
return frame;
} else {
// 简化:实际需拆分为 FF/CF/FC,此处仅示意
std::cout << "[DoCAN] Multi-frame not fully implemented\n";
return {};
}
}

static Bytes unpackResponse(const Bytes& canFrame) {
if (canFrame.empty()) return {};
Byte pci = canFrame[0];
N_PCI_TYPE type = static_cast (pci & 0xF0);
if (type == N_PCI_TYPE::SINGLE) {
int len = pci & 0x0F;
return Bytes(canFrame.begin() + 1, canFrame.begin() + 1 + len);
}
// 实际需重组多帧
return {};
}
};
3.3 UDS 基础处理类

class UDSHandler {
private:
std::map std ::function const Bytes&)>> handlers;
bool securityLocked = true ;

public :
UDSHandler() {
// 注册服务
handlers[UDS_SID::READ_DATA] = [ this ]( const Bytes& req) { return handleReadData(req); };
handlers[UDS_SID::DIAG_SESSION_CTRL] = [ this ]( const Bytes& req) { return handleSessionCtrl(req); };
handlers[UDS_SID::TESTER_PRESENT] = [ this ]( const Bytes& req) { return handleTesterPresent(req); };
}

// 主处理入口
Bytes processRequest(const Bytes& udsReq) {
if (udsReq.empty()) return buildNegativeResponse( 0x00 , NRC::GENERAL_REJECT);
Byte sid = udsReq[ 0 ];
UDS_SID service = static_cast (sid);
if (handlers.find(service) == handlers.end()) {
return buildNegativeResponse(sid, NRC::SERVICE_NOT_SUPPORTED);
}
return handlers[service](udsReq);
}

private :
// 否定响应
Bytes buildNegativeResponse(Byte sid, NRC nrc) {
return { 0x7F , sid, static_cast (nrc)};
}

// 肯定响应
Bytes buildPositiveResponse(Byte sid, const Bytes& data = {}) {
Bytes resp;
resp.push_back(sid + 0x40 );
resp.insert(resp.end(), data.begin(), data.end());
return resp;
}

// 22 读数据
Bytes handleReadData(const Bytes& req) {
if (req.size() < 3 ) return buildNegativeResponse( 0x22 , NRC::INVALID_FORMAT);
Byte didHigh = req[ 1 ];
Byte didLow = req[ 2 ];
uint16_t did = (didHigh << 8 ) | didLow;

// 模拟数据字典
if (did == 0xF190 ) {
return buildPositiveResponse( 0x22 , { 0xF1 , 0x90 , 0x01 , 0x02 , 0x03 });
} else if (did == 0xF187 ) {
return buildPositiveResponse( 0x22 , { 0xF1 , 0x87 , 'V' , '1' , '.' , '0' });
}
return buildNegativeResponse( 0x22 , NRC::GENERAL_REJECT);
}

// 10 会话控制
Bytes handleSessionCtrl(const Bytes& req) {
if (req.size() < 2 ) return buildNegativeResponse( 0x10 , NRC::INVALID_FORMAT);
Byte subfunc = req[ 1 ];
// 抑制肯定响应检查(bit7 = 1 时不发响应)
bool suppressResp = (subfunc & 0x80 ) != 0 ;
Byte session = subfunc & 0x7F ;

std :: cout << "[UDS] Switch to session: " << std ::hex << ( int )session << std :: endl ;
if (!suppressResp) {
return buildPositiveResponse( 0x10 , {subfunc, 0x00 , 0x32 , 0x01 , 0xF4 }); // 仿真实例
}
return {}; // 无响应
}

// 3E 待机握手
Bytes handleTesterPresent(const Bytes& req) {
bool suppress = (req.size() > 1 ) && ((req[ 1 ] & 0x80 ) != 0 );
if (!suppress) {
return buildPositiveResponse( 0x3E , {});
}
return {};
}
};
3.4 主程序示例(诊断请求模拟)

int main() {
UDSHandler uds;
DoCANTransport canLayer;

// 模拟发送:22 F1 90 读取 DID 0xF190
Bytes udsReq = {0x22, 0xF1, 0x90};
Bytes canFrame = DoCANTransport::packRequest(udsReq);

std::cout << "Send CAN: ";
for (auto b : canFrame) printf("%02X ", b);
std::cout << std::endl;

// 模拟 ECU 收到 CAN 帧,解包得到 UDS 请求
Bytes receivedUdsReq = DoCANTransport::unpackResponse(canFrame);
Bytes udsResp = uds.processRequest(receivedUdsReq);

std::cout << "UDS Response: ";
for (auto b : udsResp) printf("%02X ", b);
std::cout << std::endl;

// 测试会话切换(抑制响应)
Bytes sessionReq = {0x10, 0x83}; // subfunc 0x83 = 0x03 + 抑制bit
canFrame = DoCANTransport::packRequest(sessionReq);
receivedUdsReq = DoCANTransport::unpackResponse(canFrame);
udsResp = uds.processRequest(receivedUdsReq);
if (udsResp.empty()) {
std::cout << "[TesterPresent] No response (suppressed)" << std::endl;
}

return 0;
}
输出示例:

Send CAN: 03 22 F1 90 
UDS Response: 62 F1 90 01 02 03
[UDS] Switch to session: 3
[TesterPresent] No response (suppressed)
四、关键服务实现要点 4.1 $27 安全访问(种子密钥简化)

class SecureUDSHandler : public UDSHandler {
int seed = 0x1234;
int expectedKey = 0x5678; // 实际应为算法:key = seed ^ 0x5555


Bytes handleSecurityAccess(const Bytes& req) {
Byte subfunc = req[1];
if (subfunc == 0x05) { // 请求种子
return buildPositiveResponse(0x27, {0x05, (Byte)(seed >> 8), (Byte)seed});
} else if (subfunc == 0x06) { // 发送密钥
int key = (req[2] << 8) | req[3];
if (key == expectedKey) {
securityLocked = false;
return buildPositiveResponse(0x27, {0x06});
}
return buildNegativeResponse(0x27, NRC::INVALID_KEY);
}
return buildNegativeResponse(0x27, NRC::INVALID_FORMAT);
}
};
4.2 14 DTC 处理示例

// 简化 DTC 读取(按状态掩码 0xFF)
Bytes handleReadDTC(const Bytes& req) {
if (req.size() >= 2 && req[1] == 0x02) {
// 模拟返回 2 个 DTC
return {0x59, 0x02,
0x01, 0x23, 0x45, 0x80, // DTC 1 + status
0x01, 0x67, 0x89, 0x20}; // DTC 2
}
return buildNegativeResponse(0x19, NRC::INVALID_FORMAT);
}
五、总结

特性

描述

标准化

UDS 统一了诊断服务格式,减少重复开发

灵活性

支持自定义 DID、Routine、安全算法

可扩展

基于 ISO 15765-2 可承载任意长度诊断数据

工程落地

现代汽车诊断工具(CANoe、PCAN、ZLG)均支持 UDS + DoCAN

本 C++ 示例展示了:

  • UDS 请求/响应处理框架

  • DoCAN 单帧打包/解包

  • 22/$3E 服务的简化实现

  • 否定响应与抑制响应机制

实际 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.

相关推荐
热点推荐
差严子怡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系列国内销量上涨

艺术要闻

致敬,刘欢硬笔书法欣赏

公开课

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

军事要闻

特朗普"罕见"提二战中美同盟 日本慌了

无障碍浏览 进入关怀版