![]()
一、概述
上傳下載功能單元是UDS協議中用于ECU軟件刷寫和內存數據讀寫的核心組成部分。在實際應用中,該功能單元主要服務于兩個場景:
ECU軟件刷寫:將新的固件或軟件包下載到ECU的非易失性存儲器中
數據上傳:從ECU的內存中讀取運行日志、故障快照等診斷數據
本文將詳細講解三個核心服務:0x34 RequestDownload(請求下載)、0x36 TransferData(傳輸數據)和0x37 RequestTransferExit(請求傳輸終止),并提供完整的C++代碼示例。
二、0x34 RequestDownload(請求下載)服務 2.1 服務功能
診斷儀通過0x34服務向ECU發起下載請求,告知ECU即將下載數據的起始地址和總長度。ECU在肯定響應中返回其能夠接收的最大數據塊大小。
2.2 報文格式詳解 請求報文格式
字節位置
參數名稱
長度(字節)
0
服務ID
1
0x34
1
dataFormatIdentifier
1
高4位:壓縮算法,低4位:加密算法
2
addressAndLengthFormatIdentifier
1
高4位:memoryAddress長度,低4位:memorySize長度
memoryAddress
變長
數據寫入的起始地址
memorySize
變長
待傳輸數據的總長度
參數說明:
dataFormatIdentifier:0x00表示無壓縮無加密,其他值由車企自定義
addressAndLengthFormatIdentifier:例如0x44表示memoryAddress和memorySize各占4字節
memoryAddress:地址長度由addressAndLengthFormatIdentifier的高4位指定
memorySize:長度由addressAndLengthFormatIdentifier的低4位指定
字節位置
參數名稱
長度(字節)
0
服務ID+0x40
1 (0x74)
1
lengthFormatIdentifier
1
maxNumberOfBlockLength
變長
2.3 支持的否定響應碼
NRC
名稱
0x13
incorrectMessageLength
報文長度錯誤
0x22
conditionsNotCorrect
條件不滿足
0x31
requestOutOfRange
參數超出范圍
0x33
securityAccessDenied
安全訪問未通過
三、0x36 TransferData(傳輸數據)服務 3.1 服務功能
0x36服務用于實際數據的傳輸。在下載場景中,診斷儀將數據分塊發送給ECU;在上傳場景中,ECU在響應中返回數據。
3.2 報文格式詳解 請求報文格式(下載場景)
字節位置
參數名稱
長度(字節)
0
服務ID
1
0x36
1
blockSequenceCounter
1
塊序列計數器,從0x01開始
transferRequestParameterRecord
變長
待傳輸的數據
肯定響應格式(上傳場景)
字節位置
參數名稱
長度(字節)
0
服務ID+0x40
1 (0x76)
1
blockSequenceCounter
1
transferResponseParameterRecord
變長
3.3 塊序列計數器機制
從0x01開始計數
每發送一塊數據,計數器加1
達到0xFF后循環從0x00開始
確保數據塊的順序性和完整性
診斷儀使用0x37服務正常終止與ECU之間的數據傳輸會話。
4.2 報文格式 請求報文
字節位置
參數名稱
長度(字節)
0
服務ID
1 (0x37)
transferRequestParameterRecord
變長(可選)
肯定響應
字節位置
參數名稱
長度(字節)
0
服務ID+0x40
1 (0x77)
transferResponseParameterRecord
變長(可選)
五、完整交互流程示例
診斷儀 ECU
| |
|------ 0x34 RequestDownload -------->|
| (地址:0x60200010, 長度:0xFFFF) |
| |
|<------ 0x74 Positive Response -------|
| (maxBlockLength:0x0081) |
| |
|------ 0x36 TransferData ----------->|
| (塊序號:0x01, 數據塊1) |
| |
|<------ 0x76 Positive Response -------|
| (塊序號:0x01) |
| |
|------ 0x36 TransferData ----------->|
| (塊序號:0x02, 數據塊2) |
| |
|<------ 0x76 Positive Response -------|
| (塊序號:0x02) |
| |
|------ 0x37 RequestTransferExit ---->|
| |
|<------ 0x77 Positive Response -------|
| |
六、C++代碼實現 6.1 數據結構定義6.2 使用示例#include
#include
#include
#include
#include
namespace UDS {
// 否定響應碼枚舉
enumclass NRC :uint8_t {
OK = 0x00,
GENERAL_REJECT = 0x10,
SERVICE_NOT_SUPPORTED = 0x11,
SUBFUNCTION_NOT_SUPPORTED = 0x12,
INCORRECT_MESSAGE_LENGTH = 0x13,
CONDITIONS_NOT_CORRECT = 0x22,
REQUEST_SEQUENCE_ERROR = 0x24,
REQUEST_OUT_OF_RANGE = 0x31,
SECURITY_ACCESS_DENIED = 0x33,
INVALID_DATA = 0x71,
BLOCK_SEQUENCE_COUNTER_ERROR = 0x73
};
// 數據格式標識符
struct DataFormatIdentifier {
uint8_t compression : 4; // 壓縮算法
uint8_t encryption : 4; // 加密算法
DataFormatIdentifier() : compression(0), encryption(0) {}
uint8_t toByte() const { return (compression << 4) | encryption; }
void fromByte(uint8_t byte) {
compression = (byte >> 4) & 0x0F;
encryption = byte & 0x0F;
}
};
// 傳輸狀態機
enumclass TransferState {
IDLE, // 空閑
DOWNLOADING, // 下載中
UPLOADING, // 上傳中
COMPLETED // 傳輸完成
};
// 傳輸上下文
struct TransferContext {
TransferState state;
uint32_t memoryAddress;
uint32_t memorySize;
uint32_t bytesTransferred;
uint8_t nextBlockCounter;
uint16_t maxBlockLength;
bool isDownload;
TransferContext()
: state(TransferState::IDLE)
, memoryAddress(0)
, memorySize(0)
, bytesTransferred(0)
, nextBlockCounter(1)
, maxBlockLength(0)
, isDownload(true) {}
};
/**
* @brief UDS傳輸層處理類
*
* 實現了0x34/0x36/0x37服務的核心邏輯
*/
class UDSTransferHandler {
public:
UDSTransferHandler() : securityPassed_(false) {}
virtual ~UDSTransferHandler() = default;
/**
* @brief 處理0x34 RequestDownload服務
* @param request 完整請求報文
* @param response 輸出參數,存放響應報文
* @return 處理結果NRC
*/
NRC handleRequestDownload(const std::vector& request,
std::vector& response) {
// 檢查最小報文長度
if (request.size() < 4) {
return NRC::INCORRECT_MESSAGE_LENGTH;
}
// 檢查安全訪問
if (!securityPassed_) {
return NRC::SECURITY_ACCESS_DENIED;
}
// 解析地址和長度格式標識符
uint8_t addrLenFormat = request[2];
uint8_t addrLen = (addrLenFormat >> 4) & 0x0F;
uint8_t sizeLen = addrLenFormat & 0x0F;
// 檢查報文長度是否匹配
if (request.size() != static_cast(3 + addrLen + sizeLen)) {
return NRC::INCORRECT_MESSAGE_LENGTH;
}
// 解析數據格式標識符
DataFormatIdentifier dfi;
dfi.fromByte(request[1]);
// 檢查數據格式是否支持
if (dfi.compression != 0 || dfi.encryption != 0) {
return NRC::REQUEST_OUT_OF_RANGE;
}
// 解析內存地址
uint32_t memoryAddress = 0;
for (int i = 0; i < addrLen; i++) {
memoryAddress = (memoryAddress << 8) | request[3 + i];
}
// 解析數據總長度
uint32_t memorySize = 0;
for (int i = 0; i < sizeLen; i++) {
memorySize = (memorySize << 8) | request[3 + addrLen + i];
}
// 檢查地址和長度是否有效
if (!isValidMemoryRange(memoryAddress, memorySize)) {
return NRC::REQUEST_OUT_OF_RANGE;
}
// 檢查預置條件(如:是否已進入編程會話)
if (!checkPreconditions()) {
return NRC::CONDITIONS_NOT_CORRECT;
}
// 保存傳輸上下文
transferCtx_.state = TransferState::DOWNLOADING;
transferCtx_.memoryAddress = memoryAddress;
transferCtx_.memorySize = memorySize;
transferCtx_.bytesTransferred = 0;
transferCtx_.nextBlockCounter = 1;
transferCtx_.isDownload = true;
transferCtx_.maxBlockLength = getMaxBlockLength();
// 構建肯定響應
response.clear();
response.push_back(0x74); // 服務ID + 0x40
// maxNumberOfBlockLength參數(假設最大塊長度為0x81 = 129字節)
uint16_t maxBlockLen = transferCtx_.maxBlockLength;
if (maxBlockLen <= 0xFF) {
response.push_back(0x01); // lengthFormatIdentifier: 1字節長度
response.push_back(static_cast(maxBlockLen));
} else {
response.push_back(0x02); // lengthFormatIdentifier: 2字節長度
response.push_back(static_cast((maxBlockLen >> 8) & 0xFF));
response.push_back(static_cast(maxBlockLen & 0xFF));
}
return NRC::OK;
}
/**
* @brief 處理0x36 TransferData服務
* @param request 完整請求報文
* @param response 輸出參數,存放響應報文
* @return 處理結果NRC
*/
NRC handleTransferData(const std::vector& request,
std::vector& response) {
// 檢查傳輸狀態
if (transferCtx_.state != TransferState::DOWNLOADING &&
transferCtx_.state != TransferState::UPLOADING) {
return NRC::REQUEST_SEQUENCE_ERROR;
}
// 最小報文檢查(至少包含服務ID和塊序號)
if (request.size() < 2) {
return NRC::INCORRECT_MESSAGE_LENGTH;
}
// 獲取塊序列計數器
uint8_t receivedCounter = request[1];
// 檢查塊序號是否正確
if (receivedCounter != transferCtx_.nextBlockCounter) {
return NRC::BLOCK_SEQUENCE_COUNTER_ERROR;
}
// 對于下載場景,處理數據
if (transferCtx_.state == TransferState::DOWNLOADING) {
// 提取數據負載
std::vector data(request.begin() + 2, request.end());
// 檢查數據長度是否超過ECU接收能力
if (data.size() > transferCtx_.maxBlockLength) {
return NRC::INVALID_DATA;
}
// 檢查是否會超出總數據長度
if (transferCtx_.bytesTransferred + data.size() > transferCtx_.memorySize) {
return NRC::INVALID_DATA;
}
// 寫入數據到內存
if (!writeDataToMemory(transferCtx_.memoryAddress + transferCtx_.bytesTransferred,
data.data(), data.size())) {
return NRC::GENERAL_REJECT;
}
// 更新傳輸進度
transferCtx_.bytesTransferred += data.size();
transferCtx_.nextBlockCounter++;
// 構建肯定響應
response.clear();
response.push_back(0x76); // 服務ID + 0x40
response.push_back(receivedCounter);
// 可選:添加CRC校驗值
// response.push_back(calculateCRC(data));
}
// 對于上傳場景,返回數據
elseif (transferCtx_.state == TransferState::UPLOADING) {
// 計算本次要上傳的數據大小
uint32_t remaining = transferCtx_.memorySize - transferCtx_.bytesTransferred;
uint32_t blockSize = std::min(remaining,
static_cast(transferCtx_.maxBlockLength));
// 讀取數據
std::vector data(blockSize);
if (!readDataFromMemory(transferCtx_.memoryAddress + transferCtx_.bytesTransferred,
data.data(), blockSize)) {
return NRC::GENERAL_REJECT;
}
// 構建肯定響應(包含上傳數據)
response.clear();
response.push_back(0x76); // 服務ID + 0x40
response.push_back(receivedCounter);
response.insert(response.end(), data.begin(), data.end());
// 更新傳輸進度
transferCtx_.bytesTransferred += blockSize;
transferCtx_.nextBlockCounter++;
}
return NRC::OK;
}
/**
* @brief 處理0x37 RequestTransferExit服務
* @param request 完整請求報文
* @param response 輸出參數,存放響應報文
* @return 處理結果NRC
*/
NRC handleRequestTransferExit(const std::vector& request,
std::vector& response) {
// 檢查傳輸狀態
if (transferCtx_.state != TransferState::DOWNLOADING &&
transferCtx_.state != TransferState::UPLOADING) {
return NRC::REQUEST_SEQUENCE_ERROR;
}
// 檢查是否所有數據都已傳輸完成
if (transferCtx_.bytesTransferred != transferCtx_.memorySize) {
// 數據不完整,可選擇性返回錯誤
// 這里允許提前終止
}
// 執行傳輸結束后的處理
if (!onTransferComplete()) {
return NRC::GENERAL_REJECT;
}
// 重置傳輸上下文
transferCtx_.state = TransferState::COMPLETED;
// 構建肯定響應
response.clear();
response.push_back(0x77); // 服務ID + 0x40
return NRC::OK;
}
// 設置安全訪問標志
void setSecurityPassed(bool passed) { securityPassed_ = passed; }
// 重置傳輸狀態(用于異常恢復)
void resetTransfer() {
transferCtx_ = TransferContext();
}
protected:
// 以下為虛函數,實際使用時需要根據具體ECU實現
/**
* @brief 檢查內存地址范圍是否有效
*/
virtual bool isValidMemoryRange(uint32_t address, uint32_t size) {
// 實際實現中需根據ECU的內存映射表檢查
// 例如:Flash地址范圍0x60000000-0x60FFFFFF
return (address >= 0x60000000 &&
address + size <= 0x60FFFFFF);
}
/**
* @brief 檢查ECU狀態是否滿足傳輸條件
*/
virtual bool checkPreconditions() {
// 檢查是否已進入擴展會話或編程會話
returntrue;
}
/**
* @brief 獲取ECU能接收的最大塊長度
*/
virtual uint16_t getMaxBlockLength() {
// 實際實現中需要考慮:
// 1. 傳輸層緩沖區大小(如CAN FD的64字節)
// 2. 應用層處理能力
return0x81; // 129字節
}
/**
* @brief 將數據寫入內存
*/
virtual bool writeDataToMemory(uint32_t address, const uint8_t* data, uint32_t length) {
// 實際實現中需調用具體的Flash驅動
std::cout << "Writing " << length << " bytes to address 0x"
<< std::hex << address << std::dec << std::endl;
returntrue;
}
/**
* @brief 從內存讀取數據
*/
virtual bool readDataFromMemory(uint32_t address, uint8_t* data, uint32_t length) {
std::cout << "Reading " << length << " bytes from address 0x"
<< std::hex << address << std::dec << std::endl;
returntrue;
}
/**
* @brief 傳輸完成后的處理
*/
virtual bool onTransferComplete() {
std::cout << "Transfer completed. Total bytes: "
<< transferCtx_.bytesTransferred << std::endl;
returntrue;
}
private:
TransferContext transferCtx_;
bool securityPassed_;
};} // namespace UDS
七、工程實踐注意事項 7.1 傳輸層考量#include
#include
usingnamespace UDS;
/**
* @brief 演示UDS下載流程的完整示例
*/
void demoDownloadFlow() {
UDSTransferHandler handler;
// 模擬安全訪問通過
handler.setSecurityPassed(true);
std::vector request;
std::vector response;
NRC result;
// Step 1: 發送0x34 RequestDownload
// 34 00 44 60 20 00 10 00 00 FF FF
request = {0x34, 0x00, 0x44,
0x60, 0x20, 0x00, 0x10, // memoryAddress = 0x60200010
0x00, 0x00, 0xFF, 0xFF}; // memorySize = 0x0000FFFF
result = handler.handleRequestDownload(request, response);
if (result == NRC::OK) {
std::cout << "RequestDownload success. Response: ";
for (auto byte : response) {
printf("%02X ", byte);
}
std::cout << std::endl;
} else {
std::cout << "RequestDownload failed with NRC: 0x"
<< std::hex << static_cast(result) << std::dec << std::endl;
return;
}
// Step 2: 模擬發送多塊數據
for (int blockNum = 1; blockNum <= 5; blockNum++) {
// 構造數據塊
std::vector data(128, static_cast(blockNum)); // 測試數據
request.clear();
request.push_back(0x36); // 服務ID
request.push_back(blockNum); // 塊序列計數器
request.insert(request.end(), data.begin(), data.end());
result = handler.handleTransferData(request, response);
if (result == NRC::OK) {
std::cout << "TransferData block " << blockNum << " success. Response: ";
for (auto byte : response) {
printf("%02X ", byte);
}
std::cout << std::endl;
} else {
std::cout << "TransferData block " << blockNum << " failed with NRC: 0x"
<< std::hex << static_cast(result) << std::dec << std::endl;
return;
}
}
// Step 3: 發送0x37 RequestTransferExit
request = {0x37};
result = handler.handleRequestTransferExit(request, response);
if (result == NRC::OK) {
std::cout << "RequestTransferExit success. Response: ";
for (auto byte : response) {
printf("%02X ", byte);
}
std::cout << std::endl;
} else {
std::cout << "RequestTransferExit failed with NRC: 0x"
<< std::hex << static_cast(result) << std::dec << std::endl;
}
}
/**
* @brief 演示錯誤處理場景
*/
void demoErrorHandling() {
UDSTransferHandler handler;
handler.setSecurityPassed(true);
std::vector request;
std::vector response;
NRC result;
std::cout << "\n=== 錯誤場景測試 ===" << std::endl;
// 場景1: 未發送34服務直接發送36服務
request = {0x36, 0x01, 0x01, 0x02, 0x03};
result = handler.handleTransferData(request, response);
std::cout << "Test 1 - Missing 34 service: NRC = 0x"
<< std::hex << static_cast(result) << std::dec;
std::cout << " (Expected: 0x24 - REQUEST_SEQUENCE_ERROR)" << std::endl;
// 場景2: 錯誤的塊序列計數器
handler.resetTransfer();
request = {0x34, 0x00, 0x44, 0x60, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10};
handler.handleRequestDownload(request, response);
request = {0x36, 0x05, 0x01, 0x02, 0x03}; // 塊序號5,期望是1
result = handler.handleTransferData(request, response);
std::cout << "Test 2 - Wrong block sequence: NRC = 0x"
<< std::hex << static_cast(result) << std::dec;
std::cout << " (Expected: 0x73 - BLOCK_SEQUENCE_COUNTER_ERROR)" << std::endl;
// 場景3: 安全訪問未通過
UDSTransferHandler handler2;
request = {0x34, 0x00, 0x44, 0x60, 0x20, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10};
result = handler2.handleRequestDownload(request, response);
std::cout << "Test 3 - Security not passed: NRC = 0x"
<< std::hex << static_cast(result) << std::dec;
std::cout << " (Expected: 0x33 - SECURITY_ACCESS_DENIED)" << std::endl;
}int main() {
std::cout << "=== UDS 0x34/0x36/0x37 Service Demo ===" << std::endl;
demoDownloadFlow();
demoErrorHandling();
return0;
}
在實際CAN或CAN FD總線上實現時,需要注意:
多幀傳輸:當數據塊超過單幀CAN報文長度(CAN為8字節,CAN FD為64字節)時,需要實現ISO 15765-2傳輸層協議
流控制:ECU通過流控制幀通知診斷儀發送速率
超時處理:需要合理設置P2Server、P2*Server等超時參數
完整性校驗:建議在36服務的響應中增加CRC校驗
斷點續傳:支持刷寫中斷后的恢復機制
版本驗證:刷寫前驗證固件版本和兼容性
八、總結// 示例:內存地址對齊檢查
bool isAddressAligned(uint32_t address, uint32_t alignment) {
return (address & (alignment - 1)) == 0;
}// 示例:Flash編程時的扇區擦除
bool eraseFlashSectors(uint32_t startAddr, uint32_t endAddr) {
// 根據具體Flash特性實現擦除邏輯
// 注意:擦除操作可能耗時較長,需要適當延長響應超時
return true;
}
本文詳細介紹了UDS協議中的上傳下載功能單元,重點講解了0x34、0x36和0x37三個服務的報文格式、參數含義、響應行為及錯誤處理機制。通過完整的C++代碼實現,展示了如何在ECU軟件中集成這些服務。
關鍵要點回顧:
0x34服務:啟動下載/上傳會話,協商傳輸參數
0x36服務:實際數據傳輸,使用塊序列計數器保證順序
0x37服務:正常終止傳輸會話
狀態機管理:確保服務的正確調用順序
安全機制:配合安全訪問服務保護敏感操作
實際項目中,還需要結合具體的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.