目录
1:Flash简介
FLASH存储器是闪速存储器,它的主要特点是在不加电的情况下能长期保持存储的信息。就其本质而言,Flash Memory属于EEPROM(电擦除可编程只读存储器)类型。它既有ROM的特点,又有很高的存取速度,而且易于擦除和重写, 功耗很小。
2:嵌入式Flash特性
- 对于 STM32F40x 和 STM32F41x,容量高达 1 MB;对于 STM32F42x 和 STM32F43x,容量高达 2 MB
- 128 位宽数据读取
- 字节、半字、字和双字数据写入
- 扇区擦除与全部擦除
- 低功耗模式
STM32F40x和STM32F41x Flash 模块构成
可以看到
- 主存储器
该部分用来存放代码和数据常数(如const类型的数据)。分为12个扇区,前4个扇区为16KB大小,然后扇区4是64KB大小,扇区5~11是128K大小,从上图可以看出主存储器的起始地址就是0X0800 0000,B0、B1都接GND的时候,就是从0X0800 0000开始运行代码
2.系统存储器
这个主要用来存放STM32F4的bootloader代码,此代码是出厂的时候就固化在STM32F4里面了,专门来给主存储器下载代码的。当B0接3.3V,B1接GND的时候,从该存储器启动(即进入串口下载模式)。
3.OTP区域
即一次性可编程区域,共528字节,被分成两个部分,前面512字节(32字节为1块,分成16块),可以用来存储一些用户数据(一次性的,写完一次,永远不可以擦除!!),后面16字节,用于锁定对应块。
4.选项字节
用于配置读保护、BOR级别、软件/硬件看门狗以及器件处于待机或停止模式下的复位。
3:Flash 闪存的读取
STM23F4的FLASH读取是很简单的。例如,我们要从地址addr,读取一个字(字节为8位,半字为16位,字为32位),可以通过如下的语句读取:
data=*(vu32*)addr;
将addr强制转换为vu32指针,然后取该指针所指向的地址的值,即得到了addr地址的值。类似的,将上面的vu32改为vu16,即可读取指定地址的一个半字。
在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。
STM32F4的闪存编程由6个32位寄存器控制,他们分别是
- FLASH访问控制寄存器(FLASH_ACR)
- FLASH秘钥寄存器(FLASH_KEYR)
- FLASH选项秘钥寄存器(FLASH_OPTKEYR)
- FLASH状态寄存器(FLASH_SR)
- FLASH控制寄存器(FLASH_CR)
- FLASH选项控制寄存器(FLASH_OPTCR)
其中秘钥寄存器中有两个键值KEY1=0X45670123 KEY2=0XCDEF89AB
STM32F4 Flash标准编程的步骤如下
- 检查FLASH_SR中的BSY位,确保当前未执行任何FLASH操作。
- 将FLASH_CR寄存器中的PG位置1,激活FLASH编程。
- 针对所需存储器地址(主存储器块或OTP区域内)执行数据写入操作:
—并行位数为x8时按字节写入(PSIZE=00)
—并行位数为x16时按半字写入(PSIZE=01)
—并行位数为x32时按字写入(PSIZE=02)
—并行位数为x64时按双字写入(PSIZE=03)
- 等待BSY位清零,完成一次编程。
按以上四步操作,就可以完成一次FLASH编程。不过有几点要注意:1,编程前,要确保要写入地址的FLASH已经擦除。2,要先解锁(否则不能操作FLASH_CR)。3,编程操作对OPT区域也有效,方法一模一样。
4:闪存擦除
FLASH编程的时候,要先判断所写地址是否被擦除了,STM32F4的闪存擦除分为两种
- 扇区擦除
- 整片擦除
扇区擦除步骤
- 检查FLASH_CR的LOCK是否解锁,如果没有则先解锁
- 检查FLASH_SR寄存器中的BSY 位,确保当前未执行任何FLASH操作
- 在FLASH_CR寄存器中,将SER位置1,并从主存储块的12个扇区中选择要擦除的扇区 (SNB)
- 将FLASH_CR寄存器中的STRT位置1,触发擦除操作 等待BSY位清零
整片擦除步骤
- 检查 FLASH_SR 寄存器中的 BSY 位,以确认当前未执行任何 Flash 操作
- 将 FLASH_CR 寄存器中的 MER 位置 1(STM32F405xx/07xx 和 STM32F415xx/17xx 器件)
- 将 FLASH_CR 寄存器中的 MER 和 MER1 位置 1(STM32F42xxx 和 STM32F43xxx 器件)
- 将 FLASH_CR 寄存器中的 STRT 位置 1
- 等待 BSY 位清零
5:代码实现
讲几个重要的函数
void FLASH_Unlock(void) { if((FLASH->CR & FLASH_CR_LOCK) != RESET) { /* Authorize the FLASH Registers access */ FLASH->KEYR = FLASH_KEY1; FLASH->KEYR = FLASH_KEY2; } } //解锁用的,先查询CR寄存器的位31,看是否上锁,若上锁,则再给KEYR密钥寄存器写入两个密钥值进行解锁
void FLASH_DataCacheCmd(FunctionalState NewState) { /* Check the parameters */ assert_param(IS_FUNCTIONAL_STATE(NewState)); if(NewState != DISABLE) { FLASH->ACR |= FLASH_ACR_DCEN; } else { FLASH->ACR &= (~FLASH_ACR_DCEN); } } //FLASH擦除期间,必须禁止数据缓存,操作ACR访问控制寄存器的位10
uint16_t STMFLASH_GetFlashSector(u32 addr); //根据要操作的地址,来查询处在哪个扇区
再来看看 FLASH_EraseSector扇区擦除函数中的核心部分
/* if the previous operation is completed, proceed to erase the sector */ FLASH->CR &= CR_PSIZE_MASK; FLASH->CR |= tmp_psize; FLASH->CR &= SECTOR_MASK; FLASH->CR |= FLASH_CR_SER | FLASH_Sector; FLASH->CR |= FLASH_CR_STRT; /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation(); /* if the erase operation is completed, disable the SER Bit */ FLASH->CR &= (~FLASH_CR_SER); FLASH->CR &= SECTOR_MASK;
- CR控制寄存器位8、9置0
- 根据VCC来操作CR寄存器的位8、9
- CR寄存器位3~7置0
- 操作CR寄存器位3~6来选择要擦除的扇区,并置位1,激活扇区擦除
- 操作CR寄存器的位16来触发擦除操作
- 等待操作并查询SR寄存器,查询操作的状态
- 操作完成后,清除掉一些相应位
写入函数FLASH_ProgramWord
FLASH->CR &= CR_PSIZE_MASK; FLASH->CR |= FLASH_PSIZE_WORD; FLASH->CR |= FLASH_CR_PG; *(__IO uint32_t*)Address = Data; /* Wait for last operation to be completed */ status = FLASH_WaitForLastOperation(); /* if the program operation is completed, disable the PG Bit */ FLASH->CR &= (~FLASH_CR_PG);
- CR控制寄存器位8、9置0
- 操作CR寄存器位8、9,来确定编程并行位数
- 操作CR寄存器的位0,来激活编程
- 把写入的数据放进扇区地址中
- 等待操作并查询SR寄存器,查询操作的状态
- 操作完成后,清除掉CR寄存器位0
至于读函数STMFLASH_ReadWord,那就很简单了
//读取指定地址的半字(16位数据) //faddr:读地址 //返回值:对应数据. u32 STMFLASH_ReadWord(u32 faddr) { return *(vu16*)faddr; }