跳转至

5.6 STM32驱动开发-读写Flash和模拟EEPROM

前言

这里主要讲解 Breeze Mini 的FLASH相关操作,完整代码在工程目录下的 Drivers 子目录下的 stm32f10x_driver_flash.cstm32f10x_driver_flash.hstm32f10x_driver_eeprom.cstm32f10x_driver_eeprom.h 中,该代码主要实现对STM32自身的FLASH进行读写来模拟实现EEPROM的功能,进而存储控制算法所需的相关常数。

相关知识

ICP、ISP和IAP简介

ICP、ISP和IAP是对基于FLASH的IC进行烧写的3种方式,这里简单介绍一下3者之间的区别。

ICP

ICP(In-Circuit Programming)即 "在电路编程",指的是通过外接的仿真/下载器(JLink),通过特定的通信协议标准(JTAG或者SWD),可对所有FLASH空间进行烧写的一种编程方式。使用ICP方式烧写只需满足IC上电这个条件,结合MDK等IDE的话还可以进行在硬件单步调试,这是ICP编程的一个优势。

ISP

ISP(In-System Programming)即 "在系统编程",指的是不通过外接的仿真/下载器,只通过板载的通信接口(USART)将代码烧写到部分FLASH空间的一种编程方式。使用ISP方式烧写需满足IC和晶振上电,且预先要有一段程序已被烧写进去。

IAP

IAP(In-Application Programming)即 "在应用编程",指的是在MCU运行的状态下,不通过外接的仿真/下载器,只通过板载的通信接口(USART)将代码烧写到部分FLASH空间的一种编程方式。实现IAP方式时,FLASH空间需要有两块存储区,一块被称为BOOT区,一个被称为主程序区,程序上电时选择从BOOT区启动,此时若有对主程序区的编程需求,则会对其进行编程。否则程序指针会跳转到主程序区去执行代码。

3种方式总结

这里用一个比较形象的比喻来描述3者之间的区别:

  1. ICP是把房子和地基都拆了重建,人需要等两者造好之后才能入住(主程序不能运行)

  2. ISP是把房子拆了重建,但是地基还保留着,人需要等房子造好后才能入住(主程序不能运行)

  3. IAP是对造好的房子进行内饰装修,此时人是可以入住的(主程序能运行)

STM32 FLASH简介

STM32不同型号的控制器的FLASH的容量也有所不同,最小的只有16K字节,最大的则有1024K字节,Breez Mini 选择的 STM32F103TBU6 的FLASH容量为128K字节,属于中等容量产品。中等容量产品的闪存模块结构如下图所示:

由上图可见,STM32的闪存模块由 主存储器信息块闪存存储器接口寄存器 3部分组成。

主存储器

主存储器用来存放代码和常数数据(如const类型的数据),对于STM32F103TBU6来说,其主存储器每页上有1K字节,共128页。从上图可以看出主存储器的起始地址为 0x08000000

信息块

该部分分为2个部分,其中 System memory 中存储ST官方自带的启动程序,是为了能够进行串口下载用的。Option Bytes 一般用于配置写保护、读保护等功能。

闪存存储器接口寄存器

该寄存器用于控制闪存读写。对主存储器和信息块的写入由内部的闪存编程擦除控制器管理,编程与擦除的高电压由内部产生。在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地执行,即在进行写或擦除操作时,不能读取代码或数据。

闪存的读取

内置的闪存模块可以在通用地址空间直接寻址,任何32位数据的读操作都能够访问闪存模块的内容并得到相应的数据。读接口在闪存端包含一个读控制器,还包含一个AHB接口与CPU的衔接。这个接口的主要工作是产生读闪存的控制信号并预取CPU要求的指令块,预取指令块仅用于在I-Code总线上的取指操作,数据常量是通过D-Code总线访问的。这两条总线的访问目标是相同的闪存模块,访问D-Code将比预取指令优先级更高。

这里要特别注意闪存等待时间,因为CPU的运行速度比FLASH快得多,STM32F103的FLASH最快访问速度是不超过24MHz的,所以若CPU的工作频率超过24MHz,则必须设置FLASH的等待时间,我们这里使CPU工作在72MHz,则FLASH的等待周期就 必须设置为2,该设置是通过 FLASH_ACR 寄存器进行的。

比如我们要从地址addr中读取一个半字(16位)数据,则可以通过下行所示代码读取:

1
data = *(vu16*) addr;

