STM32学习笔记13-FLASH闪存

FLASH简介

  • STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分,通过闪存存储器接口(外设)可以对程序存储器和选项字节进行擦除和编程
  • 读写FLASH的用途:

                利用程序存储器的剩余空间来保存掉电不丢失的用户数据

                通过在程序中编程(IAP),实现程序的自我更新

  • 在线编程(In-Circuit Programming – ICP)用于更新程序存储器的全部内容,它通过JTAGSWD协议或系统加载程序(Bootloader)下载程序
  • 在程序中编程(In-Application Programming – IAP)可以使用微控制器支持的任一种通信接口下载程序

闪存模块组织

FLASH基本结构

FLASH解锁 

FPEC共有三个键值:

  • 1)RDPRT= 0x000000A5
  • 2)KEY1 = 0x45670123
  • 3)KEY2 = 0xCDEF89AB
  • 解锁的方式:
  • 复位后,FPEC被保护,不能写入FLASH_CR,也就是复位后FLASH默认是锁着的,然后在FLASH_KEYR先写入KEY1,再写入KEY2,解锁。错误的操作序列会在下次复位前锁死FPECFLASH_CR。

  • 解锁之后如何加锁呢?我们操作完成之后要尽快把FLASH重新加锁,以防止意外情况。

  • 加锁:

  • 设置FLASH_CR中的LOCK位锁住FPECFLASH_CR。

  • 接下来看一下如何使用C语言指针访问存储器:

 

程序存储器全擦除

程序存储器擦除

 

程序存储器编程

擦除之后我们就可以执行写入的流程了,STM32的闪存在写入之前会检查指定地址有没有擦除,如果没有擦除就写入STM32则不执行写入操作,除非写入的全是0,这一个数据是个例外。

 

选项字节

简单介绍一下,了解即可。

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

选项字节编程

  1. 检查FLASH_SRBSY位,以确认没有其他正在进行的编程操作
  2. 解锁FLASH_CROPTWRE
  3. 设置FLASH_CROPTPG位为1
  4. 写入要编程的半字到指定的地址
  5. 等待BSY位变为0
  6. 读出写入的地址并验证数据

选项字节擦除

  1. 检查FLASH_SRBSY位,以确认没有其他正在进行的闪存操作
  2. 解锁FLASH_CROPTWRE
  3. 设置FLASH_CROPTER位为1
  4. 设置FLASH_CRSTRT位为1
  5. 等待BSY位变为0
  6. 读出被擦除的选择字节并做验证

器件电子签名

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

1)闪存容量寄存器:

          基地址:0x1FFF F7E0

          大小:16

2)产品唯一身份标识寄存器:

          基地址: 0x1FFF F7E8

          大小:96

FLASH应用

读写内部FLASH

代码整体规划如下:

 

接下来看一下库函数:

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);//指定地址写入半字

 完整代码:

MyFLASH.c:

#include "stm32f10x.h"                  // Device header


/*读取字(32位)
*/
uint32_t MyFLASH_ReadWord(uint32_t Address)
{
	return *((__IO uint32_t *)(Address));
}

/*读取半字(16位)
*/
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));
}

/*全擦除
*/
void MyFLASH_EraseAllPages(void)
{
	FLASH_Unlock();//第一步对FLASH解锁
	FLASH_EraseAllPages();//第二步直接调库函数
	FLASH_Lock();//第三步锁上FLASH
}

/*页擦除
* 参数 PageAddress:要擦除的页地址
*/
void MyFLASH_ErasePage(uint32_t PageAddress)
{
	FLASH_Unlock();//第一步对FLASH解锁
	FLASH_ErasePage(PageAddress);//第二步直接调库函数
	FLASH_Lock();//第三步锁上FLASH
}

/*编程,写入一个字
* 参数1 Address:要写入的地址
* 参数2 Data:32位的数据
*/
void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data)
{
	FLASH_Unlock();//第一步对FLASH解锁
	FLASH_ProgramWord(Address, Data);//第二步直接调库函数
	FLASH_Lock();//第三步锁上FLASH
}

/*编程,写入半字
* 参数1 Address:要写入的地址
* 参数2 Data:16位的数据
*/
void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
	FLASH_Unlock();//第一步对FLASH解锁
	FLASH_ProgramHalfWord(Address, Data);//第二步直接调库函数
	FLASH_Lock();//第三步锁上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];

