演示视频:https://www.bilibili.com/video/BV11YdbYuEJb/?share_source=copy_web&vd_source=0e4269581b0bc60d57a80c9a27c98905

一、前言

在 STM32 的嵌入式开发中,经常会遇到掉电数据保存设备身份识别的需求。比如:

  • 保存用户配置参数,即使断电也能保留

  • 为每台设备分配唯一编号,方便售后追踪

  • 记录运行日志或状态,便于调试和维护

STM32 内部自带 Flash 存储区,不仅能存储程序,还能被当作数据存储区使用(类似 EEPROM)。
同时,芯片还内置了一个 96 位的 唯一 ID,这为设备身份管理提供了天然的硬件保障。

本文结合我在项目中的实测,演示如何:

  1. 使用内部 Flash 写入、读取、初始化数据

  2. 掉电后保持数据不丢失

  3. 读取并显示芯片唯一 ID 以及 Flash 大小

全文包含原理分析 + 完整源码

二、演示目标

  • 使用最后一页 Flash 存储 16 位数据,程序掉电后仍可恢复。

  • 存储区首位置放标志(0xA5A5),判断是否已初始化。

  • 支持用户按键触发数据写入或清空。

  • 上电自动读取 Flash 存储区数据显示在 OLED。

  • 读取 STM32 内部 Flash 容量、唯一 ID并在 OLED 上显示。

三、原理分析

1. STM32 Flash 写读基本原理(以 STM32F1 为例)

  • 擦除页面(1 KB)时,所有位变为 1(即 0xFF 或半字 0xFFFF

  • 编程只能将 1 写为 0,不能反向

  • 写入之前必须先擦除整个页,否则可能写入失败

  • Flash 擦写次数有限(一般在万次级别)

2. 唯一 ID 与 Flash 大小地址

  • Flash 容量存在地址 0x1FFFF7E0(16 位数值:闪存大小,单位 KB)

  • 唯一 ID 位于 0x1FFFF7E8 起的 96 位区域,可读作三个段分别显示

四、源码

(1) MyFLASH.c:Flash 工具函数封装

#include "stm32f10x.h"                  // Device header

uint32_t MyFLASH_ReadWord(uint32_t Address)
{
	return *((__IO uint32_t *)(Address));
}

uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{
	return *((__IO uint16_t *)(Address));
}

uint8_t MyFLASH_ReadByte(uint32_t Address)
{
	return *((__IO uint8_t *)(Address));
}

void MyFlASH_EraseAllPages(void)
{
	FLASH_Unlock();
	FLASH_EraseAllPages();
	FLASH_Lock();
}

void MyFlASH_ErasePage(uint32_t PageAddress)
{
	FLASH_Unlock();
	FLASH_ErasePage(PageAddress);
	FLASH_Lock();
}

void MyFLASH_ProgramWord(uint32_t Address,uint32_t Data)
{
	FLASH_Unlock();
	FLASH_ProgramWord(Address,Data);
	FLASH_Lock();
}

void MyFLASH_ProgramHalfWord(uint32_t Address,uint16_t Data)
{
	FLASH_Unlock();
	FLASH_ProgramHalfWord(Address,Data);
	FLASH_Lock();
}

  • 使用易变存取方式读取 Flash 内容

  • 写操作中使用解锁/写入/锁定流程

  • 可扩展写入字(ProgramWord)函数

(2) Store.c:Flash 数据存储模块

#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"
#define STORE_START_ADDRESS 0x0800FC00
#define STORE_COUNT 512

uint16_t Store_Data[STORE_COUNT];

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);
	}
}

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]);
	}
}

void Store_Clear(void)
{
	for(uint16_t i=1;i<STORE_COUNT;i++)
	{
		Store_Data[i]=0x0000;
	}
	Store_Save();
}
  • 使用 0xA5A5 判断是否初始化,避免重复擦写

  • 后续数据用 0x0000 表示空值

  • 可通过 Store_Save()Store_Clear() 更新 Flash 数据

(3) main.c:OLED 显示与存储交互

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Store.h"
#include "Key.h"

uint8_t KeyNum;

int main(){
	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);
	}
}
  • 按键 1:修改数据(自增、加值等)并保存

  • 按键 2:清空数据区

  • OLED 显示当前标志位与部分数据项

(4) Flash 大小与芯片唯一 ID 的读取示例

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"

int main(){
	OLED_Init();
	OLED_ShowString(1,1,"F_SIZE:");
	OLED_ShowHexNum(1,8,*((__IO uint16_t *)(0x1FFFF7E0)),4);
	OLED_ShowString(2,1,"U_ID:");
	OLED_ShowHexNum(2,6,*((__IO uint16_t *)(0x1FFFF7E8)),4);
	OLED_ShowHexNum(2,11,*((__IO uint16_t *)(0x1FFFF7E8+0x02)),4);
	OLED_ShowHexNum(3,1,*((__IO uint32_t *)(0x1FFFF7E8+0x04)),8);
	OLED_ShowHexNum(4,1,*((__IO uint32_t *)(0x1FFFF7E8+0x08)),8);
	
	while(1){
		
	}
}
  • 显示 Flash 容量(单位 KB,如 1024 或 2048 等)

  • 显示芯片唯一 ID 的完整 96 位(分段 16 位 + 16 位 + 32 位 + 32 位格式)

五、实测说明

  • 上电后,如果首次运行,标志位为 0xFFFF → 写入标志与清空 → OLED 显示 0xA5A5

  • 按键 1 操作数据修改并保存,掉电重启依旧保持

  • 按键 2 清空,OLED 显示后数据区变为 0

  • 同时显示 Flash 容量和唯一 ID,方便硬件追踪与序列号显示

六、总结

本文通过示例代码讲解了STM32内部Flash的读写操作及芯片唯一ID的读取方法,帮助开发者实现数据持久存储和硬件识别,为嵌入式项目的稳定性和功能扩展提供了实用参考。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