![]()
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、子功能、寻址方式
会话与安全管理
请求格式与参数有效性检查
调用具体服务处理函数
构造响应(正响应或否定响应)并向下传递
假设诊断仪发送了一条物理寻址的 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—— 安全访问未通过
0x22 服务的标准请求格式为 [服务ID, DID高字节, DID低字节],总长度应为3字节。若请求长度不足3字节,则返回否定响应:
0x7F 22 0x13—— 报文长度错误
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的有效性、安全要求等。
以下代码模拟了一个简化但完整的DCM模块,实现了物理寻址下的 0x22 服务处理。代码包含了:请求检查、否定响应生成、服务分发、DID回调、数据获取。
5.1 使用示例#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();
}
};
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)子功能支持 (如
0x19ReadDTCInformation 包含复杂子功能)定时器与响应超时管理
安全访问算法的具体实现(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.