FLASH、EEPROM和FLASH模拟EEPROM
FLASH和EEPROM
FLASH存储器和EEPROM(电可擦可编程只读存储器)都属于非易失性存储器,但它们在工作原理、使用方式、应用场景等方面有一些区别。
FLASH存储器
FLASH(闪存)是一种非易失性存储器,能够在没有电源的情况下保存数据。它的工作原理类似于EEPROM,但在数据擦除和写入的方式上有所不同。
特点:
- 大容量:FLASH存储器一般提供比EEPROM更大的存储容量,通常从几兆字节(MB)到几百兆字节(甚至几个GB)。
- 块擦除:FLASH存储器以块(通常为几KB到几MB)为单位进行擦除,擦除操作需要清空整个块,而不能像EEPROM那样按字节擦除。这使得FLASH的擦写次数比EEPROM更低,一般在10,000到100,000次之间。
- 写入速度较快:相比EEPROM,FLASH的写入速度更快,适用于需要快速写入的应用场景。
- 应用广泛:FLASH主要用于存储大容量数据,广泛应用于固态硬盘(SSD)、U盘、存储卡、智能手机、嵌入式设备等。
- 编程和擦除:FLASH可以通过电气方式编程和擦除,但擦除和编程过程比EEPROM要复杂得多,且无法像EEPROM那样轻松按字节更新数据。
类型:
- NAND Flash:主要用于大容量存储,速度快,成本低,广泛用于移动存储设备(如U盘、SD卡、SSD等)。
- NOR Flash:速度较慢但可按字节进行读写,主要用于代码存储,适合嵌入式系统。
EEPROM vs FLASH
特性 | EEPROM | FLASH |
---|---|---|
存储容量 | 较小,一般为几KB到几MB | 较大,可以达到几GB |
数据擦除方式 | 按字节擦除 | 按块擦除(大块范围,一般几KB至几MB) |
写入速度 | 较慢,尤其在单个字节写入时 | 较快,适合大块数据写入 |
擦写次数 | 较多,通常达到100,000次以上 | 较少,通常为10,000到100,000次 |
应用场景 | 存储配置参数、序列号、校准数据等 | 存储操作系统、应用程序、数据文件等 |
价格 | 较贵,尤其在小容量时 | 相对便宜,适用于大容量存储 |
- EEPROM通常用于存储少量的数据,比如设备配置、用户设置等,需要频繁的、字节级的读写操作。它的主要优点是可以单独字节擦写,但容量较小,写入速度较慢。
- FLASH则适用于大容量的存储需求,广泛用于存储操作系统、应用程序、媒体文件等。由于其按块擦除的特性,写入速度较快,但擦写次数较少,适合大数据量的存储。
对于嵌入式系统来说,EEPROM适用于需要频繁更新的小量数据,而FLASH则适合存储较大的程序或数据集。
STM32F429中的FLASH
组织架构
STM32F429 本身没有自带 EEPROM,但是 STM32F429 具有 IAP(在应用编程)功能,所以我们可以把它的 FLASH 当成 EEPROM 来使用。对于F429IGT6来说,其FLASH容量为1024KB。FLASH模块分为:
- 主存储器:该部分用来存放代码和数据常数(如 const 类型的数据)。分为两个 Bank,每个 Bank 分为 12 个扇区,前 4 个扇区为 16KB 大小,第五个扇区是 64KB 大小,剩下的 7 个扇区都是 128K大小,总共 1M 字节。两个 Bank 就是 2M 字节。不同容量的 STM32F429,拥有的扇区数不一样。以STM32F429IGT6为例,只有 Bank1,拥有 12 个扇区,1M 字节容量。主存储器的起始地址一般为 0X08000000,B0、B1 都接 GND 的时候,就是从0X08000000 开始运行代码的。
- 系统存储器:主要用来存放 STM32F429的 bootloader代码,此代码在出厂时已经固化在 STM32F429 内,专门用来给主存储器下载代码。当 B0 接 V3.3,B1 接 GND 的时候,从该存储器启动(即进入串口下载模式)。
- OTP区域(一次性可编程区域):共 528 字节,被分成两个部分,前面 512字节(32 字节为1块,分成16块),可以用来存储一些用户数据(一次性,写完一次,永远不可以擦除),后面 16 字节,用于锁定对应块。
- 选项字节:用于配置读保护、BOR 级别、软件/硬件看门狗以及器件处于待机或停止模式下的复位。
在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正
确地进行;即在进行写或擦除操作时,不能进行代码或数据的读取操作。
读取
为了准确读取 Flash 数据,必须根据 CPU 时钟 (HCLK) 频率和器件电源电压在 Flash 存取控制寄存器 (FLASH_ACR) 中正确地设置等待周期数 (LATENCY)。当电源电压低于 2.1V 时,必须关闭预取缓冲器。
等待周期通过FLASH_ACR寄存器的LATENCY[2:0]三个位设置。
STM32读取FLASH很简单,例如要从地址addr读取一个字(32位),只需要一句话:
1 |
|
将 faddr 强制转换为 volatile uint32_t
指针,然后取该指针所指向的地址的值,即得到了 faddr 地址的值。类似的,将上面的 volatile uint32_t
改为 volatile uint8_t
,即可读取指定地址的一个字节。此处的 volatile
关键字告诉编译器该地址的内容可能随时改变,因此禁止编译器对该地址做任何优化。通常用于表示硬件寄存器或外设的内存地址,因为这些内容的值可能由外部事件(如外部硬件)改变。
编程和擦除
执行任何 Flash编程操作(擦除或编程)时,CPU时钟频率(HCLK)不能低于 1MHz。在对 STM32F429 的 Flash 执行写入或擦除操作期间,任何读取 Flash 的尝试都会导致总线阻塞。只有在完成编程操作后,才能正确处理读操作。这意味着,写/擦除操作进行期间不能从 Flash 中执行代码或数据获取操作。
STM32F429 用户闪存的编程一般由 6 个 32 位寄存器控制,他们分别是:
- FLASH 访问控制寄存器(FLASH_ACR)
- FLASH 秘钥寄存器(FLASH_KEYR)
- FLASH 选项秘钥寄存器(FLASH_OPTKEYR)
- FLASH 状态寄存器(FLASH_SR)
- FLASH 控制寄存器(FLASH_CR)
- FLASH 选项控制寄存器(FLASH_OPTCR)
STM32F429复位后,FLASH 编程操作是被保护的,不能写入 FLASH_CR寄存器;通过写入特定的序列(0X45670123
和 0XCDEF89AB
)到 FLASH_KEYR 寄存器才可解除写保护,只有在写保护被解除后,我们才能操作相关寄存器。
FLASH_CR 的解锁序列为:
- 写
0X45670123
到 FLASH_KEYR - 写
0XCDEF89AB
到 FLASH_KEYR
通过这两个步骤,即可解锁 FLASH_CR,如果写入错误,那么 FLASH_CR 将被锁定,直到下次复位后才可以再次解锁。
STM32F429 闪存的编程位数可以通过 FLASH_CR 的 PSIZE 字段配置,PSIZE 的设置必须
和电源电压匹配:
当开发板电压为3.3V时,PSIZE
为10
,并行位数32位。也就是擦除或编程都需要以32位为基础进行。
FLASH在编程时必须要求目标写入地址的FLASH已经被擦除(值为0xFFFFFFFF
),否则将无法写入。
F429的FLASH标准编程步骤如下:
- 检查FLASH_CR中的BSY位,确保总线空闲
- 将FLASH_CR寄存器的PG位置1,激活FLASH编程
- 针对所需存储地址(主存储器块或 OTP 区域内)执行数据写入操作:
- 并行位数为x32时按字写入(PSIZE=02)
- 等待BSY位清零
注意:
- 编程前确保写入地址已经擦除
- 先解锁FLASH_CR后编程
扇区擦除步骤如下:
- 检查FLASH_CR的LOOK是否解锁,如果没有则先解锁
- 检查FLASH_CR中的BSY位,确保总线空闲
- FLASH_CR的SER位置1,并从主存储块的12个扇区中选择要擦除的扇区(SNB)
- FLASH_CR寄存器中STRT位置1,触发擦除
- 等待BSY位清零
寄存器
FLASH_ACR - Flash访问控制寄存器
重点为LATENCY[2:0]位,应根据MCU电压和频率设置,否则死机。
FLASH_KEYR - Flash密钥寄存器
用于解锁FLASH_CR,必须按顺序写入KEY1和KEY2
FLASH_CR - Flash控制寄存器
- LOCK位:用于指示 FLASH_CR 寄存器是否被锁住,该位在检测到正确的解锁序列后,硬件将其清零。在一次不成功的解锁操作后,在下次系统复位之前,该位将不再改变。
- PG位:用于选择编程操作,在往 FLASH 写数据的时候,该位需要置 1。
- SER位:位用于选择扇区擦除操作,在扇区擦除的时候,需要将该位置 1。
- PSIZE[1:0]位:设置编程宽度,一般设置 PSIZE =2 即可(32 位)。
- STRT位:该位用于开始一次擦除操作。在该位写入 1 ,将执行一次擦除操作。
- SNB[3:0]位:用于选择要擦除的扇区编号,取值范围为 0~15。
FLASH_SR - Flash状态寄存器
- BSY位:确认BANK是否正在执行编程操作
代码
读取
1 |
|
写入
写入相对来说复杂一点,首先要判断写入地址是否正确(不能超出FLASH地址范围,不能不是4的倍数),判断通过后,解锁FLASH,禁用数据缓存,然后判断待写入地址范围是否为0xFFFFFFFF
。如果是,表明该地址已经被擦除;如果不是,表明该地址需要先擦除,再写入。
1 |
|
这里有几个需要注意的点:
- 擦除和写入期间必须禁用数据缓存,否则缓存器中的缓存内容和新的内容可以不一致导致bug。禁用数据缓存通过位操作
FLASH->ACR &= ~(1 << 10)
实现,1 << 10
表示1
左移10位,~
表示取反。 - 擦除操作通过
HAL_FLASHEx_Erase()
实现,HAL库对于该函数的定义为:
1 |
|
形参1为存储擦除配置的结构体,形参2为存储报错信息的变量。
结构体格式为:
1 |
|
- 全部擦除完成并等待
FLASH_WaitForLastOperation(FLASH_WAITETIME)
传回HAL_OK
后,才可以开始进行。写入函数为HAL_FLASH_Program
:
1 |
|
形参1用于指定写入的是字节(8位)、半个字(16位)、字(32位)或双字(64位);形参2为写入地址,形参3为待写入数据。连续写入时,形参3一般设置为指针,每次循环对相应地址+1处理。
4. 写入完成后,启用数据缓存,FLASH上锁。