[应用相关] STM32:内部 Flash 写读和唯一 ID 获取

[复制链接]
Jiangxiaopi 发表于 2025-8-26 08:42 | 显示全部楼层 |阅读模式
一、前言
在 STM32 的嵌入式开发中,经常会遇到掉电数据保存、设备身份识别的需求。比如:

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

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

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

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

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

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

掉电后保持数据不丢失

读取并显示芯片唯一 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的读取方法,帮助开发者实现数据持久存储和硬件识别,为嵌入式项目的稳定性和功能扩展提供了实用参考。
————————————————
版权声明:本文为CSDN博主「jingjing~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhu_xian_gang/article/details/150105238

您需要登录后才可以回帖 登录 | 注册

本版积分规则

55

主题

224

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部