目录
[1:Flash 是什么?](#1:Flash 是什么?)
[2:Flash 的结构](#2:Flash 的结构)
[3:CPU 怎么执行程序?](#3:CPU 怎么执行程序?)
[4:为什么还需要 SRAM?](#4:为什么还需要 SRAM?)
[7:flash 存储内容](#7:flash 存储内容)
[8:flash 的几大应用](#8:flash 的几大应用)
[① Bootloader](#① Bootloader)
[② IAP(In-Application Programming)(在应用编程)](#② IAP(In-Application Programming)(在应用编程))
[③ EEPROM 模拟 (Emulated EEPROM)](#③ EEPROM 模拟 (Emulated EEPROM))
[2:Flash 的三大操作](#2:Flash 的三大操作)
[①. 读操作(无门槛)](#①. 读操作(无门槛))
Flash介绍
1:Flash 是什么?
Flash 可以说是 STM32 最重要的存储器之一。
今天的 Flash,就是 STM32 芯片里面那块掉电不会丢失数据的存储器。
它负责保存:
- 用户程序(main()、各种函数)
- 常量数据
- 字库
- 参数
- BootLoader
- 固件
CPU 上电后第一件事情就是:去 Flash 读取程序,然后开始执行。
2:Flash 的结构
STM32 的 Flash 没有 Page、Sector 混着用。
对于 F1 系列:只有 Page(页)
整个 Flash:64KB Flash→64 页→每页 1KB
地址变化:起始地址为0x08000000,1KB为1024 Byte转为16进制0x400
Page0为08000000→Page1为08000400直到0800FC00为最后一页
3:CPU 怎么执行程序?
很多人都会误解:Flash 是不是把程序复制到 SRAM 再运行?
对于 STM32F103:不是,CPU 可以直接读取 Flash。
4:为什么还需要 SRAM?
因为:Flash,适合保存,不适合频繁修改。SRAM:适合频繁读写
因此CPU是从 Flash 读取代码,去 SRAM 修改变量。
所以:
代码在 Flash。
数据在 SRAM。
这是最经典的结构。
5:flash的两个操作
(1)读取
CPU可以直接读取flash,0x08000000→取指令→执行 。程序运行就是这样。
(2)写入
Flash不能像RAM一样随便修改,写入前需要擦除以后才可以写入
擦除指的是把该页内容全部写入1
6:擦除单位Page
STM32F103最小擦除单位:Page
7:flash 存储内容
情况1:程序代码
情况2:用户参数
比如,
系统配置类:背光亮度等级
用户偏好类:语言选择:中文/英文。
运行统计类:设备通电次数
8:flash 的几大应用
① Bootloader
本质:一段上电后最先运行的代码,负责"交接工作"。
STM32 上电后,根据 Boot 引脚(BOOT0/BOOT1)的状态决定从哪里启动。如果是从 Flash 启动,它固定从 0x08000000开始执行,Bootloader 就放在0x08000000这里。
Bootloader的任务:
<1>初始化最基本的硬件(时钟、串口/USB/CAN)。
<2>判断是否需要升级程序(比如检测按键、检测APP完整性、接收升级指令)。
<3>跳转到用户应用程序(APP)去执行。

② IAP(In-Application Programming)(在应用编程)
本质:利用 Flash 的可擦写特性,在运行代码的同时改写 Flash 里的其他代码。
它与 Bootloader 的关系
很多人混淆这两个概念。其实:
-
Bootloader 是载体(那段代码)。
-
IAP 是功能(通过串口/USB/无线等方式更新固件的功能)。
通常,我们在 Bootloader 中实现 IAP 功能。
流程如下:
-
设备上电,进入 Bootloader。
-
收到主机发来的新固件数据包(Bin 文件)。
-
Bootloader 擦除 APP 所在扇区。
-
Bootloader 将收到的数据写入 Flash 的 APP 区域。
-
校验 CRC,成功后跳转。
为什么叫"在应用编程"?
因为你可以设计成:即使进入了 APP,APP 也能接收到升级指令,然后强制跳回 Bootloader,让 Bootloader 重新覆盖 APP 区域。这就实现了"一边跑一边修"。
防变砖机制(双保险)
为了防止升级中途断电导致变砖,高级一点的设计是:
-
预留两份 APP 空间(A/B 区)。
-
当前跑 A 区,收到升级包后写到 B 区。
-
B 区校验通过后,修改标志位,下次重启跳转到 B 区。
-
这样即使 B 区写坏了,还能回到 A 区。
③ EEPROM 模拟 (Emulated EEPROM)
本质:利用 Flash 的非易失性,假装自己是 EEPROM。
Flash 比 EEPROM 难用在于:
-
擦除单位大:Flash 擦 1KB,EEPROM 擦 1Byte。
-
寿命短:Flash 1万次,EEPROM 100万次。
STM32F103C8T6 没有 EEPROM。但我们需要存参数(如校准系数、配置),这些参数不能存在 RAM(掉电丢),也不能每次都烧录(太麻烦)。
ST 官方提供了 AN2594 应用笔记和对应的 EEPROM Emulation 库,强烈建议直接用官方的,不要自己造轮子,里面的状态机很复杂。
STM32f103flash资源
1:基本参数




2:Flash 的三大操作
①. 读操作(无门槛)
- 直接通过地址指针读取,和读 SRAM 一样
- 内建预取缓冲区,可以加速指令读取
②.写操作(需解锁)
- Flash 默认是锁住的,防止误操作
- 必须按顺序写入密钥才能解锁:先写
0x45670123,再写0xCDEF89AB到FLASH_KEYR寄存器 - 写操作必须按**半字(16 bit)**写入,不能按字节写
- 写入前目标地址必须是
0xFFFF(擦除后的状态)
③.擦除操作(2种方式)
- Flash 的物理特性:只能把 1 变成 0,不能把 0 变成 1
- 要把 0 变回 1,只能靠擦除,擦除会将整页或整片变为全
0xFFFF - 两种擦除模式:
- 页擦除:擦除某一页(1 KB),对其他页无影响
- 整片擦除:擦除全部 64 KB,包括你正在运行的程序!慎用
3:编程/擦除流程(标准库)
// 1. 解锁 Flash
FLASH_Unlock();
// 2. 等待 Flash 空闲(可选,大多数函数内部已处理)
// while(FLASH_GetFlagStatus(FLASH_FLAG_BSY) == SET);
// 3a. 页擦除
FLASH_ErasePage(0x0800_FC00); // 擦除最后一页(第63页)
// 3b. 或 整片擦除(慎用!会擦掉程序本身)
// FLASH_EraseAllPages();
// 4. 编程写入(半字单位)
FLASH_ProgramHalfWord(0x0800_FC00, 0x1234);
FLASH_ProgramHalfWord(0x0800_FC02, 0x5678);
// 5. 上锁保护
FLASH_Lock();
// 6. 读取验证(直接读地址就行)
uint16_t data = *(volatile uint16_t *)0x0800_FC00;
4:几大应用
1.模拟 EEPROM --- STM32F103C8T6 没有真正的 EEPROM,但可以用最后几页 Flash 来存配置参数(你的备份寄存器项目就是这个思路的入口)
2.IAP(在线升级) --- 通过串口/CAN 接收新固件,写入 Flash 的高地址区域,然后跳转执行
3.写保护 --- 通过 FLASH_WRPR 可以把某些页设为写保护,防止误擦
4.选项字节(Option Bytes) --- 位于 0x1FFFF800 区域,控制读保护、看门狗、复位等,操作时需要额外解锁 FLASH_OPTKEYR
5:与BKP寄存器的区别

读取flash程序实例
1:字节寻址
在包括 STM32(ARM Cortex-M)在内的几乎所有现代计算机架构 中,内存是按字节编址 的------每个地址指向一个字节(8 bits)。
所以 0x08000000 存的是 1 字节,这是对的。但这不是 flash 的特殊限制,你的 RAM(比如 0x20000000)也是这样的。

2:大端和小端
小端和大端------就是"多字节数据怎么排队"
当你存一个多字节的值(比如 uint32_t 是 4 字节),这 4 个字节在内存里是按什么顺序放的,这就是端序(endianness)。


3:写入半字地址处理
一个地址存一个字节,此处写入的是半字,是两个字节,所以是两个地址存刚好两个字节也就是一个半字,所以循环中,需要给数据的i索引乘上2
MyFLASH_ProgramHalfWord(STORE_START_ADDRESS +i * 2, 0x0000); 让地址索引一次+2
4:创建自己的SRAM
objectivec
void Store_Init(void)
{
/*判断是不是第一次使用*/
if (MyFLASH_ReadHalfWord(STORE_START_ADDRESS) != 0xA5A5) //读取第一个半字的标志位,if成立,则执行第一次使用的初始化
{
MyFLASH_ErasePage(STORE_START_ADDRESS); //擦除指定页
MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5); //在第一个半字写入自己规定的标志位,用于判断是不是第一次使用
for (uint16_t i = 1; i < STORE_COUNT; i ++) //循环STORE_COUNT次,除了第一个标志位
{
MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, 0x0000); //除了标志位的有效数据全部清0
}
}
/*上电时,将闪存数据加载回SRAM数组,实现SRAM数组的掉电不丢失*/
for (uint16_t i = 0; i < STORE_COUNT; i ++) //循环STORE_COUNT次,包括第一个标志位
{
Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i * 2); //将闪存的数据加载回SRAM数组
}
}
作用:系统上电时,检查 Flash 里有没有存档;如果没有,就创建初始存档;如果有,就把存档读到 SRAM,实现我们手写的sram数值掉电不丢失,

5:从SRAM写入到flash
objectivec
void Store_Save(void)
{
MyFLASH_ErasePage(STORE_START_ADDRESS); //擦除指定页
for (uint16_t i = 0; i < STORE_COUNT; i ++) //循环STORE_COUNT次,包括第一个标志位
{
MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, Store_Data[i]); //将SRAM数组的数据备份保存到闪存
}
}
先擦除后写入,包括标志位,也索性从写一下,不想麻烦就i从1起始也可以

6:清空SRAM数组
objectivec
void Store_Clear(void)
{
for (uint16_t i = 1; i < STORE_COUNT; i ++) //循环STORE_COUNT次,除了第一个标志位
{
Store_Data[i] = 0x0000; //SRAM数组有效数据清0
}
Store_Save(); //保存数据到闪存
}
