学习STM32微控制器中FLASH闪存操作

摘要:FLASH闪存的存储结构;如何向FLASH中读取数据、写入数据、擦除数据;

FLASH闪存是一种非易失性存储,掉电后数据不丢失。

FLASH闪存包括程序存储器、系统存储器、选项字节三部分,其中程序存储器为存储程序的地方,系统存储器中的参数不可修改。

闪存读写会使程序暂停。

FLASH的分区
分区 作用 起始地址
程序存储器 存储C语言编译后的程序代码 0x 0800 0000
系统存储器 存储BootLoader(启动参数) 0x 1FFF F000
选项字节 存储一些独立于程序代码的配置参数 0x 1FFF F800

1.程序存储器

程序存储器,存储程序的部分,采用分页存储的方式。

(1)可以在程序存储器未占用的部分,存储一些重要参数。

(2)IAP:通过程序修改FLASH中的程序文件,如果是远程的话就是OTA

1. 程序存储器全擦除

将程序存储器中的全部数据清除

2.程序存储器页擦除

指定特定页进行页擦除

3.程序存储器写入

在特定的地址写入数据,注意只能以半字(16bits)和全字(32bits)的格式写入


2.闪存存储器接口(FPEC)

通过闪存存储器接口可以对程序存储器和选项字节进行擦除和编程

1.加锁和解锁

FLASH自身加锁对闪存数据进行保护,在对闪存内的数据修改前需要对FLASH解锁,防止误操作。

键值:相当于用于解锁的密码

RDPRT键=0xA5:解除读保护

KEY1=0x45670123:解除写入保护的一级密码

KEY2=0xCDEF89AB:解除写入保护的二级密码

解锁

在FLASH_KEYR(钥匙寄存器)先写入KEY1,再写入KEY2后才能解锁。

(1)复位后,FPEC被保护,不能写入FLASH_CR(控制寄存器)

(2)错误的操作序列会在下次复位前锁死FPEC,只能复位才能再次尝试解锁。

加锁

设置FLASH_CR中的LOCK位锁住FPEC

2.读写FLASH数据

 使用指针访问存储器

#define __IO volatile

//使用指针读对应地址的数据
uint16_t Data=*((__IO uint16_t *)(0x8000000));
//使用指针写对应地址的数据
*((__IO uint16_t *)(0x8000000))=0x1234;

volatile的作用

防止数据被优化以及变量赋值冲突问题

3.选项字节

 

[31:24]和[15:8]分别为[23:16]和[7:0]的反码,为系统自动生成,不需要用户配置

RDP:写入RDPRT键后解除读保护

USER:配置硬件看门狗和进入停机/待机模式是否产生复位

Data0/1:用户可自定义使用

WRP0/1/2/3:配置写入保护,每一个位对应保护4个存储页(中容量) 64K,每个寄存器对应4位

1.选项字节擦除

2.选项字节写入

4.器件电子签名

每一个芯片都有其独特的电子签名储存在FLASH空间内。

电子签名地址

闪存容量寄存器:基地址(0x 1FFF F7E0),16位

产品唯一身份标识寄存器:基地址(0x 1FFF F7E8) 96位

5.开发工具

1.函数库

1.标准库

//FLASH解锁
void FLASH_Unlock(void);

//FLASH上锁
void FLASH_Lock(void);

//页擦除,输入:页起始地址,返回:擦除状态
FLASH_Status FLASH_ErasePage(uint32_t Page_Address);

//全擦除
FLASH_Status FLASH_EraseAllPages(void);

//在指定地址,写入一个字的数据(4byte)
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data);
//在指定地址,写入半字的数据(2byte)
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);

2.FLASH库(进一步封装)

//读取指定地址的数据,数据大小为4byte
uint32_t MyFLASH_ReadWord(uint32_t Address);
//读取指定地址的数据,数据大小为2byte
uint16_t MyFLASH_ReadHalfWord(uint32_t Address);
//读取指定地址的数据,数据大小为1byte
uint8_t MyFLASH_ReadByte(uint32_t Address);

//擦除所有数据
void MyFLASH_EraseAllPages(void);
//擦除指定页,需要输入该页的基地址
void MyFLASH_ErasePage(uint32_t PageAddress);

//指定地址写入4bite
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data);
//指定地址写入2bite
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);

3.Store库

由于实际上使用标准库向FLASH中读写数据还是比较不方便的。

因此我们采用的思路是先将FLASH中的数据读取出来,在闪存外对数据进行修改,然后再写入FLASH中。

//SRAM数组
uint16_t Store_Data[STORE_COUNT];

//初始化,如未写入数据,则将FLASH中第一页清空(除标志位),若已写入数据,则将数据读取到SRAM数组
void Store_Init(void);

//擦除第一页,并将SRAM数组中的数据写入到FLASH
void Store_Save(void);

//SRAM数组清零后写入到FLASH
void Store_Clear(void);

2.STLINK Utility

用STLINK Utility来读取和修改FLASH闪存中的数据

Connect to the target:连接到设备

Disconnect from the target:断开与设备的连接,软件与设备连接时会与下载程序冲突

Address:软件显示的基地址

Size:数据显示长度

Data Width:数据显示格式

6.实验

 0.硬件接线

1.读写FLASH内部数据

1.主函数

在OLED显示SRAM数组的前4个数据,按下按键1,数据自加,并写入到FLASH中,按下按键2,清除数据。

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

uint8_t KeyNum;					//定义用于接收按键键码的变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();				//OLED初始化
	Key_Init();					//按键初始化
	Store_Init();				//参数存储模块初始化,在上电的时候将闪存的数据加载回Store_Data,实现掉电不丢失
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Flag:");
	OLED_ShowString(2, 1, "Data:");
	
	while (1)
	{
		KeyNum = Key_GetNum();		//获取按键键码
		
		if (KeyNum == 1)			//按键1按下
		{
			Store_Data[1] ++;		//变换测试数据
			Store_Data[2] += 2;
			Store_Data[3] += 3;
			Store_Data[4] += 4;
			Store_Save();			//将Store_Data的数据备份保存到闪存,实现掉电不丢失
		}
		
		if (KeyNum == 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);
	}
}

2.实验现象

2.读取芯片ID

1.主函数

去对应的地址,读取数据

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

int main(void)
{
	OLED_Init();						//OLED初始化
	
	OLED_ShowString(1, 1, "F_SIZE:");	//显示静态字符串
	OLED_ShowHexNum(1, 8, *((__IO uint16_t *)(0x1FFFF7E0)), 4);		//使用指针读取指定地址下的闪存容量寄存器
	
	OLED_ShowString(2, 1, "U_ID:");		//显示静态字符串
	OLED_ShowHexNum(2, 6, *((__IO uint16_t *)(0x1FFFF7E8)), 4);		//使用指针读取指定地址下的产品唯一身份标识寄存器
	OLED_ShowHexNum(2, 11, *((__IO uint16_t *)(0x1FFFF7E8 + 0x02)), 4);
	OLED_ShowHexNum(3, 1, *((__IO uint32_t *)(0x1FFFF7E8 + 0x04)), 8);
	OLED_ShowHexNum(4, 1, *((__IO uint32_t *)(0x1FFFF7E8 + 0x08)), 8);
	
	while (1)
	{
		
	}
}

2.实验现象

作者:疯狂生长的陈大花

物联沃分享整理
物联沃-IOTWORD物联网 » 学习STM32微控制器中FLASH闪存操作

发表回复