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

UDS诊断服务处理详解:DCM模块的设计与实现(以0x22服务为例)

0
分享至


1. 概述

在AUTOSAR架构中,DCM(Diagnostic Communication Manager) 是诊断通信的核心模块,负责解析UDS协议、执行诊断服务并管理ECU的诊断状态。它就像ECU的“诊断大脑”,协调从底层接收到数据、执行服务、生成响应并发送的全流程。

本文以 0x22 ReadDataByIdentifier(按标识符读取数据) 服务为例,深入讲解DCM内部的处理机制,并提供可运行的C++示例代码。

2. UDS诊断栈中的DCM位置

诊断报文通过物理层(CAN/LIN/ETH)→ 网络层(CanTp)逐级向上递交。CanTp 负责将长报文重组为完整的UDS请求数据。当CanTp完成重组后,会调用DCM的入口函数(例如 Dcm_MainFunction 或 Dcm_ProcessRequest),将完整请求数据传递给DCM。

DCM承担以下职责:

  • 解析服务ID、子功能、寻址方式

  • 会话与安全管理

  • 请求格式与参数有效性检查

  • 调用具体服务处理函数

  • 构造响应(正响应或否定响应)并向下传递

3. 服务解析与检查流程(以0x22为例)

假设诊断仪发送了一条物理寻址的 22 11 22 请求,其中:

  • 服务ID = 0x22 (ReadDataByIdentifier)

  • DID(Data Identifier) = 0x1122

DCM会按顺序执行以下检查:

3.1 提取服务ID

从请求数据 [22, 11, 22] 中取出首字节 0x22,识别为 ReadDataByIdentifier 服务。

3.2 寻址方式判定

DCM从底层接收的PDU信息中获取CAN ID。如果CAN ID属于诊断物理寻址范围,则判定为物理寻址,要求ECU必须单独响应;如果是功能寻址,则需要根据UDS协议中“抑制正响应位”(在子功能中)决定是否响应(避免总线冲突)。为简化,本例假定为物理寻址,总是需要响应。

3.3 会话与安全检查

DCM内部维护当前诊断会话类型(默认会话0x01、编程会话0x02等)以及安全等级(Secured 或 Unlocked)。对于 0x22 服务:

  • 通常允许在默认会话下读取非敏感DID

  • 但某些安全关键DID(如VIN、序列号、加密密钥)要求ECU处于 解锁状态 或 特定会话

如果当前状态不满足要求,DCM立即构造否定响应,例如:

  • 0x7F 22 0x7E —— 子功能不支持或当前会话下不允许

  • 0x7F 22 0x33 —— 安全访问未通过

3.4 请求格式检查

0x22 服务的标准请求格式为 [服务ID, DID高字节, DID低字节],总长度应为3字节。若请求长度不足3字节,则返回否定响应:

  • 0x7F 22 0x13 —— 报文长度错误

3.5 DID有效性检查

DCM查询本地配置的DID列表(例如通过配置表或回调函数表)。如果 0x1122 未在ECU中定义,则返回否定响应:

  • 0x7F 22 0x31 —— 请求超出范围

若以上检查全部通过,DCM转入数据获取阶段。

4. DCM内部层次化调用结构

AUTOSAR DCM模块采用分层设计,将服务分发、参数校验、数据获取解耦。典型的调用层次如下:

Dcm_ProcessRequest (主入口)
├─ Dcm_MainStateMachine (状态机:空闲 -> 请求处理)
├─ Dcm_ServiceTableLookup (根据服务ID查找处理函数)
├─ Dcm_Service_ReadDataByIdentifier (服务级处理)
│ ├─ 执行服务通用检查(子功能支持等)
│ ├─ 提取DID
│ ├─ 调用 DID分发函数 Dcm_DID_ReadHandler
│ └─ 若失败则生成否定响应
└─ Dcm_DID_ReadHandler_0x1122 (DID级处理)
├─ 调用RTE端口读取应用数据(如传感器值)
├─ 或调用NvM读取存储块(如VIN)
└─ 组装正响应数据 [0x62, DID高, DID低, 数据...]

这种层次化设计的优点:

  • 高扩展性 :新增DID只需添加一个处理回调,无需修改核心解析逻辑。

  • 职责分离 :服务级处理负责通用逻辑,DID级处理负责具体数据访问。

  • 可配置性 :通过配置表即可控制DID的有效性、安全要求等。

