无主之地2配置高吗|看真人裸体BBBBB|秋草莓丝瓜黄瓜榴莲色多多|真人強奷112分钟|精品一卡2卡3卡四卡新区|日本成人深夜苍井空|八十年代动画片

網易首頁 > 網易號 > 正文 申請入駐

汽車診斷刷寫技術詳解與C++代碼實現

0
分享至


引言

隨著汽車電子電氣架構日益復雜,電子控制單元(ECU)的數量和功能不斷增加。ECU軟件的在線升級(OTA)或售后診斷刷寫成為不可或缺的技術。統一診斷服務(UDS)協議作為國際標準(ISO 14229),為診斷通信和刷寫功能提供了統一框架。本文將深入講解基于UDS的刷寫流程,并給出C++代碼示例,幫助開發人員快速掌握診斷刷寫實現方法。

一、UDS診斷協議基礎

UDS(Unified Diagnostic Services)位于OSI模型的第五層(會話層)和第七層(應用層),本質上是服務集合。診斷儀(Tester)向ECU發送請求(Request),ECU返回肯定響應(Positive Response)或否定響應(Negative Response)。

1.1 常用術語

術語

SID

服務標識符,例如0x10會話控制、0x27安全訪問

DID

數據標識符,用于讀取或寫入特定數據

RID

例程控制標識符,用于執行耗時操作(如擦除、校驗)

NRC

否定響應碼,如0x12子功能不支持、0x35非法密鑰

DTC

診斷故障代碼


1.2 請求與響應格式

診斷請求格式

  • 格式1:[SID] + [Sub-function]

  • 格式2:[SID] + [DID]

  • 格式3:[SID] + [Sub-function] + [DID]

肯定響應:對應SID最高位置1,即SID + 0x40,后跟參數。否定響應0x7F + SID + NRC(例如7F 27 35表示安全訪問服務非法密鑰)。

1.3 尋址模式

  • 物理尋址:點對點,訪問單個ECU(標準幀CAN ID通常為ECU物理地址,如0x701)。

  • 功能尋址:廣播,一對多(標準幀常用0x7DF)。

1.4 報文傳輸機制(ISO 15765-2)

單幀(SF)和多個幀(首幀FF、流控幀FC、連續幀CF)管理數據長度超過8字節的通信。

  • 單幀:首字節高4位為0,低4位為數據長度(如0x03表示3字節數據)。

  • 首幀:首字節高4位為1,低4位及第二字節組合表示總數據長度。

  • 流控幀:首字節高4位為3,低4位為流狀態(0=繼續發送),后續字節為塊大小和最小間隔。

  • 連續幀:首字節高4位為2,低4位為序列號(從1開始遞增)。

二、基于UDS的完整刷寫流程

刷寫過程分為預編程、主編程、后編程三個階段。

2.1 預編程步驟(功能尋址,廣播所有ECU)

服務及其參數

10 03

進入擴展會話模式,禁止ECU間正常通信并關閉DTC存儲

3E 80

周期性發送在線請求,維持非默認會話

31 01 02 03

檢查編程前置條件

85 02

關閉DTC設置

28 03 03

禁止非診斷報文發送與接收

22 xx yy

讀取被刷寫ECU的狀態(如當前軟件版本)


2.2 主編程步驟(物理尋址,點對點)

服務及其參數

10 02

進入編程會話,ECU進入Bootloader

27 09/0A

請求種子/發送密鑰(安全訪問)

31 01 FF 00

擦除內存(例程控制)

2E F1 5A

寫入指紋信息(診斷儀標識)

34 xx yy zz

請求下載(指定地址、長度)

36 00 + data

傳輸數據(多次調用)

37

請求退出傳輸

31 01 02 02

檢查編程完整性(如校驗和)

31 01 FF 01

檢查依賴性和一致性

11 01

ECU硬復位


2.3 后編程步驟(功能尋址)

服務及其參數

10 03

再次進入擴展會話(復位后的ECU需同步)

28 00 03

開啟非診斷報文收發

85 01

開啟DTC設置

10 01

回到默認會話,停止發送3E 80

