STM32外设-Flash闪存-(学习笔记)

上次我们学习完了窗口看门狗,接下来学习Flash闪存。

一.Flash简介

1.简介

我们可以对照DMA中的存储器映像来了解一下。ROM是掉电不丢失,RAM是掉电丢失。

我们本节的任务就是对第一条的这些存储器进行读写。我们需要用到闪存管理器接口这个外设。

第二条可以通过程本身来修改自己,实现程序的更新。这个在程序中编程就是IAP,和OTA类似,相当于远程升级。

第三条我们用的STLINK就是SWD,他和系统加载程序Bootloader是一样的,会把程序都给覆盖掉。

第四条就是高级一点,在程序中编程。怎么实现呢,我们首先需要自己写一个Bootloader程序。并且存放在程序更新时,不会覆盖的地方。需要更新的话,我们控制程序跳到自己写的bootloader里面来。在这里面我们就可以接收任意口穿过来的数据了。比如串口,USB,蓝牙转串口,wifi转串口。这个传过来的数据就是我们待更新的程序。我们把收到的数据,写在前面,程序正常运行的地方,写完之后,再把程序跳转到正常运行的地方。

这样通过wifi转串口就可以实现无线下载程序。

2.闪存分频表

这是我们中容量产品的闪存分配表。

我们需要打开这个进行查找。

第一个是:主存储器也就是我们说的程序存储器,用来存放程序代码的。这是最主要也是容量最大的一块。

第二个是:信息块,启动程序代码,就是刚刚说的系统存储器,存放的是原厂写入的bootloader用于窜口下载。用户选择字节就是选项字节。存放一些独立的参数,这个选项字节,在手册里一般都称作选择字节。

第三个是:闪存接口寄存器,实际上这里并不属于闪存,我们看他的地址就可以知道。地址都是40开头的,说明闪存存储器接口寄存器,就是一个普通的外设。和之前的GPIO,USART,TIM等等,都是一个性质的东西。这些存储器他们的存储介质,也都是SRAM。这个闪存存储器接口,就是上面那些闪存的管理员,这些寄存器就是用来控制擦除和编程这个过程的。

主存+信息块组成真正的闪存。启动程序代码就是系统寄存器,然后用户选择字节就是选项字节,他们两个一起组成信息块。

还有就是,只要地址以000,400,800,c00,结尾的基本都是页的起始地址。Flash擦除只能按单元擦除,擦除完变成0,然后只可以由1变为0,不可以由0变为1。

系统存储器他的起始地址是:0x1FFF F000,容量是2K。这些介绍过。选项字节起始地址是:0x1FFF F800,容量是16个字节。

这里还可以发现,我们平时说的芯片闪存容量是64K,128K它只得只是主存储器的容量。

最后是闪存接口寄存器,里面包括KEY键寄存器,SR状态寄存器,CR控制寄存器等等。外设的起始地址是0x4002 2000。每个寄存器都是32位,也就是四字节。

3.基本结构图

闪存存储区接口,还有另一个名字,在手册中叫:闪存擦除和编程控制器(FPEC)。

二.Flash擦除和编程

1.解锁Flash

我们接下来学习如何操作控制器(FPEC)来对程序存储器和选项字节进行擦除和编程。

这和之前W25Q64一样,W25Q64在写入之前需要写使能,然后Flash操作之前需要解锁。这里解锁的方式和独立看门狗一样,都是通过在键寄存器写入指定键值来解锁。使用键寄存器的好处就是更能防止误操作。每一个指令必须输入密码才可以完成。

首先FPEC,共有三个键值,RDPRT键是解除读保护的密钥,值是0xA5。

然后接着看,复位后Flash的值是锁着的,我们需要先用KEY1和KEY2这两个钥匙都输入正确,才能解锁,即使你程序跑飞了,歪打正着正好写入KEY1,也难以保证下一次歪打正着写入了KEY2。

加锁的话很简单,我们只需要在FLASH_CR中的LOCK位写1就可以加锁了。

2.使用指针访问存储器

因为STM32内部的存储器是挂在总线上的。所以这时读取某个存储器就非常简单了。

第一步我们需要给定要读取的地址,这里以0x0800 0000为例,第二步,在地址前面加上强制类型转换。这里把这个变量强制转换为了Uint16_t的指针类型。想以几位的数据读取出地址,就要用uint几_t。这个指针类型前面还加入了一个__IO,在stm32库函数中这是一个宏定义。这个宏定义对应下面C语言的关键字 volatile,这是易变的数据的意思,在这个数据前面加上volatile是一个安全保障措施。加这个关键字的目的,用一句话来说就是防止变量被编译器优化。

