4.7 DSP——数据读取(0x22与0x2E的DID查找与信号读写)
DID不是一个简单的地址——它是一个递归结构
Arctic Core的DID配置不是“DID→dataRecord“的简单键值对。一个DID由DID引用链组成——每个DID可以递归包含其他DID。readDidData()是递归遍历这个图的函数。
DID配置结构
typedef struct {
uint16 DspDidIdentifier; // DID编号 (如 0xF190)
uint32 DspDidDataByteSize; // 总数据长度
boolean DspDidDynamicllyDefined; // 是否动态DID
Dcm_DspDidInfoType DspDidInfo; // 读写权限、会话/安全需求
Dcm_DspDidRefType DspDidRef; // 子信号引用表 (EOL终止)
} Dcm_DspDidType;
typedef struct {
uint8 DspDidRefType; // DATA_PORT_SYNCH / ASYNCH / ECU_SIGNAL / NVM_BLOCK
union {
struct { Dcm_SynchDataReadFncType SynchDataReadFnc; ... } synch;
struct { Dcm_AsynchDataReadFncType AsynchDataReadFnc; ... } asynch;
struct { Dcm_SRDataReadFncType SRDataReadFnc; ... } sr;
struct { uint16 NvMBlockId; ... } nvm;
} data;
uint16 DspDidRefBitPosition; // 在DID数据内的起始bit位置
uint16 DspDidRefBitLength; // 信号bit长度
uint8 DspDidRefEndianess; // BIG_ENDIAN / LITTLE_ENDIAN
} Dcm_DspDidRefType;
getDidData —— 四种端口类型
static Std_ReturnType getDidData(
Dcm_DspDidType *did, uint8 *txBuf, uint16 *txPos, ...)
{
// 1. 写入DID头 (2字节+数据)
if (includeDID) {
txBuf[(*txPos)++] = (did->DspDidIdentifier >> 8) & 0xFF;
txBuf[(*txPos)++] = did->DspDidIdentifier & 0xFF;
}
// 2. 遍历DID引用表
Dcm_DspDidRefType *ref = did->DspDidRef;
while (ref->DspDidRefType != Arc_EOL) {
switch (ref->DspDidRefType) {
case DATA_PORT_SYNCH: // 同步数据端口
// 立即回调读取数据——无延迟
ref->data.synch.SynchDataReadFnc(
&txBuf[*txPos], ref->DspDidRefBitLength);
break;
case DATA_PORT_ASYNCH: // 异步数据端口
opStatus = DCM_READ_OK;
ref->data.asynch.AsynchDataReadFnc(
&txBuf[*txPos], ref->DspDidRefBitLength, &opStatus);
if (opStatus == DCM_READ_PENDING) {
return E_PENDING; // 等下次MainFunction再取
}
break;
case DATA_PORT_ECU_SIGNAL: // SR端口 (Sender/Receiver)
ReadSRSignal(ref->DspDidRefBitPosition,
ref->DspDidRefBitLength,
txBuf);
break;
case DATA_PORT_BLOCK_ID: // NvM块
NvM_ReadBlock(ref->data.nvm.NvMBlockId, &txBuf[*txPos]);
break;
}
*txPos += ref->DspDidRefBitLength / 8;
ref++;
}
return E_OK;
}
核心洞察:四种端口类型映射了ECU中数据的四种来源——SYNCH=RTE同步端口(实时信号)、ASYNCH=RTE异步端口(可能需要等待)、ECU_SIGNAL=CDD传感器信号(直读)、NVM_BLOCK=非易失存储块。DSP不需要知道每个信号的物理地址——它只知道该调用什么回调。
ReadSRSignal —— 位级别的信号提取
static void ReadSRSignal(uint16 bitPosition, uint16 bitLength,
uint8 *dataBuffer)
{
// 读取原始字节
uint8 rawBuffer[DCM_MAX_SR_SIGNAL_BYTES];
DspDataReadDataFnc.SRDataReadFnc(rawBuffer, bitPosition, bitLength);
// 处理大小端转换
if (endianess == LITTLE_ENDIAN) {
// 从rawBuffer按小端序解包到dataBuffer
unpackLittleEndian(rawBuffer, dataBuffer, bitLength);
} else {
unpackBigEndian(rawBuffer, dataBuffer, bitLength);
}
}
DID递归引用——复合DID
一个DID可以引用另一个DID:
static Std_ReturnType readDidData(Dcm_DspDidType *did, ...) {
Dcm_DspDidRefType *ref = did->DspDidRef;
while (ref->DspDidRefType != Arc_EOL) {
if (ref->DspDidRefType == DATA_PORT_DID_REF) {
// 递归:引用了另一个DID
Dcm_DspDidType *subDid = lookupNonDynamicDid(ref->refDid);
readDidData(subDid, txBuf, txPos, ...); // 递归
} else {
getDidData(did, txBuf, txPos, ...); // 直接读信号
}
ref++;
}
}
0x2E写入的对称实现
写入和读取使用同一套DID配置——区别在回调方向:
// 写SR信号
static void WriteSRSignal(uint16 bitPos, uint16 bitLen, uint8 *data) {
uint8 rawBuffer[DCM_MAX_SR_SIGNAL_BYTES];
// 先把数据包成对应大小端格式
if (endianess == LITTLE_ENDIAN) {
packLittleEndian(data, rawBuffer, bitLen);
} else {
packBigEndian(data, rawBuffer, bitLen);
}
// 回调写入
DspDataWriteDataFnc.SRDataWriteFnc(rawBuffer, bitPos, bitLen);
}
本篇小结
- DID配置是一个递归引用图——每个DID可以包含任意数量的信号引用或子DID引用。
- 四种端口类型(SYNCH/ASYNCH/ECU_SIGNAL/NVM_BLOCK)覆盖了ECU中数据的全部来源——DSP不关心物理地址,只驱动回调。
- 位级别的信号提取通过ReadSRSignal和WriteSRSignal实现——支持大端和小端两种字节序。
- 异步端口(ASYNCH)通过E_PENDING+断点重入实现跨MainFunction周期的数据获取。
【下集预告】:DID读完了——现在我们去看DTC的读取接口和上传下载的blockSequenceCounter流水线。0x19如何调用DEM的API获取筛选后的DTC列表?0x34→0x36→0x37的TransferStatus状态机在DSP中怎么运作?