2. Flash驱动移植与验证
任务目标
在目标MCU(以STM32F407为例)上完成Flash驱动的移植,实现扇区擦除、双字编程与读取验证三个核心操作,并通过写入已知数据后回读比对的方式验证驱动正确性。本案例聚焦驱动层实现,不涉及Bootloader的上层跳转逻辑。
环境依赖
- 硬件:STM32F407开发板(板载1MB Flash,扇区大小16KB/128KB不等)
- 软件:STM32CubeIDE 或 Keil MDK-ARM(本文以寄存器操作实现,不依赖HAL库)
- 调试方式:串口打印或JTAG/SWD调试器观察内存
关键完整代码
#include "stm32f4xx.h"
/* Flash寄存器基地址 */
#define FLASH_KEY1 0x45670123U
#define FLASH_KEY2 0xCDEF89ABU
#define FLASH_OPTKEY1 0x08192A3BU
#define FLASH_OPTKEY2 0x4C5D6E7FU
/* 等待Flash操作完成 */
static void Flash_WaitBusy(void) {
while ((FLASH->SR & FLASH_SR_BSY) != 0) {
/* 等待BSY位清零 */
}
}
/* 解锁Flash控制寄存器 */
static void Flash_Unlock(void) {
if ((FLASH->CR & FLASH_CR_LOCK) != 0) {
FLASH->KEYR = FLASH_KEY1;
FLASH->KEYR = FLASH_KEY2;
}
}
/* 锁定Flash控制寄存器 */
static void Flash_Lock(void) {
FLASH->CR |= FLASH_CR_LOCK;
}
/* 擦除指定扇区(扇区编号0~11) */
void Flash_EraseSector(uint8_t sector_num) {
Flash_Unlock();
Flash_WaitBusy();
/* 设置扇区号与擦除使能 */
FLASH->CR &= ~FLASH_CR_SNB_Msk;
FLASH->CR |= ((uint32_t)sector_num << FLASH_CR_SNB_Pos) | FLASH_CR_SER;
FLASH->CR |= FLASH_CR_STRT;
Flash_WaitBusy();
/* 清除擦除标志位 */
FLASH->CR &= ~(FLASH_CR_SER | FLASH_CR_SNB_Msk);
Flash_Lock();
}
/* 编程64位双字数据到指定地址(地址必须8字节对齐) */
void Flash_ProgramDoubleWord(uint32_t addr, uint64_t data) {
Flash_Unlock();
Flash_WaitBusy();
/* 设置编程模式 */
FLASH->CR |= FLASH_CR_PG;
/* 写入低32位,再写高32位(触发编程) */
*(volatile uint32_t*)addr = (uint32_t)(data & 0xFFFFFFFF);
*(volatile uint32_t*)(addr + 4) = (uint32_t)(data >> 32);
Flash_WaitBusy();
/* 关闭编程模式 */
FLASH->CR &= ~FLASH_CR_PG;
Flash_Lock();
}
/* 从指定地址读取64位数据 */
uint64_t Flash_ReadDoubleWord(uint32_t addr) {
uint64_t val;
val = *(volatile uint32_t*)addr;
val |= ((uint64_t)(*(volatile uint32_t*)(addr + 4)) << 32);
return val;
}
/* 验证函数:擦除-写入-回读比对 */
int Flash_Verify(void) {
uint32_t test_addr = 0x08020000; /* 扇区4起始地址(16KB扇区) */
uint64_t write_data = 0x0123456789ABCDEFULL;
uint64_t read_data;
/* 1. 擦除扇区 */
Flash_EraseSector(4);
/* 2. 验证擦除后内容为0xFF */
read_data = Flash_ReadDoubleWord(test_addr);
if (read_data != 0xFFFFFFFFFFFFFFFFULL) {
return -1; /* 擦除失败 */
}
/* 3. 写入测试数据 */
Flash_ProgramDoubleWord(test_addr, write_data);
/* 4. 回读比对 */
read_data = Flash_ReadDoubleWord(test_addr);
if (read_data != write_data) {
return -2; /* 编程或读取失败 */
}
return 0; /* 全部通过 */
}
/* 主函数调用示例 */
int main(void) {
int result;
/* 初始化系统时钟(略,使用默认配置) */
result = Flash_Verify();
if (result == 0) {
/* 验证通过:此处可设置断点或串口输出 */
while (1);
} else {
/* 验证失败:错误码为result */
while (1);
}
}
运行与验证方法
- 将上述代码加入STM32F407工程,编译并下载到开发板。
- 在
Flash_Verify()返回后设置断点,或通过调试器观察result变量值。 - 判定标准:
result == 0:擦除后全0xFF、写入数据回读一致,驱动功能正确。result == -1:擦除操作未生效,检查扇区号映射或电源电压。result == -2:编程或读取路径有误,检查地址对齐与编程时序。
常见问题
- 编程后数据全为0xFF:确认目标地址在擦除后未被写保护,且编程前已执行扇区擦除。
- HardFault异常:检查编程地址是否越界(F407用户Flash区为0x08000000~0x080FFFFF),以及是否误操作了OTP或选项字节区域。
- 扇区编号混淆:F407的扇区0~3为16KB,扇区4为64KB,扇区5~11为128KB。擦除前务必确认所选扇区与地址范围匹配。