1. 项目背景与核心需求
在嵌入式系统开发中,存储管理一直是个值得深入探讨的话题。最近我在使用GD32E10x系列MCU开发一个工业数据采集设备时,遇到了一个典型的存储需求:需要将程序固件和采集到的数据分开存储。这种需求在实际项目中非常普遍,比如在需要远程升级的设备中,我们需要确保数据存储区不受固件更新的影响;或者在数据记录设备中,需要保证即使程序崩溃,历史数据也不会丢失。
GD32E10x系列微控制器提供了两块独立的FLASH存储区,这为我们的需求提供了硬件基础。主FLASH通常用于存储程序代码,而第二块FLASH则可以用来存储配置参数、运行日志或采集到的数据。这种分离存储的方案相比使用外部EEPROM或FRAM,既节省了成本又简化了电路设计。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. GD32E10x存储架构解析
2.1 FLASH存储区划分
GD32E10x的FLASH存储器分为两个物理上独立的区域:
- 主存储区(Main Flash Memory):通常为0x0800 0000起始的64KB或128KB空间
- 信息存储区(Information Block):包含系统存储区(System Memory)和选项字节(Option Bytes)
以GD32E103C8T6为例,其存储分布如下:
code复制0x0800 0000 - 0x0801 FFFF : 128KB 主FLASH
0x1FFF 0000 - 0x1FFF 77FF : 30KB 系统存储区(存放Bootloader)
0x1FFF F800 - 0x1FFF F80F : 选项字节区
2.2 FLASH特性与限制
在使用GD32E10x的FLASH存储数据前,必须了解几个关键特性:
- 擦除操作以页为单位,GD32E10x的页大小为1KB或2KB
- 写入前必须先擦除,且只能将1变为0,不能将0变为1
- 写入操作必须以16位半字(2字节)为单位进行
- 频繁擦写会影响FLASH寿命(典型值约10,000次)
重要提示:直接操作FLASH时,必须确保不会意外擦除或修改正在执行的代码区域,否则会导致程序崩溃。
3. 双FLASH存储方案实现
3.1 存储区规划策略
合理的存储区划分是方案成功的关键。我的实践经验是:
- 主FLASH保留最后若干页作为数据存储区
- 例如:128KB FLASH中,前120KB用于程序,最后8KB(4页)用于数据
- 为不同数据类型分配独立区域
- 配置参数:单独占用1页,更新频率低
- 运行日志:循环写入,需要磨损均衡处理
- 采集数据:按时间顺序存储,可能需要压缩
典型的存储布局示例:
code复制0x0800 0000 - 0x0801 DFFF : 程序区 (120KB)
0x0801 E000 - 0x0801 FFFF : 数据区 (8KB)
- 0x0801 E000 - 0x0801 E7FF : 配置参数 (1页)
- 0x0801 E800 - 0x0801 F7FF : 运行日志 (7页)
- 0x0801 F800 - 0x0801 FFFF : 保留 (1页)
3.2 FLASH操作底层驱动
GD32标准外设库提供了FLASH操作的API,但直接使用可能不够灵活。我通常会封装更适合数据存储的接口:
c复制// FLASH初始化
void flash_init(void) {
fmc_unlock();
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGERR);
}
// 擦除指定页
int flash_erase_page(uint32_t page_addr) {
if(page_addr < FLASH_DATA_BASE) return -1; // 防止误擦程序区
fmc_page_erase(page_addr);
while(fmc_flag_get(FMC_FLAG_BUSY));
return 0;
}
// 写入数据(自动处理对齐)
int flash_write(uin
