4.8 DSP——DTC与上传下载(0x19与0x34-37)
DTC读取——DSP与DEM之间的协议
0x19本身不存储DTC——它只是一个翻译官。DSP收到0x19 0x02 0x08(按confirmed状态掩码列出DTC),它必须向DEM请求DTC数据。
DspUdsReadDtcInformation —— byStatusMask流程
case 0x02: { // reportDTCByStatusMask
uint8 statusMask = pMsgContext->reqData[2];
// 1. 设置DEM筛选条件
Dem_SetDTCFilter(
DEM_DTC_STATUS_MASK, statusMask,
DEM_DTC_FORMAT_UDS,
DEM_DTC_ORIGIN_PRIMARY_MEMORY);
// 2. 迭代获取每个筛选出的DTC
uint8 resPos = 3; // Byte0=SID+0x40=0x59, Byte1=sub0x02, Byte2=mask
Dem_DTCType dtc;
uint8 dtcStatus;
while (Dem_GetNextFilteredDTC(&dtc, &dtcStatus) == E_OK) {
// 3字节DTC
pMsgContext->resData[resPos++] = dtc.DTCByte1; // High
pMsgContext->resData[resPos++] = dtc.DTCByte2; // Middle
pMsgContext->resData[resPos++] = dtc.DTCByte3; // Low
// 1字节状态
pMsgContext->resData[resPos++] = dtcStatus;
}
pMsgContext->resDataLen = resPos;
*dataNegRespCode = DCM_E_POSITIVERESPONSE;
break;
}
核心洞察:DSP和DEM的分工非常清楚——DSP负责协议层面的参数解析和响应构建;DEM负责DTC的存储、筛选、消抖、快照管理。DSP永远不直接操作DTC的内存映射——它通过
Dem_GetNextFilteredDTC、Dem_GetStatusOfDTC、Dem_GetExtendedDataRecordByDTC等API向DEM请求数据。
DTC扩展数据获取
case 0x06: { // reportDTCExtDataRecordByDTCNumber
uint8 dtc[3] = {reqData[2], reqData[3], reqData[4]};
uint8 recNum = reqData[5];
// 先获取DTCAuditRecord
Dem_GetStatusOfDTC(dtc, DEM_DTC_FORMAT_UDS, &dtcStatus);
// 0xFF = 所有记录, 0xFE = OBD所有记录
if (recNum == 0xFF) {
for (int i = 0; Dem_GetExtendedDataRecordByDTC(dtc, i, ...) == E_OK; i++) {
// 写入扩展数据
}
}
break;
}
上传下载——TransferStatus状态机
Arctic Core用全局变量TransferStatus管理整个0x34→0x36→0x37的传输状态:
typedef struct {
Dcm_TransferType transferType; // DOWNLOAD / UPLOAD / NO_TRANSFER
uint8 blockSequenceCounter; // 当前期望的块序号
uint32 nextAddress; // 下一块要写入/读取的地址
uint32 remainingSize; // 剩余未传输的总字节数
uint32 blockSize; // 单块最大长度
} Dcm_DspTransferStatusType;
0x34 RequestDownload
void DspUdsRequestDownload(...) {
uint8 dataFormatId = reqData[0];
uint8 addrLenFmt = reqData[1];
uint8 addrWidth = addrLenFmt & 0x0F; // 低4位=地址字节数
uint8 sizeWidth = (addrLenFmt >> 4) & 0x0F; // 高4位=长度字节数
uint32 memoryAddress = readBytes(&reqData[2], addrWidth);
uint32 memorySize = readBytes(&reqData[2+addrWidth], sizeWidth);
// 调用用户回调获取maxBlockSize
Dcm_ProcessRequestDownload(..., &blockSize, &respCode);
// 初始化传输状态
TransferStatus.transferType = DCM_DOWNLOAD;
TransferStatus.blockSequenceCounter = 1; // 从1开始
TransferStatus.nextAddress = memoryAddress;
TransferStatus.remainingSize = memorySize;
TransferStatus.blockSize = blockSize;
// 响应格式: [0x40, blockSize(2-4字节)]
...
}
0x36 TransferData
void DspUdsTransferData(...) {
uint8 blockCtr = reqData[1]; // 收到的块序号
// 序列检查
if (TransferStatus.transferType == DCM_NO_DATA_TRANSFER) {
*negCode = DCM_E_REQUEST_SEQUENCE_ERROR; return;
}
if (blockCtr != TransferStatus.blockSequenceCounter) {
if (blockCtr == ((TransferStatus.blockSequenceCounter - 1) & 0xFF)) {
// 相同的块被重复发了——可能是上一帧的ACK丢失
// 静默重发上一帧的响应
} else {
*negCode = DCM_E_WRONG_BLOCK_SEQUENCE_COUNTER; return;
}
}
// 下载方向: 写内存
if (TransferStatus.transferType == DCM_DOWNLOAD) {
uint16 dataLen = reqDataLen - 2;
Dcm_WriteMemory(0, TransferStatus.nextAddress,
dataLen, &reqData[2], ...);
TransferStatus.nextAddress += dataLen;
TransferStatus.remainingSize -= dataLen;
}
// 递增块序号
TransferStatus.blockSequenceCounter++;
if (TransferStatus.blockSequenceCounter == 0) {
TransferStatus.blockSequenceCounter = 1; // 0xFF → 0x01 回转
}
// 响应: [blockCtr, responseParamRecord]
...
}
本篇小结
- 0x19通过DEM API获取筛选后的DTC——DSP不存储DTC,只做协议转换。
- 上传下载的TransferStatus全局状态机管理从Request到Transfer到Exit的完整生命周期。
- blockSequenceCounter从1开始,0xFF→0x01回转——重复块被识别为ACK丢失而静默重传。
- DCM与DEM的接口是单向依赖——DSP调用DEM API,DEM不反向调用DSP。
【下集预告】:DSP是应用层——但数据是从底下CAN帧搬上来的。CanTp.c怎么实现ISO 15765-2的单帧和巨帧逻辑?SF/FF/CF/FC在1747行C代码里长什么样?我们拆CanTp。