27:内部flash闪存的操作

1、内部flash的简介

在这里插入图片描述STM32F1系列的flash闪存(掉电不丢失)包含:
    ①程序存储器flash(存储编译好的程序),地址是以0x0800 0000开始。
    ②系统存储器(存储BootLoader程序,不允许修改),地址以0x1FFF F000开始。
    ③选项字节(存储配置参数)三个部分,地址以0x1FFF F800开始
通过闪存存储器接口(外设)可以对程序存储器flash和选项字节进行擦除和编程。

读写FLASH的用途:
   利用程序存储器的剩余空间来保存掉电不丢失的用户数据
   通过在程序中编程(IAP),实现程序的自我更新

2、闪存模块组织

在这里插入图片描述
在这里插入图片描述

如上图所示:①stm32的中容量产品中的程序存储器flash的最小存储单元页(Page),每一页的存储空间大小是1KB。而程序存储器flash占有了128页(f103c8t6只有64页,即64KB),系统存储器占用了2KB的存储空间,选项字节占用了16B。②而下面的寄存器(FPEC)就是上面存储器的管理员,通过对这些寄存器的配置,就能够对上面的存储空间进行擦除,写入等操作。而FLASH以000 ,400,800,c00结尾的地址就是每一页的首地址

【注】擦除写入等操作的注意事项参考W25Q64的那一节。

3、对程序存储器flash的操作

在操作前必须要对FPEC进行解锁(防止误操作),解锁的一共3个密码,按照将值写入FLASH_KEYR寄存器即可。其中解锁的密码如下:
   RDPRT键 = 0x000000A5 //解除对程序存储器的读保护
   KEY1 = 0x45670123
   KEY2 = 0xCDEF89AB
解锁:
   在FLASH_KEYR先写入KEY1,再写入KEY2,解除不能写入FLASH_CR
加锁:
   设置FLASH_CR中的LOCK位锁住FPEC和FLASH_CR

3.1、闪存全擦除

在这里插入图片描述

如上图所示:在执行擦除之前先读取LOCK位,判断FLASH_CR是否锁住,如果锁住了就解锁,没有锁住就执行下面的操作。解锁完成后将FLASH_CR寄存器中的MER位置1(全擦除),然后将START位置1,开始执行。擦除的执行过程中BSY位会置1代表忙状态,置0代表不忙,擦除完成。

3.1、闪存页擦除

在这里插入图片描述

3.2、闪存写入过程

在写入之前会检测是否擦除,入门没有擦除则不会执行写入
在这里插入图片描述【注】对闪存的读写操作过程中,CPU会暂停,直到读写接收后CPU又开始执行,这是一个很不好的弊端

4、对选项字节的操作

在这里插入图片描述USER:配置硬件看门狗和进入停机/待机模式是否产生复位
RDP:写入RDPRT键(0x000000A5)后解除程序存储器的读保护
Data0/1:用户可自定义使用
WRP0/1/2/3:配置写保护,每1个位对应保护4个存储页(中容量),即1个字节对应保护32页,4个字节对应保护128页(正好是中容量的全部容量)。
【注】nxxx:代表写入与之对应的字节的反码,由硬件执行。

4.1、选项字节擦除

在这里插入图片描述
【注】解除了FLASH_CR寄存器的锁后还要解除选项字节的锁,即1、在FLASH_KEYR先写入KEY1,再写入KEY2,解除不能写入FLASH_CR。2、FLASH_OPTKEYR先写入KEY1,再写入KEY2,解除选项字节的锁。

4.2、选项字节编程

在这里插入图片描述

5、读内部闪存FLASH进行读写

5.1、读取FLASH里面指定地址的数据

①MyFlash.c的代码程序如下:

#include "stm32f10x.h"                  // Device header

/*
 * 读取一个字的数据(32位/4个字节的数据)
 */
uint32_t MyFlash_ReadWord(uint32_t Address)
{
   return *((__IO uint32_t *)(Address));
}
 
 /*
 * 读取半个字的数据(16位/2个字节的数据)
 */
uint16_t MyFlash_ReadHalfWord(uint32_t Address)
{
   return *((__IO uint16_t *)(Address));
}
 
/*
 * 读取一个字节的数据(8位)
 */
uint8_t MyFlash_ReadByte(uint32_t Address)
{
   return *((__IO uint8_t *)(Address));
}

②主函数文件的代码程序

/*
	内部闪存flash的使用
*/

#include "stm32f10x.h"                 
#include "OLED.h"
#include "MyFlash.h"

int main(void)
{
	OLED_Init();
	OLED_Clear();
    
    OLED_ShowHexNum(1,1,MyFlash_ReadWord(0x08000000),8);//以十六进制显示一个字(32位)的数据
    OLED_ShowHexNum(2,1,MyFlash_ReadHalfWord(0x08000000),4);
    OLED_ShowHexNum(3,1,MyFlash_ReadByte(0x08000000),2);
    
	while(1)
	{
		
	}
}

