一、库函数
1. void FLASH_Unlock(void);
功能:解锁 FLASH
2. void FLASH_Lock(void);
功能:锁定 FLASH
3. FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
功能:按页擦除 FLASH
4. FLASH_Status FLASH_EraseAllPages(void);
功能:整片擦除(全芯片 FLASH 清空)
5. FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
功能:写 32 位数据(1 个字)到 FLASH
6. FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
功能:写 16 位数据(半个字)到 FLASH,STM32 最小写入单位就是 16 位
二、读取FLASH
从指定地址读取 32 位数据(1 个字)
uint32_t MyFLASH_ReadWord(uint32_t Address)
{
return *((__IO uint32_t *)(Address));
}
从指定地址读取 16 位数据(半字)
这是 STM32 FLASH 最常用的读取方式
uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{
return *((__IO uint16_t *)(Address));
}
从指定地址读取 8 位数据(单字节)
uint8_t MyFLASH_ReadByte(uint32_t Address)
{
return *((__IO uint8_t *)(Address));
}
为什么读 FLASH 不用 Unlock / Lock?
因为:
- 写 FLASH、擦 FLASH 是修改硬件,必须解锁
- 读 FLASH 只是查看数据,不需要解锁
三、整片擦除&页擦除
整片擦除
void MyFLASH_EraseAllPages(void)
{
FLASH_Unlock();
FLASH_EraseAllPages();
FLASH_Lock();
}
官方 FLASH 规则是:必须先解锁 → 才能擦 → 擦完必须上锁
1.先解锁(开门)
-
官方函数:全片擦除(清空整个FLASH)
-
上锁(关门)
注:把整个 STM32 的 FLASH 全部清空, 程序都会被删掉,正常存参数不用这个。
页擦除
void MyFLASH_ErasePage(uint32_t PageAddress)
{
FLASH_Unlock();
FLASH_ErasePage(PageAddress);
FLASH_Lock();
}
1.先解锁(开门)
-
官方函数:只擦这一页
-
上锁(关门)
四、写FLASH
写 32 位(一个字)
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
FLASH_Unlock();
FLASH_ProgramWord(Address, Data);
FLASH_Lock();
}
往 FLASH 的某个地址,写入一个 32 位整数
写 16 位(半字)
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
FLASH_Unlock();
FLASH_ProgramHalfWord(Address, Data);
FLASH_Lock();
}
往 FLASH 的某个地址,写入一个 16 位整数
五、初始化、备份、擦除
#define STORE_START_ADDRESS 0x0800FC00
#define STORE_COUNT 512
uint16_t Store_Data[STORE_COUNT];
初始化
执行流程
- 芯片上电
- 执行 Store_Init
- 读取 FLASH 首地址
- 无标志 → 擦除 → 写入标志 → 初始化默认值(第一次时写入0xA5A5,用来当标志位)
- 有标志 → 直接读取数据到 RAM
- 初始化完成,程序正常运行
总结:上电初始化,让存储系统 Ready!
void Store_Init(void)
{
if (MyFLASH_ReadHalfWord(STORE_START_ADDRESS) != 0xA5A5)
{
MyFLASH_ErasePage(STORE_START_ADDRESS);
MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5);
for (uint16_t i = 1; i < STORE_COUNT; i ++)
{
MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, 0x0000);
}
}
for (uint16_t i = 0; i < STORE_COUNT; i ++)
{
Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i * 2);
}
}
说明:一个地址存放 1 字节,半字(HalfWord)= 2 字节,所以地址每次要偏移 2,也就是 i*2。
设计思想
1.FLASH 不能频繁操作
- 读写寿命有限(约 1 万~10 万次)
- 写入速度慢
- 必须先擦除再写入.
2. RAM 高速但掉电丢失
- 运行时变量放在 RAM
- 需要保存时才写入 FLASH
3. Store_Init 做的事
- 上电自动恢复数据
- 自动判断是否格式化
- 全程无需用户干预
备份(借用RAM修改FLASH)
由于 FLASH 硬件特性限制:只能将位从 1 改写为 0,无法直接将 0 改写为 1,想要修改数据必须先执行整页擦除(擦除后所有位恢复为 1),才能重新写入新数据。
因此在实际使用中,不会直接操作 FLASH,而是先将 FLASH 里的数据读取到 RAM 数组中进行修改、运算等操作;待数据处理完成后,再一次性擦除 FLASH 页,并将更新后的数组写回 FLASH,既保证操作效率,也能避免频繁擦写损耗 FLASH 寿命。
void Store_Save(void)
{
MyFLASH_ErasePage(STORE_START_ADDRESS);
for (uint16_t i = 0; i < STORE_COUNT; i ++)
{
MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, Store_Data[i]);
}
}
总结
Store_Save= 保存数据到 FLASH- 必须先擦除,再写入
- 数据来源是 RAM,写入目标是 FLASH
- 掉电不丢失靠 FLASH,不是 RAM
- 这是 STM32 最标准、最稳定的掉电保存方案
擦除
将存储区中除标志位外 的所有用户数据清零,并保存至 FLASH,实现恢复出厂设置的效果。
void Store_Clear(void)
{
for (uint16_t i = 1; i < STORE_COUNT; i ++)
{
Store_Data[i] = 0x0000;
}
Store_Save();
}
执行流程
- 调用
Store_Clear() - 将 RAM 数组
Store_Data[1] ~ Store_Data[511]全部设为 0 - 调用
Store_Save() - 擦除 FLASH 并将全零数据写入
- 完成清空,数据恢复默认值
总结
1. 保留 "有效性" 标志
只清数据,不清 0xA5A5。
- 清完后:FLASH 依然是已初始化状态。
- 下次开机:直接读取清零后的数据,不会重复执行格式化流程。
2. 操作逻辑安全
- 先改 RAM(安全、快速)
- 再调用 Save 写回 FLASH
- 符合整套模块的设计规范
六、调用
按键 1 → 修改数据并永久保存
按键 2 → 清空所有数据
OLED 屏幕 → 实时显示数据
断电再上电 → 数据不丢失
uint8_t KeyNum;
int main(void)
{
OLED_Init();
Key_Init();
Store_Init();
OLED_ShowString(1, 1, "Flag:");
OLED_ShowString(2, 1, "Data:");
while (1)
{
KeyNum = Key_GetNum();
if (KeyNum == 1)
{
Store_Data[1] ++;
Store_Data[2] += 2;
Store_Data[3] += 3;
Store_Data[4] += 4;
Store_Save();
}
if (KeyNum == 2)
{
Store_Clear();
}
OLED_ShowHexNum(1, 6, Store_Data[0], 4);
OLED_ShowHexNum(3, 1, Store_Data[1], 4);
OLED_ShowHexNum(3, 6, Store_Data[2], 4);
OLED_ShowHexNum(4, 1, Store_Data[3], 4);
OLED_ShowHexNum(4, 6, Store_Data[4], 4);
}
}