STM32内部读写FLASH

很多情况下,在STM32中写入一些数据,在某些不可控因素下其数据无法保存。因此,解决此问题就要用到FLASH.

什么是内部 Flash? 

Flash 是一种非易失性存储器,STM32 的程序和常量数据就存在 Flash 中。它的关键特点是:

特性说明
地址映射Flash 地址从 0x08000000 开始
掉电不丢电源断了之后数据还在
读操作和 RAM 一样,可以直接读
写操作必须擦除后写入(写之前必须全是 1)
写入单位至少为半字(16位)
擦除单位以页为单位(STM32F103 是 1KB)

Flash 是如何读数据的? 

STM32 的 Flash 是memory-mapped(内存映射)的,即:

Flash 被挂在总线上,和 SRAM 一样的访问方式,读取数据只需要访问地址。

底层硬件会自动完成数据译码、电荷检测、字线控制等,所以我们可以像访问 RAM 一样访问 Flash。

STM32 读取和写入内部 Flash 的本质区

操作是否需要解锁是否需要擦除最小单位是否会改变 Flash 数据
读取否 否 1字节/2字节/4字节否 (只读)
写入是 是(必须)半字(16位)是 (只能1→0)

 为什么不需要解锁就能读取?

因为 读取 Flash 是只读操作,不会修改 Flash 结构,不存在写保护一说,也不需要擦除。Flash 读取只需要:

uint16_t data = *(volatile uint16_t *)0x0800FC00;

STM32 会自动从 Flash 控制器中读取该地址的数据。

读取代码详解

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));
}
通用结构分析

*((__IO 类型 *)(Address)) 是什么意思?

组成部分含义
(类型 *)把地址强制转换成指针类型
__IOvolatile,防止编译器优化(告诉编译器:值可能随时变化)
*(指针)取出地址中的值(解引用)

举个例子: 

*((__IO uint16_t *)0x0800FC00) → 读取地址0x0800FC00的2字节数据

注意事项
不要读取未对齐地址(比如读取 uint16_t 不能从奇数地址读取)

不要在 Flash 正在写入时读取 Flash

写入 Flash 的完整流程 

写入 Flash 是一个有顺序、受保护的敏感操作,必须严格遵守 STM32 的流程!

要求原因
写入前必须解锁防止误写程序或数据
写入前必须擦除因为 Flash 只能从 1 → 0,不能反向
写入地址必须对齐每次只能写一个 “半字”
写入时 Flash 忙,不能再操作 Flash必须等待写完成(BSY = 0)

 

  ┌────────────┐│ Flash_Unlock│ ← 解锁 Flash 写保护└─────┬──────┘↓┌──────────────┐│Flash_ErasePage│ ← 擦除所在页(将所有位置1)└─────┬────────┘↓┌──────────────────────┐│Flash_ProgramHalfWord │ ← 写入一个16位值(地址必须对齐)└────────────┬─────────┘↓┌────────────┐│ Flash_Lock │ ← 锁回 Flash,防止误写└────────────┘
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();MyFLASH_ErasePage(Address);  FLASH_ProgramWord(Address, Data);FLASH_Lock();
}void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{FLASH_Unlock();MyFLASH_ErasePage(Address);  FLASH_ProgramHalfWord(Address, Data);FLASH_Lock();
}

把 STM32 的内部 Flash 最后一页 0x0800FC00 用作数据存储区

函数名功能说明
Store_Init()开机时检查是否初始化过,若无则初始化数据,再加载到 RAM 数组
Store_Save()把当前 RAM 中的数据整体写入 Flash(覆盖)
Store_Clear()清除 RAM 中的数据,再写入 Flash(相当于清空)

Store_Init() 函数解释 

 

#define STORE_START_ADDRESS		0x0800FC00
#define STORE_COUNT				512uint16_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);}
}

 

空间计算(为什么是 512?)
每个 uint16_t 是 2 字节(16 位),所以:

512 × 2 字节 = 1024 字节 = 1KB

正好使用 Flash 的最后一页(1KB 大小)来存储 512 个 uint16_t。

这是一种 对齐页大小的设计,防止写入跨页、浪费空间或出错。

步骤说明
读取魔数检查 Flash 中是否已经初始化过(首地址是否为 0xA5A5)
若未初始化则擦除该页,写入魔数 + 写入 511 个 0x0000
读取数据把 Flash 中的所有数据读入 RAM 数组 Store_Data[]

为什么用魔数 0xA5A5?

Flash 出厂是全 0xFFFF,你写入 0xA5A5 后,可以作为初始化标志,下次重启时避免重复擦除。

Store_Save() 函数解释(将 RAM 数据写回 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]);}
}
步骤说明
先擦除页Flash 写之前必须擦除
顺序写入Store_Data[] 数组中 512 个半字写入 Flash

