stm32f103c8t6学习笔记(学习B站up江科大自化协)-FLASH闪存

发布于:2024-05-09 ⋅ 阅读:(28) ⋅ 点赞:(0)

FLASH简介

        ·闪存的操作较为麻烦,涉及擦除、编程、等待忙、解锁等等操作,需要把指令和数据写入到外设的相应寄存器,外设会自动去操作对应的存储空间。

        ·外设可以对程序存储器和选项字节进行擦除和编程,不能对系统存储器进行操作,因为系统存储器是原厂写入的bootloader程序,不允许修改

        ·对于C8T6程序存储容量是64K,一般写一个程序只占前边很小一部分空间,剩下的大部分空间可以利用,比如存储一些自定义的数据,充分利用资源,但是需要注意不能覆盖原有程序,否则将程序破坏,无法运行。存储少量参数一般选最后几页就行了

        ·在程序中编程:首先需要自己写一个bootloader程序,存放在程序更新时不会覆盖的地方,比如放在存储区域靠后的部分,需要更新程序时,控制程序跳转到我们自己写的bootloader里,这里边可以接受任意通信接口传过来的数据,如串口、蓝牙转串口、USB、WIFi转串口等等,传过来的数据就是待更新的程序,然后控制FLASH读写,把收到的程序写入到存储器前边程序正常运行的地方,写完之后再控制程序跳转到正常运行的地方,或直接复位,完成程序的自我升级。

        实现自我升级的过程和系统的bootloader一样,由于程序要实现自我升级,需要布置一个辅助的小机器人来临时干活,不过系统存储器的bootloader写死了,只能用串口下载到指定位置,启动方式也不方便,只能配置BOOT引脚触发启动,如果自己写bootloader的话就可以自定义收的方式和写的位置以及启动的方式,整个升级过程程序都可以自主完成,实现在程序中编程,更进一步可以实现远程升级

        ·小容量产品,页数较少,只有32页,每页1k;中容量产品128页每页1k;大容量产品256页每页2k

        ·分为三个块,

        第一个是主存储器,也就是程序存储器,用于存放程序代码,是最主要也是容量最大的一块

        第二个是信息块,里边可以分为启动程序代码和用户选择字节,其中启动程序代码就是系统存储器,用于存放原厂写入的bootloader,用于串口下载;用户选择字节是是选项字节,存放独立的参数,在手册中称为选择字节(Option Bytes),是同一个东西

        第三块是闪存存储器接口寄存器,这一块的寄存器并不属于闪存,因为地址都是40开头的,说明是一个普通的外设,和GPIO、定时器、串口等属于一个性质的东西,存储介质都是SRAM。闪存存储器接口属于上边的管理员,用于控制擦除和编程这个过程的

        ·读取指定存储器直接使用指针即可,用不到外设

        ·对主存储器进行分页是为了更好的管理闪存,擦除和写保护都是以页为单位的

        ·闪存的特性类似,写入前必须擦除,擦除必须以最小单位进行,擦除后数据位全变为1,数据只能1写0不能0写1,擦除和写入之后都要等待忙

        ·看地址范围可以发现,只要地址以000、400、800、C00结尾的一定是页的起始地址

        ·平时说的芯片闪存容量是64K、128K指的是主存储器的容量,下边信息块的容量虽然也是闪存但是不统计在这个容量里

FLASH基本结构

        ·整个闪存分为程序存储器、系统存储器和选项字节三个部分

        ·“闪存存储器接口“ 和 “闪存编程和擦除控制器(FPEC)实际上是同一个东西”,别名而已。闪存存储器接口相当于闪存的管理员,可以对程序存储器进行擦除和编程,也可以对选项字节进行擦除和编程,系统存储器不可以

        ·选项字节很大一部分配置位是配置主程序存储器读写保护的,所以可以配置读写保护

操作控制器FPEC对程序存储器和选项字节进行擦除和编程

一、FLASH解锁

         ·操作和W25Q64类似,W25Q64操作之前需要写使能,FLASH操作之前也需要进行解锁,目的是为了防止误操作。解锁的方式和独立看门狗一样,在键寄存器写入指定的键值实现,好处是防止误操作,每一个指令必须输入密码才能完成

        ·RDPRD键是解除保护的密钥,值是0xa5,三个键值是是随意定义的,只要定义的不是很简单,不会随意解锁即可

        复位之后FLASH默认上锁,在FLASH_KER寄存器中,先写入KEY1再写入KEY2解锁,安全性较高,如果没按顺序正确写入密码,整个模块会完全锁死,除非复位

        控制寄存器中有个lock位,在这一位写1就可以重新锁住闪存了

二、使用指针访问存储器

        ·STM32内部的存储器是直接挂载在总线上的,这时读写某个寄存器只需要使用C语言的指针即可。

