Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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_GetNextFilteredDTCDem_GetStatusOfDTCDem_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]
    ...
}

本篇小结

  1. 0x19通过DEM API获取筛选后的DTC——DSP不存储DTC,只做协议转换。
  2. 上传下载的TransferStatus全局状态机管理从Request到Transfer到Exit的完整生命周期。
  3. blockSequenceCounter从1开始,0xFF→0x01回转——重复块被识别为ACK丢失而静默重传。
  4. DCM与DEM的接口是单向依赖——DSP调用DEM API,DEM不反向调用DSP。

【下集预告】:DSP是应用层——但数据是从底下CAN帧搬上来的。CanTp.c怎么实现ISO 15765-2的单帧和巨帧逻辑?SF/FF/CF/FC在1747行C代码里长什么样?我们拆CanTp。