5. C++代码示例(模拟DCM处理0x22服务)

以下代码模拟了一个简化但完整的DCM模块,实现了物理寻址下的 0x22 服务处理。代码包含了:请求检查、否定响应生成、服务分发、DID回调、数据获取。

#include  
          
#include
#include
#include
#include

// 模拟诊断请求和响应的数据结构
struct DiagRequest {
std::vector data; // 收到的UDS请求数据(不含CAN ID)
bool isFunctionalAddress; // true=功能寻址, false=物理寻址
};

struct DiagResponse {
std::vector data;
bool isPositive = false; // true=正响应, false=否定响应
};

// ECU当前诊断状态(简化)
class EcuDiagnosticState {
public:
enum SessionType { DEFAULT_SESSION = 0x01, PROGRAMMING_SESSION = 0x02 };
enum SecurityLevel { SECURED, UNLOCKED };

SessionType currentSession = DEFAULT_SESSION;
SecurityLevel currentSecurity = SECURED;

bool isSecurityAccessAllowedForDid(uint16_t did) const {
// 模拟:DID 0x1122 是安全相关的,需要解锁
if (did == 0x1122) {
return (currentSecurity == UNLOCKED);
}
// 其他DID默认允许
return true;
}
};

// 数据标识符回调函数类型:输入DID,输出数据字节流
using DidReadCallback = std::function(uint16_t did)>;

// DCM模块
class Dcm {
public:
Dcm() {
// 注册DID及对应的读取回调
registerDid(0x1122, [this](uint16_t did) -> std::vector {
// 模拟通过RTE读取应用数据(例如发动机转速,占2字节)
uint16_t dummyEngineSpeed = 3500; // 单位RPM
return { static_cast((dummyEngineSpeed >> 8) & 0xFF),
static_cast(dummyEngineSpeed & 0xFF) };
});
registerDid(0xF190, [this](uint16_t did) -> std::vector {
// 模拟从NvM读取VIN(17字节ASCII)
std::string vin = "WDD12345678901234";
return std::vector(vin.begin(), vin.end());
});
}

// 外部调用接口:CanTp递交流程
DiagResponse processRequest(const DiagRequest& request) {
// 1. 主状态机:空闲 -> 请求处理
std::cout << "[DCM] 进入请求处理状态" << std::endl;

// 2. 服务分发:根据服务ID查找处理函数
if (request.data.empty()) {
return createNegativeResponse(0x00, 0x13); // 报文长度错误
}
uint8_t serviceId = request.data[0];
auto it = serviceHandlers.find(serviceId);
if (it == serviceHandlers.end()) {
return createNegativeResponse(serviceId, 0x11); // 服务不支持
}

// 3. 调用对应服务的处理函数
return it->second(request);
}

private:
// 服务处理函数表
std::map> serviceHandlers;

// DID回调表
std :: map < uint16_t , DidReadCallback> didCallbacks;

// ECU状态
EcuDiagnosticState diagState;

// 注册DID读取回调
void registerDid(uint16_t did, DidReadCallback callback) {
didCallbacks[did] = callback;
}

// 创建否定响应
DiagResponse createNegativeResponse(uint8_t serviceId, uint8_t nrc) {
DiagResponse resp;
resp.isPositive = false ;
resp.data = { 0x7F , serviceId, nrc };
return resp;
}

// 创建正响应(0x62服务负责拼接)
DiagResponse createPositiveResponseForReadByIdentifier(uint16_t did, const std::vector& data) {
DiagResponse resp;
resp.isPositive = true ;
resp.data.push_back( 0x62 ); // 正响应服务ID = 0x62
resp.data.push_back( static_cast < uint8_t >((did >> 8 ) & 0xFF ));
resp.data.push_back( static_cast < uint8_t >(did & 0xFF ));
resp.data.insert(resp.data.end(), data.begin(), data.end());
return resp;
}

// 服务级处理:0x22 ReadDataByIdentifier
DiagResponse handleReadDataByIdentifier(const DiagRequest& request) {
// 1. 请求格式检查(最小长度3字节)
if (request.data.size() < 3 ) {
std :: cout << "[DCM] 错误: 0x22请求长度不足" << std :: endl ;
return createNegativeResponse( 0x22 , 0x13 ); // 报文长度错误
}

// 2. 提取DID
uint16_t did = ( static_cast < uint16_t >(request.data[ 1 ]) << 8 ) | request.data[ 2 ];

// 3. 会话与安全检查
// (寻址方式简化:假设物理寻址总是需要响应,且无抑制位)
if (!diagState.isSecurityAccessAllowedForDid(did)) {
std :: cout << "[DCM] 错误: DID 0x" << std ::hex << did << " 需要解锁" << std :: endl ;
return createNegativeResponse( 0x22 , 0x33 ); // 安全访问未通过
}

// 可选:检查当前会话是否允许此服务(默认会话允许0x22,编程会话也允许,此例略)

// 4. DID有效性检查(是否已注册回调)
auto it = didCallbacks.find(did);
if (it == didCallbacks.end()) {
std :: cout << "[DCM] 错误: DID 0x" << std ::hex << did << " 未配置" << std :: endl ;
return createNegativeResponse( 0x22 , 0x31 ); // 请求超出范围
}

// 5. 调用DID级处理回调获取数据
std :: vector < uint8_t > data;
try {
data = it->second(did);
} catch (...) {
std :: cout << "[DCM] 错误: DID 0x" << std ::hex << did << " 读取失败" << std :: endl ;
return createNegativeResponse( 0x22 , 0x22 ); // 条件不满足(数据暂时不可用)
}

// 6. 构造正响应
std :: cout << "[DCM] 成功读取DID 0x" << std ::hex << did << ",数据长度=" << std ::dec << data.size() << std :: endl ;
return createPositiveResponseForReadByIdentifier(did, data);
}

// 构造函数中注册服务处理函数
void initServiceHandlers() {
serviceHandlers[ 0x22 ] = [ this ]( const DiagRequest& req) {
return handleReadDataByIdentifier(req);
};
}

public :
Dcm() {
initServiceHandlers();
}
};
5.1 使用示例