OLED的显示结果:

20000660
0660
60

读取一个字的数据如下:

在这里插入图片描述
读取半字的数据如下:

在这里插入图片描述

读取一个字节的数据如下:

在这里插入图片描述综上:一个地址单元存储空间的大小是一个字节。得出如下图的结论:传输的地址是0x80000000,若显示一个字(4个字节)则数据以传输的地址为开始,向后揽括4个字节的数据,结果是20 00 06 60。如显示半字(2个字节),结果是06 60。所以显示是以后位地址的数据在前。
在这里插入图片描述

5.2、写入FLASH里面指定地址的数据

记住在指定地址写入之前要进行擦除,而擦除的区域一定是存储代码后剩余的区域,如果将代码也擦除了,那么FLASH里面什么都没有了,单片机也不再执行程序。

①MyFlash.c的代码程序如下:

#include "stm32f10x.h"                  // Device header

/*
 * 读取一个字的数据(32位/4个字节的数据)
 */
uint32_t MyFlash_ReadWord(uint32_t Address)
{
   return *((__IO uint32_t *)(Address));
}
 
 /*
 * 读取半个字的数据(16位/2个字节的数据)
 */
uint16_t MyFlash_ReadHalfWord(uint32_t Address)
{
   return *((__IO uint16_t *)(Address));
}
 
/*
 * 读取一个字节的数据(8位)
 */
uint8_t MyFlash_ReadByte(uint32_t Address)
{
   return *((__IO uint8_t *)(Address));
}

/*
 * Flash全部擦除
 */
void MyFlash_EraseAllPages(void)
{
   FLASH_Unlock();//解锁
   FLASH_EraseAllPages();//全部擦除
   FLASH_Lock();//上锁
}

/*
 * Flash页擦除(地址为页的首地址)
 */
void MyFlash_ErasePage(uint32_t PageAddress)
{
   FLASH_Unlock();//解锁
   FLASH_ErasePage(PageAddress);//擦除指定地址的数据
   FLASH_Lock();//上锁
}

/*
 * 指定地址写入一个字(4个字节)的数据
 */
void MyFlash_ProgramWord(uint32_t Address,uint32_t Data)
{
   FLASH_Unlock();//解锁
   FLASH_ProgramWord(Address,Data);//指定地址写入一个字的数据
   FLASH_Lock();//上锁
}

/*
 * 指定地址写入半个字(2个字节)的数据
 */
void MyFlash_ProgramHalfWord(uint32_t Address,uint16_t Data)
{
   FLASH_Unlock();//解锁
   FLASH_ProgramHalfWord(Address,Data);//指定地址写入半字的数据
   FLASH_Lock();//上锁
}

②主函数文件的代码程序

/*
	内部闪存flash的使用
*/

#include "stm32f10x.h"                 
#include "OLED.h"
#include "MyFlash.h"

int main(void)
{
	OLED_Init();
	OLED_Clear();
    
    OLED_ShowHexNum(1,1,MyFlash_ReadWord(0x08000000),8);
    OLED_ShowHexNum(2,1,MyFlash_ReadHalfWord(0x08000000),4);
    OLED_ShowHexNum(3,1,MyFlash_ReadByte(0x08000000),2);
    
    
    MyFlash_ErasePage(0x0800FC00);//指定最后1页的地址擦除
    MyFlash_ProgramWord(0x0800FC00,0x12345678);//在最后一页的起始地址写入数据
    MyFlash_ProgramHalfWord(0x0800FC04,0x1234);
    
	while(1)
	{
		
	}
}

在这里插入图片描述
由于每次操作读写闪存Flash时,都要直接指定地址地址操作,这样十分的不方便。所以我们可以在SRAM(存储临时变量空间)里面开发一个数组变量,空间的大小正好为1KB,即uin16_t Store_Data[512];每次改变数组里面的数据后,就将数据写入到FLASH闪存里面的最后一页的空间(1KB)里面。这样断电了,Flash闪存是掉电不丢失的,就相当于备份了数组的数据。而断电后上电,在将FLASH里面存储的数据读回到数组里面。

①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数组
	}
}

/*
 * 将数据备份到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数组的数据备份保存到闪存
	}
}

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

【一些小知识点】

1、下图为程序所保存的空间范围,起始空间地址为0x0800 0000,范围大小为0xFC(252B)。当然如果程序大小>252B,也可以将程序的保存空间增大。

在这里插入图片描述

2、程序下载时对空间进行擦除,第一个是将全部的空间(64KB)擦除,第二个是将下载程序所占用的空间擦除

在这里插入图片描述

3、计算程序所占用FLASH空间的大小以及变量所占用SRAM空间的大小。程序所占用空间的大小 = 前面3个数相加(2784+1788+4 = 4576B)得出,程序所占用的内存为4576字节。变量所占SRAM的空间大小 = 后面2数相加(4+2660 = 2664B)

在这里插入图片描述
当然我们也可以直接双击Target1,进行查看即可。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值