【GD32实战】FMC Flash单字节读写与页擦除操作详解

张开发
2026/4/20 13:53:45 15 分钟阅读

分享文章

【GD32实战】FMC Flash单字节读写与页擦除操作详解
1. GD32 FMC模块基础认知第一次接触GD32的FMC模块时我也被各种专业术语搞得一头雾水。简单来说FMCFlash Memory Controller就是芯片内部管理Flash存储器的管家。想象你有个智能保险箱FMC就是这个保险箱的电子锁系统——它决定了你如何存钱、取钱以及什么时候需要清空整个抽屉。GD32的FMC有个很实用的特性支持单字节操作。这就像保险箱允许你每次只存取一枚硬币而不是必须整捆钞票一起处理。实际项目中我经常用它来存储设备参数、运行日志等小数据。比如最近做的智能电表项目就用FMC保存了用户设置的费率参数即使断电也不会丢失。与EEPROM相比内置Flash的优势很明显零成本芯片自带、容量大通常几十KB到几百KB。但要注意Flash的擦写寿命一般在1万到10万次之间频繁写入时需要做磨损均衡。我曾经有个项目因为没注意这点导致设备运行三个月后数据存储异常。2. 单字节写入实战详解先来看最核心的单字节写入函数。下面这个是我在多个项目中优化过的版本比官方例程多了实时校验机制uint8_t FMC_FLASH_Write(uint32_t Address, uint8_t *pData, uint16_t Size) { fmc_state_enum FLASHStatus; uint16_t i; uint32_t AddressTemp Address; // 必须步骤解锁Flash fmc_unlock(); // 清除所有错误标志位容易遗漏的关键步骤 fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_OPERR | FMC_FLAG_WPERR | FMC_FLAG_PGMERR | FMC_FLAG_PGSERR); for(i0; iSize; i) { // 实际写入操作 FLASHStatus fmc_byte_program(AddressTemp, pData[i]); // 立即校验防止写入异常 if(*(volatile uint8_t*)AddressTemp ! pData[i]) { fmc_lock(); // 异常时记得重新上锁 return 1; } if(FLASHStatus ! FMC_READY) { fmc_lock(); return 2; } AddressTemp; } fmc_lock(); return 0; }重点说明几个容易踩坑的地方地址对齐GD32的Flash虽然支持单字节写但起始地址最好4字节对齐。我有次用0x08001001这样的奇数地址虽然能写但效率会降低。写前检查目标地址必须处于擦除状态值为0xFF否则写入会失败。建议先读取判断必要时先执行擦除。中断处理写操作期间要禁用中断我在早期版本没注意这点导致偶尔出现数据错位。实测发现连续写入单字节时每字节耗时约20us主频108MHz。如果需要高速写入可以考虑页编程模式但复杂度会提高。3. 灵活读取与安全擦除读取操作相对简单但有些细节优化能让代码更健壮。这是我常用的增强版读取函数uint8_t FMC_FLASH_Read(uint32_t Address, uint8_t *pData, uint16_t Size) { // 地址有效性检查防止越界访问 if(Address FLASH_BASE || Address FLASH_END){ return 1; } uint32_t rdAddr Address; for(uint16_t i0; iSize; i) { // 添加volatile防止编译器优化 pData[i] *(volatile uint8_t*)rdAddr; rdAddr; // 可选超时机制 if(i 65535) return 2; } return 0; }页擦除是风险较高的操作一旦误擦可能导致程序崩溃。这是我总结的安全擦除流程地址对齐校验擦除地址必须是页大小的整数倍#define PAGE_SIZE 4096U uint32_t erase_addr PageAddress (~(PAGE_SIZE-1));双重确认机制实际项目中我会要求连续两次确认才执行擦除if(user_confirmed 2) { fmc_page_erase(erase_addr); }关键数据备份擦除前先把重要数据读到RAM缓存有个血泪教训有次调试时忘记注释掉擦除代码结果把固件程序区给擦除了只能重新烧录。现在我会在代码里加入保护区判断if(erase_addr APPLICATION_START_ADDR) { return ERR_DANGEROUS_AREA; }4. 完整实验与调试技巧以GD32F303为例我们使用扇区4地址0x08010000开始做实验。完整测试流程如下地址定义建议用宏管理#define TEST_PAGE1 0x08010000 #define TEST_PAGE2 (TEST_PAGE1 PAGE_SIZE)测试模式设计uint8_t test_pattern[] {0xAA, 0x55, 0x01, 0xFE}; FMC_FLASH_Write(TEST_PAGE1, test_pattern, sizeof(test_pattern));Keil调试技巧在Memory窗口直接输入0x08010000查看Flash内容使用Live Watch功能监控变量实时变化设置数据断点右键变量选择Set Access Breakpoint常见问题排查指南写入失败检查Flash是否解锁目标地址是否已擦除数据异常确认没有其他线程在操作Flash擦除卡死检查供电是否稳定芯片温度是否过高有个实用小技巧在开发阶段可以在Flash初始化时自动填充测试图案这样调试时能直观看到哪些区域被修改过。我通常会在项目代码里保留这样的调试段#ifdef DEBUG FMC_FLASH_Write(DEBUG_AREA, (uint8_t*)DBG, 3); #endif最后强调一个安全规范产品发布前一定要移除所有调试用的Flash操作代码或者加上权限控制。有次我们设备出厂后客户通过串口发送特定指令意外触发了Flash擦除导致设备变砖。现在我们会做双重保护生产模式禁用调试接口关键Flash操作需要验证硬件跳线状态

更多文章