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

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.

相关推荐
热点推荐
中国为什么总说要扩大消费,却迟迟没有进行大规模收入分配改革?

中国为什么总说要扩大消费,却迟迟没有进行大规模收入分配改革?

素衣读史
2026-06-25 22:12:10
昔日国乒名将陈龙灿发声!张本宇留日另有原因,当年选择不简单

昔日国乒名将陈龙灿发声!张本宇留日另有原因,当年选择不简单

潋滟晴方DAY
2026-08-13 13:06:45
反转!中国男篮被爆出内幕,郭士强这次可能被冤枉,真错怪他了

反转!中国男篮被爆出内幕,郭士强这次可能被冤枉,真错怪他了

宗介说体育
2026-09-27 07:59:37
贷款中介,这几天集体变安静了

贷款中介,这几天集体变安静了

21世纪经济报道
2026-09-27 22:07:57
王曼昱头号贵人,不是刘国梁也不是王励勤,竟是已离开国家队的他

王曼昱头号贵人,不是刘国梁也不是王励勤,竟是已离开国家队的他

完善法
2026-09-09 04:59:50
国产显卡撞上性能墙!砺算LX 7G100号称对标RTX 4060:实测勉强赢过AMD九年前的RX 580

国产显卡撞上性能墙!砺算LX 7G100号称对标RTX 4060:实测勉强赢过AMD九年前的RX 580

快科技
2026-09-26 14:11:07
太瘆人了!日本男子潜入单身女子家100多次,打扫卫生、补充饮料,甚至偷走内衣后再买新的换回去。

太瘆人了!日本男子潜入单身女子家100多次,打扫卫生、补充饮料,甚至偷走内衣后再买新的换回去。

日本物语
2026-09-27 20:34:09
一座1亿瓦的核反应堆,只有洗衣机大小,运行63年却没发一度电,专门生产一些造出来就快速消失的东西,已帮助了数亿生命

一座1亿瓦的核反应堆,只有洗衣机大小,运行63年却没发一度电,专门生产一些造出来就快速消失的东西,已帮助了数亿生命

徐德文科学频道
2026-09-27 14:20:48
刘欢离世爆出真实家底,不拍戏不代言,不靠综艺为何坐拥上亿身家

刘欢离世爆出真实家底,不拍戏不代言,不靠综艺为何坐拥上亿身家

东方不败然多多
2026-09-27 16:17:04
刘欢临终细节:妻子卢璐和女儿刘一丝守在床边,母女俩哭到站不住

刘欢临终细节:妻子卢璐和女儿刘一丝守在床边,母女俩哭到站不住

情感大头说说
2026-09-28 05:34:17
刚拿冠军就退役,当晚直接打包行李走人,这姑娘的操作把全网看傻

刚拿冠军就退役,当晚直接打包行李走人,这姑娘的操作把全网看傻

静若梨花
2026-08-29 10:42:55
嫁错人太可怕!被前夫公开骂“性欲太强”的她,活成张柏芝对照组

嫁错人太可怕!被前夫公开骂“性欲太强”的她,活成张柏芝对照组

阿讯说天下
2026-08-02 04:31:47
19岁松岛辉空,正式公布退役计划

19岁松岛辉空,正式公布退役计划

乒乓乐园
2026-08-13 12:18:37
索尼低价区试水?《战神劳菲》能成为PS首个试金石吗

索尼低价区试水?《战神劳菲》能成为PS首个试金石吗

游民星空
2026-09-27 19:13:07
港媒:大陆高铁已被“粪便淹没”!再不解决后果严重,真的假的?

港媒:大陆高铁已被“粪便淹没”!再不解决后果严重,真的假的?

铭记历史呀
2026-09-02 02:21:33
电影院的至暗时刻:174亿票房、场均不到6人、五大公司全亏

电影院的至暗时刻:174亿票房、场均不到6人、五大公司全亏

草莓解说体育
2026-09-27 17:07:15
人活多久,看起夜就知道?寿命短的人,起夜一般有这3个特征

人活多久,看起夜就知道?寿命短的人,起夜一般有这3个特征

芹姐说生活
2026-09-10 23:50:29
20岁德国天才后卫闪耀德甲,开价3500万欧,良心价,皇马情有独钟

20岁德国天才后卫闪耀德甲,开价3500万欧,良心价,皇马情有独钟

零度眼看球
2026-09-28 07:36:21
日本人怒斥高市,非要硬刚中国,这下全世界都知道敌国条款了

日本人怒斥高市,非要硬刚中国,这下全世界都知道敌国条款了

爱看剧的阿峰
2026-09-27 13:15:55
人大代表建议机关事业单位双休调整为“大周休3天,小周休2天”

人大代表建议机关事业单位双休调整为“大周休3天,小周休2天”

职场资深秘书
2026-09-27 19:40:23
2026-09-28 07:52:49
新能源自动驾驶 incentive-icons
新能源自动驾驶
专注于半导体行业资讯
1015文章数 348关注度
往期回顾 全部

科技要闻

星舰第14飞,为什么准备绕地球六圈?

头条要闻

小伙汽车电瓶被偷刚报完案车又被砸 强忍蚊虫蹲守一宿

头条要闻

小伙汽车电瓶被偷刚报完案车又被砸 强忍蚊虫蹲守一宿

体育要闻

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

娱乐要闻

刘欢去世后谣言四起,韩磊被翻旧账

财经要闻

“为了看短剧,我妈两年花了22万 ”

汽车要闻

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

态度原创

健康
数码
亲子
房产
公开课

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

数码要闻

Beats 360上手体验

亲子要闻

深度长文:2.5 亿精子闯关,奔赴 10 厘米外的卵子,最终赢家只有 1 个

房产要闻

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

公开课

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

无障碍浏览 进入关怀版