14 FF FF FF

清除所有DTC(物理尋址)


三、C++代碼舉例

以下代碼實現一個簡單的UDS診斷通信類,支持單幀/多幀處理,并實現刷寫核心步驟。

3.1 診斷通信類定義(UDSClient.h)

#pragma once
#include
#include

// 模擬CAN數據幀(實際項目需替換為硬件接口)
struct CanFrame {
uint32_t id; // CAN ID
uint8_t data[8]; // CAN數據
uint8_t len; // 有效數據長度(實際CAN幀長度通常為8)
};

class UDSClient {
public:
UDSClient(uint32_t physAddr, uint32_t funcAddr);
virtual ~UDSClient() = default;

// 底層CAN發送(需用戶實現)
virtual bool sendCanFrame(const CanFrame& frame);
// 底層CAN接收,帶超時(ms)
virtual bool receiveCanFrame(CanFrame& frame, int timeoutMs);

// ----- UDS服務 -----
// 單幀請求(不超過8字節)并等待響應
std::vector requestRaw(const std::vector& req, int timeoutMs = 100);

// 多幀發送(自動分包,ISO 15765-2)
bool requestMultiFrame(const std::vector& fullData, int timeoutMs = 1000);

// 高等級服務封裝
bool diagnosticSessionControl(uint8_t sessionType, bool physAddr = true);
bool securityAccess(uint8_t level, const std::vector& key = {});
bool routineControl(uint16_t rid, uint8_t subfunc, const std::vector& data = {});
bool requestDownload(uint32_t address, uint32_t size);
bool transferData(uint8_t blockSeq, const std::vector& data);
bool requestTransferExit();
bool ecuReset(uint8_t resetType);

// 刷寫流程示例
bool performFlash(const std::vector& firmware, uint32_t startAddr);

private:
// 解析響應,如果是否定響應則拋出NRC異常
void checkResponse(const std::vector& resp, uint8_t expectedSid);
// 組裝多幀發送
void buildMultiFrame(const std::vector& data, std::vector & frames) ;
// 接收流控幀并發送連續幀
bool sendMultiFrameData(const std::vector & cfFrames, int timeoutMs);

uint32_t physId; // 物理尋址CAN ID
uint32_t funcId; // 功能尋址CAN ID
};
3.2 關鍵方法實現(UDSClient.cpp) 3.2.1 單幀請求

std::vector UDSClient::requestRaw(const std::vector& req, int timeoutMs) {
if (req.size() > 8) return {}; // 多幀請調用requestMultiFrame

CanFrame frame;
frame.id = funcId; // 根據實際場合可動態切換為physId
frame.len = 8;
frame.data[0] = (req.size() & 0x0F); // 單幀標識+長度
memcpy(&frame.data[1], req.data(), req.size());
if (req.size() < 7) {
// 未使用字節填充為0xAA或0x55,依規范
memset(&frame.data[1 + req.size()], 0xAA, 7 - req.size());
}

if (!sendCanFrame(frame)) return {};
CanFrame resp;
if (!receiveCanFrame(resp, timeoutMs)) return {};
return std::vector(resp.data, resp.data + resp.len);
}
3.2.2 多幀發送(ISO 15765-2實現)

bool UDSClient::requestMultiFrame(const std::vector& fullData, int timeoutMs) {
std::vector frames;
buildMultiFrame(fullData, frames);
if (frames.empty()) return false;

// 發送首幀
if (!sendCanFrame(frames[0])) return false;

// 等待流控幀
CanFrame fc;
if (!receiveCanFrame(fc, timeoutMs)) return false;
if ((fc.data[0] >> 4) != 3) return false; // 不是流控幀
uint8_t flowStatus = fc.data[0] & 0x0F;
if (flowStatus != 0) return false; // 非繼續發送,錯誤處理

// 發送剩余連續幀
for (size_t i = 1; i < frames.size(); ++i) {
if (!sendCanFrame(frames[i])) return false;
}
return true;
}

