1-11 STM32内部FLASH(数据掉电保存)

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文件确认程序占用范围 |

相关推荐
微功夫信息技术6 分钟前
分层多智能体强化学习驱动的非急救转运公平 - 效率统一调度系统研究与实践
人工智能·学习·算法·动态规划
dianziyao_23 分钟前
第 4.1 篇:让 Codex 理解你的双链——AI 辅助发现笔记间的关联
人工智能·笔记
LongRunning24 分钟前
【BLE】STM32WB55_低功耗(十一)
stm32
芯片人00739 分钟前
纯硬件一键开关机ASIC芯片,为什么比 MCU 方案更适合更可靠
人工智能·单片机·嵌入式硬件·物联网·芯片
dadaobusi1 小时前
学习:开源项目Cheshire
学习
撩得Android一次心动2 小时前
Jetpack Compose 知识点整理1【个人用】
android·学习·kotlin·android jetpack·compose
染予2 小时前
Ka 阵面星历下发与执行逻辑说明
stm32·嵌入式硬件
芯片人0073 小时前
武汉广昇科技的GMUX1308A模拟开关芯片成功导入中国台湾上市企业联昌电子,助力汽车电子供应链自主可控
科技·嵌入式硬件·物联网·汽车·芯片
xian_wwq3 小时前
【学习笔记】OWASP Agentic 应用安全(二)
笔记·学习·安全·owasp
小弥儿4 小时前
GitHub今日热榜 | 2026-09-04:Agent省 token 成今日主线
学习·开源·github