2、Flash存储器详解:NOR Flash与NAND Flash的区别,STM32的Flash组织架构

Flash存储器,说白了就是单片机里用来存代码和掉电不丢失数据的地方。很多初学者觉得它就是一块“黑盒子”,写进去就完事了。但实际项目中,选错Flash类型或者搞不清内部结构,轻则程序跑飞,重则芯片直接锁死。今天我们就把它彻底讲透。

2.1 NOR Flash 与 NAND Flash 的核心区别

这两兄弟虽然都叫Flash,但脾气秉性完全不同。我刚开始做嵌入式时,以为它们只是容量不同,结果在项目里吃了大亏——用NAND Flash直接当代码存储器,结果CPU死活跑不起来。

先看一张对比表,一目了然:

对比项 NOR Flash NAND Flash
读取方式 随机读取,支持XIP(片上执行) 按页读取,不支持XIP
写入方式 按字节/字写入,速度较慢 按页写入(通常512B~4KB),速度快
擦除方式 按扇区擦除(通常64KB~128KB) 按块擦除(通常128KB~1MB)
坏块管理 出厂无坏块,无需管理 出厂可能有坏块,需要ECC和坏块表
典型容量 1MB~256MB 128MB~8GB+
典型应用 存储代码、关键参数 大容量数据、文件系统

关键结论:STM32内部集成的都是NOR Flash,因为CPU需要直接从Flash中取指令执行(XIP)。如果你外挂NAND Flash,代码不能直接在上面跑,必须先加载到RAM中。

为什么会这样?NOR Flash的读取接口和SRAM很像,地址线、数据线独立,CPU可以直接寻址。而NAND Flash是I/O复用接口,读取时先发命令、再发地址、最后读数据,这个过程CPU没法直接当内存用。

我记得有个同事做数据记录仪,选了颗大容量NAND Flash,结果发现每次读数据都要写一套驱动,而且坏块问题搞得他焦头烂额。后来换成NOR Flash,虽然容量小了点,但开发周期缩短了一半。

2.2 STM32的Flash组织架构

STM32内部的Flash可不是一整块大饼,它被分成了三个区域:主存储器、系统存储器、选项字节。每个区域各司其职,搞混了可是会出问题的。

先看这张结构图,我手绘的,你感受一下:

STM32 Flash 组织架构图 主存储器(Main Memory) 存放用户代码和常量数据 扇区0 扇区1 扇区2 扇区3 扇区4 容量:64KB~2MB(视具体型号) | 擦除单位:扇区(1KB~128KB) 系统存储器(System Memory) 存放Bootloader,出厂预置,用户不可擦除 选项字节(Option Bytes) 配置读保护、看门狗、启动模式等硬件选项 0x0800 0000 0x1FFF 0000 0x1FFF F800

2.3 主存储器(Main Memory)

这就是我们平时下载程序的地方。STM32把主存储器分成若干个扇区(Sector),不同型号的扇区大小不一样。

举个例子,STM32F103C8T6(蓝板子那个)有64KB Flash,分成64个1KB的扇区。而STM32F407ZGT6有1MB Flash,前4个扇区是16KB,后面都是128KB。

注意:擦除操作是按扇区来的,不能只擦除一个字节。如果你只想改一个字节的数据,必须先把整个扇区读到RAM,修改后再擦除整个扇区,最后写回去。这个操作如果中途断电,数据就丢了。

我曾经在一个OTA升级项目里,因为没处理好扇区备份,升级过程中断电,结果整个应用程序都废了。后来我加了个双备份机制,才彻底解决这个问题。

2.4 系统存储器(System Memory)

这个区域出厂时就已经写好了Bootloader,用户是没办法擦除或修改的。它的作用就是通过串口、USB、CAN等接口下载程序。

你想想看,为什么新买的STM32芯片,第一次烧录不需要外部编程器?就是靠这个系统存储器里的Bootloader。芯片上电后,如果检测到BOOT0引脚拉高,就会执行这里的代码,等待用户通过串口发送程序。

嗯,这里要注意:系统存储器的地址是固定的,不同系列可能不一样。F1系列在0x1FFF F000,F4系列在0x1FFF 0000。如果你自己写Bootloader,千万别把这个区域给覆盖了——你也覆盖不了,硬件上就锁死了。

2.5 选项字节(Option Bytes)

选项字节是个容易被忽视但极其重要的区域。它控制着芯片的硬件配置,比如:

  • 读保护(RDP):防止别人读出你的程序代码
  • 看门狗模式:硬件看门狗还是软件看门狗
  • 启动模式:从Flash、系统存储器还是SRAM启动
  • BOR(欠压复位):电压低于阈值时自动复位

选项字节的修改方式比较特殊——它需要解锁、擦除、写入三步,而且修改后必须复位才能生效。

个人建议:量产前一定要设置好读保护。我见过太多产品被抄板,就是因为没开RDP。开了之后,别人即使拿到芯片,也没法通过调试接口读出你的固件。

操作选项字节的代码示例(以STM32F4为例):

// 解锁选项字节
FLASH_OB_Unlock();

// 设置读保护等级为1(中等保护)
FLASH_OB_RDPConfig(OB_RDP_Level_1);

// 启动选项字节编程
FLASH_OB_Launch();

// 等待操作完成
while(FLASH_GetFlagStatus(FLASH_FLAG_BSY) != RESET);

// 锁定选项字节
FLASH_OB_Lock();

注意:一旦设置了读保护,下次想解除就必须先擦除整个主存储器。所以调试阶段别开,量产前再开。

2.6 地址映射与实战要点

STM32的Flash地址从0x0800 0000开始,但CPU复位后其实是从0x0000 0000取第一条指令。那怎么映射过去的?

实际上,0x0000 0000是个别名区,它会被映射到Flash、系统存储器或SRAM,具体取决于BOOT引脚的配置。默认情况下(BOOT0=0),它映射到Flash的0x0800 0000。

我整理了一个地址映射速查表:

地址范围 区域 说明
0x0800 0000 ~ 0x0801 FFFF 主存储器(128KB) 用户代码区
0x1FFF 0000 ~ 0x1FFF 77FF 系统存储器 Bootloader
0x1FFF F800 ~ 0x1FFF F80F 选项字节 16字节配置区
0x1FFF F810 ~ 0x1FFF F81F 选项字节(备份) 冗余备份

实战建议:写Bootloader时,把应用程序放在0x0801 0000之后,前4KB留给中断向量表和Bootloader本身。这样即使应用程序崩溃,Bootloader还能通过串口重新烧录。

好了,Flash这块的内容就讲到这里。记住一句话:NOR Flash存代码,NAND Flash存数据;主存储器是干活的地方,系统存储器是救命的,选项字节是锁门的。搞清楚了这些,你写程序时心里就有底了。