STM32(二十九)——读写、擦除FLASH

一、库函数

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.先解锁(开门)

  1. 官方函数:全片擦除(清空整个FLASH)

  2. 上锁(关门)

注:把整个 STM32 的 FLASH 全部清空, 程序都会被删掉,正常存参数不用这个。

页擦除

void MyFLASH_ErasePage(uint32_t PageAddress)

{

FLASH_Unlock();

FLASH_ErasePage(PageAddress);

FLASH_Lock();

}

1.先解锁(开门)

  1. 官方函数:只擦这一页

  2. 上锁(关门)

四、写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];

初始化

执行流程
  1. 芯片上电
  2. 执行 Store_Init
  3. 读取 FLASH 首地址
  4. 无标志 → 擦除 → 写入标志 → 初始化默认值(第一次时写入0xA5A5,用来当标志位)
  5. 有标志 → 直接读取数据到 RAM
  6. 初始化完成,程序正常运行

总结:上电初始化,让存储系统 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();
}
执行流程
  1. 调用 Store_Clear()
  2. 将 RAM 数组 Store_Data[1] ~ Store_Data[511] 全部设为 0
  3. 调用 Store_Save()
  4. 擦除 FLASH 并将全零数据写入
  5. 完成清空,数据恢复默认值

总结

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);
	}
}
相关推荐
風清掦2 小时前
【江科大STM32学习笔记-09】USART串口协议 - 9.2 USART串口数据包
笔记·stm32·单片机·嵌入式硬件·学习
慧一居士2 小时前
TanStack功能介绍和使用场景,对应 vue,react 完整使用示例
前端·vue.js
新晨4372 小时前
Git跨分支文件恢复:如何将其他分支的内容安全拷贝到当前分支
前端·git
一枚菜鸟_2 小时前
02-React+TypeScript基础速览
前端·taro
踩着两条虫2 小时前
VTJ.PRO 在线应用开发平台入门与项目初始化
前端·人工智能·ai编程
流星雨在线2 小时前
大前端通用性能优化(高频场景专项)
前端·性能优化
方安乐2 小时前
ESLint代码规范(一)
前端·javascript·代码规范
【 STM32开发 】2 小时前
【STM32 + CubeMX】低功耗 -- Standby 待机模式
单片机·嵌入式硬件
酉鬼女又兒2 小时前
零基础快速入门前端JavaScript Array 常用方法详解与实战(可用于备赛蓝桥杯Web应用开发)
开发语言·前端·javascript·chrome·蓝桥杯