void UDSClient::buildMultiFrame(const std::vector& data, std::vector & frames) {
size_t totalLen = data.size();
frames.clear();

// 首幀:1 + 0A(高4位1,低4位高位字節),第二字節為長度低8位
CanFrame first;
first.len = 8;
first.id = physId; // 多幀通常用物理尋址
first.data[0] = 0x10 | ((totalLen >> 8) & 0x0F);
first.data[1] = totalLen & 0xFF;
size_t offset = 0;
size_t copyLen = std::min((size_t)6, totalLen);
memcpy(&first.data[2], data.data(), copyLen);
offset += copyLen;
frames.push_back(first);

uint8_t seq = 1;
while (offset < totalLen) {
CanFrame cf;
cf.len = 8;
cf.id = physId;
cf.data[0] = 0x20 | (seq++ & 0x0F);
size_t toCopy = std::min((size_t)7, totalLen - offset);
memcpy(&cf.data[1], data.data() + offset, toCopy);
// 剩余字節填充
if (toCopy < 7) memset(&cf.data[1 + toCopy], 0xAA, 7 - toCopy);
frames.push_back(cf);
offset += toCopy;
}
}
3.2.3 安全訪問(27服務)

bool UDSClient::securityAccess(uint8_t level, const std::vector& key) {
// 請求種子
std::vector req = {0x27, level}; // level如0x09
auto resp = requestRaw(req);
if (resp.empty()) return false;
checkResponse(resp, 0x27); // 內部檢查是否否定響應

// 肯定響應格式:67 level seed[0..n]
if ((resp[0] != 0x67) || (resp.size() < 3)) return false;
std::vector seed(resp.begin() + 2, resp.end());

// 此處應調用外部算法計算密鑰(例:seed解碼)
std::vector computedKey = seed; // 實際需實現安全算法
if (!key.empty()) computedKey = key;

// 發送密鑰
std::vector sendKey = {0x27, static_cast(level + 1)};
sendKey.insert(sendKey.end(), computedKey.begin(), computedKey.end());
auto keyResp = requestRaw(sendKey);
if (keyResp.empty()) return false;
checkResponse(keyResp, 0x27);
return (keyResp[0] == 0x67);
}
3.2.4 請求下載(34服務)

bool UDSClient::requestDownload(uint32_t address, uint32_t size) {
// 格式:34 + 地址長度(4字節) + 地址 + 大小長度(4字節) + 大小
std::vector req = {0x34};
req.push_back(0x44); // 地址長度=4字節,內存大小長度=4字節(常用組合)
// 地址高位在前
req.push_back((address >> 24) & 0xFF);
req.push_back((address >> 16) & 0xFF);
req.push_back((address >> 8) & 0xFF);
req.push_back(address & 0xFF);
// 大小
req.push_back((size >> 24) & 0xFF);
req.push_back((size >> 16) & 0xFF);
req.push_back((size >> 8) & 0xFF);
req.push_back(size & 0xFF);


auto resp = requestRaw(req);
if (resp.empty()) return false;
checkResponse(resp, 0x34);
// 響應:74 + 最大長度(可選)
return (resp[0] == 0x74);
}
3.2.5 數據傳輸(36服務)及退出(37)

bool UDSClient::transferData(uint8_t blockSeq, const std::vector& data) {
std::vector req = {0x36, blockSeq};
req.insert(req.end(), data.begin(), data.end());
// 如果總長度小于8字節,使用單幀;否則需多幀。為簡化,假定data不超過7字節
auto resp = requestRaw(req);
if (resp.empty()) return false;
checkResponse(resp, 0x36);
return (resp[0] == 0x76);
}


bool UDSClient::requestTransferExit() {
std::vector req = {0x37};
auto resp = requestRaw(req);
if (resp.empty()) return false;
checkResponse(resp, 0x37);
return (resp[0] == 0x77);
}
3.3 刷寫主流程示例

