32中的Flash读取周期设置
STM32 FLASH 简介
不同型号的STM32F40xx/41xx,其 FLASH容量也有所不同,最小的只有128K字节,最大 的则达到了 1024K 字节。
STM32F4 的闪存模块由主存储器、系统存储器、OPT区域和选项字节等4部分组成。
主存储器,该部分用来存放代码和数据常数(如const类型的数据)。分为12个扇区,前4 个扇区为16KB大小,扇区4为64KB大小,扇区5~11为128KB大小,不同容量的STM32F4, 拥有的扇区数不一样,比如我们的STM32F407ZGT6,拥有12个扇区。
系统存储器,主要用来存放 STM32F4 的 bootloader 代码,此代码在出厂的时候就固化在 STM32F4 里面了,专门用来给主存储器下载代码的。当B0接V3.3,B1接GND的时候,从该 存储器启动(即进入串口下载模式)。
OTP区域,即一次性可编程区域,总共528字节大小,被分成两个部分,前面512字节(32 字节为1块,分成16块),可以用来存储一些用户数据(一次性的,写完一次,永远不可以擦 除!!),后面16字节,用于锁定对应块。
选项字节,用于配置读保护、BOR级别、软件/硬件看门狗以及器件处于待机或停止模式下 的复位。
闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制结构。
在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行。既在进行写或擦除操作时,不能进行代码或数据的读取操作。
1. 主存储器(Main Memory)
- 作用:存放用户代码和常量数据(比如
const定义的变量),是我们最常操作的区域。 - 结构(以 STM32F407ZGT6 为例):
- 分为 12 个扇区(Sector),扇区大小不统一:
- 扇区 0~3:16KB
- 扇区 4:64KB
- 扇区 5~11:128KB
- 起始地址:
0x08000000 - 启动逻辑:当 BOOT0、BOOT1 都接 GND 时,单片机从这里启动并运行用户代码。
- 分为 12 个扇区(Sector),扇区大小不统一:
2. 系统存储器(System Memory)
- 作用:出厂固化了 STM32 的 Bootloader(引导程序),专门用于串口下载代码。
- 关键特性:用户无法修改这部分代码,出厂时就已固定。
- 启动逻辑:当 BOOT0 接 V3.3、BOOT1 接 GND 时,单片机从这里启动,进入串口下载模式(ISP 模式)。
3. OTP 区域(One-Time Programmable,一次性可编程区域)
- 作用:存储用户数据,只能写一次,写完后无法擦除或修改,适合存设备序列号、加密密钥等永久信息。
- 结构:
- 总大小 528 字节:
- 前 512 字节:分为 16 块(每块 32 字节),用于存储用户数据。
- 后 16 字节:用于锁定对应的 OTP 块(锁定后无法再修改该块数据)。
- 总大小 528 字节:
4. 选项字节(Option Bytes)
- 作用:配置单片机的关键安全和运行参数,比如:
- Flash 读保护(防止代码被读取)
- BOR(掉电复位)级别
- 看门狗类型(软件 / 硬件看门狗)
- 待机 / 停止模式下的复位行为
- 特点:这部分配置可以通过特定的 Flash 操作修改,常用于产品安全和功耗控制。
5. Flash 存储接口寄存器
- 作用:控制 Flash 的读写、擦除操作,是 Flash 模块的 “控制中枢”。
- 关键规则:执行 Flash 写操作时,任何对 Flash 的读操作都会锁定总线,直到写操作完成后,读操作才能恢复。
| BOOT0 | BOOT1 | 启动区域 | 用途 |
|---|---|---|---|
| 0 | 0 | 主存储器 | 运行用户代码 |
| 1 | 0 | 系统存储器 | 串口下载模式 |
| 0/1 | 1 | 内置 SRAM | 调试 / 特殊应用场景 |
一. 闪存的读取
STM32F4 可以通过内部的I-Code指令总线或D-Code数据总线访问内置闪存模块,本章主 要讲解的数据读写,即通过D-Code数据总线来访问内部闪存模块。为了准确读取Flash数据, 必须根据CPU时钟(HCLK)频率和器件电源电压在Flash存取控制寄存器(FLASH_ACR)中 正确地设置等待周期数(LATENCY)。当电源电压低于 2.1V 时,必须关闭预取缓冲器。Flash 等待周期与CPU时钟频率之间的对应关系,如表46.1.1.2所示:
一、表格核心信息解读
这张表的核心作用是:根据系统时钟(HCLK)频率和供电电压,确定 Flash 读取时需要设置的等待周期(WS),确保 Flash 读取稳定可靠。
不同电压范围下,对应时钟频率的等待周期要求不同:
电压越高(2.7V~3.6V),Flash 读取速度越快,能支持更高的时钟频率而无需增加等待周期
电压越低(如 1.8V~2.1V 且预取关闭),Flash 读取速度受限,需要更多等待周期 当电源电压低于 2.1V 时,必须关闭预取缓冲器,此时最高时钟和等待周期的对应关系会更严格
二、举个例子帮你理解
假设你的 STM32F4 供电电压为 3.3V(属于 2.7V~3.6V 范围),系统时钟 HCLK 配置为 168MHz:
1.在 2.7V~3.6V 列中,找到150 < HCLK ≤ 168,对应等待周期为 5 WS(即 6 个 CPU 周期)
2.你需要在FLASH_ACR寄存器中,将LATENCY位设置为5
3.若供电电压为 1.8V~2.1V 且关闭预取,168MHz 的 HCLK 需要设置为 7 WS(8 个 CPU 周期)
三、关键使用注意事项
1.必须先配置 Flash 等待周期,再配置系统时钟
若先把时钟拉到 168MHz 再配置等待周期,会因为 Flash 跟不上时钟速度导致程序跑飞。正确顺序是:
// 1. 配置FLASH等待周期(根据电压和目标时钟设置) FLASH->ACR |= FLASH_ACR_LATENCY_5WS; // 3.3V下168MHz用5WS // 2. 配置预取缓冲和指令缓存(电压≥2.1V时建议开启) FLASH->ACR |= FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN; // 3. 再配置PLL,将HCLK提升到目标频率2.预取缓冲器的开启条件
电压≥2.1V:建议开启预取缓冲(PRFTEN位),可以提升指令读取效率 电压 < 2.1V:必须关闭预取缓冲,否则无法稳定运行
3.时钟与电压的对应关系不能错
比如 3.3V 下,120MHz 的 HCLK 可以用 4 WS;但如果电压降到 2.4V~2.7V,120MHz 的 HCLK 需要用 5 WS,不按表设置会导致程序运行异常。
四、ART 加速器(自适应实时存储器加速器)的作用
在 STM32F4 运行在 168MHz 时,Flash 读取需要 6 个 CPU 等待周期,但 ART 加速器可以把这个等待时间 “隐藏” 起来:
原理:通过指令缓存和预取机制,提前把 Flash 里的指令读到缓存中
效果:CPU 直接从缓存取指令,大部分时候相当于 0 等待周期,接近无等待的运行效率
注意:这不是 “消除” 了等待周期,而是把等待时间通过预取和缓存隐藏在了后台,让 CPU 不用空转等待。
五、Flash 读取代码解析
data = * (volatile uint32_t *)addr;这行代码的作用是:从 addr 地址读取一个 32 位数据,下面拆解每个部分:
| 代码部分 | 作用说明 |
|---|---|
addr | 要读取的 Flash 地址(比如0x08000000) |
(volatile uint32_t *) | 把addr强制转换为指向 32 位无符号整数的 volatile 指针 |
* | 取该指针指向地址的值,完成读取 |
为什么要用 volatile?
volatile 是防止编译器优化的关键字,它告诉编译器: 这个地址的值可能会被硬件(比如 Flash 控制器)意外修改
不要把这行代码优化掉,每次读取都必须直接访问 Flash 地址
如果不加 volatile,编译器可能会认为这行代码是 “多余的”,直接优化成一个固定值,导致读取失败。
读取不同长度的数据
- 读取 8 位(字节):
data = * (volatile uint8_t *)addr; - 读取 16 位(半字):
data = * (volatile uint16_t *)addr; - 读取 32 位(字):就是上面的代码
六、Flash 读 vs Flash 写
- 读取:非常简单,直接用指针访问地址即可,不需要特殊操作
- 写入:复杂很多,需要遵循 STM32F4 的 Flash 编程流程:
- 解锁 Flash 控制寄存器
- 擦除目标扇区(Flash 只能按扇区擦除,不能直接改写单个字节)
- 按字 / 半字 / 字节写入数据
- 等待操作完成并上锁寄存器
七、实际开发中的注意事项
- 地址范围:Flash 主存储器的地址范围是
0x08000000 ~ 0x080FFFFF(以 1MB 容量为例),超出范围会导致 HardFault 错误。 - 读取速度:虽然有 ART 加速器,但连续随机读取时,缓存命中率下降,还是会出现等待周期,顺序读取的效率最高。
- 权限问题:如果开启了 Flash 读保护,读取受保护区域会失败,甚至会锁死芯片
补充:
预取缓存器有什么用?
一、核心作用:解决「CPU 跑得快,Flash 读得慢」的矛盾
STM32F4 的 CPU 核心(Cortex-M4)工作频率可以高达 168MHz,但内部 Flash 的读取速度跟不上 CPU 的时钟周期,必须通过设置「等待周期(LATENCY)」来给 Flash 留出读取时间。
- 没有预取缓冲时:CPU 每取一条指令,都要等 Flash 完成读取(插入等待周期),执行效率会被拉低。
- 开启预取缓冲后:缓冲器会提前从 Flash 中读取后续指令并缓存起来,CPU 取指令时直接从缓冲器读取,大部分时候可以「无等待」执行。
二、具体工作原理
预取缓冲器是一个硬件级的指令流水线,工作流程如下:
- 当 CPU 执行指令时,预取缓冲器会提前预判下一条 / 下几条指令的地址,主动向 Flash 发起读取请求。
- Flash 完成读取后,指令会被存入预取缓冲器的 FIFO 队列中。
- CPU 取指令时,直接从缓冲器中读取,无需等待 Flash 的读取延迟。
- 即使 Flash 读取需要等待周期,这些等待时间也会被预取缓冲器「隐藏」在后台,不会打断 CPU 的执行流程。
三、开启后的实际收益
提升指令执行效率开启预取缓冲后,即使设置了较高的 Flash 等待周期,CPU 的指令执行效率也能接近「零等待」的水平。比如 168MHz 下设置 5 个等待周期,开启预取后,实际平均等待周期会大幅降低。
减少 CPU 空转没有预取缓冲时,CPU 每取一条指令都要插入等待周期,相当于 CPU 在空等 Flash;开启后,等待周期被预取操作覆盖,CPU 可以连续执行指令。
配合 I-Code 总线优化STM32F4 的 I-Code 指令总线专门用于读取指令,预取缓冲器和指令缓存(ICACHE)配合使用时,能进一步提升指令读取的命中率,减少对 Flash 的访问次数。
四、关键限制与注意事项
必须满足电压条件当供电电压低于 2.1V 时,必须关闭预取缓冲器(如你图片中的表格所示),此时 Flash 读取速度更慢,预取缓冲无法稳定工作,反而可能导致程序跑飞。
和 Flash 等待周期配合使用预取缓冲器只能「隐藏」等待周期,不能消除等待周期。等待周期的设置必须严格按照电压和时钟频率的对应表来配置,否则即使开启预取,也会因为 Flash 读取失败导致程序异常。
对分支指令的效果有限预取缓冲器对顺序执行的指令效率提升最明显;遇到分支、跳转指令时,预取的指令可能无效,此时仍会产生一定的等待周期,这时候指令缓存(ICACHE)的作用会更突出。
