学习笔记——stm32Flash闪存

目录

一、FLASH简介

1.1读写FLASH的用途:

1.2闪存模块组织

1.3FLASH基本结构

二、FLASH相关操作

2.1解锁

2.2指针访问寄存器

2.3擦除和编程

三、选项字节

3.1选项字节擦除

3.2选项字节编程

3.3 软件设置

四、器件电子签名

五、接口实现

5.1 底层

5.2 应用层

5.3 小应用


一、FLASH简介

STM32F1系列的FLASH由程序存储器(存储代码)、系统存储器(原厂写入不可修改的Bootloader)、选项字节(独立配置参数)三部分组成,其中程序存储器和选项字节可通过闪存存储器接口来操作(实际就是一个外设,相当于“管理员”,可进行擦除和编程)

关于bootloader,也叫自举程序,用途是程序自我更新,串口下载。在程序更新过程中,bootloader接收usart1数据,刷新到程序存储器,这时主程序还处于瘫痪状态。刷新完成,再启动主程序,就可以执行新程序,相当于“刷机”

1.1读写FLASH的用途:

  • 利用程序存储器中的剩余空间来保存需要掉电不丢失的用户数据
  • 程序中编程(IAP),实现程序的自我更新

IAP:In Application Programming,程序中编程,可支持任一种通信接口下载更新程序

ICP:In Circuit Programming,在线/电路中编程,用于更新程序存储器中的全部内容,它通过JTAG、SWD协议(ST-Link)或Bootloader(串口)进行下载程序

1.2闪存模块组织

以中容量为例:

1.3FLASH基本结构

二、FLASH相关操作

2.1解锁

对于FLASH的解锁(读不用解锁!)相当于W25Q64中的写使能,实际是对FLASH_KEYR寄存器进行操作

  • FPEC共有3个键值:PDPRT键、KEY1、KEY2(都可自定义)
  • 解锁操作通过先写KEY1、再写KEY2
  • 上锁通过FLASH_CR的LOCK位锁住FPEC和FLASH_CR
  • 复位后FPEC被保护不可写,误操作顺序会锁死直到复位

2.2指针访问寄存器

因为对于闪存的编程是以页为单位,为避免覆盖和有序使用,往往根据地址来操作,与地址相关当然用指针比较高效

  • 读寄存器:uint16_t Data = *((__IO uint16_t *)(0x8000 0400));       
  • 写寄存器:   *((__IO uint16_t *)(0x8000 0400)) = 0x1234;

其中:#define __IO volatile 表示易变数据,防止编译器优化。keil编译器默认是最低优化等级,对于编译器的优化可以用来去除无用的代码、降低代码空间,提高执行效率。但优化之后,可能会在某些地方弄巧成拙,比如利用变量计数空循环实现延时就可能被优化掉。另外,编译器还会利用缓存来加速代码,比如要频繁读写内存的某个数据,最常见的优化方式就是先把数据转移到高速缓存,在这里面进行读写速度会更高效,用完写回内存即可,但如果在读写过程中,同时程序有多个线程,比如中断函数中改变了原始数据,但高速缓存并不知情,就会导致更改数据不同步,这时候加上volatile就能避免这类问题

2.3擦除和编程

        1.全擦除流程

判断是否要解锁->FLASH_CR中的MER=1(表示全擦)、START=1(启动擦除)->循环擦除同时判断忙状态

void MYFLASH_EraseAllPages(void)//全擦除
{
	FLASH_Unlock();
	FLASH_EraseAllPages();
	FLASH_Lock();
}

        2.页擦除流程

判断是否要解锁->FLASH_CR中的PR=1(表示页擦)、写页起始地址选择要擦的页、START=1(启动擦除)->循环擦除同时判断忙状态

void MYFLASH_ErasePage(uint32_t Page_Address)//页擦除
{
	FLASH_Unlock();
	FLASH_ErasePage(Page_Address);
	FLASH_Lock();
}

        3.编程/写入

写前会自动检查有没有擦除(尤其是写1,因为FLASH里的数据只能由1变0而不能由0变1)

判断是否要解锁->FLASH_CR中的PG=1(表示编程)->在指定地址写入半字(启动编程)

void MYFLASH_WriteWord(uint32_t Address,uint32_t Data) //以字大小写数据
{
	FLASH_Unlock();
	FLASH_ProgramWord(Address,Data);
	FLASH_Lock();
}

void MYFLASH_WriteHalfWord(uint32_t Address,uint16_t Data) //以半字大小写数据
{
	FLASH_Unlock();
	FLASH_ProgramHalfWord(Address,Data);
	FLASH_Lock();
}
//写入字节较为麻烦,最好用缓存RAM来复制修改再写回flash

注意:任何对于闪存的读写操作都会导致CPU暂停,其他代码会得不到执行,对中断的影响尤为严重!

三、选项字节

3.1选项字节擦除

3.2选项字节编程

3.3 软件设置

