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可不是一整块大饼,它被分成了三个区域:主存储器、系统存储器、选项字节。每个区域各司其职,搞混了可是会出问题的。
先看这张结构图,我手绘的,你感受一下:
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存数据;主存储器是干活的地方,系统存储器是救命的,选项字节是锁门的。搞清楚了这些,你写程序时心里就有底了。