bool UDSClient::performFlash(const std::vector& firmware, uint32_t startAddr) {
// 1. 預編程(假設已通過功能尋址完成,此處僅作示例)
// 切換到物理尋址,進入編程會話
if (!diagnosticSessionControl(0x02, true)) return false;
// 2. 安全訪問(種子密鑰算法假設已實現)
if (!securityAccess(0x09)) return false;
// 3. 寫入指紋(可選)
std::vector fingerprint = {0x54, 0x65, 0x73, 0x74}; // "Test"
std::vector writeFinger = {0x2E, 0xF1, 0x5A};
writeFinger.insert(writeFinger.end(), fingerprint.begin(), fingerprint.end());
auto fpResp = requestRaw(writeFinger);
if (fpResp.empty() || (fpResp[0] != 0x6E)) return false;
// 4. 擦除內存(例程控制)
if (!routineControl(0xFF00, 0x01)) return false; // 啟動擦除
// 5. 請求下載
if (!requestDownload(startAddr, firmware.size())) return false;
// 6. 分塊傳輸數據(每個塊最多7字節,實際會使用多幀或循環36服務)
const size_t blockSize = 7;
uint8_t seq = 0;
for (size_t off = 0; off < firmware.size(); off += blockSize) {
size_t len = std::min(blockSize, firmware.size() - off);
std::vector block(firmware.begin() + off, firmware.begin() + off + len);
if (!transferData(seq++, block)) return false;
}
// 7. 傳輸退出
if (!requestTransferExit()) return false;
// 8. 完整性校驗
if (!routineControl(0x0202, 0x01)) return false;
// 9. ECU復位
if (!ecuReset(0x01)) return false;
return true;
}


void UDSClient::checkResponse(const std::vector& resp, uint8_t expectedSid) {
if (resp.empty()) throw std::runtime_error("No response");
if (resp[0] == 0x7F) {
if (resp.size() >= 3)
throw std::runtime_error("NRC: 0x" + std::to_string(resp[2]));
throw std::runtime_error("Negative response");
}
if ((resp[0] != (expectedSid + 0x40))) {
throw std::runtime_error("Unexpected response SID");
}
}
注意:以上代碼為教學示例,實際產品需考慮超時重傳、流控幀的塊大小限制、序列號正確管理、多幀響應接收等完整ISO 15765-2實現。
四、總結

汽車診斷刷寫是一項嚴謹的系統工程,要求開發人員深入理解UDS協議棧、NRC錯誤處理、尋址模式以及ISO 15765-2傳輸層。本文從原理出發,結合C++核心類設計,展示了從單幀/多幀通信到安全訪問、下載等關鍵步驟的實現。實際應用中還需適配不同ECU的個性化要求(如安全算法、DID/RID定義等),希望本示例能為汽車電子軟件工程師提供實用的參考起點。

特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。

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-05-14 16:18:17
A股:剛剛,國務院國資委發布,不出意外的話,下周將迎來新變化

A股:剛剛,國務院國資委發布,不出意外的話,下周將迎來新變化

云鵬敘事
2026-05-16 00:00:09
國家發改委主任鄭柵潔會見波音公司總裁奧特伯格

國家發改委主任鄭柵潔會見波音公司總裁奧特伯格

新京報
2026-05-15 20:29:22
上海87-82戰勝北京!賽后數據一清二楚,不是王哲林 最大功臣是他

上海87-82戰勝北京!賽后數據一清二楚,不是王哲林 最大功臣是他

小火箭愛體育
2026-05-15 21:32:09
難怪黃仁勛那么積極跟著特朗普訪華,一到北京就拿下了大額訂單。

難怪黃仁勛那么積極跟著特朗普訪華,一到北京就拿下了大額訂單。

魔都姐姐雜談
2026-05-14 22:09:10
人活多久,看喝酒就知道?壽命短的人,喝酒一般有這6個特征

人活多久,看喝酒就知道?壽命短的人,喝酒一般有這6個特征

芹姐說生活
2026-05-14 23:38:55
上海奪G1但3人需總結!盧偉應變差點,白邊優勢被打沒,弗格太鐵

上海奪G1但3人需總結!盧偉應變差點,白邊優勢被打沒,弗格太鐵

籃球資訊達人
2026-05-16 01:09:21
38.98萬,夸張啊...

38.98萬,夸張啊...