该行代码将addr强制转换成vu16类型的指针,然后取该地址所指向的地址的值,即得到了addr地址上所存的值。类似地将 vu16 改成 vu8,即可读取指定地址的一个字节。

闪存的写入与擦除

闪存的写入和擦除操作相对读取操作要复杂一些。

编程

STM32的闪存编程是由FPEC(闪存编程和擦除控制器)模块处理的,这个模块包含7个32位寄存器,他们分别是:

  1. FPEC键寄存器(FLASH_KEYR)

  2. 选择字节键寄存器(FLASH_OPTKEYR)

  3. 闪存控制寄存器(FLASH_CR)

  4. 闪存状态寄存器(FLASH_SR)

  5. 闪存地址寄存器(FLASH_AR)

  6. 选择字节寄存器(FLASH_OBR)

  7. 写保护寄存器(FLASH_WRPR)

其中FPEC键寄存器总共有3个键值:

  1. RDPRT键 = 0X000000A5

  2. KEY1 = 0X45670123

  3. KEY2 = 0XCDEF89AB

STM32复位后FPEC模块是被保护的,不能写入FLASH_CR寄存器,通过写入特定的序列到FLASH_KEYR寄存器可以打开FPEC模块(KEY1和KEY2),STM32闪存编程每次必须写入16位数据,当FLASH_CR寄存器的PG位为1时,在一个闪存地址写入一个半字将启动一次编程,写入任何非法数据都会使得FPEC产生总线错误。在编程过程中(BSY位为1),任何读写闪存的操作都会使CPU暂停,直到此次闪存编程结束。在对STM32的FLASH进行编程时,必须要求其写入地址的FLASH是已经被擦除过的(值为0XFFFF),否则无法写入。STM32的FLASH编程流程为:

  1. 检查FLASH_CR的LOCK是否解锁,如果没有就先解锁

  2. 检查FLASH_SR的BSY位,以确认没有其他正在进行的编程操作

  3. 设置FLASH_CR的PG位为1

  4. 在指定地址写入指定数据

  5. 等待FLASH_SR的BSY位变为0

  6. 读出写入的地址并验证数据

擦除

由于STM32的FLASH写入操作需要保证写入的地址是已经被擦除过的,所以需要再介绍一下FLASH的擦除操作,STM32的FLASH擦除分为:页擦除和整片擦除,页擦除的工作流程为:

  1. 检查FLASH_CR的LOCK是否解锁,如果没有就先解锁

  2. 检查FLASH_SR的BSY位,以确认没有其他正在进行的编程操作

  3. 设置FLASH_CR的PER位为1

  4. 用FLASH_AR选择要擦除的页

  5. 设置FLASH_CR的STRT位为1

  6. 等待FLASH_SR的BSY位变为0

  7. 读出被擦除页并验证

相关寄存器

FLASH_KEYR寄存器

该寄存器各位描述如下所示:

该寄存器主要用于解锁FPEC,必须向该寄存器写入特定的序列(KEY1和KEY2)解锁后,才能够对FLASH_CR寄存器进行写操作。

FLASH_CR寄存器

该寄存器各位描述如下所示:

这里我们只讲解该寄存器的LOCK、STRT、PER和PG共4位。

  1. LOCK位:该位用于指示FLASH_CR寄存器是否被锁住,该位在检测到正确的解锁序列后,会被硬件自动清零,在一次不成功的解锁操作后,在下次系统复位前,该位都不会再改变。

  2. STRT位:该位用于开始一次擦除操作,向该位写1将执行一次擦除操作。

  3. PER位:该位用于开始页擦除操作,向该位写1将设置页擦除模式。

  4. PG位:该位用于开始编程操作,向该位写1将设置写FLASH模式。

FLASH_SR寄存器

该寄存器的各位描述如下图所示:

该寄存器主要用于指示当前FPEC的操作编程状态。

FLASH_AR寄存器

该寄存器的各位描述如下图所示:

该寄存器主要用于设置要擦除的页的地址。

固件库操作FLASH

操作FLASH相关的库函数在 FWLib 目录下的 stm32f10x_flash.cstm32f10x_flash.h 中,但是 Breeze Mini 的飞控代码自己封装了一套操作FLASH的函数,在 Drivers 目录下的 stm32f10x_driver_flash.cstm32f10x_driver_flash.h

硬件连接

操作FLASH属于 Breeze Mini 主微控制器STM32F103TBU6的内部功能,是没有外部电路连接的。

软件设计

参考