IAP(三)固件烧写到Flash的实现

前言

  实验硬件:STM32F407ZGT6,sd卡,USB线
  本文目标:在STM32中实现IAP本地升级,通过USB MSC连接电脑放置bin文件包
  软件:Keil、CubeMX
  实现功能:前两篇文章中,我们已经实现了STM32 Bootloader的地址跳转功能,并介绍了如何通过SD卡管理升级固件文件及升级标志位的设置。本文将进入Bootloader升级流程的核心环节——将SD卡中的bin固件文件安全、可靠地烧写到STM32的内部Flash指定区域。
  传送门:
    IAP(一)bootloader地址跳转实现
    IAP(二)SD卡固件升级流程与文件管理
  OTA升级也可借鉴本文

一、获取固件大小与扇区擦除

  在进行固件升级前,首先需要获取待升级固件的实际大小,并据此计算出需要擦除的Flash扇区范围。这样可以确保只擦除App区域,避免误擦Bootloader或其他重要数据。我们使用f_stat函数获取文件信息结构体中的fsize字段。(先要挂载sd卡)

uint32_t SD_Get_File_Size(const char* filename)
{
    FILINFO fileInfo;
    FRESULT res;
  
    res = f_stat(filename, &fileInfo);
    if (res != FR_OK) {
        return 1;
    }
    return fileInfo.fsize;
}

  STM32的Flash通常以扇区(Sector)为单位擦除,不同芯片的扇区大小和数量不同。我们需要根据App区的起始地址、bin文件大小,计算出涉及的所有扇区编号。
  本文使用的是STM32F407ZGT6芯片,其他型号芯片需根据其各自分区做相应调整。

static uint32_t GetFlashSector(uint32_t addr)
{
    if (addr < 0x08000000) {
        return 0xFFFFFFFF; 
    }
    if (addr < 0x08010000) {
        return (addr - 0x08000000) / 0x4000;
    }
    else if (addr < 0x08020000) {
        return 4;
    }
    else {
        return 5 + (addr - 0x08020000) / 0x20000;
    }
}
uint32_t CalEraseSectors(uint32_t startAddr, uint32_t size, 
                               uint32_t *startSector, uint32_t *endSector)
{
    uint32_t endAddr = startAddr + size;
    if (startAddr < 0x08000000 || endAddr > 0x08100000) {
        return 1;
    }
    *startSector = GetFlashSector(startAddr);
    if (*startSector == 0xFFFFFFFF) {
        return 1;
    }
    *endSector = GetFlashSector(endAddr - 1);
    if (*endSector == 0xFFFFFFFF) {
        return 1;
    }
    return 0;
}

  进行对应扇区擦除

HAL_StatusTypeDef Erase_Sectors(uint32_t start_sector, uint32_t end_sector)
{
    HAL_StatusTypeDef status = HAL_OK;
    FLASH_EraseInitTypeDef eraseInit;
    uint32_t sectorError = 0;
    HAL_FLASH_Unlock();

    __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | 
                          FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);

    for (uint32_t i = 0; i < total; i++) {
        eraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;
        eraseInit.Sector = start_sector + i;
        eraseInit.NbSectors = 1;
        eraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3;

        status = HAL_FLASHEx_Erase(&eraseInit, &sectorError);
        if (status != HAL_OK) {
            break;
        }
    }
    HAL_FLASH_Lock();
    return status;
}

  Erase_Sectors 用于批量擦除STM32 Flash的指定范围内的扇区。其主要流程如下:

  1. 解锁Flash:HAL_FLASH_Unlock() 允许对Flash进行写/擦除操作
  2. 清除Flash相关标志位:__HAL_FLASH_CLEAR_FLAG(…) 清除之前可能遗留的Flash操作标志,防止影响本次擦除。
  3. 循环擦除每个扇区
  4. 锁定Flash:HAL_FLASH_Lock() 防止后续误操作
  5. 返回擦除状态

二、固件写入

  在完成Flash扇区的擦除后,接下来需要将SD卡中的bin固件文件分块读取,并逐步写入到STM32的App区Flash。
  由于STM32的Flash写入通常以4字节(32位)为单位进行,因此我们需要将bin文件按块读取,并按字对齐方式写入目标地址。
  整个写入过程需要注意以下几点:

  1. 分块读取:为节省RAM资源,通常采用固定大小的缓冲区(如1024字节)分批读取文件内容
  2. 对齐写入:Flash写入必须以4字节对齐,若遇到不足4字节的数据需特殊处理
  3. 异常处理:写入过程中要检测文件读取和Flash写入的返回值,确保每一步都能正确完成

  下面给出固件写入的核心实现代码

uint32_t OTA_WriteFirmware(const char* firmwarename, uint32_t flash_start_addr, uint32_t file_size)
{
    FIL file;          
    FRESULT res;     
    UINT br;      
	uint8_t read_buf[BIN_BUF_SIZE];	
    uint32_t flash_addr = flash_start_addr;
    int ret_status = 0;
    HAL_StatusTypeDef flash_status = HAL_OK; 
	//打开固件文件
    res = f_open(&file, firmwarename, FA_READ);
    if (res != FR_OK) {
        return 1; // 固件打开失败
    }
    
    //解锁Flash
    HAL_FLASH_Unlock();

    //循环读取文件并写入Flash
    while(1) {
       //读取文件数据到缓冲区
        res = f_read(&file, read_buf, BIN_BUF_SIZE, &br);
        if (res != FR_OK) {
            ret_status = -2; 
            break; // 退出循环
        }

        //读到0字节,表示文件读取完毕
        if (br == 0) break;

        //将读取到的数据按4字节写入Flash
        for (uint32_t i = 0; i < br; i += 4) {
            if (i + 4 > br) { 
                 break;
            }
            uint32_t data = *(uint32_t*)(read_buf + i); 
            flash_status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, flash_addr, data);
            if (flash_status != HAL_OK) {
                ret_status = -3; // 写入失败
                break; 
            }
            flash_addr += 4; 
        }
        if (ret_status != 0) break; 
    }

    //锁定Flash
    HAL_FLASH_Lock();

    //关闭文件
    f_close(&file);
    return ret_status;
}

  完成如上工作,STM32的IAP核心功能已经基本完成,后续章节将对IAP进行进一步完善,包括USB MSC在bootloader中访问sd卡,版本备份异常回退等功能提升系统的健壮性与用户体验

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值