一、概述
在AUTOSAR架構中,UDS 0x22服務(ReadDataByIdentifier)用于通過DID從ECU讀取應用數據或存儲數據。DCM(Diagnostic Communication Manager)本身并不存儲任何應用數據,當DCM的DID級處理函數被觸發后,需要通過RTE(Run-Time Environment)調用相應的軟件組件(SW-C)或基礎軟件模塊(BSW)來獲取實際數據。本文將深入解析實時動態數據和存儲數據兩類DID的數據獲取原理,并提供C++代碼實現示例。
二、DID數據的來源分類
DID數據通常分為兩大類:
類型
數據示例
數據來源
讀取方式
實時動態數據
車速、發動機轉速、水溫、油門踏板位置、車門狀態
應用層SW-C周期性計算或采集
通過RTE調用SW-C的輸出端口獲取
存儲數據
VIN碼、硬件版本號、軟件版本號、生產日期、配置參數
非易失性存儲器(EEPROM/Flash)
通過NvM模塊讀取
實時動態數據反映車輛當前運行狀態,具有強時效性;存儲數據則持久化于NVM中。理解這一分類是正確設計DID映射配置的前提。
三、AUTOSAR工具鏈DID配置概述
在深入代碼實現之前,首先需要理解DID在AUTOSAR工具鏈(如ETAS ISOLAR/Virtual PC、Vector DaVinci)中的配置流程,這是后續代碼能夠正確運行的基礎。
配置步驟概覽:
CDD(CANdb++ Diagnostic Description)準備 :創建DID定義文件,配置DID標識符、數據類型、數據長度等
工具鏈配置 :
在DaVinci Developer中為應用層診斷SW-C創建RAM數組(如
NvMShw_xxxx)創建NvM Block Needs,配置存儲屬性(上電讀取、下電存儲等)
將NvM Block與RAM數組關聯
關聯Client/Server接口 :
將DCM模塊的DID關聯接口至應用層診斷SW-C
將NvM模塊的DID關聯接口至應用層診斷SW-C
在SWC中建立DID的Access Point
代碼生成 :配置完成后,工具鏈會生成以下內容:
BSW模塊的C源文件和頭文件(如
Dcm.c、NvM.c)RTE骨架代碼
自定義DID處理函數的框架(待開發者填充)
在系統工程設計中,首先需要在RTE配置工具中將DID 0x0202映射到應用層軟件組件的輸出端口。這通常通過以下方式實現:
在DaVinci Developer中創建SW-C的Sender-Port,定義車速數據元素的數據類型
配置DCM模塊中DID 0x0202的數據源屬性,將其指向上述SW-C的輸出端口而非NvM存儲
配置RTE數據映射,將DCM讀取請求路由到對應的SW-C端口
這種設計使得DCM無需區分數據來源,RTE作為抽象層自動完成路由,極大提升了軟件的復用性和可維護性。
4.2 讀取流程
![]()
4.3 代碼實現示例
以下代碼演示了在AUTOSAR架構下,DCM如何通過RTE獲取實時車速數據的完整實現。
4.3.1 應用層SW-C代碼
// ============================================================================
// 文件: Sensor_SWC.h
// 描述: 應用層傳感器處理軟件組件頭文件
// ============================================================================
#ifndef SENSOR_SWC_H
#define SENSOR_SWC_H
#include "Std_Types.h" // AUTOSAR標準類型定義
#include "Rte_Sensor_SWC.h" // RTE生成的頭文件
#ifdef __cplusplus
extern "C" {
#endif
// 車速數據類型定義
typedef uint16 VehicleSpeedType; // 車速范圍: 0-65535 km/h
typedef uint16 EngineSpeedType; // 發動機轉速
typedef uint8 TemperatureType; // 溫度值
// ============================================================================
// Runnable實體函數聲明
// ============================================================================
/**
* @brief Runnable_ReadVehicleSpeed - 讀取車速值
* @details 該Runnable由RTE周期性調度,從CAN驅動讀取最新車速并更新到輸出端口
* @param none
* @return void
*/
extern FUNC(void, SENSOR_SWC_CODE) Runnable_ReadVehicleSpeed(void);
/**
* @brief Runnable_ReadEngineSpeed - 讀取發動機轉速
* @details 周期性讀取發動機轉速值
*/
extern FUNC(void, SENSOR_SWC_CODE) Runnable_ReadEngineSpeed(void);
#ifdef __cplusplus
}
#endif#endif /* SENSOR_SWC_H */
// ============================================================================
// 文件: Sensor_SWC.c
// 描述: 應用層傳感器處理軟件組件實現
// ============================================================================
#include "Sensor_SWC.h"
#include "CanIf.h" // CAN接口模塊
#include "Can.h" // CAN驅動模塊
/* 靜態變量 - 最新數據值(RAM中的Buffer) */
static VAR(VehicleSpeedType, SENSOR_SWC_VAR) s_lastVehicleSpeed = 0U;
static VAR(EngineSpeedType, SENSOR_SWC_VAR) s_lastEngineSpeed = 0U;
static VAR(uint8, SENSOR_SWC_VAR) s_speedValidFlag = 0U; // 數據有效性標志
/* CAN接收的數據結構定義 */
typedef struct {
uint16 speed_raw; // 原始車速值
uint8 signal_quality; // 信號質量標志
} CanSpeedMsgType;/**
* @brief Runnable_ReadVehicleSpeed - 周期間隔5ms運行
* @details 1. 通過CanIf讀取CAN總線上的車速信號
* 2. 進行數據處理和有效性校驗
* 3. 通過RTE接口將數據發送到輸出端口
*/
FUNC(void, SENSOR_SWC_CODE) Runnable_ReadVehicleSpeed(void)
{
CanSpeedMsgType rxMsg;
VehicleSpeedType processedSpeed = 0U;
uint8 retVal = 0U;
/* 步驟1: 通過CanIf從CAN驅動讀取原始報文 */
/* 假設車速報文CAN ID為0x3A0 */
retVal = CanIf_ReadRxPduData(0x3A0U, (uint8*)&rxMsg, sizeof(rxMsg));
if (retVal == E_OK)
{
/* 步驟2: 數據處理 - 原始值轉換為實際物理值 */
/* 假設轉換公式: 實際車速 = 原始值 * 0.1 */
if (rxMsg.signal_quality == 0x01U) /* 有效信號 */
{
processedSpeed = (VehicleSpeedType)((uint32)rxMsg.speed_raw * 10U / 100U);
s_lastVehicleSpeed = processedSpeed;
s_speedValidFlag = 1U;
}
else
{
/* 信號無效,保持上次有效值 */
s_speedValidFlag = 0U;
}
}
else
{
/* CAN讀取錯誤,維持上次值或給默認值 */
s_speedValidFlag = 0U;
}
/* 步驟3: 通過RTE_Send接口將數據發送到輸出端口 */
/* 該端口已在工具鏈中配置映射,連接到DCM的DID 0x0202數據源 */
(void)Rte_Send_VehicleSpeed(s_lastVehicleSpeed);
}
4.3.2 DCM DID級處理函數實現
// ============================================================================
// 文件: Dcm_DidProcessing.h
// 描述: DCM模塊DID級處理函數頭文件
// ============================================================================
#ifndef DCM_DID_PROCESSING_H
#define DCM_DID_PROCESSING_H
#include "Std_Types.h"
#include "Dcm_Cfg.h"
/* DCM API返回類型定義 */
#define DCM_E_OK 0x00U /* 成功完成 */
#define DCM_E_FAIL 0x01U /* 一般錯誤 */
#define DCM_E_PENDING 0x02U /* 異步操作進行中 */
/* 響應數據緩沖區最大長度 */
#define DID_MAX_RESPONSE_LEN 128U
/* 預定義的DID標識符 */
#define DID_VEHICLE_SPEED 0x0202U /* 實時車速 */
#define DID_VIN_CODE 0xF190U /* VIN碼(標準定義) */
#define DID_HW_VERSION 0xF180U /* 硬件版本號 */
#define DID_SW_VERSION 0xF188U /* 軟件版本號 */
/* DID處理函數類型定義 */
typedef uint8 (*Dcm_DidReadHandlerType)(uint8* dataBuffer, uint16* dataLength);
/**
* @brief Dcm_ReadDid_VehicleSpeed - 處理車速DID 0x0202的讀取
* @param dataBuffer - 輸出緩沖區指針,用于存放讀取的數據
* @param dataLength - 輸出數據長度指針
* @return 執行狀態碼
*/
extern FUNC(uint8, DCM_CODE) Dcm_ReadDid_VehicleSpeed(uint8* dataBuffer, uint16* dataLength);#endif /* DCM_DID_PROCESSING_H */
// ============================================================================
// 文件: Dcm_DidProcessing.c
// 描述: DCM模塊DID級處理函數實現
// ============================================================================
#include "Dcm_DidProcessing.h"
#include "Rte_Dcm.h" /* RTE頭文件,包含Rte_Read_SWC接口 */
#include "SchM_Dcm.h"/**
* @brief Dcm_ReadDid_VehicleSpeed - 處理車速DID 0x0202的讀取
* @details
* 1. 通過RTE_Read接口獲取SW-C提供的最新車速值
* 2. 將收到的數據封裝到數據緩沖區中
* 3. 設置數據長度返回給DCM上層處理
*/
FUNC(uint8, DCM_CODE) Dcm_ReadDid_VehicleSpeed(uint8* dataBuffer, uint16* dataLength)
{
VehicleSpeedType speedValue = 0U;
uint8 retStatus = DCM_E_OK;
/* 參數有效性檢查 - MISRA C規范要求 */
if ((NULL_PTR == dataBuffer) || (NULL_PTR == dataLength))
{
return DCM_E_FAIL;
}
/* 通過RTE接口讀取SW-C中的車速數據 */
/* Rte_Read_VehicleSpeed已在工具鏈配置時生成 */
if (Rte_Read_VehicleSpeed(&speedValue) == RTE_E_OK)
{
/* 將車速值以小端序格式寫入響應緩沖區 */
/* 車速數據長度為2字節 */
dataBuffer[0U] = (uint8)(speedValue & 0xFFU); /* 低字節 */
dataBuffer[1U] = (uint8)((speedValue >> 8U) & 0xFFU); /* 高字節 */
*dataLength = 2U;
retStatus = DCM_E_OK;
}
else
{
/*
* RTE讀取失敗時的錯誤處理:
* 返回NRC 0x22 - 條件不滿足
*/
retStatus = DCM_E_FAIL;
}
return retStatus;
}
4.3.3 DCM配置回調注冊
五、通過NvM獲取存儲數據——以VIN碼DID 0xF190為例 5.1 配置存儲映射// ============================================================================
// 文件: Dcm_Cfg.c
// 描述: DCM啟動配置與DID回調映射(由工具鏈自動生成框架,開發者填充)
// ============================================================================
#include "Dcm_Cfg.h"
#include "Dcm_DidProcessing.h"
/* DID處理函數表 */
/* 該結構體由配置工具自動生成,開發者需要將自定義處理函數填充到對應DID條目 */
const Dcm_DidReadHandlerType Dcm_DidReadHandlerTable[][2] = {
/* {DID值, 處理函數指針} */
{DID_VEHICLE_SPEED, Dcm_ReadDid_VehicleSpeed}, /* 0x0202 - 實時車速 */
{DID_VIN_CODE, Dcm_ReadDid_VinCode}, /* 0xF190 - VIN碼 */
{DID_HW_VERSION, Dcm_ReadDid_HardwareVersion}, /* 0xF180 - 硬件版本 */
/* 更多的DID條目根據需要添加 */
};/* DCM主控邏輯接收到0x22服務請求時,框架層代碼會查找該表 */
uint8 Dcm_DispatchReadDidRequest(uint16 did, uint8* responseBuffer, uint16* responseLen)
{
uint8 idx;
for (idx = 0U; idx < NUM_OF_HANDLERS; idx++)
{
if (Dcm_DidReadHandlerTable[idx][0] == did)
{
if (Dcm_DidReadHandlerTable[idx][1] != NULL_PTR)
{
return ((Dcm_DidReadHandlerTable[idx][1])(responseBuffer, responseLen));
}
else
{
return DCM_E_FAIL; /* 空函數指針 */
}
}
}
return DCM_E_FAIL; /* 未找到對應的DID */
}
VIN碼等存儲數據的配置需要在工具鏈中建立DID到NvM Block的映射:
在DaVinci Developer中為診斷SW-C創建NvData類型的接口,定義VIN碼結構體
配置NvM Block Descriptor,指定RAM Block大小(如17字節存儲VIN碼)、存儲屬性(立即存儲或周期存儲)
在執行NvM_ReadAll接口的初始化階段,將Flash中的VIN碼數據同步到RAM Mirror
DCM的DID配置中標記數據來源為
NvM_SERVICE,設置對應的NvM塊ID;這樣在運行時,DCM級處理函數會調用NvM_ReadBlock而非Rte_Read,實現通過存儲管理塊(而非實時RTE)讀取持久數據
![]()
在AUTOSAR NvM架構中:
Nv Block :實際存儲在EEPROM或Flash中的數據
RAM Block :上電讀取時的數據鏡像,便于快速訪問
Immediate Block :用于Crash Data,具有最高優先級(優先級0),發生事件時必須立即寫入存儲,不能被延遲
NvM提供兩種數據同步訪問方式:
同步方式
特點
適用場景
隱式同步
應用與NvM共享RAM Block,應用直接讀寫RAM,NvM負責與NVM的數據同步
對響應時間敏感,實時性要求高的讀取場景
顯式同步
應用需顯式調用NvM_ReadBlock/NvM_WriteBlock發起讀寫請求
對數據一致性要求嚴格,或處理大塊數據的場景
5.4 讀取流程
![]()
5.5 代碼實現示例
5.5.1 NvM Block配置結構(BSW層配置)
// ============================================================================
// 文件: NvM_Cfg.h
// 描述: NvM模塊配置定義(由工具鏈生成,可手工修改)
// ============================================================================
#ifndef NVM_CFG_H
#define NVM_CFG_H
#include "Std_Types.h"
/* 定義NvM塊ID */
#define NVM_BLOCK_ID_VIN 0x01U /* VIN碼塊ID */
#define NVM_BLOCK_ID_HW_VERSION 0x02U /* 硬件版本塊ID */
#define NVM_BLOCK_ID_SW_VERSION 0x03U /* 軟件版本塊ID */
#define NVM_BLOCK_ID_CALIB_DATA 0x04U /* 標定數據塊ID */
/* VIN碼存儲定義(17位字符 + 1字節結束符) */
#define VIN_DATA_LENGTH 18U
#define VIN_STANDARD_LENGTH 17U /* 標準VIN碼長度 */
/* NvM塊屬性配置 */
typedef struct {
uint16 blockId; /* 塊標識符 */
uint16 blockSize; /* 數據長度(字節) */
uint8 priority; /* 優先級(0最高,255最低) */
boolean isImmediate; /* 是否為立即塊(Crash Data) */
boolean readAllInit; /* 是否上電自動讀取 */
} NvM_BlockConfigType;
/* 實際NvM塊配置表(由BSW配置工具生成) */
const NvM_BlockConfigType NvM_BlockConfigTable[] = {
{NVM_BLOCK_ID_VIN, VIN_DATA_LENGTH, 10U, FALSE, TRUE},
{NVM_BLOCK_ID_HW_VERSION, 16U, 20U, FALSE, TRUE},
{NVM_BLOCK_ID_SW_VERSION, 32U, 20U, FALSE, TRUE},
{NVM_BLOCK_ID_CALIB_DATA, 256U, 5U, FALSE, TRUE}
};#endif /* NVM_CFG_H */
5.5.2 NvM回調函數實現
// ============================================================================
// 文件: NvM_Callbacks.c
// 描述: NvM模塊回調函數實現
// ============================================================================
#include "NvM_Callbacks.h"
#include "NvM.h"
#include "Dcm.h"
/* 異步讀取狀態結構體 */
typedef struct {
uint16 requestedDid; /* 請求的DID */
uint8 responseBuffer[DID_MAX_RESPONSE_LEN]; /* 響應緩沖區 */
uint16 responseLen; /* 實際數據長度 */
boolean pending; /* 異步請求進行中標志 */
} NvM_AsyncReadContextType;
/* 全局異步讀取上下文 */
static VAR(NvM_AsyncReadContextType, NVM_VAR) s_asyncReadCtx;
/* VIN碼數據RAM Mirror */
static VAR(uint8, NVM_VAR) s_vinData[VIN_DATA_LENGTH] = {0U};
/**
* @brief Dcm_ReadDid_VinCode - VIN碼DID處理函數
* @details 通過NvM_ReadBlock發起異步讀取請求
*/
FUNC(uint8, DCM_CODE) Dcm_ReadDid_VinCode(uint8* dataBuffer, uint16* dataLength)
{
NvM_RequestResultType result;
/* 參數校驗 */
if ((NULL_PTR == dataBuffer) || (NULL_PTR == dataLength))
{
return DCM_E_FAIL;
}
/* 檢查是否已經有正在進行的異步讀取請求 */
if (s_asyncReadCtx.pending == TRUE)
{
/* 異步讀取進行中,根據NvM規范返回E_PENDING */
/* DCM框架層會等待回調完成后再組裝響應 */
return DCM_E_PENDING;
}
/* 保存上下文信息 */
s_asyncReadCtx.requestedDid = DID_VIN_CODE;
s_asyncReadCtx.pending = TRUE;
/*
* 發起NvM異步讀取請求
* NvM_ReadBlock原型: uint8 NvM_ReadBlock(NvM_BlockIdType BlockId, void* BufferPtr)
* 該API將讀取請求放入異步隊列,完成時自動調用注冊的回調函數
*/
result = NvM_ReadBlock(NVM_BLOCK_ID_VIN, (void*)&s_vinData[0]);
if (result == NVM_REQ_OK)
{
/*
* 請求已成功提交,數據暫未返回
* NvM完成讀取后會調用回調函數NvM_ReadVinCallback
* 此處返回E_PENDING,DCM等待回調完成后再回復客戶端
*/
return DCM_E_PENDING;
}
else if (result == NVM_REQ_PENDING)
{
/* 請求已在隊列中,等待處理 */
return DCM_E_PENDING;
}
else
{
/* 請求失敗,清除pending標志 */
s_asyncReadCtx.pending = FALSE;
return DCM_E_FAIL;
}
}/**
* @brief NvM_ReadVinCallback - NvM讀取完成回調函數
* @details
* 1. 當NvM_ReadBlock異步讀取操作完成后,NvM模塊會調用此回調
* 2. 在回調中將讀取到的數據復制到響應緩沖區
* 3. 通知DCM模塊繼續完成響應發送
*/
FUNC(void, NVM_CODE) NvM_ReadVinCallback(NvM_BlockIdType BlockId, NvM_RequestResultType Result)
{
uint8* dataBuffer = NULL_PTR;
uint16 dataLength = 0U;
/* 檢查塊ID是否正確 */
if (BlockId != NVM_BLOCK_ID_VIN)
{
return;
}
if (Result == NVM_REQ_OK)
{
/* 讀取成功,獲取DCM的響應緩沖區指針 */
if (Dcm_GetResponseBuffer(&dataBuffer, &dataLength) == E_OK)
{
/* 將VIN碼數據復制到DCM響應緩沖區,并設置數據長度 */
(void)memcpy(dataBuffer, s_vinData, VIN_STANDARD_LENGTH);
/* DCM模塊完成正響應幀組裝 */
Dcm_SendPositiveResponse(DID_VIN_CODE, dataBuffer, VIN_STANDARD_LENGTH);
}
}
else
{
/* 讀取失敗,需要填充否定響應(NRC 0x22: 條件不滿足) */
uint8 nrc = 0x22U;
Dcm_SendNegativeResponse(DID_VIN_CODE, nrc);
}
/* 清除pending標志 */
s_asyncReadCtx.pending = FALSE;
}
5.5.3 NvM初始化與周期調度實現
六、響應發送與各層報文字段說明// ============================================================================
// 文件: NvM_SchM.c
// 描述: NvM模塊初始化與周期性任務調度(由SchM配置工具編排集成)
// ============================================================================
#include "SchM_NvM.h"
#include "NvM.h"
#include "NvM_Callbacks.h"
/* 注冊回調函數到NvM模塊 */
FUNC(void, NVM_CODE) NvM_InitCallbacks(void)
{
/* 注冊異步讀取完成回調 - NvM配置中以ServicePort形式映射到相應回調入口 */
/* 實際框架調用方式:NvM_SetReadCallback(NVM_BLOCK_ID_VIN, NvM_ReadVinCallback); */
}/**
* @brief NvM_MainFunction - NvM主調度函數
* @details
* 該函數需要周期性地被SchM(System Clock Manager)調用。
* 它以固定的時間間隔(例如10ms)運行,負責:
* 1. 從異步任務隊列中取出讀寫請求并執行
* 2. 處理基于優先級的任務調度
* 3. 在上電階段執行NvM_ReadAll將數據從NVM同步到RAM Mirror
* 4. 在需要時將RAM Mirror中的數據寫回NVM(WriteAll或WriteBlock)
* 5. 檢查CRC校驗,管理數據完整性
* 6. 處理錯誤并觸發相應的回調
*/
FUNC(void, NVM_CODE) NvM_MainFunction(void)
{
/* 調用NvM模塊主函數,處理隊列中的請求 */
NvM_MainFunction_Internal();
}
DID級處理函數將數據返回后,DCM主狀態機會觸發響應發送流程:
DCM層 :DCM將已通過PduR注冊的Tx回調與響應數據組裝成I-PDU,調用
PduR_DcmTransmitPduR層 :根據配置的路由表確定目的地(通常是CanTp或直接發往CanIf)并轉發
CanTp層 :如果需要分段(N_Cro+N_Data),CanTp負責生成FF/CF/FC等N-PDU并調用
CanIf_TransmitCanIf層 :調用CAN驅動發送CAN幀到總線
以下是實際數據報文格式說明:
報文方向
報文內容(十六進制)
字段解析
請求
22 02 02
22 = SID(0x22);02 02 = DID
正響應
62 02 02 00 20
62 = SID+0x40;02 02 = DID;00 20 = 數據(32 km/h)
負響應
7F 22 31
7F = 負響應標識;22 = SID;31 = NRC(請求超出范圍)
七、常見NRC處理
NRC
名稱
觸發場景
SID
數據字段
診斷儀側含義
0x13
incorrectMessageLength
報文長度或格式錯誤
7F
22
請求報文長度無效
0x14
responseTooLong
響應數據超過傳輸層長度限制
7F
22
響應數據超過CanTp最大分段限制
0x22
conditionsNotCorrect
讀取數據的條件不滿足(如車速條件)
7F
22
當前條件不允許讀取該DID
0x31
requestOutOfRange
DID不存在或當前會話不支持
7F
22
請求的DID無效
0x33
securityAccessDenied
安全訪問未解鎖
7F
22
需要先通過0x27服務解鎖安全等級
八、總結
本文詳細解析了AUTOSAR架構下UDS 0x22服務的DID數據讀取機制,重點闡述了:
數據來源分類 :實時動態數據和存儲數據兩大類,以及它們各自的獲取方式和配置要點
實時數據讀取路徑 :通過RTE調用應用層SW-C的Runnable,并提供完整代碼示例
存儲數據讀取路徑 :通過NvM模塊的異步讀取機制,涵蓋NvM Block配置、同步方式選擇、回調處理與周期調度
在實際工程開發中,開發者需要借助AUTOSAR工具鏈(如ETAS ISOLAR/Virtual PC、Vector DaVinci、EB tresos)完成DCM、NvM、RTE等相關模塊的配置,生成代碼框架后,再根據本文提供的示例填充具體的數據讀取邏輯。合理區分DID的數據來源類型并選擇正確的數據獲取路徑,是確保診斷服務正確、高效運行的關鍵。
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
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.