使用指针读指定地址下的寄存器

        ·第一步给定读取存储器的地址,*((__IO uint16_t *)(0x08000000))在这里的括号里边由于只有一个数,所以可以不加括号;但是如果要对地址进行加减,必须加上括号,并在括号里面进行加减,否则运算的优先级会有问题

        ·第二步在地址前边加上强制类型转换,这里将变量强制转换为了uint16_t的指针类型,如果想以16位的方式读出指定地址的数据,就转换为uint16_t*,如果想以8位读出就uint8_t*,想浮点类型就float*或double*。

        在指针类型前边加了__IO,在STM32中这时一个宏定义,#define __IO volatile。对应c语言的关键字volatile,volatile直译就是易变的数据,加上这个是一种安全保障措施,在程序逻辑上没有作用,只是用于防止编译器优化。keil编译器默认情况下是最低优化等级,加不加volatile都不影响,如果提高编译优化等级就会产生问题。

        编译优化的作用是取出无用的繁杂代码,降低代码空间,提升运行效率,但是优化之后在某些地方编译器会弄巧成拙,比如想使用变量计数空循环的方式实现延时函数,编译器优化的时候会觉得这段代码没用会直接优化掉,这时可以在前边加上volatile告诉编译器不进行优化,原封不动的执行。

        编译器会利用缓存加速代码,比如要频繁读写内存的某个变量,最常见的优化方式是先把变量移到高速缓存里,在STM32里有一个类似缓存的工作组寄存器,这些寄存器的访问速度最快,先把变量放在缓存里,需要读写的时候直接读写缓存即可,用完之后再写回内存,这是优化的方案。但是如果程序存在多个线程,比如中断函数,在中断函数里改变了原始变量,但是缓存并不知道已经更改了,下次程序还看缓存中的变量,会造成数据更改不同步的问题,解决方法也是在变量定义前边加上一个volatile,告诉编译器运行的时候要从内存里找,不使用缓存进行优化

        如果开起了编译器优化,在无意义加减变量,多线程更改变量,读写与硬件相关的存储器时,都需要加上volatile防止被编译器优化。如果默认不开启编译器优化则无所谓,加不加都一样

         ·第三步使用*号,指针取内容,把指针指向的存储器取出来,这个值是指定存储器的值,取出来后赋值给自定义的变量Data。

        对于闪存的读取是不需要解锁的,因为读取是只看寄存器,不对寄存器进行更改,所需权限低,无需解锁可直接读

使用指针写指定地址下的寄存器

        ·如上边的图,等号左边先给定地址,再强转为指针,最后指针取内容,就是指定地址的值,直接进行赋值,对其赋值比如0x1234,完成指定地址写的功能。

        由于是写入数据,并且指定的是闪存的地址,闪存在程序运行时是只读的,不能轻易更改,对闪存进行更改的时候需要较高的权限,需要提前解锁,并且套上后边的 “ 程序存储器编程 ” 的流程,如果地址写的是SRAM的地址,比如0x20000000,则可以直接写入,因为SRAM在运行的时候是可读可写的

流程图

程序存储器编程(写入)

        ·在擦除之后就可以执行写入的流程,STM32的闪存在写入之前会检查指定地址有没有擦除,如果没有擦除就执行写入,STM32则不执行写入操作,除非写入的数据全是0

程序存储器页擦除(写入前必须擦除)

        ·擦除之后所有的数据位变为1,擦除的最小单位就是1页,1k,1024字节 

        ·页擦除类似于下边的全擦除,第一步是解锁的流程,和全擦除一样

        ·第二步置控制寄存器里PER(page Erase)位为1,在AR地址寄存器中选择要擦除的页,最后置控制寄存器的Start位为1,置Start为1也是触发条件,Start为1芯片开始干活,芯片看到per = 1就知道接下来要执行页擦除,然后继续看AR寄存器的数据,AR寄存器要提前写入一个页起始的地址,芯片就会将指定的一页给擦除

        擦除开始之后页需要等待busy位,最后读出并验证数据不用看

程序存储器全擦除

        ·把所有的页都擦除掉

        ·第一步是读取lock位,查看芯片锁没锁,如果lock位 = 1,锁住了就执行解锁过程,解锁过程就是在KEYR寄存器先写入KEY1再写入KEY2,如果当前没有锁住就无需解锁,不过在库函数中是直接执行解锁,不管有没有上锁

        ·解锁之后,首先置控制寄存器里的MER(Mass Erase)位为1,然后再置Start位为1,其中Start位为1是触发条件,Start为1之后芯片开始干活,芯片看到MER位是1,接下来就会执行全擦除的过程,内部电路自动执行全擦除的过程

        ·擦除过程开始之后程序要执行等待,判断状态寄存器的BSY(BUSY)位是否为1,BSY位表示芯片是否处于忙状态,BSY为1表示芯片忙,如果判断为1则跳转回来继续判断,直到BSY为0跳出循环,结束全擦除过程

        ·最后一步是读出并验证所有页的数据,一般是测试程序才需要做的,正常情况下全擦除完成了,默认就是成功,如果要全部读出来验证的话工作量太大了,所以最后一步一般不管 


网站公告

今日签到

点亮在社区的每一天
去签到