目录
[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介绍)
[13. 编写:读写内部FLASH](#13. 编写:读写内部FLASH)
[11.1 工程目标](#11.1 工程目标)
[10.1 工程结构](#10.1 工程结构)
[14. 编写:读写内部ID](#14. 编写:读写内部ID)
STM32 内部FLASH详解
1. STM32 FLASH简介
- STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程 (系统存储器是用来存放原厂写入的用于串口下载的BootLoader的。不允许我们进行修改)
- 读写FLASH的用途:
- 利用程序存储器的剩余空间来保存掉电不丢失的用户数据
- 通过在程序中编程(IAP),实现程序的自我更新 (直接修改程序本身,不避开程序,与OTA类似)
- 在线编程(In-Circuit Programming -- ICP)用于更新程序存储器的全部内容,它通过JTAG、SWD协议或系统加载程序(Bootloader)下载程序 (也就是我们平时用的下载程序的方式 )
- 在程序中编程(In-Application Programming -- IAP)可以使用微控制器支持的任一种通信接口下载程序 (也就是自己写一个BootLoader程序,放到程序不会覆盖到的地方。在需要升级时,让程序跳转到自己写的BootLoader中。根据自己的 协议,比如蓝牙,串口,WIFI等 控制FLASH读写。覆盖原有的程序。这其实也是系统的BootLoader一样)
2. STM32 FLASH与SRAM
- ROM的存储介质是FLASH、RAM的存储介质是SRAM
- FLASH 闪存掉电不丢失、SRAM掉电丢失
- 闪存主要有程序存储器、系统存储器、选项字节三个部分。
- 其中程序存储器是空间最大 ,最主要的部分。所以也乘坐主存储器。起始地址为0x0800 0000 ,是用来存储编译后的代码 所以我们平时所说的FLASH容量,往往指的是FLASH中程序存储器的容量
- 系统存储器起始地址为0x 1FFF F000 ,用于存储BootLoader ,用于串口下载
- 选项字节其实地址为0x1FFF F800 ,用于存储一些独立的配置参数
- SRAM区域分为运行内存SRAM、外设寄存器、内核外设寄存器
- SRAM运行内存的起始地址是0x2000 0000 , 用于存储运行过程的临时变量
- 外设寄存器的起始地址是0x4000 0000 , 用于存储各个外设的配置参数
- 内核外设寄存器 的起始地址是0xE000 0000 , 用于存储内核各个外设的配置参数
3. STM32 FLASH 容量、内容介绍
-
FLASH在 STM32中根据不同的型号,容量也不同。
- 以stm32F10x系列为例
- 小容量产品:32页 ,每页1K
- 中容量产品:128页,每页1K
- 大容量产品:256页,每页2K
-
以中容量为例讲解:
这个图中,把FLASH分为了三个块:
- 主存储器(程序存储器):用来存放编译后的程序
- 信息快: 又可以分为两个
- 启动程序代码 (系统存储器): 存放原厂写入的BootLoader,用于串口下载
- 用户选择字节(选项字节) :用于存放一些独立的参数
- 闪存存储器接口寄存器:这个的地址是40开头的 ,根据SRAM的地址分配可以看到。闪存存储器接口寄存器是一个外设,与GPIO、定时器、串口等是一个性质的东西。 闪存存储器接口可以理解为上述FLASH闪存的管理员。是用来控制闪存的擦除和编程的
对于F103 C8T6主存储器有0-63页,共64页,也就是64K
- 地址的规律:只要是000、400、800、C00 结尾的。就是页的起始地址。
- 启动程序代码(系统存储器)占用了1K空间,地址为0x1FFF F000
- 用户选择字节(选项字节):只有16个字节的配置参数
闪存存储器接口寄存器,每个寄存器占4个字节。
4. STM32 FLASH 读写注意事项
-
通过闪存存储器接口(外设)我们可以对程序存储器和选项字节进行擦除和编程。 **但系统存储器是不可以修改的。**它是用来存放原厂写入的用于串口下载的BootLoader的。
-
在选取FLASH存储区域时,一定不要覆盖原有的程序。不然就运行不了了。
-
所有的FLASH闪存 的写入和擦除规定了:
- 写入前必须擦除
- 擦除必须以最小单位进行(这里为页 1K)
- 擦除后数据位全变为1
- 数据只能1写0 ,不能0写1
- 擦除和写入需要等待忙
-
在写入数据时,如果指定地址没有被擦除,那么就不会执行编程。同时提出警告(例外是写入0000,这样不会出问题。)
-
在写入数据时,如果指定地址为写保护状态,那么也不会执行编程。同时提出警告。
-
在整片擦除时,信息块不受影响(系统存储器和选项字节)
-
在编程过程中(BSY为1)时,任何读写闪存的操作都会使CPU暂停。直到此次读写结束。
这时读写内部闪存存储数据的一个弊端。在闪存忙的时候,代码执行会暂停。
会导致 在读写内部闪存的时候,中断响应不及时等对时间要求比较严格的。
-
对选项字节来编程的时候:WRP0位都是反逻辑位:0为实施写保护,1为取消写保护(因为闪存擦除之后都是1,1 是默认的)
-
善用STM32 ST-LINK Utility 调试工具
5. STM32 FLASH 基本结构
以C8T6为例
6. STM32 FLASH 读写步骤
6.1 FLASH 解除或添加 读、写保护的方法
FLASH需要再写入之前解除写保护。这里的操作方式和独立看门狗一样。是通过键寄存器写入特定的键值来实现。可以防止误操作
- FPEC共有三个键值:
- RDPRT键 (解除读保护)= 0x000000A5
- KEY1 (解除写保护1)= 0x45670123
- KEY2 (解除写保护2)= 0xCDEF89AB
- 解锁:
- 复位后,FPEC被保护,不能写入FLASH_CR(默认是锁的)
- 在FLASH_KEYR先写入KEY1,再写入KEY2,解锁
- 错误的操作序列会在下次复位前锁死FPEC和FLASH_CR
- 加锁:
- 设置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语言中易变的数据,这是一个安全保障措施。
- 一能防止编译器优化,(连续对某个变量赋值,或者执行空循环 会在开启优化时被优化)
- 二能告诉编译器,这个变量是个易变的数据。每次读取都要到位,直接从内存中找,不要去缓存中读取。
6.3 FLASH 闪存 全 擦除 时 过程
- 读取LOCK位,看看芯片是否被锁,
- 如果锁住,就执行解锁过程(在KEYR寄存器先写入KEY1,再写入KEY2)
- 如果解锁,就置控制寄存器的MER(Mass Erase 大规模擦除)再置STRT(Start 开始)为1
- 判断状态控制器BSY(Busy 忙)是否为1 , 如果为1 就继续判断。等待他忙完(BSY = 0)跳出循环
- 最后一步验证一般不管。
6.4 FLASH 闪存 页 擦除 时 过程
- 读取LOCK位,看看芯片是否被锁,
- 如果锁住,就执行解锁过程(在KEYR寄存器先写入KEY1,再写入KEY2)
- 如果解锁,就置控制寄存器的PER(Page Erase 大规模擦除)、 ****然后在AR(Address Register 地址寄存器)选择要擦除的页。 最后置STRT(Start 开始)为1
- 判断状态控制器BSY(Busy 忙)是否为1 , 如果为1 就继续判断。等待他忙完(BSY = 0)跳出循环
- 最后一步验证一般不管。
6.5 FLASH 闪存 写入 时 过程
STM32的闪存在写入之前会检查指定地址有没有擦除。如果没有擦除就写入, STM32则不执行写入操作(除非写入的数据全是0)
- 读取LOCK位,看看芯片是否被锁,
- 如果锁住,就执行解锁过程(在KEYR寄存器先写入KEY1,再写入KEY2)
- 如果解锁,就置控制寄存器的PG**(Programming** 程序编制)表示我们即将写入数据、
- 在指定的地址写入半字(16位):
*((__IO uint16_t *)(0x08000000)) = 0x1234;
(不需要置STRT了) - 判断状态控制器BSY(Busy 忙)是否为1 , 如果为1 就继续判断。等待他忙完(BSY = 0)跳出循环
- 最后一步验证一般不管。
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 选项字节 的擦除和编程
选项字节本身也是闪存,所以在写入前也要擦除。流程和程序存储器类似,但细节有些出入
擦除 选项字节时 过程
- 解锁FLASH闪存 (手册中没写,但一定要打开 这个相当于 门口大锁)
- 检查FLASH_SR的BSY位,以确认没有其他正在进行的闪存操作 (事前等待)
- 解锁FLASH_CR的OPTWRE (Option Write Enable 选项写入使能)位 (相当于卧室小锁)
- 设置FLASH_CR的OPTER(Option Erase 选项清除)位为1 (即将擦除选项字节)
- 设置FLASH_CR的STRT(Start 开始)位为1 (触发芯片开始干活)
- 等待BSY位变为0 (等待忙完)
- 读出被擦除的选择字节并做验证
编程 选项字节时 过程
- 解锁FLASH闪存 (手册中没写,但一定要打开 这个相当于 门口大锁)
- 检查FLASH_SR的BSY位,以确认没有其他正在进行的闪存操作 (事前等待)
- 解锁FLASH_CR的OPTWRE (Option Write Enable 选项写入使能)位 (相当于卧室小锁)
- 设置FLASH_CR的OPTPG(Option Programming 选项编程)位为1 (即将开始编程写入)
- 写入要编程的半字(16位)到指定的地址
- 等待BSY位变为0 (等待忙完)
- 读出被擦除的选择字节并做验证
9 . STM32 FLASH 器件的电子签名
电子签名存放在闪存存储器模块的系统存储区域(就是BootLoader哪里),包含的芯片识别信息在出厂时编写,不可更改,使用指针读指定地址下的存储器可获取电子签名
闪存容量寄存器:(显示闪存容量) 基地址:0x1FFF F7E0 大小:16位
产品唯一身份标识寄存器:( 可以用来作为序列号、防被盗、激活秘钥) 基地址: 0x1FFF F7E8 大小:96位 可以以字节、半字、字的方式读取
10. STM32 ST-LINK Utility调试工具的使用
- 连接
- 插入好ST link之后点击连接(不能在stm32休眠模式下连接)
- 退出
- 在使用完ST-LINK Utility调试工具后要及时断开,不然KEIl下载不了程序
- 查看程序数据
- 连接后,下面窗口中就是闪存中的数据了
- 可以指定地址 查看数据、指定查看范围、指定以什么类型去看(字节、半字、字、)
-
查看选项字节配置
- 进入
- 功能介绍
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)
{
}
}