UDS bootloader程序下位机和上位机 31服务擦除,34服务请求下载,36服务下载,37服务退出下载 可支持NXP,英飞凌等各种芯片。

搞过汽车电子的兄弟应该都知道,UDS刷写流程简直就是ECU开发的必修课。今天咱们就唠唠基于UDS协议的Bootloader开发,重点拆解那几个核心服务。老规矩,咱们边撸代码边分析,保证都是实战干货。

先看31服务擦除Flash这个硬核操作。这可不是随便发个指令就能搞定的,不同厂家的芯片擦除粒度差别贼大。比如NXP的S32K系列最小擦除单位是4K,而英飞凌的TC3xx可能得按16K来操作。这时候就得这么玩:

// 31服务擦除处理函数
void Service31_EraseMemory(uint32_t startAddr, uint32_t size) {
    // 计算需要擦除的块数
    uint16_t blockNum = (size + FLASH_BLOCK_SIZE - 1) / FLASH_BLOCK_SIZE;
    
    // 发送擦除进度给上位机
    SendProgress(0x31, blockNum);
    
    for(int i=0; i<blockNum; i++){
        Flash_Erase(startAddr + i*FLASH_BLOCK_SIZE);
        SendProgress(0x31, blockNum - i -1); // 剩余块数
    }
}

这里有个坑要注意,某些芯片擦除时需要关闭中断,玩过STM32的应该都懂。另外进度反馈建议用剩余块数而不是完成数,防止上位机超时判断出错。

接下来是34服务请求下载,这个服务的关键在于协商传输参数。上位机会发过来文件大小和地址,下位机得根据自身情况决定是否允许下载。看这段参数校验代码:

// 34服务处理逻辑
uint8_t HandleService34(uint32_t address, uint32_t dataSize) {
    if(address < APP_START_ADDR || address >= APP_END_ADDR){
        return ADDRESS_OUT_OF_RANGE; // 地址越界
    }
    
    if(dataSize > MAX_FLASH_SIZE - (address - APP_START_ADDR)){
        return INSUFFICIENT_SPACE; // 空间不足
    }
    
    currentAddress = address;
    remainingBytes = dataSize;
    return SUCCESS;
}

这里有个小技巧,MAXFLASHSIZE最好动态计算,因为不同型号的ECU可能焊了不同容量的Flash芯片。比如我们项目里用英飞凌的SAK-TC234时,就遇到过16M和32M两种版本混用的情况。

重头戏来了——36服务数据传输。这个服务要实现流式传输和校验,建议采用滑动窗口机制。看这段带CRC校验的伪代码:

// 36服务数据接收
void Service36_DataTransfer(uint8_t* data, uint16_t len) {
    static uint8_t buffer[1024];
    static uint16_t bufferIndex = 0;
    
    memcpy(buffer + bufferIndex, data, len);
    bufferIndex += len;
    
    if(bufferIndex >= FLASH_PAGE_SIZE) {
        uint32_t crc = CalculateCRC32(buffer, FLASH_PAGE_SIZE);
        if(crc != expectedCRC) {
            SendNegativeResponse(0x36, CRC_ERROR);
            return;
        }
        
        Flash_Program(currentAddress, buffer);
        currentAddress += FLASH_PAGE_SIZE;
        bufferIndex = 0;
        remainingBytes -= FLASH_PAGE_SIZE;
    }
}

这里有个细节,英飞凌的TC2xx系列必须按256字节对齐编程,而NXP的S32K可以单字节写入。所以FLASHPAGESIZE要根据具体芯片调整,甚至要做动态适配。

最后是37服务退出下载,这里最容易翻车。很多新手以为直接跳转地址就完事了,其实还要处理缓存和状态标志:

__ramfunc void Service37_ExitTransfer() {
    // 刷新数据缓存
    FLASH_ClearDataCache();
    
    // 校验完整性
    if(VerifyChecksum() != PASS) {
        SendNegativeResponse(0x37, VERIFY_FAILED);
        return;
    }
    
    // 更新标志位
    WriteBootFlag(BOOT_SUCCESS);
    
    // 跳转到APP
    JumpToApp();
}

特别注意ramfunc这个修饰符,在NXP芯片上必须把跳转代码放在RAM中执行,因为Flash正在被擦写。还有JumpToApp前一定要关闭所有外设中断,否则分分钟死机给你看。

实际项目中还要处理各种异常情况,比如突然断电恢复后的断点续传。我们之前给某德系车厂做项目时,就实现了基于31服务的智能恢复机制——通过读取Flash特定地址的魔数,判断上次刷写中断的位置,然后自动续传。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