3. Bootloader中实现Flash驱动并读取固件

Bootloader中实现Flash驱动并读取固件 学习卡片

任务目标

在Bootloader中实现一个最小可用的Flash驱动,支持从指定起始地址连续读取固件数据到内存缓冲区,为后续固件校验与加载提供基础读取能力。本案例以STM32F4系列内部Flash为硬件目标,驱动代码聚焦于读操作,不涉及擦除与编程。

硬件前提与依赖

  • 目标芯片:STM32F4系列(以STM32F407为例,内部Flash基地址0x08000000)
  • 需要芯片头文件:stm32f4xx.h(提供Flash寄存器定义与地址映射)
  • 无需额外第三方库,直接操作寄存器实现

核心实现

Flash读取驱动包含三个层次:地址合法性检查、单字读取、批量读取到缓冲区。由于STM32F4内部Flash支持32位总线访问,直接通过指针解引用即可完成读取,无需解锁或等待忙状态(读操作不占用Flash控制器编程接口)。

#include "stm32f4xx.h"

/* Flash起始与结束地址(STM32F407为1MB Flash) */
#define FLASH_BASE_ADDR      0x08000000u
#define FLASH_END_ADDR       0x080FFFFFu
#define FLASH_ERASED_VAL     0xFFFFFFFFu

/**
 * @brief  从指定Flash地址读取一个32位字
 * @param  addr: 目标地址,必须4字节对齐
 * @param  data: 输出指针,存放读取到的数据
 * @retval 0: 成功; -1: 参数错误
 */
static int32_t Flash_ReadWord(uint32_t addr, uint32_t *data)
{
    /* 地址必须4字节对齐且落在Flash有效范围内 */
    if ((addr & 0x3u) || (addr < FLASH_BASE_ADDR) || (addr > FLASH_END_ADDR - 3u)) {
        return -1;
    }
    if (data == 0) {
        return -1;
    }

    *data = *(volatile uint32_t *)addr;
    return 0;
}

/**
 * @brief  从Flash连续读取固件数据到内存缓冲区
 * @param  flash_addr: Flash起始地址(4字节对齐)
 * @param  buffer:     目标缓冲区指针
 * @param  size:       要读取的字节数(必须为4的倍数)
 * @retval 0: 成功; -1: 参数错误
 */
int32_t Flash_ReadFirmware(uint32_t flash_addr, uint8_t *buffer, uint32_t size)
{
    uint32_t i;
    uint32_t word_buf;

    /* 参数合法性检查 */
    if ((flash_addr & 0x3u) || (buffer == 0) || (size == 0u) || (size & 0x3u)) {
        return -1;
    }
    if ((flash_addr < FLASH_BASE_ADDR) || (flash_addr + size > FLASH_END_ADDR + 1u)) {
        return -1;
    }

    /* 按32位字逐个读取并拷贝到缓冲区 */
    for (i = 0; i < size; i += 4u) {
        if (Flash_ReadWord(flash_addr + i, &word_buf) != 0) {
            return -1;
        }
        /* 小端模式:直接按字节拷贝 */
        buffer[i]     = (uint8_t)(word_buf & 0xFFu);
        buffer[i + 1] = (uint8_t)((word_buf >> 8) & 0xFFu);
        buffer[i + 2] = (uint8_t)((word_buf >> 16) & 0xFFu);
        buffer[i + 3] = (uint8_t)((word_buf >> 24) & 0xFFu);
    }

    return 0;
}

调用示例与验证方法

在Bootloader主函数中,从Flash地址0x08010000(固件存放区起始)读取前64字节到RAM缓冲区,并检查读取结果。验证通过条件:Flash_ReadFirmware返回0,且缓冲区内容与Flash中实际烧录的数据一致。

/* 在main函数中调用 */
uint8_t fw_buffer[64];
int32_t ret;

ret = Flash_ReadFirmware(0x08010000u, fw_buffer, sizeof(fw_buffer));

/* 验证判定:
 * 1. ret == 0 表示读取成功;
 * 2. 若Flash 0x08010000处烧录了已知测试数据(如0xDEADBEEF),
 *    则 fw_buffer[0]~[3] 应为 EF BE AD DE(小端序)。
 * 3. 可通过调试器内存窗口对比Flash源地址与buffer内容是否一致。
 */
if (ret == 0) {
    /* 读取成功,可继续后续固件校验流程 */
}

常见问题

  • 地址未对齐:Flash地址和读取字节数必须为4的倍数,否则驱动返回-1。
  • 越界访问:读取范围超出Flash末尾(0x080FFFFF)会触发硬件错误,驱动已做边界检查。
  • 读取全FF:若目标地址未烧录过数据,Flash内容为0xFFFFFFFF,这是正常现象,不代表驱动故障。