文章目录
1. FLASH简介
STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程。
读写FLASH的用途:
- 利用程序存储器的剩余空间来保存掉电不丢失的用户数据
通过在程序中编程(IAP),实现程序的自我更新
- 在线编程(In-Circuit Programming – ICP)用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序。
- 在程序中编程(In-Application Programming – IAP)可以使用微控制器支持的任一种通信接口下载程序。
2. 闪存模块组织
C8T6的主存储器从1到63,一共64页
主存储器(程序存储器)
功能和结构:
- 用于存放程序代码,是存储器中最主要和容量最大的一部分。
- 进行了分页管理,擦除和写保护都是以页为单位进行的。
操作特点:
- 写入前必须进行擦除操作,且擦除必须以最小单位进行。
- 擦除后数据位全变为1,数据只能从1写为0,不能从0写为1。
- 每次擦除和写入操作后,都需要等待忙(等待操作完成)。
信息块:
- 启动程序代码(系统存储器):存放原厂写入的Bootloader,用于串口下载。
- 用户选择字节(选项字节):存放一些独立的参数。
- 闪存存储器接口存储器(闪存管理员):类似GPIO等的外设,地址为0x40000000,不属于闪存,存储介质是SRAM,用于控制擦除和编程过程。
页的起始地址:
- 程序存储器的起始地址为0x08000000,之后是一个字节一个地址,依次线性分配。
- 每页起始地址的规律是地址以000、400、800、C00结尾,这些地址一定是页的起始地址。
3. FLASH基本结构
4. FLASH解锁
FPEC共有三个键值:
- RDPRT键 = 0x000000A5
- KEY1 = 0x45670123
- KEY2 = 0xCDEF89AB
解锁:
- 复位后,FPEC被保护,不能写入FLASH_CR
- 在FLASH_KEYR先写入KEY1,再写入KEY2,解锁
- 错误的操作序列会在下次复位前锁死FPEC和FLASH_CR
解锁方式:通过在键寄存器写入指定的键值
FPEC共有三个键值:
- RDPRT键:用于解除读保护的密钥。
- KEY1和KEY2:解锁时需要的两个键值,这两个键值可以随便定义。
默认状态:复位后的FLASH默认是锁定的。
解锁过程:
- 写入KEY1键值到KEYR寄存器,然后再写入KEY2键值,只有按顺序写入这两个键值,才能成功解锁。
注意事项:
- 如果程序尝试错误操作解锁,一旦没有按顺序写入KEY1和KEY2,整个模块将被完全锁死,除非复位。
加锁:设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR
加锁方式:控制寄存器中LOCK位置1,用于锁定模块。
3. 使用指针访问储存器
使用指针读指定地址下的存储器:
uint16_t Data = *((__IO uint16_t *)(0x08000000));
使用指针写指定地址下的存储器:
*((__IO uint16_t *)(0x08000000)) = 0x1234;
其中:
#define __IO volatile
因为STM32内部的存储器是直接挂在总线上的,直接使用C语言的指针,来访问即可
使用指针写指定地址下的存储器
1. 先确定地址,再强制转换为指针,最后通过指针取内容:
- 这将完成指定地址的写操作。
2. 直接对指定地址赋值:
*(__IO uint16_t *)(0x08000000) = 0x1234;
- 将值
0x1234
写入到指定地址0x08000000
。
写操作注意事项:
- 写入的数据一般是存储在闪存中的,闪存是只读的,不能轻易更改。因此,需要先解锁并进行存储器编程设置。
如果指定的地址是 SRAM 的地址(例如 0x20000000
),可以直接写入:
- 因为 SRAM 在程序运行时是可读可写的。
通过上述步骤,可以使用指针完成对指定地址存储器的写操作。注意,针对不同存储器类型(如闪存和 SRAM),写操作的步骤和要求可能有所不同。
4. 程序储存器编程(写入)
写入流程
-
检查擦除状态:
- 在 STM32 的闪存写入之前,会检查指定地址是否已经擦除。如果地址未擦除且尝试写入非全零数据,STM32 将不执行写入操作。只有在写入全零数据时,即使地址未擦除,写入操作也能进行,因为写入全零不会导致错误。
-
解锁闪存:
- 首先需要解锁闪存,解除写保护。
-
置位 PG 位:
- 设定控制寄存器的 PG(编程)位为 1,以表示即将进行数据写入。
-
写入数据:
- 在指定地址写入半字(16 位数据),代码:
*(__IO uint16_t *)(0x08000000) = 0x1234;
写入数据触发开始写入操作。写入半字后,芯片进入忙状态(BSY 位为 1)。需要等待 BSY 位清零,表示写入完成。
优化读写闪存的方法
- 如果需要频繁读写闪存,最好的方法是先将闪存的一页读到 SRAM 中,进行读写操作后,再擦除闪存页,最后将修改后的数据整体写回闪存。这种方法可以使闪存的读写操作更加灵活,类似于对 SRAM 的读写操作。
5. 程序储存器页擦除
-
检查 LOCK 位:
- 读取 LOCK 位以检查芯片是否上锁。
- 如果 LOCK=1,表示芯片上锁,需要执行解锁过程。在 KEYR 寄存器中,先写入 KEY1,再写入 KEY2。
- 如果 LOCK=0,则芯片未上锁,无需解锁。
-
设置 PER 位:
- 置控制寄存器的 PER(Page Erase)位为 1,表示即将执行页擦除操作。
-
设置 AR 寄存器:
- 在地址寄存器(AR)中选择要擦除的页。提前将要擦除页的起始地址写入 AR 寄存器。
-
触发擦除操作:
- 置控制寄存器的 STRT 位为 1,触发擦除操作。STRT 为 1 时,芯片开始执行擦除操作。
- 芯片会检查 PER=1,知道要执行页擦除,然后继续查看 AR 寄存器中的页起始地址以确定要擦除的具体页。
-
等待 BSY 位清零:
- 擦除开始后,等待芯片的忙状态(BSY)位清零,表示擦除操作完成。
-
读出并验证数据(可选):
- 最后,可以读出并验证数据以确保擦除成功。对于测试程序来说,通常不需要进行此操作,因为工作量较大。
6. 程序储存器全擦除
检查 LOCK 位:
- 读取 LOCK 位以检查芯片是否上锁。
- 如果 LOCK=1,表示芯片上锁,需要执行解锁过程。在 KEYR 寄存器中,先写入 KEY1,再写入 KEY2。
- 如果 LOCK=0,则芯片未上锁,无需解锁。
设置 MER 位:
- 解锁后,置控制寄存器中的 MER(Mass Erase)位为 1,表示即将执行全片擦除操作。
触发擦除操作:
- 置控制寄存器的 STRT(Start)位为 1,触发擦除操作。STRT 为 1 后,芯片开始执行擦除操作。
- 芯片检查到 MER 位为 1,就知道要执行全片擦除,内部电路自动开始全片擦除过程。
等待擦除完成:
- 擦除需要时间。擦除开始后,程序需执行等待,判断状态寄存器的 BSY(Busy)位是否为 1。
- BSY 位为 1 表示芯片处于忙状态,等待 BSY 位清零表示擦除操作完成。
7. 选项字节
RDP:写入RDPRT键(0x000000A5)后解除读保护
USER:配置硬件看门狗和进入停机/待机模式是否产生复位
Data0/1:用户可自定义使用
WRP0/1/2/3:配置写保护,每一个位对应保护4个存储页(中容量)
nRDP 和 RDP 的意义在于,当你写入 RDP 数据时,需要同时在 nRDP 写入该数据的反码。其目的是确保数据写入的有效性和安全性。如果芯片检测到这两个存储器中的数据不是反码的关系,说明数据无效且有错误,对应功能将不会执行。这是一种保障措施。写入反码的过程由硬件自动计算并写入。
RDP(Read Protect,读取保护):
- 这是读保护配置位。
- 如果 RDP 不为 A5,闪存将处于读取保护状态,无法通过调试器读取程序,从而避免被盗取。
WRP0/1/2/3:
- 这四个字节(共32位)对应每一位的保护页。
- 每一位保护 4 页,总共保护 32*4=128 页。
- 这样正好对应中密度容量的最大 128 页。
8. 选项字节编程
- 检查FLASH_SR的BSY位,以确认没有其他正在进行的编程操作
- 解锁FLASH_CR的OPTWRE位
- 设置FLASH_CR的OPTPG位(Option Programming)为1 -> 表示即将写入选项字节
- 写入要编程的半字到指定的地址 -> 指针写入操作
- 等待BSY位变为0
- 读出写入的地址并验证数据
9. 选项字节擦除
- 解锁闪存
- 检查FLASH_SR的BSY位,以确认没有其他正在进行的闪存操作(事前等待)
- 解锁FLASH_CR的OPTWRE位(Option Write Enable) -> 选项字节的解锁(选项字节有单 独的锁) -> 在FLASH_OPTKETR里先写入KEY1,再写入KEY2。
- 设置FLASH_CR的OPTER位(Option Erase)为1 -> 表示即将要擦除选项字节
- 设置FLASH_CR的STRT位为1 -> 触发芯片,开始干活,这样芯片就会启动擦除选项字 节的工作
- 等待BSY位变为0
- 读出被擦除的选择字节并做验证
10. 器件电子签名
电子签名存放在闪存存储器模块的系统存储区域,包含的芯片识别信息在出厂时编写,不可更改,使用指针读指定地址下的存储器可获取电子签名(STM32的ID号)。
闪存容量寄存器:
- 基地址:0x1FFF F7E0
- 大小:16位
产品唯一身份标识寄存器:
- 基地址: 0x1FFF F7E8
- 大小:96位
可以使用唯一ID号进行加密操作,写入一段程序在指定设备运行 -> 在程序中加入ID号判断.
11. 代码示例
STM32读写内部FLASH