ST-Link Utility中对于选项字节读保护、写保护可以便捷设置

四、器件电子签名

其实就是STM32的ID号,电子签名存放在闪存的系统存储区域,包含的芯片识别信息在出厂时编写,不可更改,可通过指针读指定地址下的存储器获取电子签名

  • 闪存容量寄存器,              基地址:0x1FFF F7E0,大小16位
  • 产品唯一身份标识寄存器,基地址:0x1FFF F7E8,大小96位

五、接口实现

5.1 底层

#include "stm32f10x.h"
#include "myflash.h"

//flash不需要初始化,直接实现接口即可

uint32_t MYFLASH_ReadWord(uint32_t address) 	//读取字
{
	return *((__IO uint32_t*)(address)); 
}

uint16_t MYFLASH_ReadHalfWord(uint32_t address) //读取半字
{
	return *((__IO uint16_t*)(address));
}

uint8_t MYFLASH_ReadByte(uint32_t address) 		//读取字节
{
	return *((__IO uint8_t*)(address));
}

void MYFLASH_EraseAllPages(void)//全擦除
{
	FLASH_Unlock();
	FLASH_EraseAllPages();
	FLASH_Lock();
}

void MYFLASH_ErasePage(uint32_t Page_Address)//页擦除
{
	FLASH_Unlock();
	FLASH_ErasePage(Page_Address);
	FLASH_Lock();
}

void MYFLASH_WriteWord(uint32_t Address,uint32_t Data) //以字大小写数据
{
	FLASH_Unlock();
	FLASH_ProgramWord(Address,Data);
	FLASH_Lock();
}

void MYFLASH_WriteHalfWord(uint32_t Address,uint16_t Data) //以半字大小写数据
{
	FLASH_Unlock();
	FLASH_ProgramHalfWord(Address,Data);
	FLASH_Lock();
}
//写入字节较为麻烦,最好用SRAM来复制修改再写回flash








5.2 应用层

#include "stm32f10x.h"
#include "store.h"
#include "myflash.h"

#define Store_StartPage    (0x0800FC00)
#define Store_Num          (512)

//Store_Init读取闪存数据进SRAM数组,后续读取、修改都基于SRAM
//需要掉电不丢失,调用Store_Save把数组所有内容写进闪存
//需要清空闪存,调用Store_Clear清除除标记外的所有数据

uint16_t Store_Data[Store_Num]; //对应该页数据,变量类型对应半字

void Store_Init(void) //确认闪存标记+复制数据到SRAM
{
	if(MYFLASH_ReadHalfWord(Store_StartPage) != 0x0327)//第一个半字当作标志位 
	{
		MYFLASH_ErasePage(Store_StartPage);
		MYFLASH_WriteHalfWord(Store_StartPage,0x0327); //第一次使用,标志位进行标记
		for(uint16_t i = 1; i < Store_Num; i++)
		{
			MYFLASH_WriteHalfWord(Store_StartPage+i*2,0x0000); //清零该页所有剩余数据
		}
	}
	for(uint16_t i = 0; i < Store_Num; i++) //该页整体备份到SRAM数组里
	{
		Store_Data[i] = MYFLASH_ReadHalfWord(Store_StartPage+i*2);
	}
}

void Store_Save(void)//把SRAM数组内容备份到闪存里
{
	MYFLASH_ErasePage(Store_StartPage);
	for(uint16_t i = 0; i < Store_Num; i++)
	{
		MYFLASH_WriteHalfWord(Store_StartPage+i*2,Store_Data[i]);
	}
}

void Store_Clear(void)//清零数据,同时更新到闪存
{
	for(uint16_t i = 1; i < 512; i++) //不清标记
	{
		Store_Data[i] = 0x0000;
	}
	Store_Save();	
}

5.3 测试程序

#include "stm32f10x.h"                  // Device header
#include "swi2c.h"
#include "systick.h"
#include "store.h" 
#include "key.h"

int main(void)
{
	uint8_t KeyNum;
	
	Store_Init();
	Key_Init();
	SW_OLED_Init();
	OLED_CLEAR();

	OLED_ShowString(0,0,"Flag:",2);
	OLED_ShowString(0,2,"Data:",2);	
	
	while (1)
	{
		KeyNum = Scan_KeyNum();
		if(KeyNum == 1)
		{
			Store_Data[1] ++;
			Store_Data[2] += 2;
			Store_Data[3] += 3;
			Store_Data[4] += 4;
			Store_Save();
		
		}
		if(KeyNum == 2)
		{
			Store_Clear();
		
		}
		OLED_ShowHexNum(40,0,Store_Data[0],4,2);
		OLED_ShowHexNum(0,4,Store_Data[1],4,2);
		OLED_ShowHexNum(40,4,Store_Data[2],4,2);
		OLED_ShowHexNum(0,6,Store_Data[3],4,2);
		OLED_ShowHexNum(40,6,Store_Data[4],4,2);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值