首先说一下,keil编译器默认情况下是最低优化等级。编译器优化用途就是去除繁杂无用的代码。降低代码空间。提升运行效率。但是有时候,编译器会优化掉比如说你定义了一个空循环的延时函数,编译器就会认为,你这个空循环没有用到,就会给你优化掉。这时就可以加入volatile这个函数,来告诉编译器无论如何,都不要优化掉这个函数。另外编译器还会利用缓存来加速代码。

总结一下就是,当你开启了编译器优化的时候,在无意义加减变量多线程更改变量,读写与硬件相关的存储器时,都需要加入volatile防止被编译器优化。

所以说这一部分是一个指针变量,已经指向了0x0800 0000这个地址。

然后就是加一个解引用,把指针指向的这个地址里面的内容给读取出来。

其实UP主这里讲的不太严谨,0x8000 0000不是地址,只是一个普通的十六进制数,强转为uint16_t*也就是指针变量时才变为了地址,整体扩住后再加一个*也就是解引用才是在对应的寄存器地址取值。

取出来之后我们可以把这个值存放在自定义的变量Data中,这样子就把这个寄存器的值取出完成了就完成了指定地址读的任务。对于闪存的读取来说,是不需要解锁的,因为只是看看存储器,不对存储器进行更改。

接着就是指定地址写,和上面一样,先给定十六进制(这个也就是寄存器地址的映射)然后强制转换为指针变量,此时他就是寄存器的地址(因为通过映射,寄存器地址的值和这个十六进制一样),之后加一个volatile关键字防止变量被优化,然后加一个*(解引用),找到通过这个地址指向的寄存器的值,然后进行修改。

需要注意的是,因为这个语句是写入数据,并且指定的是闪存的地址,闪存在程序运行时,是只读的,不能轻易更改。而我们需要对闪存进行更改。这个所需的权限就比较高。需要提前解锁,并且还需要后面的操作流程。如果这里写的是SRAM的地址,那就可以直接写入了。

3.程序存储器全擦除

第一步就是读取lock位看看芯片锁没锁,如果锁住了就执行解锁过程就是在KEYR寄存器先写入KEY1再写入KEY2。库函数是直接执行解锁过程,无需在意锁没锁,省去了判断步骤。解锁完成后,首先置控制寄存器CR里面的MER=1,STRT=1。其中STAR=1是触发条件。STAR=1后芯片开始干活。然后芯片看到MER位是1,他就知道接下来要干的活就是全擦除。这样内部电路就会接收全擦除的过程。然后继续,擦除也是需要一定时间的,执行擦除后,芯片要进行等待,看看BSY位是否为1。BSY位表示芯片是否处于忙状态,为1的话表示芯片正在忙。之后BSY为0时进行擦除门之后就是读出并验证所有页的数据。

4.程序存储器页擦除

STM32的闪存也是写入之前必须擦除的。擦除之后所有的数据位变为1。擦除的最小单位就是一页1K,1024字节。页擦除和全擦除差不多,上面的还是解锁,进入擦除,等待,不过中间的置控制寄存器CR的PER位为1,然后再AR寄存器中选择要擦除的页。最后置控制寄存器的STRT位为1。置STRT位为1也是触发条件。STRT为1芯片开始干活,然后看到PER为1就知道要执行页擦除。然后闪存不止一页,芯片还要知道,要擦除哪一页。所以他会继续看AR寄存器的数据。AR寄存器我们需要提前写入一个页起始地址。这样芯片就会把指定的一页给擦除掉。之后就是等待BSY位,然后读出验证数据。

5.程序存储器写入

擦除之后我们就可以执行写入的操作了,STM32的闪存会在写入之前检查指定地址是否是擦除。如果没有擦除就写入,STM32则不执行写入。写入之前也是执行解锁。然后第二步,我们需要置控制寄存器的PG位为1。表示我们即将写入数据。之后第三步就在指定的地址接入半字。

这一步我们需要用到这个代码。使用指针在指定地址写入数据。另外注意的是,写入操作只能以半字的形式写入,在stm32中有几个术语,字,半字,字节。

其中字word就是32位数据,半字Halfword就是16位数据,字节Byte就是8位数据。这里就是以半字进行写入就是一次性写入两个字节。如果要写入32位就分两次完成。

然后写入数据的代码就是下一步的开始条件不需要像擦除那样,置STRT位了。写入半字之后芯片会进入忙状态,我们的待BSY请0,这样就写入完成了。

三.选项字节

1.选项字节了解

这一块了解即可。

图里的起始地址,就是我们选项字节的起始地址。

这些数据在表格的这里。把这个16个字节展开就是上面的图。

之后这里面有一半的名称前面都带了一个n,这个意思就是你在写入RDP数据时要同时在nDRP写入数据的反码。其他的都是一个道理。这样写入操作才是有效的,如果芯片检测到这两个存储器不是反码关系,那么就代表数据无效,有错误。对应的功能就不执行。这是一个安全保护措施。当然写入反码的过程硬件会自动计算并写入。函数也是封装好了。

