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

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

UDS 診斷服務精講:$22 ReadDataByIdentifier、$2E WriteDataByIdentifier 與 $31 RoutineC...

0
分享至


UDS 診斷服務精講: 、 2E WriteDataByIdentifier 與 $31 RoutineControl


在基于 ISO 14229 的 UDS 診斷協議中,$22$2E$31是最常用的三個服務,分別用于讀取數據寫入數據執行例程。它們構成了診斷交互的核心:獲取車輛信息、標定配置、觸發復雜動作。本文將詳細講解這三個服務的協議格式、正負響應機制,并提供可直接運行的 C++ 模擬代碼,幫助開發者快速理解與實現。

一、$22 ReadDataByIdentifier(按標識符讀數據) 1.1 功能說明

$22服務允許診斷儀(Tester)讀取一個或多個數據標識符(DID,Data Identifier)對應的數據值。DID 通常為兩個字節(如0xF190表示 VIN 碼)。ECU 收到請求后,從內部存儲或實時計算中獲得數據,并以$62響應回傳。

1.2 協議格式

  • 請求22 DID [DID2 ...]

    • 22:服務 ID

    • DID:高字節 + 低字節(例如F1 90

    • 可連續請求多個 DID(但多數實現一次只請求一個,避免長度過長)

  • 正響應62 DID dataRecord

    • 62:響應服務 ID

    • DID:回顯請求中的 DID

    • dataRecord:該 DID 對應的數據字節(長度由 DID 定義決定)

  • 負響應7F 22 NRC

    • 7F:負響應固定值

    • 22:原始請求服務 ID

    • NRC:負響應碼(見下文)

1.3 常見負響應碼(NRC)

NRC

含義

0x13

報文長度或格式錯誤(例如 DID 數量不匹配)

0x22

條件不滿足(例如車輛未上 ON 檔)

0x31

DID 不支持或當前會話不可用

0x33

安全訪問未解鎖,DID 受保護


1.4 C++ 代碼示例:模擬 ECU 處理 $22 請求

#include  

#include
#include
#include
using namespace std;

// 模擬 ECU 內部 DID 數據庫
class EcuDataBase {
public:
EcuDataBase() {
// 預定義 DID 及其數據(VIN 碼示例)
dataStore[0xF190] = { 'L', 'F', 'V', '3', 'A', '2', '1', 'K', '8', 'M', '5', '0', '0', '0', '0', '1' };
// 軟件版本號 DID = 0xF188
dataStore[0xF188] = { 0x01, 0x02, 0x03 };
}

bool isDIDSupported(uint16_t did) const {
return dataStore.find(did) != dataStore.end();
}

const vector& getData(uint16_t did) const {
static vector empty;
auto it = dataStore.find(did);
if (it != dataStore.end())
return it->second;
return empty;
}

// 模擬條件檢查(如安全等級、ON檔狀態等)
bool isReadable(uint16_t did) const {
// 示例:0xF190 任何時候可讀;0xF188 需要解鎖
if (did == 0xF188)
return securityUnlocked; // 需要安全解鎖
return true;
}

void setSecurityUnlocked(bool locked) { securityUnlocked = locked; }

private:
map> dataStore;
bool securityUnlocked = false;
};

// 處理 $22 請求的函數
// 輸入:完整請求報文(首字節為 0x22),輸出:響應報文
vector handleReadDataByIdentifier(const vector& request, EcuDataBase& ecu) {
const uint8_t SID = 0x22;
const uint8_t POS_RESP_SID = 0x62;

// 負響應輔助
auto negResp = [&](uint8_t nrc) -> vector {
return { 0x7F, SID, nrc };
};

if (request.size() < 3) // 至少 SID + DID(2字節)
return negResp(0x13); // 格式錯誤

// 檢查是否支持該 DID
uint16_t did = (request[1] << 8) | request[2];
if (!ecu.isDIDSupported(did))
return negResp(0x31); // 不支持

// 檢查讀取條件
if (!ecu.isReadable(did))
return negResp(0x33); // 安全未解鎖

// 構造正響應
vector response;
response.push_back(POS_RESP_SID);
response.push_back(request[1]); // DID 高字節
response.push_back(request[2]); // DID 低字節
const auto& data = ecu.getData(did);
response.insert(response.end(), data.begin(), data.end());

return response;
}

// 演示
int main() {
EcuDataBase ecu;
// 請求讀取 VIN (DID=0xF190)
vector request = { 0x22, 0xF1, 0x90 };
vector response = handleReadDataByIdentifier(request, ecu);

cout << "Request: ";
for (auto b : request) printf("%02X ", b);
cout << "\nResponse: ";
for (auto b : response) printf("%02X ", b);
cout << endl;

// 請求需要解鎖的 DID (0xF188) 且未解鎖
request = { 0x22, 0xF1, 0x88 };
response = handleReadDataByIdentifier(request, ecu);
cout << "\nRequest (locked): ";
for (auto b : request) printf("%02X ", b);
cout << "\nResponse: ";
for (auto b : response) printf("%02X ", b);
cout << endl;

// 解鎖后再次請求
ecu.setSecurityUnlocked(true);
response = handleReadDataByIdentifier(request, ecu);
cout << "\nRequest (unlocked): ";
for (auto b : request) printf("%02X ", b);
cout << "\nResponse: ";
for (auto b : response) printf("%02X ", b);
cout << endl;

return 0;
}

輸出示例(實際運行時):

Request: 22 F1 90 
Response: 62 F1 90 4C 46 56 33 41 32 31 4B 38 4D 35 30 30 30 30 31

Request (locked): 22 F1 88
Response: 7F 22 33

Request (unlocked): 22 F1 88
Response: 62 F1 88 01 02 03
二、$2E WriteDataByIdentifier(按標識符寫數據) 2.1 功能說明

$2E服務用于診斷儀向 ECU 寫入一個 DID 對應的數據值。典型應用包括寫入 VIN 碼、配置字、車架號、標定參數等。注意:可讀 DID 未必可寫,寫操作通常受更嚴格的安全和條件約束。

2.2 協議格式

  • 請求2E DID dataRecord

    • 2E:服務 ID

    • DID:兩字節標識符

    • dataRecord:要寫入的數據字節流(長度由 DID 定義決定)

  • 正響應6E DID(僅回顯 DID,不返回數據內容)

  • 負響應7F 2E NRC

2.3 常見負響應碼(NRC)

NRC

含義

0x13

數據長度或格式與 DID 定義不符

0x22

當前條件不滿足寫入(如車速不為0)

0x31

DID 不支持寫入(只讀 DID)

0x33

需要安全訪問解鎖

0x72

寫入時內部錯誤(如校驗失敗、非易失存儲故障)


重點坑:動態定義的 DID(如通過 $2C 服務動態創建)通常不能作為 $2E 的寫入目標。
2.4 C++ 代碼示例:模擬 ECU 處理 $2E 請求

#include  

#include
// 沿用前面的 EcuDataBase,擴展寫入支持

class EcuDataBaseWithWrite : public EcuDataBase {
public:
// 檢查是否允許寫入
bool isWriteable(uint16_t did) const {
// 示例:0xF190 可寫但需安全解鎖;0xF188 只讀
if (did == 0xF190) return securityUnlockedWrite;
if (did == 0xF188) return false; // 只讀 DID
return false; // 其它 DID 不允許寫
}

bool writeData(uint16_t did, const vector& data) {
if (!isWriteable(did))
return false;
// 長度校驗(示例:VIN 必須為 17 字節)
if (did == 0xF190 && data.size() != 17)
return false;
dataStore[did] = data; // 覆蓋寫入
return true;
}

void setWriteUnlocked(bool unlocked) { securityUnlockedWrite = unlocked; }

private:
bool securityUnlockedWrite = false;
};

// 處理 $2E 請求
vector handleWriteDataByIdentifier(const vector& request, EcuDataBaseWithWrite& ecu) {
const uint8_t SID = 0x2E;
const uint8_t POS_RESP_SID = 0x6E;

auto negResp = [&](uint8_t nrc) -> vector {
return { 0x7F, SID, nrc };
};

if (request.size() < 4) // SID + DID(2) + 至少1字節數據
return negResp(0x13);

uint16_t did = (request[1] << 8) | request[2];
vector writeData(request.begin() + 3, request.end());

// 檢查 DID 是否支持寫入
if (!ecu.isWriteable(did))
return negResp(0x31);

// 執行寫入
if (!ecu.writeData(did, writeData))
return negResp(0x72); // 寫入失敗(長度或條件)

// 正響應
return { POS_RESP_SID, request[1], request[2] };
}

int main() {
EcuDataBaseWithWrite ecu;
// 嘗試寫入 VIN (DID=0xF190) 但未解鎖
vector newVin = { '1','G','N','P','K','D','K','E','X','R','1','2','3','4','5','6','7' };
vector request = { 0x2E, 0xF1, 0x90 };
request.insert(request.end(), newVin.begin(), newVin.end());

vector resp = handleWriteDataByIdentifier(request, ecu);
cout << "Write without unlock: ";
for (auto b : resp) printf("%02X ", b);
cout << endl;

// 解鎖并寫入
ecu.setWriteUnlocked(true);
resp = handleWriteDataByIdentifier(request, ecu);
cout << "Write with unlock: ";
for (auto b : resp) printf("%02X ", b);
cout << endl;

// 驗證讀取
vector readReq = { 0x22, 0xF1, 0x90 };
auto readResp = handleReadDataByIdentifier(readReq, ecu);
cout << "Verify read after write: ";
for (auto b : readResp) printf("%02X ", b);
cout << endl;

return 0;
}

輸出示例

Write without unlock: 7F 2E 31 
Write with unlock: 6E F1 90
Verify read after write: 62 F1 90 31 47 4E 50 4B 44 4B 45 58 52 31 32 33 34 35 36 37
三、$31 RoutineControl(例程控制) 3.1 功能說明

$31服務用于觸發 ECU 內部一段預定義的例程(Routine),例如自檢、內存擦除、鑰匙學習、刷寫前檢查等。與$22/$2E操作靜態數據不同,$31執行的是動作(函數回調)。例程通過兩字節的例程標識符(RID)區分。

3.2 控制類型(sub-function)

含義

0x01

啟動例程 (Start)

0x02

停止例程 (Stop)

0x03

請求例程結果 (RequestResults)

請求中可攜帶附加參數(例如擦除起始地址、長度),由 RID 定義決定。

3.3 協議格式

  • 請求31 routineControlType RID [routineData...]

  • 正響應71 routineControlType RID [routineStatusData...]

    • Start:可能返回預計執行時間或狀態

    • RequestResults:返回例程執行的結果數據(如故障碼個數)

  • 負響應7F 31 NRC

3.4 常見負響應碼(NRC)

NRC

含義

0x12

子功能(controlType)不支持

0x13

報文長度錯誤(缺少必需參數)

0x22

當前條件不滿足(如發動機運轉中不能擦除)

0x24

順序錯誤(例如未 Start 就 RequestResults)

0x31

RID 不支持或參數超出范圍

0x33

需要安全訪問解鎖

0x72

例程執行失敗(例如擦除校驗錯誤)


3.5 C++ 代碼示例:模擬 ECU 處理例程控制

#include  

#include
#include
#include
#include

enum RoutineState { IDLE, RUNNING, FINISHED };
class EcuRoutineController {
public:
EcuRoutineController() : state(IDLE), resultReady(false) {}

// 注冊例程回調
void registerRoutine(uint16_t rid, function&)> startFunc,
function stopFunc,
function()> resultFunc) {
routines[rid] = { startFunc, stopFunc, resultFunc };
}

// 處理 $31 請求
vector handleRoutineControl(const vector& request) {
const uint8_t SID = 0x31;
const uint8_t POS_RESP_SID = 0x71;
auto negResp = [&](uint8_t nrc) -> vector {
return { 0x7F, SID, nrc };
};

if (request.size() < 4) // SID + SubFunc + RID(2)
return negResp(0x13);

uint8_t ctrlType = request[1];
uint16_t rid = (request[2] << 8) | request[3];
vector param(request.begin() + 4, request.end());

// 檢查子功能支持
if (ctrlType != 0x01 && ctrlType != 0x02 && ctrlType != 0x03)
return negResp(0x12);

// 檢查 RID 是否存在
auto it = routines.find(rid);
if (it == routines.end())
return negResp(0x31);

auto& routine = it->second;
bool success = false;
vector resultData;

switch (ctrlType) {
case 0x01: // Start
if (state != IDLE)
return negResp(0x24); // 順序錯誤:已運行或未正確停止
success = routine.startFunc(param);
if (success) {
state = RUNNING;
// 模擬異步執行,這里簡單同步,實際可啟動線程
// 為演示效果,立即標記完成(真實場景需延遲)
state = FINISHED;
resultReady = true;
resultCache = routine.resultFunc(); // 預存結果
}
break;
case 0x02: // Stop
if (state != RUNNING && state != FINISHED)
return negResp(0x24);
success = routine.stopFunc();
if (success) state = IDLE;
break;
case 0x03: // RequestResults
if (!resultReady)
return negResp(0x24); // 未 Start 或未完成
resultData = routine.resultFunc();
// 不改變狀態
break;
}

if (!success)
return negResp(0x72); // 例程執行失敗

// 構造正響應
vector response = { POS_RESP_SID, ctrlType, uint8_t(rid >> 8), uint8_t(rid & 0xFF) };
response.insert(response.end(), resultData.begin(), resultData.end());
return response;
}

private:
struct Routine {
function&)> startFunc;
function stopFunc;
function()> resultFunc;
};
map routines;
RoutineState state;
bool resultReady;
vector resultCache;
};

