![]()
一、服務概述
例程控制(RoutineControl,0x31)服務是UDS協議中用于觸發ECU內部預定義程序執行的核心服務。與寫入數據(0x2E)服務不同,0x31服務執行的是動態過程而非靜態數據寫入,例如:
軟件刷寫場景:擦除內存、CRC校驗、完整性驗證
生產下線場景:底盤標定、傳感器校準、ECU自檢
診斷維護場景:電池健康檢測、電機角度自學習、閥門執行器動作測試
字節位置 參數名稱 說明
Byte RoutineControl Request SID = 0x31
Byte sub-function 例程控制類型(高4位保留,低4位有效)
Byte -4 routineIdentifier 例程標識符(RID,2字節)
Byte -N routineControlOptionRecord 可選參數(長度由RID定義)
2.2 子功能定義(sub-function)子功能值
名稱
0x01
startRoutine
啟動例程
0x02
stopRoutine
停止例程
0x03
requestRoutineResults
請求例程執行結果
注意:子功能字節的Bit 7抑制肯定響應位(suppressPosRspMsgIndicationBit)僅在0x01-0x03作為RoutineControlType使用時有效。2.3 例程的三種類型
類型
支持子功能
生命周期
典型應用
短例程
僅0x01
同步執行,響應0x01時即完成
內存擦除、CRC計算
長例程
0x01/0x02/0x03
異步執行,運行固定時長后自結束
攝像頭標定(10s)、電池檢測
持續例程
0x01/0x02/0x03
無固定時長,需0x02主動停止
閥門動作測試、故障注入
三、響應報文格式 3.1 肯定響應
字節位置 參數名稱 說明
Byte RoutineControl Response SID = 0x71
Byte sub-function 與請求相同
Byte -4 routineIdentifier 例程標識符
Byte -N routineInfo/statusRecord 可選(狀態信息/結果數據)
3.2 否定響應字節位置 參數名稱
Byte 0x7F(否定響應標識)
Byte 0x31(請求服務ID)
Byte NRC(否定響應碼)
3.3 支持的NRC列表NRC
名稱
觸發條件
0x12
sub-function not supported
RID不支持當前請求的控制類型(如對短例程請求0x02)
0x13
incorrect message length
報文長度錯誤(過短/過長)
0x22
conditions not correct
前置條件不滿足(如車速未歸零、引擎未熄火)
0x24
request sequence error
順序錯誤(如未發start直接發stop)
0x31
request out of range
RID無效或參數超范圍
0x33
security access denied
需要安全訪問但未解鎖
四、C++代碼實現示例 4.1 枚舉和結構體定義
4.2 例程控制服務實現類#include
#include
#include
#include
#include
#include
// UDS服務ID定義
constexpr uint8_t UDS_SID_ROUTINE_CONTROL = 0x31;
constexpr uint8_t UDS_SID_ROUTINE_CONTROL_RESP = 0x71;
constexpr uint8_t UDS_NEGATIVE_RESPONSE = 0x7F;
// 子功能定義
enum class RoutineControlType : uint8_t {
START_ROUTINE = 0x01,
STOP_ROUTINE = 0x02,
REQUEST_RESULTS = 0x03
};
// 否定響應碼(NRC)
enum class NRC : uint8_t {
NONE = 0x00,
SUB_FUNCTION_NOT_SUPPORTED = 0x12,
INCORRECT_MESSAGE_LENGTH = 0x13,
CONDITIONS_NOT_CORRECT = 0x22,
REQUEST_SEQUENCE_ERROR = 0x24,
REQUEST_OUT_OF_RANGE = 0x31,
SECURITY_ACCESS_DENIED = 0x33
};
// 例程類型枚舉
enum class RoutineType {
SHORT, // 短例程:同步執行,響應即完成
LONG, // 長例程:異步執行,固定時長后自結束
PERSISTENT // 持續例程:需主動停止
};
// 例程狀態
enum class RoutineState {
IDLE, // 未啟動
RUNNING, // 運行中
COMPLETED, // 已完成
STOPPED // 已停止
};
// 例程信息結構體
struct RoutineInfo {
uint16_t id; // 例程標識符
RoutineType type; // 例程類型
bool needSecurity; // 是否需要安全訪問
std::vector requiredSessionIds; // 支持的診斷會話
std::function(const std::vector&)> startFunc; // 啟動回調
std::function()> stopFunc; // 停止回調
std::function()> resultFunc; // 結果回調
std::chrono::steady_clock::time_point startTime; // 啟動時間(用于長例程超時檢測)
int executionTimeMs; // 執行時長(毫秒,僅長例程有效)
RoutineState state; // 當前狀態
};
// 診斷請求消息結構體
struct DiagnosticRequest {
uint8_t sid;
uint8_t subFunction;
std::vector data;
};// 診斷響應消息結構體
struct DiagnosticResponse {
bool isPositive;
uint8_t sid; // 肯定響應時為0x71,否定響應時為0x7F
uint8_t requestSid; // 否定響應時原服務ID
uint8_t nrc; // 否定響應碼
std::vector responseData;
};
4.3 具體例程注冊示例class RoutineControlService {
private:
std::map m_routines;
bool m_securityAccessGranted; // 簡化的安全訪問狀態
uint8_t m_currentSessionId; // 當前診斷會話
std::mutex m_mutex;
// 檢查安全訪問(簡化實現)
NRC checkSecurityAccess(const RoutineInfo& routine) {
if (routine.needSecurity && !m_securityAccessGranted) {
return NRC::SECURITY_ACCESS_DENIED;
}
return NRC::NONE;
}
// 檢查診斷會話支持
NRC checkSessionSupport(const RoutineInfo& routine) {
for (auto sessionId : routine.requiredSessionIds) {
if (m_currentSessionId == sessionId) {
return NRC::NONE;
}
}
return NRC::REQUEST_OUT_OF_RANGE;
}
// 檢查前置條件(示例:車速、引擎狀態等)
virtual NRC checkPreconditions(uint16_t routineId) {
// 子類可重寫此方法實現具體的前置條件檢查
// 例如檢查車速是否為0、引擎是否熄火等
return NRC::NONE;
}
// 檢查請求順序
NRC checkRequestSequence(RoutineControlType requestType, const RoutineInfo& routine) {
if (requestType == RoutineControlType::START_ROUTINE) {
if (routine.state != RoutineState::IDLE && routine.state != RoutineState::STOPPED) {
return NRC::REQUEST_SEQUENCE_ERROR;
}
} else if (requestType == RoutineControlType::STOP_ROUTINE ||
requestType == RoutineControlType::REQUEST_RESULTS) {
if (routine.state != RoutineState::RUNNING && routine.state != RoutineState::COMPLETED) {
return NRC::REQUEST_SEQUENCE_ERROR;
}
}
return NRC::NONE;
}
// 異步后臺任務(用于長例程)
void asyncRoutineExecution(RoutineInfo& routine) {
std::this_thread::sleep_for(std::chrono::milliseconds(routine.executionTimeMs));
std::lock_guard lock(m_mutex);
auto it = m_routines.find(routine.id);
if (it != m_routines.end() && it->second.state == RoutineState::RUNNING) {
it->second.state = RoutineState::COMPLETED;
std::cout << "[Async] Routine 0x" << std::hex << routine.id
<< " completed after " << std::dec << routine.executionTimeMs << "ms" << std::endl;
}
}
public:
RoutineControlService() : m_securityAccessGranted(false), m_currentSessionId(0x01) {}
// 注冊例程
void registerRoutine(const RoutineInfo& info) {
std::lock_guard lock(m_mutex);
m_routines[info.id] = info;
}
// 設置診斷會話
void setDiagnosticSession(uint8_t sessionId) {
m_currentSessionId = sessionId;
}
// 設置安全訪問狀態
void setSecurityAccessGranted(bool granted) {
m_securityAccessGranted = granted;
}
// 主處理函數
DiagnosticResponse handleRequest(const DiagnosticRequest& request) {
std::lock_guard lock(m_mutex);
DiagnosticResponse response;
// 1. 最小長度檢查(至少SID + subFunction + 2字節RID)
if (request.data.size() < 3) {
response.isPositive = false;
response.sid = UDS_NEGATIVE_RESPONSE;
response.requestSid = request.sid;
response.nrc = static_cast(NRC::INCORRECT_MESSAGE_LENGTH);
return response;
}uint8_t subFuncByte = request.subFunction;
uint8_t routineControlType = subFuncByte & 0x1F; // 提取低5位
bool suppressPosRsp = (subFuncByte & 0x80) != 0; // Bit7抑制肯定響應
// 2. 提取RID
uint16_t routineId = (request.data[0] << 8) | request.data[1];
// 3. 提取可選參數
std::vector optionRecord;
if (request.data.size() > 2) {
optionRecord.assign(request.data.begin() + 2, request.data.end());
}
// 4. 檢查RID是否存在
auto it = m_routines.find(routineId);
if (it == m_routines.end()) {
response.isPositive = false;
response.sid = UDS_NEGATIVE_RESPONSE;
response.requestSid = request.sid;
response.nrc = static_cast(NRC::REQUEST_OUT_OF_RANGE);
return response;
}
RoutineInfo& routine = it->second;
// 5. 檢查子功能是否支持
bool subFuncSupported = false;
switch (static_cast (routineControlType)) {
case RoutineControlType::START_ROUTINE:
subFuncSupported = true;
break;
case RoutineControlType::STOP_ROUTINE:
subFuncSupported = (routine.type != RoutineType::SHORT);
break;
case RoutineControlType::REQUEST_RESULTS:
subFuncSupported = (routine.type != RoutineType::SHORT);
break;
default:
subFuncSupported = false;
break;
}
if (!subFuncSupported) {
response.isPositive = false;
response.sid = UDS_NEGATIVE_RESPONSE;
response.requestSid = request.sid;
response.nrc = static_cast(NRC::SUB_FUNCTION_NOT_SUPPORTED);
return response;
}
// 6. 檢查診斷會話
NRC sessionCheck = checkSessionSupport(routine);
if (sessionCheck != NRC::NONE) {
response.isPositive = false;
response.sid = UDS_NEGATIVE_RESPONSE;
response.requestSid = request.sid;
response.nrc = static_cast(sessionCheck);
return response;
}
// 7. 檢查安全訪問
NRC securityCheck = checkSecurityAccess(routine);
if (securityCheck != NRC::NONE) {
response.isPositive = false;
response.sid = UDS_NEGATIVE_RESPONSE;
response.requestSid = request.sid;
response.nrc = static_cast(securityCheck);
return response;
}
// 8. 檢查前置條件
NRC preConditionCheck = checkPreconditions(routineId);
if (preConditionCheck != NRC::NONE) {
response.isPositive = false;
response.sid = UDS_NEGATIVE_RESPONSE;
response.requestSid = request.sid;
response.nrc = static_cast(preConditionCheck);
return response;
}
// 9. 檢查請求順序
NRC sequenceCheck = checkRequestSequence(static_cast (routineControlType), routine);
if (sequenceCheck != NRC::NONE) {
response.isPositive = false;
response.sid = UDS_NEGATIVE_RESPONSE;
response.requestSid = request.sid;
response.nrc = static_cast(sequenceCheck);
return response;
}
// 10. 執行例程控制邏輯
std::vector resultData;
switch (static_cast (routineControlType)) {
case RoutineControlType::START_ROUTINE:
routine.state = RoutineState::RUNNING;
routine.startTime = std::chrono::steady_clock::now();
if (routine.startFunc) {
resultData = routine.startFunc(optionRecord);
}
if (routine.type == RoutineType::SHORT) {
routine.state = RoutineState::COMPLETED;
} else if (routine.type == RoutineType::LONG) {
// 啟動異步任務
std::thread(&RoutineControlService::asyncRoutineExecution, this, std::ref(routine)).detach();
}
break;
case RoutineControlType::STOP_ROUTINE:
if (routine.stopFunc) {
resultData = routine.stopFunc();
}
routine.state = RoutineState::STOPPED;
break;
case RoutineControlType::REQUEST_RESULTS:
if (routine.resultFunc) {
resultData = routine.resultFunc();
}
break;
}
// 11. 構造肯定響應
if (!suppressPosRsp) {
response.isPositive = true;
response.sid = UDS_SID_ROUTINE_CONTROL_RESP;
response.responseData.push_back(routineControlType);
response.responseData.push_back((routineId >> 8) & 0xFF);
response.responseData.push_back(routineId & 0xFF);
// 添加結果數據
response.responseData.insert(response.responseData.end(), resultData.begin(), resultData.end());
} else {
// 抑制肯定響應
response.isPositive = true;
response.responseData.clear();
}
return response;
}
};
五、輸出示例class ECUApplication {
private:
RoutineControlService m_routineService;
bool m_memoryErased = false;
bool m_cameraCalibrated = false;
float m_calibrationResult[4] = {0};
public:
ECUApplication() {
registerMemoryEraseRoutine();
registerCameraCalibrationRoutine();
registerValveActuatorRoutine();
}
// 示例1:短例程 - 內存擦除
void registerMemoryEraseRoutine() {
RoutineInfo eraseRoutine;
eraseRoutine.id = 0xFF00;
eraseRoutine.type = RoutineType::SHORT;
eraseRoutine.needSecurity = true; // 需要安全訪問
eraseRoutine.requiredSessionIds = {0x01, 0x02, 0x03}; // 支持默認、編程擴展、擴展會話
eraseRoutine.state = RoutineState::IDLE;
eraseRoutine.startFunc = [this](const std::vector& params) -> std::vector {
// 參數解析:起始地址(4字節) + 長度(4字節)
if (params.size() >= 8) {
uint32_t startAddr = (params[0] << 24) | (params[1] << 16) | (params[2] << 8) | params[3];
uint32_t length = (params[4] << 24) | (params[5] << 16) | (params[6] << 8) | params[7];
std::cout << "[Routine] Erasing memory at 0x" << std::hex << startAddr
<< " size: " << std::dec << length << " bytes" << std::endl;
// 模擬內存擦除操作
std::this_thread::sleep_for(std::chrono::milliseconds(100));
m_memoryErased = true;
// 返回例程信息
return {0x01}; // routineInfo = 0x01 表示短例程完成
}
return {};
};
m_routineService.registerRoutine(eraseRoutine);
}
// 示例2:長例程 - 攝像頭標定(異步執行10秒)
void registerCameraCalibrationRoutine() {
RoutineInfo calibrationRoutine;
calibrationRoutine.id = 0x1234;
calibrationRoutine.type = RoutineType::LONG;
calibrationRoutine.needSecurity = false;
calibrationRoutine.requiredSessionIds = {0x01, 0x03}; // 默認和擴展會話
calibrationRoutine.executionTimeMs = 10000; // 10秒
calibrationRoutine.state = RoutineState::IDLE;
calibrationRoutine.startFunc = [this](const std::vector& params) -> std::vector {
// 標定參數:AA BB CC
std::cout << "[Routine] Starting camera calibration with params: ";
for (auto b : params) printf("%02X ", b);
std::cout << std::endl;
// 啟動標定異步任務(實際業務邏輯)
std::thread([this]() {
std::this_thread::sleep_for(std::chrono::seconds(10));
// 模擬標定完成,存儲結果
m_cameraCalibrated = true;
m_calibrationResult[0] = 0.123f;
m_calibrationResult[1] = 45.678f;
m_calibrationResult[2] = 9.012f;
m_calibrationResult[3] = 30.040f;
std::cout << "[Routine] Camera calibration completed!" << std::endl;
}).detach();
return {}; // 無額外參數
};
calibrationRoutine.resultFunc = [this]() -> std::vector {
// 返回標定結果:4個float各轉4字節(小端序示例)
std::vector results;
if (m_cameraCalibrated) {
for (int i = 0; i < 4; i++) {
uint32_t val = *reinterpret_cast(&m_calibrationResult[i]);
results.push_back(val & 0xFF);
results.push_back((val >> 8) & 0xFF);
results.push_back((val >> 16) & 0xFF);
results.push_back((val >> 24) & 0xFF);
}
}
return results;
};
m_routineService.registerRoutine(calibrationRoutine);
}
// 示例3:持續例程 - 閥門動作測試
void registerValveActuatorRoutine() {
RoutineInfo valveRoutine;
valveRoutine.id = 0x5678;
valveRoutine.type = RoutineType::PERSISTENT;
valveRoutine.needSecurity = true;
valveRoutine.requiredSessionIds = {0x03}; // 僅擴展會話
valveRoutine.state = RoutineState::IDLE;
bool valveActive = false;
valveRoutine.startFunc = [&valveActive](const std::vector& params) -> std::vector {
uint8_t testMode = (params.size() > 0) ? params[0] : 0x01;
std::cout << "[Routine] Starting valve actuator test, mode: " << (int)testMode << std::endl;
valveActive = true;
// 啟動閥門持續動作線程
std::thread([&valveActive, testMode]() {
int cycle = 0;
while (valveActive) {
std::cout << "[Valve] Cycle " << ++cycle << ": OPEN -> CLOSE" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
}
std::cout << "[Valve] Actuator test stopped" << std::endl;
}).detach();
return {testMode}; // 返回啟動的模式
};
valveRoutine.stopFunc = [&valveActive]() -> std::vector {
valveActive = false;
std::cout << "[Routine] Stopping valve actuator test" << std::endl;
return {0x00, 0x01}; // 狀態記錄:停止碼 0x01
};
m_routineService.registerRoutine(valveRoutine);
}
// 處理診斷請求
void runDiagnosticTest() {
// 設置診斷環境
m_routineService.setDiagnosticSession(0x03); // 擴展會話
m_routineService.setSecurityAccessGranted(true); // 已解鎖
// 測試1:內存擦除(短例程)
std::cout << "\n========== Test 1: Memory Erase (Short Routine) ==========" << std::endl;
DiagnosticRequest req1;
req1.sid = UDS_SID_ROUTINE_CONTROL;
req1.subFunction = static_cast(RoutineControlType::START_ROUTINE);
req1.data = {0xFF, 0x00, 0x44, 0xAA, 0xAA, 0xAA, 0xAA, 0xBB, 0xBB, 0xBB, 0xBB}; // RID+起始地址+長度
auto resp1 = m_routineService.handleRequest(req1);
printResponse(resp1);
// 測試2:攝像頭標定啟動(長例程)
std::cout << "\n========== Test 2: Camera Calibration Start (Long Routine) ==========" << std::endl;
DiagnosticRequest req2;
req2.sid = UDS_SID_ROUTINE_CONTROL;
req2.subFunction = static_cast(RoutineControlType::START_ROUTINE);
req2.data = {0x12, 0x34, 0xAA, 0xBB, 0xCC};
auto resp2 = m_routineService.handleRequest(req2);
printResponse(resp2);
// 等待長例程執行
std::this_thread::sleep_for(std::chrono::seconds(12));
// 測試3:請求標定結果
std::cout << "\n========== Test 3: Request Calibration Results ==========" << std::endl;
DiagnosticRequest req3;
req3.sid = UDS_SID_ROUTINE_CONTROL;
req3.subFunction = static_cast(RoutineControlType::REQUEST_RESULTS);
req3.data = {0x12, 0x34};
auto resp3 = m_routineService.handleRequest(req3);
printResponse(resp3);
// 測試4:閥門動作測試啟動(持續例程)
std::cout << "\n========== Test 4: Valve Actuator Start (Persistent Routine) ==========" << std::endl;
DiagnosticRequest req4;
req4.sid = UDS_SID_ROUTINE_CONTROL;
req4.subFunction = static_cast(RoutineControlType::START_ROUTINE);
req4.data = {0x56, 0x78, 0x02}; // RID + 測試模式2
auto resp4 = m_routineService.handleRequest(req4);
printResponse(resp4);
// 等待閥門動作幾秒鐘
std::this_thread::sleep_for(std::chrono::seconds(5));
// 測試5:停止閥門測試
std::cout << "\n========== Test 5: Stop Valve Actuator ==========" << std::endl;
DiagnosticRequest req5;
req5.sid = UDS_SID_ROUTINE_CONTROL;
req5.subFunction = static_cast(RoutineControlType::STOP_ROUTINE);
req5.data = {0x56, 0x78};
auto resp5 = m_routineService.handleRequest(req5);
printResponse(resp5);
// 測試6:錯誤場景 - RID不存在
std::cout << "\n========== Test 6: Invalid RID (Negative Test) ==========" << std::endl;
DiagnosticRequest req6;
req6.sid = UDS_SID_ROUTINE_CONTROL;
req6.subFunction = static_cast(RoutineControlType::START_ROUTINE);
req6.data = {0x99, 0x99}; // 不存在的RID
auto resp6 = m_routineService.handleRequest(req6);
printResponse(resp6);
}
void printResponse(const DiagnosticResponse& resp) {
if (!resp.isPositive) {
std::cout << "[Response] NEGATIVE: 0x" << std::hex << (int)resp.sid
<< " 0x" << (int)resp.requestSid << " NRC=0x" << (int)resp.nrc << std::dec << std::endl;
} else if (!resp.responseData.empty()) {
std::cout << "[Response] POSITIVE: 0x" << std::hex << (int)UDS_SID_ROUTINE_CONTROL_RESP << " ";
for (auto b : resp.responseData) {
printf("%02X ", b);
}
std::cout << std::dec << std::endl;
} else {
std::cout << "[Response] POSITIVE (suppressed)" << std::endl;
}
}
};int main() {
ECUApplication app;
app.runDiagnosticTest();
return 0;
}
六、關鍵實現要點========== Test 1: Memory Erase (Short Routine) ==========
[Routine] Erasing memory at 0x44AAAAAA size: 3131963051 bytes
[Response] POSITIVE: 0x71 01 FF 00 01
========== Test 2: Camera Calibration Start (Long Routine) ==========
[Routine] Starting camera calibration with params: AA BB CC
[Response] POSITIVE: 0x71 01 12 34
========== Test 3: Request Calibration Results ==========
[Async] Routine 0x1234 completed after 10000ms
[Routine] Camera calibration completed!
[Response] POSITIVE: 0x71 03 12 34 D9 CE F4 3E 68 12 37 42 7B 14 10 41 F5 28 F0 41
========== Test 4: Valve Actuator Start (Persistent Routine) ==========
[Routine] Starting valve actuator test, mode: 2
[Response] POSITIVE: 0x71 01 56 78 02
[Valve] Cycle 1: OPEN -> CLOSE
[Valve] Cycle 2: OPEN -> CLOSE
[Valve] Cycle 3: OPEN -> CLOSE
========== Test 5: Stop Valve Actuator ==========
[Routine] Stopping valve actuator test
[Response] POSITIVE: 0x71 02 56 78 00 01
[Valve] Actuator test stopped========== Test 6: Invalid RID (Negative Test) ==========
[Response] NEGATIVE: 0x7F 0x31 NRC=0x31
同步vs異步處理:長例程必須使用異步執行,避免阻塞診斷通信
順序檢查:持續例程必須檢查start→stop的順序合法性
狀態管理:需維護每個RID的生命周期狀態
超時處理:長例程應支持超時自動完成并更新狀態
抑制肯定響應:支持sub-function的Bit7位來控制是否響應
以上實現了符合ISO 14229標準的0x31服務完整處理邏輯,可應用于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.