STM32 内部FLASH详解

目录

[STM32 内部FLASH详解](#STM32 内部FLASH详解)

[1. STM32 FLASH简介](#1. STM32 FLASH简介)

[2. STM32 FLASH与SRAM](#2. STM32 FLASH与SRAM)

[3. STM32 FLASH 容量、内容介绍](#3. STM32 FLASH 容量、内容介绍)

[4. STM32 FLASH 读写注意事项](#4. STM32 FLASH 读写注意事项)

[5. STM32 FLASH 基本结构](#5. STM32 FLASH 基本结构)

[6. STM32 FLASH 读写步骤](#6. STM32 FLASH 读写步骤)

[6.1 FLASH 解除或添加 读、写保护的方法](#6.1 FLASH 解除或添加 读、写保护的方法)

[6.2 FLASH 如何使用指针 读写存储器的方法](#6.2 FLASH 如何使用指针 读写存储器的方法)

[6.3 FLASH 闪存 全 擦除 时 过程](#6.3 FLASH 闪存 全 擦除 时 过程)

[6.4 FLASH 闪存 页 擦除 时 过程](#6.4 FLASH 闪存 页 擦除 时 过程)

[6.5 FLASH 闪存 写入 时 过程](#6.5 FLASH 闪存 写入 时 过程)

[7. STM32 选项字节 的组织和用途](#7. STM32 选项字节 的组织和用途)

[8. STM32 选项字节 的擦除和编程](#8. STM32 选项字节 的擦除和编程)

[擦除 选项字节时 过程](#擦除 选项字节时 过程)

[编程 选项字节时 过程](#编程 选项字节时 过程)

[9 . STM32 FLASH 器件的电子签名](#9 . STM32 FLASH 器件的电子签名)

[10. STM32 ST-LINK Utility调试工具的使用](#10. STM32 ST-LINK Utility调试工具的使用)

[11. KEIL FLASH 下载程序 需要注意的设置](#11. KEIL FLASH 下载程序 需要注意的设置)

[11.1 下载程序起始位置。划定范围](#11.1 下载程序起始位置。划定范围)

[11.2 下载程序 时 对FLASH擦除选项](#11.2 下载程序 时 对FLASH擦除选项)

[11.3 查看程序占用大小](#11.3 查看程序占用大小)

[12. STM32 flash.h介绍](#12. STM32 flash.h介绍)

flash.h中三个部分的介绍

适用于所有stm32F10x设备的函数(第一部分)函数介绍

[13. 编写:读写内部FLASH](#13. 编写:读写内部FLASH)

[11.1 工程目标](#11.1 工程目标)

[10.1 工程结构](#10.1 工程结构)

MyFLASH.c

MyFLASH.h

Store.c

Store.h

main.c

[14. 编写:读写内部ID](#14. 编写:读写内部ID)

main.c


STM32 内部FLASH详解

1. STM32 FLASH简介

  • STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程 (系统存储器是用来存放原厂写入的用于串口下载的BootLoader的。不允许我们进行修改)
  • 读写FLASH的用途:
    1. 利用程序存储器的剩余空间来保存掉电不丢失的用户数据
    2. 通过在程序中编程(IAP),实现程序的自我更新 (直接修改程序本身,不避开程序,与OTA类似)
  • 在线编程(In-Circuit Programming -- ICP)用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序 (也就是我们平时用的下载程序的方式 )
  • 在程序中编程(In-Application Programming -- IAP)可以使用微控制器支持的任一种通信接口下载程序 (也就是自己写一个BootLoader程序,放到程序不会覆盖到的地方。在需要升级时,让程序跳转到自己写的BootLoader中。根据自己的 协议,比如蓝牙,串口,WIFI等 控制FLASH读写。覆盖原有的程序。这其实也是系统的BootLoader一样)

2. STM32 FLASH与SRAM

  1. ROM的存储介质是FLASH、RAM的存储介质是SRAM
  2. FLASH 闪存掉电不丢失、SRAM掉电丢失
  3. 闪存主要有程序存储器、系统存储器、选项字节三个部分。
    • 其中程序存储器是空间最大最主要的部分。所以也乘坐主存储器。起始地址为0x0800 0000 ,是用来存储编译后的代码 所以我们平时所说的FLASH容量,往往指的是FLASH中程序存储器的容量
    • 系统存储器起始地址为0x 1FFF F000 ,用于存储BootLoader ,用于串口下载
    • 选项字节其实地址为0x1FFF F800 ,用于存储一些独立的配置参数
  4. SRAM区域分为运行内存SRAM、外设寄存器、内核外设寄存器
    • SRAM运行内存的起始地址是0x2000 0000 , 用于存储运行过程的临时变量
    • 外设寄存器的起始地址是0x4000 0000 , 用于存储各个外设的配置参数
    • 内核外设寄存器 的起始地址是0xE000 0000 , 用于存储内核各个外设的配置参数

3. STM32 FLASH 容量、内容介绍

  1. FLASH在 STM32中根据不同的型号,容量也不同。

    • 以stm32F10x系列为例
    • 小容量产品:32页 ,每页1K
    • 中容量产品:128页,每页1K
    • 大容量产品:256页,每页2K
  2. 以中容量为例讲解:

    这个图中,把FLASH分为了三个块:

    • 主存储器(程序存储器):用来存放编译后的程序
    • 信息快: 又可以分为两个
      1. 启动程序代码 (系统存储器): 存放原厂写入的BootLoader,用于串口下载
      2. 用户选择字节(选项字节) :用于存放一些独立的参数
    • 闪存存储器接口寄存器:这个的地址是40开头的 ,根据SRAM的地址分配可以看到。闪存存储器接口寄存器是一个外设,与GPIO、定时器、串口等是一个性质的东西。 闪存存储器接口可以理解为上述FLASH闪存的管理员。是用来控制闪存的擦除和编程的

    对于F103 C8T6主存储器有0-63页,共64页,也就是64K

    • 地址的规律:只要是000、400、800、C00 结尾的。就是页的起始地址。
    1. 启动程序代码(系统存储器)占用了1K空间,地址为0x1FFF F000
    2. 用户选择字节(选项字节):只有16个字节的配置参数

    闪存存储器接口寄存器,每个寄存器占4个字节。

4. STM32 FLASH 读写注意事项

  1. 通过闪存存储器接口(外设)我们可以对程序存储器和选项字节进行擦除和编程。 **但系统存储器是不可以修改的。**它是用来存放原厂写入的用于串口下载的BootLoader的。

  2. 在选取FLASH存储区域时,一定不要覆盖原有的程序。不然就运行不了了。

  3. 所有的FLASH闪存 的写入和擦除规定了:

    • 写入前必须擦除
    • 擦除必须以最小单位进行(这里为页 1K)
    • 擦除后数据位全变为1
    • 数据只能1写0 ,不能0写1
    • 擦除和写入需要等待忙
  4. 在写入数据时,如果指定地址没有被擦除,那么就不会执行编程。同时提出警告(例外是写入0000,这样不会出问题。)

  5. 在写入数据时,如果指定地址为写保护状态,那么也不会执行编程。同时提出警告。

  6. 在整片擦除时,信息块不受影响(系统存储器和选项字节)

  7. 在编程过程中(BSY为1)时,任何读写闪存的操作都会使CPU暂停。直到此次读写结束。

    这时读写内部闪存存储数据的一个弊端。在闪存忙的时候,代码执行会暂停。

    会导致 在读写内部闪存的时候,中断响应不及时等对时间要求比较严格的。

  8. 对选项字节来编程的时候:WRP0位都是反逻辑位:0为实施写保护,1为取消写保护(因为闪存擦除之后都是1,1 是默认的)

  9. 善用STM32 ST-LINK Utility 调试工具

5. STM32 FLASH 基本结构

以C8T6为例

6. STM32 FLASH 读写步骤

6.1 FLASH 解除或添加 读、写保护的方法

FLASH需要再写入之前解除写保护。这里的操作方式和独立看门狗一样。是通过键寄存器写入特定的键值来实现。可以防止误操作

  • FPEC共有三个键值:
    1. RDPRT键 (解除保护)= 0x000000A5
    2. KEY1 (解除保护1)= 0x45670123
    3. KEY2 (解除保护2)= 0xCDEF89AB
  • 解锁:
    1. 复位后,FPEC被保护,不能写入FLASH_CR(默认是锁的)
    2. 在FLASH_KEYR先写入KEY1,再写入KEY2,解锁
    3. 错误的操作序列会在下次复位前锁死FPEC和FLASH_CR
  • 加锁:
    1. 设置FLASH_CR中的LOCK位(写1)锁住FPEC和FLASH_CR

6.2 FLASH 如何使用指针 读写存储器的方法

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

  • uint16_t Data = *((__IO uint16_t *)(0x08000000));

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

  • *((__IO uint16_t *)(0x08000000)) = 0x1234;

其中: #define __IO volatile __IO就是 volatile

是C语言中易变的数据,这是一个安全保障措施。

  1. 一能防止编译器优化,(连续对某个变量赋值,或者执行空循环 会在开启优化时被优化)
  2. 二能告诉编译器,这个变量是个易变的数据。每次读取都要到位,直接从内存中找,不要去缓存中读取。

6.3 FLASH 闪存 全 擦除 时 过程

  1. 读取LOCK位,看看芯片是否被锁,
    • 如果锁住,就执行解锁过程(在KEYR寄存器先写入KEY1,再写入KEY2)
    • 如果解锁,就置控制寄存器的MER(Mass Erase 大规模擦除)再置STRT(Start 开始)为1
  2. 判断状态控制器BSY(Busy 忙)是否为1 , 如果为1 就继续判断。等待他忙完(BSY = 0)跳出循环
  3. 最后一步验证一般不管。

6.4 FLASH 闪存 页 擦除 时 过程

  1. 读取LOCK位,看看芯片是否被锁,
    • 如果锁住,就执行解锁过程(在KEYR寄存器先写入KEY1,再写入KEY2)
    • 如果解锁,就置控制寄存器的PER(Page Erase 大规模擦除)、 ****然后在AR(Address Register 地址寄存器)选择要擦除的页。 最后置STRT(Start 开始)为1
  2. 判断状态控制器BSY(Busy 忙)是否为1 , 如果为1 就继续判断。等待他忙完(BSY = 0)跳出循环
  3. 最后一步验证一般不管。

6.5 FLASH 闪存 写入 时 过程

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

  1. 读取LOCK位,看看芯片是否被锁,
    • 如果锁住,就执行解锁过程(在KEYR寄存器先写入KEY1,再写入KEY2)
    • 如果解锁,就置控制寄存器的PG**(Programming** 程序编制)表示我们即将写入数据、
  2. 在指定的地址写入半字(16位):*((__IO uint16_t *)(0x08000000)) = 0x1234; (不需要置STRT了)
  3. 判断状态控制器BSY(Busy 忙)是否为1 , 如果为1 就继续判断。等待他忙完(BSY = 0)跳出循环
  4. 最后一步验证一般不管。

7. STM32 选项字节 的组织和用途

选项字节存储的区域只有16个字节。起始地址为0x1FFF F800

其中有一半的字节前边都带了个n(比如USER和nUSER......)

这个的意思是,在写入USER时,要同时写入nUSER的反码..其他都是一样

只有芯片检测到这两个芯片是反码的关系才会执行相对应的功能。

这是一个安全保障措施(硬件会自动计算反码并填入)

每个存储器的功能

  • RDP:写入RDPRT键(0x000000A5)后解除读保护
  • USER:配置硬件看门狗和进入停机/待机模式是否产生复位
  • Data0/1:用户可自定义使用
  • WRP0/1/2/3:配置写保护,每一个位对应保护4个存储页(中容量)
    • WRP0位都是反逻辑位:0为实施写保护,1为取消写保护(因为闪存擦除之后都是1,1 是默认的)

    • ( (小容量产品32K)也是每位保护4页,所以只需要WRP0一个字节就够了)

      ( (大容量产品512K)每位保护2页,但是WRP3的位7直接剩下把所有页(62~255页)全部保护)

8. STM32 选项字节 的擦除和编程

选项字节本身也是闪存,所以在写入前也要擦除。流程和程序存储器类似,但细节有些出入

擦除 选项字节时 过程

  1. 解锁FLASH闪存 (手册中没写,但一定要打开 这个相当于 门口大锁)
  2. 检查FLASH_SR的BSY位,以确认没有其他正在进行的闪存操作 (事前等待)
  3. 解锁FLASH_CR的OPTWRE (Option Write Enable 选项写入使能)位 (相当于卧室小锁)
  4. 设置FLASH_CR的OPTER(Option Erase 选项清除)位为1 (即将擦除选项字节)
  5. 设置FLASH_CR的STRT(Start 开始)位为1 (触发芯片开始干活)
  6. 等待BSY位变为0 (等待忙完)
  7. 读出被擦除的选择字节并做验证

编程 选项字节时 过程

  1. 解锁FLASH闪存 (手册中没写,但一定要打开 这个相当于 门口大锁)
  2. 检查FLASH_SR的BSY位,以确认没有其他正在进行的闪存操作 (事前等待)
  3. 解锁FLASH_CR的OPTWRE (Option Write Enable 选项写入使能)位 (相当于卧室小锁)
  4. 设置FLASH_CR的OPTPG(Option Programming 选项编程)位为1 (即将开始编程写入)
  5. 写入要编程的半字(16位)到指定的地址
  6. 等待BSY位变为0 (等待忙完)
  7. 读出被擦除的选择字节并做验证

9 . STM32 FLASH 器件的电子签名

电子签名存放在闪存存储器模块的系统存储区域(就是BootLoader哪里),包含的芯片识别信息在出厂时编写,不可更改,使用指针读指定地址下的存储器可获取电子签名

闪存容量寄存器:(显示闪存容量) 基地址:0x1FFF F7E0 大小:16位

产品唯一身份标识寄存器:( 可以用来作为序列号、防被盗、激活秘钥) 基地址: 0x1FFF F7E8 大小:96位 可以以字节、半字、字的方式读取

  1. 连接
    • 插入好ST link之后点击连接(不能在stm32休眠模式下连接)
  2. 退出
    • 在使用完ST-LINK Utility调试工具后要及时断开,不然KEIl下载不了程序
  3. 查看程序数据
    • 连接后,下面窗口中就是闪存中的数据了
    • 可以指定地址 查看数据、指定查看范围、指定以什么类型去看(字节、半字、字、)
  1. 查看选项字节配置

    • 进入
    • 功能介绍

11. KEIL FLASH 下载程序 需要注意的设置

11.1 下载程序起始位置。划定范围

在下载程序时,我们可以选定FLASH和RAM的起始位置。这样下载程序就可以指定位置下载。并且可以限制最大到那个位置,

比如要下载一个自己写的BootLoader。就可以指定到页尾去下载。

或者程序的最后几页,打算存储数据用,那么就可以通过划定程序的下载范围,不让下载时覆盖掉自己想保存的数据。 当然,如果程序过大,你划的范围幼小,那么会下载失败。

11.2 下载程序 时 对FLASH擦除选项

一般使用用多少擦除多少。下载速度快且不会动后面的程序

11.3 查看程序占用大小

在程序编译之后,会有一段:Program Size :......

前三个数为占用FLASH程序存储的大小, 后俩数 是占用SRAM的大小

也可以在Target处直接双击,在.map文件拖到最后边。也有大小显示。

12. STM32 flash.h介绍

flash.h中三个部分的介绍

如下是flash.h中的函数声明

可以看到在flash.h中可以看到有三块区域。分别对应了

  • 这些是所有F10x设备机都可以使用的函数
  • 所有设备机都可以使用的、新的函数
  • 只有XL加大容量的设备才可以使用的、新的函数(有个预编译,定义了宏才有效)

他们的产生原因是:

  • 期初在stm32中,最初 只有小容量LD、中容量MD、大容量HD

  • 之后,加大容量XL才推出。XL是添加了一块新的、独立的闪存。即有两块

    所以设计者命名新加的一块交Bank2 。与之对应,原来的小中大容量的那一块叫做Bank1

  • 在加大容量系列出来之后,对flash.c .h中的函数 进行了适配和更新(具体更改可以在.c中查看)

适用于所有stm32F10x设备的函数(第一部分)函数介绍

和内核运行代码有关,不需要过多了解

void FLASH_SetLatency(uint32_t FLASH_Latency);

void FLASH_HalfCycleAccessCmd(uint32_t FLASH_HalfCycleAccess);

void FLASH_PrefetchBufferCmd(uint32_t FLASH_PrefetchBuffer);

void FLASH_Unlock(void);

  • 解锁
    void FLASH_Lock(void);

  • 加锁
    FLASH_Status FLASH_ErasePage(uint32_t Page_Address);

  • 闪存擦除某一页(返回值为完成状态)
    FLASH_Status FLASH_EraseAllPages(void);

  • 闪存全擦除(返回值为完成状态)
    FLASH_Status FLASH_EraseOptionBytes(void);

  • 擦除选项字节(返回值为完成状态)
    FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);

  • 在指定地址写入字
    FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);

  • 在指定地址写入半字

FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data);

  • 选项字节:自定义Data 0 、 1

FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages);

  • 写保护

FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState);

  • 读保护

FLASH_Status FLASH_UserOptionByteConfig(uint16_t OB_IWDG, uint16_t OB_STOP, uint16_t OB_STDBY);

  • 用户选项的三个配置位

uint32_t FLASH_GetUserOptionByte(void);

  • 获取用户选项的三个配置位

uint32_t FLASH_GetWriteProtectionOptionByte(void);

  • 获取写保护状态

FlagStatus FLASH_GetReadOutProtectionStatus(void);

  • 获取读保护状态

FlagStatus FLASH_GetPrefetchBufferStatus(void);

  • 获取预取缓冲区状态

void FLASH_ITConfig(uint32_t FLASH_IT, FunctionalState NewState);FlagStatus

  • 中断使能

FLASH_GetFlagStatus(uint32_t FLASH_FLAG);

  • 获取标志位

void FLASH_ClearFlag(uint32_t FLASH_FLAG);

  • 清除标志位

FLASH_Status FLASH_GetStatus(void);

  • 获取状态

FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);

  • 等待上一次操作(等待BSY) 函数内部会自动调用,并不需要我们单独调用

13. 编写:读写内部FLASH

11.1 工程目标

在闪存最后一页进行读写。

  • 为了方便读写,提高效率。在SRAM中定义数组来对数组操作,通过函数间接控制FLASH。
  • SRAM在每次更改时,都把自己以整体备份到闪存中
  • 在每次上电时,把闪存中的 数据初始化加载到SRAM数组中
  • 另外为了判断此闪存是否之前保存过数据,使用页的第一个半字来存放标志位。如果标志位有,那么上电就直接加载闪存数据到SRAM中就可以了。如果不是就把标志位放进去,然后初始化闪存,再把闪存的数据搬运到SRAM中

10.1 工程结构

  • MyFLASH.c :实现闪存最基本的三个功能:读取、擦除、编程
  • Store.c :实现对数据的读写和存储管理:定义SRAM数组,把SRAM数组自动备份到FLASH里。复位、上电后,闪存数据会自动读回道SRAM。
  • main.c : 测试读写内部FLASH

MyFLASH.c

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:FLASH读取一个32位的字
  * 参    数:Address 要读取数据的字地址
  * 返 回 值:指定地址下的数据
  */
uint32_t MyFLASH_ReadWord(uint32_t Address)
{
    return *((__IO uint32_t *)(Address));	//使用指针访问指定地址下的数据并返回
}

/**
  * 函    数:FLASH读取一个16位的半字
  * 参    数:Address 要读取数据的半字地址
  * 返 回 值:指定地址下的数据
  */
uint16_t MyFLASH_ReadHalfWord(uint32_t Address)
{
    return *((__IO uint16_t *)(Address));	//使用指针访问指定地址下的数据并返回
}

/**
  * 函    数:FLASH读取一个8位的字节
  * 参    数:Address 要读取数据的字节地址
  * 返 回 值:指定地址下的数据
  */
uint8_t MyFLASH_ReadByte(uint32_t Address)
{
    return *((__IO uint8_t *)(Address));	//使用指针访问指定地址下的数据并返回
}

/**
  * 函    数:FLASH全擦除
  * 参    数:无
  * 返 回 值:无
  * 说    明:调用此函数后,FLASH的所有页都会被擦除,包括程序文件本身,擦除后,程序将不复存在
  */
void MyFLASH_EraseAllPages(void)
{
    FLASH_Unlock();                 //解锁
    FLASH_EraseAllPages();          //全擦除
    FLASH_Lock();                   //加锁
}

/**
  * 函    数:FLASH页擦除
  * 参    数:PageAddress 要擦除页的页地址
  * 返 回 值:无
  */
void MyFLASH_ErasePage(uint32_t PageAddress)
{
    FLASH_Unlock();                 //解锁
    FLASH_ErasePage(PageAddress);   //页擦除
    FLASH_Lock();                   //加锁
}

/**
  * 函    数:FLASH编程字
  * 参    数:Address 要写入数据的字地址
  * 参    数:Data 要写入的32位数据
  * 返 回 值:无
  */
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
    FLASH_Unlock();                         //解锁
    FLASH_ProgramWord(Address, Data);       //编程字
    FLASH_Lock();                           //加锁
}

/**
  * 函    数:FLASH编程半字
  * 参    数:Address 要写入数据的半字地址
  * 参    数:Data 要写入的16位数据
  * 返 回 值:无
  */
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
    FLASH_Unlock();                         //解锁
    FLASH_ProgramHalfWord(Address, Data);   //编程半字
    FLASH_Lock();                           //加锁
}

MyFLASH.h

#ifndef __MYFLASH_H
#define __MYFLASH_H

//读出字
uint32_t MyFLASH_ReadWord(uint32_t Address);
//读出半字
uint16_t MyFLASH_ReadHalfWord(uint32_t Address);
//读出字节
uint8_t MyFLASH_ReadByte(uint32_t Address);

//清除所有页
void MyFLASH_EraseAllPages(void);
//清除某页
void MyFLASH_ErasePage(uint32_t PageAddress);

//编写字
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data);
//编写半字
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);

#endif

Store.c

#include "stm32f10x.h"                  // Device header
#include "MyFLASH.h"

#define STORE_START_ADDRESS     0x0800FC00      //存储的起始地址
#define STORE_COUNT             512             //存储数据的个数

uint16_t Store_Data[STORE_COUNT];               //定义SRAM数组

/**
  * 函    数:参数存储模块初始化
  * 参    数:无
  * 返 回 值:无
  */
void Store_Init(void)
{
    /*判断是不是第一次使用*/
    if (MyFLASH_ReadHalfWord(STORE_START_ADDRESS) != 0xA5A5)    //读取第一个半字的标志位,if成立,则执行第一次使用的初始化
    {
        MyFLASH_ErasePage(STORE_START_ADDRESS);                 //擦除指定页
        MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5);   //在第一个半字写入自己规定的标志位,用于判断是不是第一次使用
        
        for (uint16_t i = 1; i < STORE_COUNT; i ++)             //循环STORE_COUNT次,除了第一个标志位
        {
            MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, 0x0000);       //除了标志位的有效数据全部清0
        }
    }
    
    /*上电时,将闪存数据加载回SRAM数组,实现SRAM数组的掉电不丢失*/
    for (uint16_t i = 0; i < STORE_COUNT; i ++)             //循环STORE_COUNT次,包括第一个标志位
    {
        Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i * 2);      //将闪存的数据加载回SRAM数组
    }
}

/**
  * 函    数:SRAM数组保存数据到FLASH闪存
  * 参    数:无
  * 返 回 值:无
  */
void Store_Save(void)
{
    MyFLASH_ErasePage(STORE_START_ADDRESS);             //擦除指定页
    
    for (uint16_t i = 0; i < STORE_COUNT; i ++)         //循环STORE_COUNT次,包括第一个标志位
    {
        MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, Store_Data[i]);    //将SRAM数组的数据备份保存到闪存
    }
}

/**
  * 函    数:SRAM数组清零,再同步到FLASH闪存
  * 参    数:无
  * 返 回 值:无
  */
void Store_Clear(void)
{
    for (uint16_t i = 1; i < STORE_COUNT; i ++)         //循环STORE_COUNT次,除了第一个标志位
    {       
        Store_Data[i] = 0x0000;                         //SRAM数组有效数据清0
    }                       
    
    Store_Save();                                       //保存数据到闪存
}

Store.h

#ifndef __STORE_H
#define __STORE_H

//对外声明保存最后一页的SRAM数组
extern uint16_t Store_Data[];

//初始化
void Store_Init(void);
//把SRAM数组写入FLASH
void Store_Save(void);
//清除FLASH(除了标志位)
void Store_Clear(void);

#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Store.h"
#include "Key.h"

int main(void)
{
    /*模块初始化*/
    OLED_Init();                //OLED初始化
    KEY_Init();                //按键初始化
    Store_Init();               //参数存储模块初始化,在上电的时候将闪存的数据加载回Store_Data,实现掉电不丢失
    
    /*显示静态字符串*/
    OLED_ShowString(1, 1, "Flag:");
    OLED_ShowString(2, 1, "Data:");
    
    while (1)
    {
        uint8_t Key_Num = KEY_Get();    //记录按下的键   
        if (Key_Num == 1)               //按键1按下
        {
            Store_Data[1] ++;           //变换测试数据
            Store_Data[2] += 2;
            Store_Data[3] += 3;
            Store_Data[4] += 4;
            Store_Save();               //将Store_Data的数据备份保存到闪存,实现掉电不丢失
        }
        
        else if (Key_Num == 2)          //按键2按下
        {
            Store_Clear();              //将Store_Data的数据全部清0
        }
        
        OLED_ShowHexNum(1, 6, Store_Data[0], 4);    //显示Store_Data的第一位标志位
        
        OLED_ShowHexNum(3, 1, Store_Data[1], 4);    //显示Store_Data的有效存储数据
        OLED_ShowHexNum(3, 6, Store_Data[2], 4);
        OLED_ShowHexNum(4, 1, Store_Data[3], 4);
        OLED_ShowHexNum(4, 6, Store_Data[4], 4);
    }
}

14. 编写:读写内部ID

在13的基础上 只修改了main.c

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Store.h"

int main(void)
{   
    //0x1FFFF7E0为FLASH ID 寄存器位置,。是一个96位的
    
    OLED_Init();                        //OLED初始化
    
    OLED_ShowString(1, 1, "F_SIZE:");   //显示容量:
    
    OLED_ShowHexNum(1, 8, *((__IO uint16_t *)(0x1FFFF7E0)), 4);     //使用指针读取指定地址下的 闪存容量寄存器
    
    OLED_ShowString(2, 1, "U_ID:");     //显示芯片ID:
    
    //使用指针读取指定地址下的产品唯一身份标识寄存器
    //可以由字节、半字、字的方式读取
    OLED_ShowHexNum(2, 6, *((__IO uint8_t *)(0x1FFFF7E8)), 2);              //字节读出:第一个字节        (0-8位)
    OLED_ShowHexNum(2, 9, *((__IO uint8_t *)(0x1FFFF7E8) + 1), 2);          //字节读出:第二个字节        (9-16位)
    OLED_ShowHexNum(2, 12, *((__IO uint16_t *)(0x1FFFF7E8) + 1), 4);        //半字读出:第3 、4 个字节    (16-32位)
    OLED_ShowHexNum(3, 1, *((__IO uint32_t *)(0x1FFFF7E8 + 1)), 8);      //字  读出:5 6 7 8个字节     (33-64位)
    OLED_ShowHexNum(4, 1, *((__IO uint32_t *)(0x1FFFF7E8 + 2)), 8);      //字  读出:9 10 11 12个字节  (64-96位)
    
    while (1)
    {

    }
}
相关推荐
fantasy_arch4 小时前
CPU性能优化-磁盘空间和解析时间
网络·性能优化
是Dream呀6 小时前
Python从0到100(七十八):神经网络--从0开始搭建全连接网络和CNN网络
网络·python·神经网络
kaixin_learn_qt_ing7 小时前
了解RPC
网络·网络协议·rpc
安全小王子8 小时前
Kali操作系统简单介绍
网络·web安全
Hacker_LaoYi9 小时前
【漏洞分析】DDOS攻防分析(四)——TCP篇
网络·tcp/ip·ddos
爱吃水果蝙蝠汤9 小时前
DATACOM-IP单播路由(BGP)-复习-实验
网络·网络协议·tcp/ip
Sun_12_210 小时前
SQL注入(SQL lnjection Base)21
网络·数据库
网络安全Jack10 小时前
网络安全概论——身份认证
网络·数据库·web安全
易我数据恢复大师10 小时前
如何彻底删除电脑数据以防止隐私泄露
网络·电脑·数据删除·擦除