放毒
2026-05-15 19:14:23
中紀委再次重拳出擊!這4個領域將被嚴查,這4種行為將被嚴肅處理

中紀委再次重拳出擊!這4個領域將被嚴查,這4種行為將被嚴肅處理

細說職場
2026-05-15 14:01:05
鄺兆鐳U17亞洲杯首秀!送助攻后或舊傷復發,只踢半場仍獲贊

鄺兆鐳U17亞洲杯首秀!送助攻后或舊傷復發,只踢半場仍獲贊

奧拜爾
2026-05-16 02:13:48
悲催!上海一母親將700萬遺產給兒子,6年后才發現被女兒徹底拉黑

悲催!上海一母親將700萬遺產給兒子,6年后才發現被女兒徹底拉黑

火山詩話
2026-05-15 06:49:15
特朗普還沒回國,就開始放狠話了

特朗普還沒回國,就開始放狠話了

利刃號
2026-05-15 17:16:39
第一次感受到“荔枝核的威力”,泡水里20天,長成“粉盆栽”

第一次感受到“荔枝核的威力”,泡水里20天,長成“粉盆栽”

美家指南
2026-05-15 15:27:43
國宴名場面刷屏:穿紅衣的服務員火了,這才是大國該有的體面

國宴名場面刷屏:穿紅衣的服務員火了,這才是大國該有的體面

娛樂洞察點點
2026-05-15 12:40:18
“錢車兩空”!男子以租代購跑網約車,三年還清13.5萬,過戶前一夜車被拖走

“錢車兩空”!男子以租代購跑網約車,三年還清13.5萬,過戶前一夜車被拖走

網約車觀察室
2026-05-14 10:00:49
張雪宣布停產!博主:雷軍出問題你建議退款 自己出問題只補償

張雪宣布停產!博主:雷軍出問題你建議退款 自己出問題只補償

念洲
2026-05-14 14:29:33
不是洛夫頓!不是古德溫!許利民贊上海隊1人,威廉姆斯傷情出爐

不是洛夫頓!不是古德溫!許利民贊上海隊1人,威廉姆斯傷情出爐

老吳說體育
2026-05-15 23:31:25
這跟不穿有啥區別?趙露思演唱會內衣外穿:被眾嘲一套比一套辣眼

這跟不穿有啥區別?趙露思演唱會內衣外穿:被眾嘲一套比一套辣眼

胡一舸南游y
2026-05-13 15:23:56
看好誰當選臺北市長?1.4萬人網絡投票結果一面倒

看好誰當選臺北市長?1.4萬人網絡投票結果一面倒

新時光點滴
2026-05-16 00:10:48
沙拉維深情告別羅馬:我即將離開,但我的一部分靈魂將永駐于此

沙拉維深情告別羅馬:我即將離開,但我的一部分靈魂將永駐于此

懂球帝
2026-05-16 02:43:35
2026-05-16 02:59:00
新能源自動駕駛 incentive-icons
新能源自動駕駛
專注于半導體行業資訊
977文章數 347關注度
往期回顧 全部

科技要聞

直降千元起步!蘋果華為率先開啟618讓利

頭條要聞

黃仁勛在北京喝豆汁痛苦皺眉 問“這是什么東西”

頭條要聞

黃仁勛在北京喝豆汁痛苦皺眉 問“這是什么東西”

體育要聞

德約科維奇買的球隊,從第6級聯賽升入法甲

娛樂要聞

方媛為何要來《桃花塢6》沒苦硬吃?

財經要聞

騰訊掉隊,馬化騰戳破真相

汽車要聞

高爾夫GTI刷新紐北紀錄 ID. Polo GTI迎全球首秀

態度原創

健康
游戲
旅游
教育
手機

專家揭秘干細胞回輸的安全風險

《街霸6》春麗新品來了!招牌肉腿完美還原

旅游要聞

藏在沈陽鬧市的金色秘境!2 萬㎡油菜花全開,地鐵直達還免費

教育要聞

2027英國留學費用+排名+雅思要求一篇看懂

手機要聞

iPhone 17系列全系跳水,最高立減2500!

無障礙瀏覽 進入關懷版