4. Flash驱动

任务目标

本案例实现一个最小可用的Flash驱动,提供扇区擦除(sector erase)与页编程(page program)两个核心操作,并附带读取校验功能。该驱动面向STM32F4系列内置Flash,为后续Bootloader的固件下载与存储提供底层接口。

环境与依赖

  • 硬件:STM32F407(内置1MB Flash,扇区大小16KB/128KB不等)
  • 工具链:arm-none-eabi-gcc + ST-Link 烧录
  • 无外部库依赖,直接操作寄存器

关键完整代码

#include "stm32f4xx.h"

#define FLASH_KEY1         0x45670123U
#define FLASH_KEY2         0xCDEF89ABU
#define FLASH_SECTOR_SIZE  0x4000U   /* 16KB,适用于Sector 0~3 */

/* 解锁Flash控制寄存器 */
static void flash_unlock(void) {
    if (FLASH->CR & FLASH_CR_LOCK) {
        FLASH->KEYR = FLASH_KEY1;
        FLASH->KEYR = FLASH_KEY2;
    }
}

/* 等待Flash操作完成 */
static void flash_wait_busy(void) {
    while (FLASH->SR & FLASH_SR_BSY);
}

/* 擦除指定扇区(编号0~11) */
void flash_sector_erase(uint32_t sector_num) {
    flash_unlock();
    flash_wait_busy();
    
    FLASH->CR &= ~FLASH_CR_SNB;               /* 清除扇区号 */
    FLASH->CR |= (sector_num << 3) | FLASH_CR_SER;  /* 选择扇区并置擦除使能 */
    FLASH->CR |= FLASH_CR_STRT;              /* 启动擦除 */
    
    flash_wait_busy();
    FLASH->CR &= ~(FLASH_CR_SER | FLASH_CR_STRT);  /* 关闭擦除使能 */
    FLASH->CR |= FLASH_CR_LOCK;              /* 重新锁定 */
}

/* 向指定地址写入32位数据 */
void flash_program_word(uint32_t addr, uint32_t data) {
    flash_unlock();
    flash_wait_busy();
    
    FLASH->CR |= FLASH_CR_PG;                /* 编程使能 */
    *(__IO uint32_t*)addr = data;             /* 执行写入 */
    
    flash_wait_busy();
    FLASH->CR &= ~FLASH_CR_PG;               /* 关闭编程使能 */
    FLASH->CR |= FLASH_CR_LOCK;
}

/* 读取校验:比较目标地址与期望值 */
uint32_t flash_verify(uint32_t addr, uint32_t expected) {
    return (*(__IO uint32_t*)addr == expected) ? 1U : 0U;
}

/* 简单自测:擦除扇区3,写入并校验 */
void flash_demo(void) {
    uint32_t test_addr = 0x0800C000U;  /* Sector 3 起始地址 */
    
    flash_sector_erase(3);
    flash_program_word(test_addr, 0xDEADBEEFU);
    
    if (flash_verify(test_addr, 0xDEADBEEFU)) {
        /* 写入成功——实际调试中可在此设断点观察 */
        __NOP();
    }
}

运行与验证

  1. 将上述代码加入工程,确保链接脚本中 test_addr 所在扇区未被代码段占用(本例使用Sector 3,需确认代码不超过0x0800C000)。
  2. 在 main() 中调用 flash_demo(),编译并烧录。
  3. 在 flash_verify 返回后设置断点,观察返回值:若为1,说明擦除与编程操作成功;若为0,检查Flash访问权限或地址是否越界。
  4. 也可在烧录后通过调试器读取 0x0800C000 地址的值,确认其为 0xDEADBEEF。

关键说明

  • STM32F4 Flash编程最小单位为16位(半字),但本驱动使用32位写入,硬件会自动拆分,无需额外处理。
  • 擦除前必须确保目标扇区无正在执行的代码(即代码不能运行在被擦除的Flash区域)。
  • 若需擦除Sector 4及以上(128KB大扇区),需将 FLASH_SECTOR_SIZE 调整为0x20000,并相应修改测试地址。
  • 实际Bootloader中,擦除和编程地址由上位机下发的固件信息决定,本驱动提供的是最底层操作原语。