11.1 学习目标
了解STM32内部FLASH的硬件特性、存储结构及地址划分
掌握FLASH编程与擦除的基本流程(解锁→擦除→编程→锁定)
学习使用HAL库中FLASH相关API进行页擦除和半字/字编程
实现将系统配置参数(如PID参数、校准值、运行状态等)保存至内部FLASH,实现掉电不丢失
了解FLASH擦写寿命限制及磨损均衡的基本概念
11.2 项目原理与概述
11.2.1 为什么需要内部FLASH存储
在STM32嵌入式系统中,常常需要保存一些掉电后不能丢失的参数,例如:
系统配置参数(PID参数、电机运行阈值等)
传感器校准值(零点偏移、增益系数)
设备运行状态(累计运行时间、故障日志)
用户设置(温度设定值、工作模式)
传统方案是外接EEPROM芯片(如24C02),但这会增加成本和PCB面积。STM32内部FLASH除了存储用户程序外,剩余未使用的空间完全可以用来存储用户数据,实现类似EEPROM的掉电保存功能。
11.2.2 FLASH与EEPROM的区别
|---------|--------------------------------|--------------|
| 特性 | STM32F103C8T6 STM32内部FLASH | 传统EEPROM |
| 擦除单位 | 页(1KB) | 字节 |
| 写入单位 | 半字(16位) | 字节 |
| 擦写寿命 | 至少1万次 | 约100万次 |
| 写入前必须擦除 | 是 | 否(可直接覆盖) |
由于FLASH的擦写寿命远低于EEPROM,且必须整页擦除后才能写入,因此在设计数据存储方案时需要特别注意写入频率和磨损均衡。
11.3 STM32F103 内部FLASH知识点总结
11.3.1 FLASH存储结构
主存储器:存放代码和数据。
STM32F103C8T6(64KB)属于中容量产品,每页1KB,共64页(页0~63)。
起始地址 0x08000000,页n地址 = 0x08000000 + n×1024。末页(63)起始 0x0800FC00。
信息块:
系统存储器(BootROM):固化串口ISP下载程序,只读。
选项字节:配置读写保护、看门狗等,可编程修改。
接口寄存器(FLASH_CR/SR/AR):控制解锁、擦除、编程及状态查询。
11.3.2 FLASH基本操作特性
**读取:**直接指针访问,无需解锁。
**编程:**以半字(16位)或字(32位)为单位写入;地址须已擦除(内容为 0xFFFF),禁止重复写入同一地址。
**擦除:**整页擦除(每页1KB),擦除后数据全为 0xFFFF。
**解锁:**复位后默认锁定,依次向 FLASH_KEYR 写入 0x45670123 和 0xCDEF89AB 解锁;操作完成后立即重新锁定。
**寿命:**擦写至少1万次;高频写入建议采用磨损均衡或外接EEPROM。
11.3.3 选择存储页的注意事项
避免与程序代码冲突:通过查看编译生成的.map文件,确定程序占用了哪些页,选择未被占用的页作为数据存储区
优先选最后几页:建议将数据存储在FLASH末尾的几个页中,程序一般从0x08000000开始存放,占用前面的页
避开系统存储器区域:系统存储区(约0x1FFFF000)和选项字节区(0x1FFFF800)不可误操作
11.4 代码解析与实现
11.4.1 CubeMX 工程配置
由于FLASH操作不需要额外配置外设,CubeMX中无需特殊设置。只需按前期章节配置基础工程即可。需注意以下两点:
堆(Heap)大小:建议设置为0x400以上,FLASH擦除/写入函数会使用栈空间
优化等级:调试阶段建议设为-O0,避免变量被过度优化
生成代码后,在main.c中编写FLASH读写函数。
11.4.2 核心代码实现
1.地址与页定义

2.辅助函数:计算参数区校验和

3.验证校验和是否正确

4.向FLASH写入数据

5.从FLASH读取数据

6.主函数测试例程



11.5 实验现象与调试
11.5.1 预期现象
在仿真调试时,可在 while 循环入口处设置断点,并将 read_param 添加到 Watch 窗口。

写入的:

读出的:

程序运行到断点后,可以看到 read_param 已成功读取数据,但显示值与写入值存在微小差异:
写入:35.2f → 读出:35.2000008
写入:40.8f → 读出:40.7999992
写入:45.3f → 读出:45.2999992
写入:0.1f → 读出:0.100000001
这是浮点数精度问题,属于正常现象。原因在于:浮点数在内存中以二进制 IEEE 754 格式存储,许多十进制小数(如 0.1、35.2)无法精确表示,只能以近似值存储。调试器显示时会将二进制近似值转换回十进制,从而出现上述差异。实际上,写入 FLASH 的二进制数据与读回的二进制数据完全相同,校验和验证通过即可证明数据读写的一致性,属于正常情况。
11.5.2 常见问题与排查
|-----------|-------------|---------------------------------------|
| 现象 | 可能原因 | 解决方法 |
| 程序运行异常/死机 | 擦除了存储程序代码的页 | 检查使用的页地址,确保与程序存储区不冲突;查看.map文件确认程序占用范围 |