![]()
1. 概述
在AUTOSAR架構(gòu)中,DCM(Diagnostic Communication Manager) 是診斷通信的核心模塊,負(fù)責(zé)解析UDS協(xié)議、執(zhí)行診斷服務(wù)并管理ECU的診斷狀態(tài)。它就像ECU的“診斷大腦”,協(xié)調(diào)從底層接收到數(shù)據(jù)、執(zhí)行服務(wù)、生成響應(yīng)并發(fā)送的全流程。
本文以 0x22 ReadDataByIdentifier(按標(biāo)識(shí)符讀取數(shù)據(jù)) 服務(wù)為例,深入講解DCM內(nèi)部的處理機(jī)制,并提供可運(yùn)行的C++示例代碼。
2. UDS診斷棧中的DCM位置
診斷報(bào)文通過物理層(CAN/LIN/ETH)→ 網(wǎng)絡(luò)層(CanTp)逐級(jí)向上遞交。CanTp 負(fù)責(zé)將長(zhǎng)報(bào)文重組為完整的UDS請(qǐng)求數(shù)據(jù)。當(dāng)CanTp完成重組后,會(huì)調(diào)用DCM的入口函數(shù)(例如 Dcm_MainFunction 或 Dcm_ProcessRequest),將完整請(qǐng)求數(shù)據(jù)傳遞給DCM。
DCM承擔(dān)以下職責(zé):
解析服務(wù)ID、子功能、尋址方式
會(huì)話與安全管理
請(qǐng)求格式與參數(shù)有效性檢查
調(diào)用具體服務(wù)處理函數(shù)
構(gòu)造響應(yīng)(正響應(yīng)或否定響應(yīng))并向下傳遞
假設(shè)診斷儀發(fā)送了一條物理尋址的 22 11 22 請(qǐng)求,其中:
服務(wù)ID =
0x22(ReadDataByIdentifier)DID(Data Identifier) =
0x1122
DCM會(huì)按順序執(zhí)行以下檢查:
3.1 提取服務(wù)ID
從請(qǐng)求數(shù)據(jù) [22, 11, 22] 中取出首字節(jié) 0x22,識(shí)別為 ReadDataByIdentifier 服務(wù)。
3.2 尋址方式判定
DCM從底層接收的PDU信息中獲取CAN ID。如果CAN ID屬于診斷物理尋址范圍,則判定為物理尋址,要求ECU必須單獨(dú)響應(yīng);如果是功能尋址,則需要根據(jù)UDS協(xié)議中“抑制正響應(yīng)位”(在子功能中)決定是否響應(yīng)(避免總線沖突)。為簡(jiǎn)化,本例假定為物理尋址,總是需要響應(yīng)。
3.3 會(huì)話與安全檢查
DCM內(nèi)部維護(hù)當(dāng)前診斷會(huì)話類型(默認(rèn)會(huì)話0x01、編程會(huì)話0x02等)以及安全等級(jí)(Secured 或 Unlocked)。對(duì)于 0x22 服務(wù):
通常允許在默認(rèn)會(huì)話下讀取非敏感DID
但某些安全關(guān)鍵DID(如VIN、序列號(hào)、加密密鑰)要求ECU處于 解鎖狀態(tài) 或 特定會(huì)話
如果當(dāng)前狀態(tài)不滿足要求,DCM立即構(gòu)造否定響應(yīng),例如:
0x7F 22 0x7E—— 子功能不支持或當(dāng)前會(huì)話下不允許0x7F 22 0x33—— 安全訪問未通過
0x22 服務(wù)的標(biāo)準(zhǔn)請(qǐng)求格式為 [服務(wù)ID, DID高字節(jié), DID低字節(jié)],總長(zhǎng)度應(yīng)為3字節(jié)。若請(qǐng)求長(zhǎng)度不足3字節(jié),則返回否定響應(yīng):
0x7F 22 0x13—— 報(bào)文長(zhǎng)度錯(cuò)誤
DCM查詢本地配置的DID列表(例如通過配置表或回調(diào)函數(shù)表)。如果 0x1122 未在ECU中定義,則返回否定響應(yīng):
0x7F 22 0x31—— 請(qǐng)求超出范圍
若以上檢查全部通過,DCM轉(zhuǎn)入數(shù)據(jù)獲取階段。
4. DCM內(nèi)部層次化調(diào)用結(jié)構(gòu)
AUTOSAR DCM模塊采用分層設(shè)計(jì),將服務(wù)分發(fā)、參數(shù)校驗(yàn)、數(shù)據(jù)獲取解耦。典型的調(diào)用層次如下:
Dcm_ProcessRequest (主入口)
├─ Dcm_MainStateMachine (狀態(tài)機(jī):空閑 -> 請(qǐng)求處理)
├─ Dcm_ServiceTableLookup (根據(jù)服務(wù)ID查找處理函數(shù))
├─ Dcm_Service_ReadDataByIdentifier (服務(wù)級(jí)處理)
│ ├─ 執(zhí)行服務(wù)通用檢查(子功能支持等)
│ ├─ 提取DID
│ ├─ 調(diào)用 DID分發(fā)函數(shù) Dcm_DID_ReadHandler
│ └─ 若失敗則生成否定響應(yīng)
└─ Dcm_DID_ReadHandler_0x1122 (DID級(jí)處理)
├─ 調(diào)用RTE端口讀取應(yīng)用數(shù)據(jù)(如傳感器值)
├─ 或調(diào)用NvM讀取存儲(chǔ)塊(如VIN)
└─ 組裝正響應(yīng)數(shù)據(jù) [0x62, DID高, DID低, 數(shù)據(jù)...]
這種層次化設(shè)計(jì)的優(yōu)點(diǎn):
高擴(kuò)展性 :新增DID只需添加一個(gè)處理回調(diào),無(wú)需修改核心解析邏輯。
職責(zé)分離 :服務(wù)級(jí)處理負(fù)責(zé)通用邏輯,DID級(jí)處理負(fù)責(zé)具體數(shù)據(jù)訪問。
可配置性 :通過配置表即可控制DID的有效性、安全要求等。
以下代碼模擬了一個(gè)簡(jiǎn)化但完整的DCM模塊,實(shí)現(xiàn)了物理尋址下的 0x22 服務(wù)處理。代碼包含了:請(qǐng)求檢查、否定響應(yīng)生成、服務(wù)分發(fā)、DID回調(diào)、數(shù)據(jù)獲取。
5.1 使用示例#include
#include
#include
#include
#include
// 模擬診斷請(qǐng)求和響應(yīng)的數(shù)據(jù)結(jié)構(gòu)
struct DiagRequest {
std::vector data; // 收到的UDS請(qǐng)求數(shù)據(jù)(不含CAN ID)
bool isFunctionalAddress; // true=功能尋址, false=物理尋址
};
struct DiagResponse {
std::vector data;
bool isPositive = false; // true=正響應(yīng), false=否定響應(yīng)
};
// ECU當(dāng)前診斷狀態(tài)(簡(jiǎn)化)
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 是安全相關(guān)的,需要解鎖
if (did == 0x1122) {
return (currentSecurity == UNLOCKED);
}
// 其他DID默認(rèn)允許
return true;
}
};
// 數(shù)據(jù)標(biāo)識(shí)符回調(diào)函數(shù)類型:輸入DID,輸出數(shù)據(jù)字節(jié)流
using DidReadCallback = std::function(uint16_t did)>;
// DCM模塊
class Dcm {
public:
Dcm() {
// 注冊(cè)DID及對(duì)應(yīng)的讀取回調(diào)
registerDid(0x1122, [this](uint16_t did) -> std::vector {
// 模擬通過RTE讀取應(yīng)用數(shù)據(jù)(例如發(fā)動(dòng)機(jī)轉(zhuǎn)速,占2字節(jié))
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字節(jié)ASCII)
std::string vin = "WDD12345678901234";
return std::vector(vin.begin(), vin.end());
});
}
// 外部調(diào)用接口:CanTp遞交流程
DiagResponse processRequest(const DiagRequest& request) {
// 1. 主狀態(tài)機(jī):空閑 -> 請(qǐng)求處理
std::cout << "[DCM] 進(jìn)入請(qǐng)求處理狀態(tài)" << std::endl;
// 2. 服務(wù)分發(fā):根據(jù)服務(wù)ID查找處理函數(shù)
if (request.data.empty()) {
return createNegativeResponse(0x00, 0x13); // 報(bào)文長(zhǎng)度錯(cuò)誤
}
uint8_t serviceId = request.data[0];
auto it = serviceHandlers.find(serviceId);
if (it == serviceHandlers.end()) {
return createNegativeResponse(serviceId, 0x11); // 服務(wù)不支持
}
// 3. 調(diào)用對(duì)應(yīng)服務(wù)的處理函數(shù)
return it->second(request);
}private:
// 服務(wù)處理函數(shù)表
std::map> serviceHandlers;
// DID回調(diào)表
std :: map < uint16_t , DidReadCallback> didCallbacks;
// ECU狀態(tài)
EcuDiagnosticState diagState;
// 注冊(cè)DID讀取回調(diào)
void registerDid(uint16_t did, DidReadCallback callback) {
didCallbacks[did] = callback;
}
// 創(chuàng)建否定響應(yīng)
DiagResponse createNegativeResponse(uint8_t serviceId, uint8_t nrc) {
DiagResponse resp;
resp.isPositive = false ;
resp.data = { 0x7F , serviceId, nrc };
return resp;
}
// 創(chuàng)建正響應(yīng)(0x62服務(wù)負(fù)責(zé)拼接)
DiagResponse createPositiveResponseForReadByIdentifier(uint16_t did, const std::vector& data) {
DiagResponse resp;
resp.isPositive = true ;
resp.data.push_back( 0x62 ); // 正響應(yīng)服務(wù)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;
}
// 服務(wù)級(jí)處理:0x22 ReadDataByIdentifier
DiagResponse handleReadDataByIdentifier(const DiagRequest& request) {
// 1. 請(qǐng)求格式檢查(最小長(zhǎng)度3字節(jié))
if (request.data.size() < 3 ) {
std :: cout << "[DCM] 錯(cuò)誤: 0x22請(qǐng)求長(zhǎng)度不足" << std :: endl ;
return createNegativeResponse( 0x22 , 0x13 ); // 報(bào)文長(zhǎng)度錯(cuò)誤
}
// 2. 提取DID
uint16_t did = ( static_cast < uint16_t >(request.data[ 1 ]) << 8 ) | request.data[ 2 ];
// 3. 會(huì)話與安全檢查
// (尋址方式簡(jiǎn)化:假設(shè)物理尋址總是需要響應(yīng),且無(wú)抑制位)
if (!diagState.isSecurityAccessAllowedForDid(did)) {
std :: cout << "[DCM] 錯(cuò)誤: DID 0x" << std ::hex << did << " 需要解鎖" << std :: endl ;
return createNegativeResponse( 0x22 , 0x33 ); // 安全訪問未通過
}
// 可選:檢查當(dāng)前會(huì)話是否允許此服務(wù)(默認(rèn)會(huì)話允許0x22,編程會(huì)話也允許,此例略)
// 4. DID有效性檢查(是否已注冊(cè)回調(diào))
auto it = didCallbacks.find(did);
if (it == didCallbacks.end()) {
std :: cout << "[DCM] 錯(cuò)誤: DID 0x" << std ::hex << did << " 未配置" << std :: endl ;
return createNegativeResponse( 0x22 , 0x31 ); // 請(qǐng)求超出范圍
}
// 5. 調(diào)用DID級(jí)處理回調(diào)獲取數(shù)據(jù)
std :: vector < uint8_t > data;
try {
data = it->second(did);
} catch (...) {
std :: cout << "[DCM] 錯(cuò)誤: DID 0x" << std ::hex << did << " 讀取失敗" << std :: endl ;
return createNegativeResponse( 0x22 , 0x22 ); // 條件不滿足(數(shù)據(jù)暫時(shí)不可用)
}
// 6. 構(gòu)造正響應(yīng)
std :: cout << "[DCM] 成功讀取DID 0x" << std ::hex << did << ",數(shù)據(jù)長(zhǎng)度=" << std ::dec << data.size() << std :: endl ;
return createPositiveResponseForReadByIdentifier(did, data);
}
// 構(gòu)造函數(shù)中注冊(cè)服務(wù)處理函數(shù)
void initServiceHandlers() {
serviceHandlers[ 0x22 ] = [ this ]( const DiagRequest& req) {
return handleReadDataByIdentifier(req);
};
}
public :
Dcm() {
initServiceHandlers();
}
};
int main() {
Dcm dcm;
// 模擬物理尋址請(qǐng)求: 0x22 0x11 0x22
DiagRequest req;
req.data = {0x22, 0x11, 0x22};
req.isFunctionalAddress = false; // 物理尋址DiagResponse resp = dcm.processRequest(req);
std::cout << "響應(yīng): ";
for (auto b : resp.data) {
std::cout << std::hex << (int)b << " ";
}
std::cout << std::endl;
// 預(yù)期輸出正響應(yīng): 62 11 22 xx xx (xx xx為發(fā)動(dòng)機(jī)轉(zhuǎn)速數(shù)據(jù))
return 0;
}
輸出示例:
[DCM] 進(jìn)入請(qǐng)求處理狀態(tài)
[DCM] 錯(cuò)誤: DID 0x1122 需要解鎖
響應(yīng): 7f 22 33
若先執(zhí)行安全解鎖(代碼中未展現(xiàn),可通過31服務(wù)等實(shí)現(xiàn)),再次請(qǐng)求則可獲得正響應(yīng)。
6. 否定響應(yīng)碼(NRC)一覽
NRC
含義
觸發(fā)場(chǎng)景
0x11
ServiceNotSupported
服務(wù)ID未在服務(wù)表中注冊(cè)
0x13
IncorrectMessageLength
請(qǐng)求報(bào)文長(zhǎng)度不符合UDS規(guī)范
0x22
ConditionsNotCorrect
DID數(shù)據(jù)暫時(shí)不可讀(如未就緒)
0x31
RequestOutOfRange
DID未配置或超出范圍
0x33
SecurityAccessDenied
DID要求解鎖而未解鎖
0x7E
SubFunctionNotSupported
服務(wù)不支持當(dāng)前會(huì)話
7. 總結(jié)與擴(kuò)展
本文通過一個(gè)簡(jiǎn)化的C++ DCM實(shí)現(xiàn),展示了UDS 0x22 服務(wù)的檢查流程與層次化調(diào)用結(jié)構(gòu)。真實(shí)AUTOSAR DCM遠(yuǎn)比此復(fù)雜,還包括:
功能尋址抑制位處理 (
suppressPosRspMsgIndicationBit)多DID連續(xù)讀取 (
0x22允許一次請(qǐng)求多個(gè)DID)子功能支持 (如
0x19ReadDTCInformation 包含復(fù)雜子功能)定時(shí)器與響應(yīng)超時(shí)管理
安全訪問算法的具體實(shí)現(xiàn)(seed&key)
并發(fā)請(qǐng)求的隊(duì)列處理
但核心思想是一致的:嚴(yán)格的請(qǐng)求檢查 + 服務(wù)分發(fā) + 分層處理。掌握該設(shè)計(jì)模式,很容易擴(kuò)展實(shí)現(xiàn) 0x2E WriteDataByIdentifier、0x31 RoutineControl 等其他UDS服務(wù)。
希望本文對(duì)您理解UDS診斷軟件的實(shí)現(xiàn)有所幫助。
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
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.