int main() {
Dcm dcm;

// 模拟物理寻址请求: 0x22 0x11 0x22
DiagRequest req;
req.data = {0x22, 0x11, 0x22};
req.isFunctionalAddress = false; // 物理寻址

DiagResponse resp = dcm.processRequest(req);
std::cout << "响应: ";
for (auto b : resp.data) {
std::cout << std::hex << (int)b << " ";
}
std::cout << std::endl;
// 预期输出正响应: 62 11 22 xx xx (xx xx为发动机转速数据)
return 0;
}

输出示例:

[DCM] 进入请求处理状态
[DCM] 错误: DID 0x1122 需要解锁
响应: 7f 22 33

若先执行安全解锁(代码中未展现,可通过31服务等实现),再次请求则可获得正响应。

6. 否定响应码(NRC)一览

NRC

含义

触发场景

0x11

ServiceNotSupported

服务ID未在服务表中注册

0x13

IncorrectMessageLength

请求报文长度不符合UDS规范

0x22

ConditionsNotCorrect

DID数据暂时不可读(如未就绪)

0x31

RequestOutOfRange

DID未配置或超出范围

0x33

SecurityAccessDenied

DID要求解锁而未解锁

0x7E

SubFunctionNotSupported

服务不支持当前会话


7. 总结与扩展

本文通过一个简化的C++ DCM实现,展示了UDS 0x22 服务的检查流程与层次化调用结构。真实AUTOSAR DCM远比此复杂,还包括:

  • 功能寻址抑制位处理 ( suppressPosRspMsgIndicationBit )

  • 多DID连续读取 ( 0x22 允许一次请求多个DID)

  • 子功能支持 (如 0x19 ReadDTCInformation 包含复杂子功能)

  • 定时器与响应超时管理

  • 安全访问算法的具体实现(seed&key)

  • 并发请求的队列处理

但核心思想是一致的:严格的请求检查 + 服务分发 + 分层处理。掌握该设计模式,很容易扩展实现 0x2E WriteDataByIdentifier、0x31 RoutineControl 等其他UDS服务。

希望本文对您理解UDS诊断软件的实现有所帮助。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

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年起推新车

态度原创

游戏
健康
旅游
家居
数码

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

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

旅游要闻

全球首个具身智能主题乐园落地横琴长隆——以“科技+文旅”深度融合助力新质生产力发展

家居要闻

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

数码要闻

满电青春集结古都 航嘉满电青年计划9月22日登陆西安电子科技大学

无障碍浏览 进入关怀版