// 演示:內存擦除例程 RID=0xFF01
int main() {
EcuRoutineController ecu;
// 注冊擦除例程
ecu.registerRoutine(0xFF01,
[](const vector& params) -> bool {
cout << "Routine Start: Erasing memory, params size=" << params.size() << endl;
// 模擬條件檢查(例如參數必須包含有效地址)
if (params.size() < 4) return false;
return true;
},
[]() -> bool {
cout << "Routine Stop: Stop erasing" << endl;
return true;
},
[]() -> vector {
cout << "Routine Result: Erase success, 0 errors" << endl;
return { 0x00, 0x00 }; // 成功標志
}
);

// 請求啟動例程,帶參數(起始地址0x0000,長度0x1000)
vector request = { 0x31, 0x01, 0xFF, 0x01, 0x00, 0x00, 0x10, 0x00 };
auto resp = ecu.handleRoutineControl(request);
cout << "Start response: ";
for (auto b : resp) printf("%02X ", b);
cout << endl;

// 請求結果
request = { 0x31, 0x03, 0xFF, 0x01 };
resp = ecu.handleRoutineControl(request);
cout << "Result response: ";
for (auto b : resp) printf("%02X ", b);
cout << endl;

// 未 Start 就請求結果(狀態機已變化,這里演示順序錯誤,需重置狀態?實際需真實模擬)
// 為了展示 NRC 0x24,再創建一個新控制器
EcuRoutineController ecu2;
ecu2.registerRoutine(0xFF01, [](auto){return true;}, []{return true;}, []{return vector{};});
request = { 0x31, 0x03, 0xFF, 0x01 };
resp = ecu2.handleRoutineControl(request);
cout << "Result without start: ";
for (auto b : resp) printf("%02X ", b);
cout << endl;

return 0;
}