我们去掉带n的一共剩下八个寄存器了。RDP读保护配置位。第四个是每一位保护四个存储页,四个字节一共32位。1位保护4页,那么就是32*4=128页。

对于小容量的话,这里也有解释。

大容量的话同理。

2.选项字节擦除

因为选项字节也属闪存的范围,所以也需要擦除才能写入。

第一步其实也是解锁闪存,只是没有写,之后就是事前等待,确保闪存没在忙。

第二步是选项字节的解锁,选项字节是一个单独的锁。在解锁闪存后,还需要解锁选项字节的锁。

这是选项字节的锁。它上面的就是闪存的锁。解锁这个小锁也是这个流程,我们需要在OPTKERY里先写入KEY1在写入KEY2。之和和擦除闪存类似,先OPTER为1表示即将擦除选项字节,然后STRT为1,开始擦除,之后等待BSY为0,擦除选项字节就完成了。

3.选项字节写入

和普通的闪存差不多,先解除BSY在写入小锁。之后设置CR的OPTPG位为1,表示我要开始写入选项字节。在之后写入要编程的半字的地址。这个是指针写入操作。最后等待忙就写入完成了。

四.器件的电子签名

电子签名实际上就是STM32的ID号他的存放区域是系统存储器。就是这里,它不仅有bootloader程序,还有几个字节的ID号。系统存储器起始地址是0x1FFF F000。

使用这个唯一的ID号可以进行加密的数据,比如说你写一段程序,然后只能在指定设备运行。那就可以在程序的多处,加入ID号判断,如果不是指定设备的ID号,就不执行程序功能。这样即使你的程序被盗,在别的设备上也难以运行。

这就是STM32的电子签名。

五.手册观看

1.闪存编程手册

这里是IAP和ICP的介绍。

接着是术语介绍,都是什么意思。

这是一个重点,你要想编程,首先需要知道闪存是怎么分配的。

之后是一些说明文字。

然后是涉及内核和CPU的一些内容。

接着就是闪存编程擦除控制器FPEC。

还有就是小重点,任何读写闪存操作都会使CPU暂停。这其实是读写内部闪存的一个弊端。就是闪存忙的时候,代码会暂停。因为读写代码需要读闪存,闪存在忙没法读就会导致CPU暂停,这样会导致假如你使用内部闪存读取数据,然后CPU又在频繁的中断。这样读写闪存时候,中断代码就无法执行了。

之后就是选项字节。

需要注意的是这里的位都使用的是反逻辑,1表示无效,0表示有效。因为闪存擦除之后都是1。所以1会作为默认情况,比如说这里的1就是不实施写保护,0就是实施写保护。

2.寄存器说明

闪存访问控制寄存器。

FPEC键寄存器。

状态寄存器表示工作状态。

控制寄存器,用于控制电路运行。

主要了解这些。

地址寄存器。

选项字节寄存器。这些寄存器会把选项字节的内容加载进来,里面的内容和选项字节是对应得。

寄存器总表,这就是闪存的主要内容。

3.器件电子签名

器件的电子签名。

之后是寄存器。

可以作为期间号,作为密码。用来激活带安全机制的自举功能。这96位可以以字节(8位)方式读取,也可以用半字(16位)方式读取,或者全字(32位)方式读取。

下面就是所有位的内容了,就是ID号的一段数据。

到这里全部的内容就结束了。

相关推荐
周洲08304 分钟前
【无标题】STM32 串口 Printf 重定向|标准库 + HAL库 两套极简通用代码(无需MicroLIB)
stm32·单片机·嵌入式硬件
深圳市恒锐丰科技杨生18 分钟前
EG27324 2.8‑20V 双通道低侧栅极驱动芯片|带 SD 逐周关断外置 MOS 驱动方案
嵌入式硬件·硬件工程
冬夜戏雪32 分钟前
学习日记-LRU缓存成员变量,入参day1
学习
深念Y36 分钟前
Windows 11 工具链部署与 SSH 踩坑笔记
windows·笔记·ssh
Sunshing1536 分钟前
非自治连续系统稳定性定义与定理判据
笔记·学习
Brilliantwxx37 分钟前
【STM32】初步了解GPIO 从引脚到工作模式的完整拆解
单片机·嵌入式硬件
苏灵凯39 分钟前
Codex 官网前端可以抄吗?从设计到实现的技术拆解
笔记·ai·agent·codex·deepseek
andrsted1 小时前
老师可以用小程序在线整理题库、课堂检测和错题集
学习·微信小程序·小程序·学习方法
zhangrelay2 小时前
手机-笔电-等数码产品-电池健康的一些数据-2026
笔记·学习·锂电池
苏子寒3 小时前
Nano-VLLM全代码解析笔记(10)-GemmaRMSNorm和MRoPE
笔记·机器学习·ai·nlp·vllm