3. 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,这是正常现象,不代表驱动故障。