輸出示例

Routine Start: Erasing memory, params size=4
Start response: 71 01 FF 01 00 00
Routine Result: Erase success, 0 errors
Result response: 71 03 FF 01 00 00
Result without start: 7F 31 24
四、總結:NRC 排查思路

無論遇到哪個服務的負響應,都可以按照以下金字塔順序排查:

  1. 服務/子功能支持性(0x12,0x31)
    檢查對方 ECU 是否實現了該 SID 或 RID/ DID。

  2. 安全訪問狀態(0x33)
    多數寫操作和敏感讀取需要先執行$27服務解鎖。

  3. 會話模式(0x7F常伴隨0x120x31)
    某些服務僅在擴展會話下可用。

  4. 報文長度/格式(0x13)
    核對 DID 定義的長度,是否漏傳或多傳了數據。

  5. 運行條件(0x22)
    是否需要車輛處于特定狀態(ON檔、發動機停止、車速為零)。

  6. 調用順序(0x24)
    對于$31,是否嚴格遵循 Start → (Stop) → RequestResults 的順序。

  7. 內部執行錯誤(0x72)
    例程內部邏輯失敗,如擦除校驗失敗、寫入 EEPROM 超時。

掌握這些服務的協議與實現細節,能讓你在開發診斷工具或 ECU 固件時,快速定位問題,寫出穩定可靠的代碼。

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

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迎全球首秀

態度原創

教育
健康
房產
藝術
手機

教育要聞

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

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

房產要聞

老黃埔熱銷之下,珠江春,為何去化僅3成?

藝術要聞

1008米!沙特“世界第一高樓”項目,為何極有可能建成?

手機要聞

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

無障礙瀏覽 進入關懷版