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号的一段数据。

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

相关推荐
断剑zou天涯3 小时前
【算法笔记】Manacher算法
java·笔记·算法
QiZhang | UESTC3 小时前
学习日记day45
学习
菜鸟‍3 小时前
【论文学习】通过编辑习得分数函数实现扩散模型中的图像隐藏
人工智能·学习·机器学习
知识分享小能手3 小时前
CentOS Stream 9入门学习教程,从入门到精通,CentOS Stream 9 配置网络功能 —语法详解与实战案例(10)
网络·学习·centos
瑶光守护者4 小时前
【学习笔记】5G RedCap:智能回落5G NR驻留的接入策略
笔记·学习·5g
你想知道什么?4 小时前
Python基础篇(上) 学习笔记
笔记·python·学习
SHOJYS4 小时前
学习离线处理 [CSP-J 2022 山东] 部署
数据结构·c++·学习·算法
SystickInt4 小时前
32 DMA实现ROM与RAM通信
stm32·单片机·嵌入式硬件
weixin_409383124 小时前
简单四方向a*学习记录4 能初步实现从角色到目的地寻路
学习·a星
xian_wwq5 小时前
【学习笔记】可信数据空间的工程实现
笔记·学习