为什么每个数据偏移 i * 2?
因为你每个数据是 uint16_t,占用 2 字节,要避免覆盖前一个数据。 

Store_Clear() 函数解释(清除并保存) 

void Store_Clear(void)
{for (uint16_t i = 1; i < STORE_COUNT; i ++){Store_Data[i] = 0x0000;}Store_Save();
}

功能分析:
从第 1 个数据开始清 0(保留 Store_Data[0] 为魔数)

调用 Store_Save() 整页重写

main函数

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Store.h"
#include "Key.h"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);}
}

 FLASH读取芯片ID

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"int main(void)
{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 大小0x1FFFF7E0单位:KB(例如 64 → 64KB Flash)
UID[0]0x1FFFF7E8UID 第 0~15 位(低 16 位)
UID[1]0x1FFFF7EAUID 第 16~31 位(高 16 位)
UID[2]0x1FFFF7ECUID 第 32~63 位(32 位)
UID[3]0x1FFFF7F0UID 第 64~95 位(32 位)

应用场景
防伪 / 唯一性绑定:

使用 UID 作为设备编号上传服务器或产品注册

Flash 容量检测:

某些型号可能 Flash 容量不一致,可动态检测

License 系统:

生成授权码绑定到 UID,防止盗版

设备信息显示:

工程生产中可直接通过 OLED 显示设备 ID 进行追溯

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/918135.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/918135.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Oracle 12c + Pl/Sql windows系统下表空间创建、迁移,dmp备份导入,数据库字符集更改

一、开发环境 操作系统&#xff1a;win11 Oracle版本&#xff1a;12c Oracle 数据库字符集&#xff1a;AL32UTF8 Pl/Sql版本&#xff1a;14 二、表空间创建 表空间是 Oracle 数据库中一种重要的逻辑结构&#xff0c;它是数据库中数据文件的逻辑集合&#xff0c;用于存储数据库对…

GUI:QT简介

一、什么是QT&#xff1f;Qt是一套跨平台的 C 图形用户界面&#xff08;GUI&#xff09;应用程序开发框架&#xff0c;由挪威 Trolltech&#xff08;奇趣科技&#xff09;于 1991 年创建&#xff0c;2008 年被诺基亚收购&#xff0c;2012 年后由 Qt Company 负责维护。它广泛应…

oceanbase执行execute immediate create table提示无权限

问题&#xff1a;OB库4.2.5.4版本&#xff0c;执行到这一句的时候&#xff0c;报没有权限&#xff1a;[rootlnob ~]# obclient -h192.168.207.28 -P2881 -ugistarlnzyob -pxxxxxx -A Welcome to the OceanBase. Commands end with ; or \g. Your OceanBase connection id is 3…

滴滴招java开发

滴滴集团 北京&#xff08;岗位信息已经过jobleap.cn授权&#xff0c;可在csdn发布&#xff09;收录时间&#xff1a; 2025年08月01日职位描述 负责滴滴海外业务准入审核及反作弊相关系统的后端开发及系统维护&#xff1b; 职位要求 1、统招本科及以上学历&#xff0c;计算机科…

深入解析基于Zookeeper分布式锁在高并发场景下的性能优化实践指南

深入解析基于Zookeeper分布式锁在高并发场景下的性能优化实践指南 在大规模分布式系统中&#xff0c;如何保证多个节点对同一资源的有序访问&#xff0c;是提高系统稳定性与一致性的核心需求之一。Zookeeper 提供的分布式锁机制&#xff0c;以其简洁的原理和高可靠性&#xff0…

腾讯云CodeBuddy AI IDE+CloudBase AI ToolKit打造理财小助手网页

CodeBuddy 腾讯云CodeBuddy AI IDECloudBase AI ToolKit打造理财小助手网页 在线体验地址&#xff1a;理财小助手 在线仓库&#xff1a;https://cnb.cool/pickstars-2025/ai-financial-assistant &#x1f31f; Hello&#xff0c;我是摘星&#xff01; &#x1f308; 在彩虹般…

2025-08-08 李沐深度学习11——深度学习计算

文章目录1 模型构造1.1 自定义 MLP&#xff08;多层感知机&#xff09;1.1.1 __init__ (构造函数)1.1.2 forward (前向传播)1.2 使用自定义 MLP1.3 自定义 Sequential 类1.4 前向传播1.5 模块的嵌套使用2 参数管理2.1 参数访问2.2 嵌套模型2.3 参数初始化2.4 参数共享3 自定义层…

汇编语言和高级语言的差异

汇编语言与高级语言在以下几个方面存在重要的区别&#xff1a;缺少结构化流程控制。汇编语言不提供if/else、switch/case、for、while等高级控制结构&#xff0c;依赖于底层的无条件跳转和条件跳转指令来实现流程控制。这种基于标签和跳转的方式虽然极其灵活&#xff0c;但缺乏…

文件管理从基础到高级:文件描述符、超大文件切片重组与快速删除实战

文件管理从基础到高级&#xff1a;文件描述符、超大文件切片重组与快速删除实战目标读者&#xff1a;Linux/macOS 用户、后端/运维/数据工程师 环境默认&#xff1a;Linux&#xff08;GNU 工具链&#xff09;&#xff0c;macOS 类似&#xff1b;Windows 可使用 WSL1&#xff09…

RPC 解析

RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;是一种让分布式系统中的服务能够像调用本地函数一样调用远程服务的通信机制。以下是其核心原理、技术实现及组件的详细解析&#xff1a;&#x1f527; 一、RPC 核心工作原理&#xff08;10 步全流程&…

wstool的一个完整的工作流解析

moveit的仓库源码编译的时候使用的是wstool来拉取仓库的所有内容文件&#xff0c;其命令流程如下: wstool init src wstool merge -t src https://raw.githubusercontent.com/moveit/moveit/master/moveit.rosinstall wstool update -t src rosdep install -y --from-paths src…

对数函数分段定点实现

目录 一、原理介绍 二、代码实现 三、结果显示 四、移植到C语言中的应用 4.1. 定义定点数配置和参数 4.2. 实现分段查找函数 4.3. 实现 log10 近似计算函数 4.4. &#xff08;可选&#xff09;定点数转浮点数 一、原理介绍 之前的博文对数函数分段线性实…

qt系统--事件

文章目录qt系统事件处理鼠标事件鼠标移动事件处理键盘事件定时器事件窗口移动和大小改变事件结语很高兴和大家见面&#xff0c;给生活加点impetus&#xff01;&#xff01;开启今天的编程之路&#xff01;&#xff01; 作者&#xff1a;٩( ‘ω’ )و260 我的专栏&#xff1a…

Linux机器可直接使用的自动化编译文件

还在为了Linux机器上一遍遍输入编译指令苦恼吗&#xff1f;你需要make指令以及自己的makefile文件&#xff01;在makefile中写入自己的个性化指令&#xff0c;让编译速度飞起&#xff0c;支持多文件编译一下文件为个人应用&#xff0c;可以直接cp到相应项目的目录&#xff0c;每…

Linux学习-数据结构(哈希表)

1.哈希表1.哈希算法将数据通过哈希算法映射成一个关键值&#xff0c;存放都在同一位置实现数据的高效存储和查找&#xff0c;将时间复杂度尽可能降低至O&#xff08;1&#xff09;2.哈希碰撞多个数据通过哈希算法得到的键值相同&#xff0c;称为产生哈希碰撞3.哈希表构建哈希表…

Google Chrome <139.0.7236.0 UAF漏洞

【高危】Google Chrome <139.0.7236.0 UAF漏洞 漏洞描述 Google Chrome 是美国谷歌&#xff08;Google&#xff09;公司的一款Web浏览器。 受影响版本中&#xff0c;OpenscreenSessionHost::ReportAndLogError 方法的参数使用了 std::string_view 类型来接收错误消息。当一…

CentOS8 Stream 网卡配置及重启

在 CentOS 8 Stream 中&#xff0c;网卡配置已由 NetworkManager 管理&#xff0c;传统的 ifcfg-eth0 文件仍然支持&#xff0c;但推荐使用 nmcli 或 nmtui 工具进行网络配置和管理。以下是网卡配置及重启的详细步骤&#xff1a;1. 查看当前网卡状态列出所有网卡bash复制nmcli …

SpringMvc的原理深度剖析及源码解读

一、springmvc启动加载流程1、引入spring-web.jar包时&#xff0c;在这个包的META-INF/services/javax.servlet.ServletContainerInitializer文件中定义的加载类SpringServletContainerInitializer,提供给springmvc实现初始化的操作。2、在SpringServletContainerInitializer类…

【ESP32-menuconfig(1) -- Build Type及Bootloader config】

Build Type Bootloader configmenuconfig介绍Build typeCONFIG_APP_BUILD_TYPECONFIG_APP_BUILD_TYPE_PURE_RAM_APPCONFIG_APP_REPRODUCIBLE_BUILDCONFIG_APP_NO_BLOBSCONFIG_APP_COMPATIBLE_PRE_V2_1_BOOTLOADERSCONFIG_APP_COMPATIBLE_PRE_V3_1_BOOTLOADERSBootloader config…

C++信息学奥赛一本通-第一部分-基础一-第3章-第1节

C信息学奥赛一本通-第一部分-基础一-第3章-第1节 2051 偶数 #include <iostream>using namespace std;int main() {int number; cin >> number;if (number % 2 0) {cout << "yes";} }2052 范围判断 #include <iostream>using namespace std…