本内容基于江协科技STM32视频学习之后整理而得。
文章目录
1. FLASH
1.1 简介
- STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程。(系统存储器是原厂写入的Bootloader程序,不允许我们修改的,)
- 读写FLASH的用途:
- 利用程序存储器的剩余空间来保存掉电不丢失的用户数据
- 通过在程序中编程(IAP),实现程序的自我更新
- 在线编程(In-Circuit Programming – ICP)用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序
- 在程序中编程(In-Application Programming – IAP)可以使用微控制器支持的任一种通信接口下载程序
类型 | 起始地址 | 存储器 | 用途 |
---|---|---|---|
ROM | 0x0800 0000 | 程序存储器Flash | 存储C语言编译后的程序代码和常量· |
0x1FFF F000 | 系统存储器 | 存储BootLoader,用于串口下载 | |
0x1FFF F800 | 选项字节 | 存储一些独立于程序代码的配置参数 | |
RAM | 0x2000 0000 | 运行内存SRAM | 存储运行过程中的临时变量 |
0x4000 0000 | 外设寄存器 | 存储各个外设的配置参数 | |
0xE000 0000 | 内核外设寄存器 | 存储内核各个外设的配置参数 |
1.2 闪存模块组织
- 闪存存储器接口寄存器并不属于闪存,就是一个普通的外设,存储介质也都是RAM。闪存存储器接口寄存器是上面的(主存储器和信息块)的管理员,擦除和编程就是通过读写这些寄存器完成的。
- 主存储器和信息块是真正的闪存。主存储器就是程序存储器,启动程序代码就是系统存储器。用户选择字节就是选项字节,系统存储器和选项字节又可合称为信息块。
- 擦除和写保护都是以页为单位的。
- 写入前必须擦除,擦除必须以最小单位进行,擦除后数据位全变为1,数据只能1写0,不能0写1。擦除和写入之后都需要等待忙。
- 地址只要以000、400、800、C00结尾的,都一定是页的起始地址。
- 系统存储器容量是2K。
- 选项字节容量是16K。
1.3 FLASH基本结构
选项字节里面有很大一部分配置位,其实是配置主程序存储器的读写保护的。
1.4 FLASH解锁
-
FPEC(闪存编程和擦除控制器)共有三个键值:
RDPRT键 = 0x000000A5(解除读保护的密钥)
KEY1 = 0x45670123
KEY2 = 0xCDEF89AB -
解锁:
复位后,FPEC被保护,不能写入FLASH_CR
在FLASH_KEYR先写入KEY1,再写入KEY2,解锁
错误的操作序列会在下次复位前锁死FPEC和FLASH_CR -
加锁:
设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR
1.5 使用指针访问存储器
-
使用指针读指定地址下的存储器:(不需要解锁)
uint16_t Data = *((__IO uint16_t *)(0x08000000));
首先使用指针读指定地址0x08000000下的存储器,要读取该地址下的数据就把这个地址写在这。在该地址前加上强制类型转换(__IO uint16_t *),转换成了uint16_t的指针类型,__IO 是宏定义,对应C语言的关键字volatile(易变的数据),加上该关键字是防止编译器优化。 -
使用指针写指定地址下的存储器:(需要提前解锁,还需要加上程序存储器编程的流程,但如果是写入SRAM的,可以直接写入,因为SRAM是可读可写的。)
*((__IO uint16_t *)(0x08000000)) = 0x1234;
-
其中:
#define __IO volatile
1.6 程序存储器编程–写入
- 字:32位;半字:16位;字节:8位
- 擦除之后,就可以执行写入的流程了。STM32的闪存在写入之前会检查指定地址有没有擦除,如果没有擦除就写入,STM32则不执行写入操作,除非写入的全是0,这一个数据是例外,因为不擦除就写入,可能会写入错误,但全写入0的话,写入肯定是没问题的,
- 第一步也是解锁,第二步要置控制寄存器的PG位=1,表示要写入数据。然后就是在指定的地址写入半字,这时需要用到“使用指针写指定地址下的存储器*((__IO uint16_t *)(0x08000000)) = 0x1234;“。写入操作只能以半字的形式写入,写入半字之后,芯片会处于忙状态,等待一下BSY清0,这样写入数据的过程就完成了。
1.7 程序存储器页擦除
PER=1是页擦除,
STRT=1是触发条件,存储器开始干活,PER=1表明接下来要的活是页擦除,由于闪存不止一页,页擦除,芯片就要知道具体要擦哪一页,所以它要继续看AR寄存器的数据,AR寄存器要提前写入一个页起始地址,这样芯片就会把我们指定的一页给擦除掉。擦除开始之后,也要等待BSY位。
1.8 程序存储器全擦除
解锁过程:在FLASH_KEYR先写入KEY1,再写入KEY2,解锁。解锁后,首先置控制寄存器里的MER(Mass Erase)位为1,然后再置STRT(Start)位为1,STRT=1是触发条件,存储器开始干活,MER=1表明接下来要的活是全擦除,这样内部电路就会自动执行全擦除的过程。擦除也是需要花一段时间的,所以擦除过程开始后,程序要执行等待,判断状态寄存器的BSY位是否为1,BSY位表示芯片是否处于忙状态,BSY位=1表示忙。如果BSY为1,就跳转回来,继续循环判断,直到BSY=0,跳出循环。最后读出并验证所有页的数据是测试程序才要做的。正常情况下,全擦除完成了,默认就是成功了。
1.9 选项字节
- RDP:写入RDPRT键(0x000000A5)后解除读保护
- USER:配置硬件看门狗和进入停机/待机模式是否产生复位
- Data0/1:用户可自定义使用
- WRP0/1/2/3:配置写保护,每一个位对应保护4个存储页(中容量)。4个字节总共32位,一位保护4页,总共保护32*4=128页
带n的是写入反码。意思是当你在写入RDP数据时,要同时在nRDP写入数据的反码。
1.10 选项字节编程
- 检查FLASH_SR的BSY位,以确认没有其他正在进行的编程操作
- 解锁FLASH_CR的OPTWRE位
- 设置FLASH_CR的OPTPG位为1(即将写入选项字节)
- 写入要编程的半字到指定的地址(指针写入操作)
- 等待BSY位变为0
- 读出写入的地址并验证数据
1.11 选项字节擦除
- 检查FLASH_SR的BSY位,以确认没有其他正在进行的闪存操作(第一步也是解锁,这一步相当于事前等待)
- 解锁FLASH_CR的OPTWRE位(选项字节的解锁)(解锁闪存后还要再解锁选项字节,之后才能操作选项字节)(解锁选项字节,时再OPTKEYR里先写入KEY1,再写入KEY2)
- 设置FLASH_CR的OPTER位为1(即将擦除选项字节)
- 设置FLASH_CR的STRT位为1(触发芯片,开始干活,这样芯片就会启动擦除选项字节的工作)
- 等待BSY位变为0
- 读出被擦除的选择字节并做验证
1.12 器件电子签名(ID号)
-
电子签名存放在闪存存储器模块的系统存储区域,包含的芯片识别信息在出厂时编写,不可更改,使用指针读指定地址下的存储器可获取电子签名(STM32的ID号)
-
闪存容量寄存器:
基地址:0x1FFF F7E0
大小:16位 -
产品唯一身份标识寄存器:
基地址: 0x1FFF F7E8
大小:96位
1.13 FLASH库函数
// 解锁
void FLASH_Unlock(void);
// 加锁
void FLASH_Lock(void);
// 擦除某一页
FLASH_Status FLASH_ErasePage(uint32_t Page_Address);
// 全擦除
FLASH_Status FLASH_EraseAllPages(void);
// 擦除选项字节
FLASH_Status FLASH_EraseOptionBytes(void);
// 指定地址写入字
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
// 指定地址写入半字
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);
// 选项字节的写入,自定义的Data0和Data1
FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);
// 选项字节写保护
FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages);
// 选项字节读保护
FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState);
// 选项字节用户选项的三个配置位
FLASH_Status FLASH_UserOptionByteConfig(uint16_t OB_IWDG, uint16_t OB_STOP, uint16_t OB_STDBY);
// 获取选项字节当前的状态,获取用户选项的状态位
uint32_t FLASH_GetUserOptionByte(void);
// 获取写保护状态
uint32_t FLASH_GetWriteProtectionOptionByte(void);
// 获取读保护状态
FlagStatus FLASH_GetReadOutProtectionStatus(void);
// 获取预取缓冲区状态
FlagStatus FLASH_GetPrefetchBufferStatus(void);
// 中断使能
void FLASH_ITConfig(uint32_t FLASH_IT, FunctionalState NewState);
// 获取标志位
FlagStatus FLASH_GetFlagStatus(uint32_t FLASH_FLAG);
// 清除标志位
void FLASH_ClearFlag(uint32_t FLASH_FLAG);
// 获取状态
FLASH_Status FLASH_GetStatus(void);
// 等待上一次操作,等待忙,等待BSY为0
FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);