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();
}
}
运行与验证
- 将上述代码加入工程,确保链接脚本中
test_addr所在扇区未被代码段占用(本例使用Sector 3,需确认代码不超过0x0800C000)。 - 在
main()中调用flash_demo(),编译并烧录。 - 在
flash_verify返回后设置断点,观察返回值:若为1,说明擦除与编程操作成功;若为0,检查Flash访问权限或地址是否越界。 - 也可在烧录后通过调试器读取
0x0800C000地址的值,确认其为0xDEADBEEF。
关键说明
- STM32F4 Flash编程最小单位为16位(半字),但本驱动使用32位写入,硬件会自动拆分,无需额外处理。
- 擦除前必须确保目标扇区无正在执行的代码(即代码不能运行在被擦除的Flash区域)。
- 若需擦除Sector 4及以上(128KB大扇区),需将
FLASH_SECTOR_SIZE调整为0x20000,并相应修改测试地址。 - 实际Bootloader中,擦除和编程地址由上位机下发的固件信息决定,本驱动提供的是最底层操作原语。