void Store_Init(void)
{
	/* 初始化闪存,最后一页第一个半字是标志位A5A5,剩下数据全是0 */
	//0xA5A5是随便定义的标志位,如果第一个半字不是A5A5就说明是第一次使用
	if(MyFLASH_ReadHalfWord(0x08000000) != 0xA5A5)
	{
		MyFLASH_ErasePage(STORE_START_ADDRESS);//擦除最后一页
		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5);//在第一个半字的位置写入规定的标志位
		for(uint16_t i = 1;i < STORE_COUNT;i++)//把剩余的存储空间全都置为默认值0
		{
			MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i*2, 0x0000);
		}
	}
	
	/* 上电时把闪存数据转存到SRAM数组 */
	for(uint16_t i = 0;i < STORE_COUNT;i++)
	{
		Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i*2);
	}
}

/*SRAM数组备份保存到闪存
*/
void Stort_Save(void)
{
	/* 第一步擦除最后一页 */
	MyFLASH_ErasePage(STORE_START_ADDRESS);
	
	/* 第二步把数组完全备份保存到闪存最后一页 */
	for(uint16_t i = 0;i < STORE_COUNT;i++)
	{
		MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i*2, Store_Data[i]);
	}
}

/*数据清零
*/
void Store_Clear(void)
{
	for(uint16_t i = 1;i < STORE_COUNT;i++)
	{
		Store_Data[i] = 0x0000;
	}
	Stort_Save();//把更改更新到闪存
}

main.c:

#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();
	Key_Init();
	Store_Init();//第一次使用的时候初始化闪存,把闪存备份的数据加载回SRAM数组
	
	OLED_ShowString(1, 1, "Flag:");
	OLED_ShowString(2, 1, "Data:");
	
	while(1)
	{
		KeyNum = Key_GetNum();
		if(KeyNum == 1)
		{
			Store_Data[1] ++;//第0个位置是标志位,不能用
			Store_Data[2] += 2;
			Store_Data[3] += 3;
			Store_Data[4] += 4;
			Stort_Save();//把SRAM数组备份到闪存
		}
		if(KeyNum == 2)
		{
			Store_Clear();
		}
		
		OLED_ShowHexNum(1, 6, Store_Data[0], 4);//显示标志位
		OLED_ShowHexNum(3, 1, Store_Data[1], 4);
		OLED_ShowHexNum(3, 6, Store_Data[2], 4);
		OLED_ShowHexNum(4, 1, Store_Data[3], 4);
		OLED_ShowHexNum(4, 6, Store_Data[4], 4);
	}
}
STM32是一款非常流行的嵌入式微控制器系列,它具有强大的性能和丰富的外设资源。在学习STM32时,掌握如何进行Flash读写是非常重要的。 Flash是一种非易失性存储器,可以用来存储程序代码和数据。在STM32中,Flash存储器通常用来存储应用程序代码。下面是一个简单的Flash读写程序的示例: 1.首先,我们需要包含适用于所使用的STM32型号的头文件。例如,对于STM32F4系列,我们需要包含"stm32f4xx.h"。 2.然后,我们需要定义一个指向Flash存储器的指针变量。例如,可以使用如下代码:`uint32_t* flash_address = (uint32_t*)0x08000000;`其中0x08000000是Flash存储器的起始地址。 3.要读取Flash存储器中的数据,我们可以通过以下代码实现:`data = *flash_address;`其中data是一个变量,用于存储读取到的数据。 4.要写入数据Flash存储器中,我们可以通过以下代码实现:`*flash_address = data;`其中data是要写入的数据。 需要注意的是,STM32Flash存储器是有写保护机制的,因此在写入数据之前,我们需要禁用写保护。可以使用以下代码禁用写保护:`FLASH->KEYR = 0x45670123; FLASH->KEYR = 0xCDEF89AB;`然后才能进行数据写入。 另外,为了确保数据的完整性,我们可以使用CRC校验来验证Flash存储器中的程序代码的正确性。可以使用库函数来计算校验和,然后将其与预期的校验和进行比较以进行验证。 综上所述,掌握STM32Flash读写操作对于嵌入式系统的开发非常重要。上述示例代码可以帮助我们快速进行Flash读写操作,同时注意写保护和数据校验可以提高数据的安全性和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值