小松的STM32教程(14)——内部flash模拟eeprom

2023-05-16

为什么要用Flash来模拟EEPORM

Flash更适合作为程序存储器,EEPROM更适合作为数据存储器,存储大量数据的时候就需要使用EEPROM,廉价的MCU往往只有FLASH而没有EEPROM,所以这个时候就需要外加EEPROM或者选择一些别的办法,使用Flash模拟EEPROM就是一个很好的选择,说白了就是使用stm32内部flash里面一部分空间用来保存数据,不只是保存程序代码。

FLAHS和EEPROM有什么区别

Flash是按照扇区操作,EEPROM是按字节操作

核心代码讲解

	offaddr=WriteAddr-STM32_FLASH_BASE;		//实际偏移地址.
	secpos=offaddr/STM_SECTOR_SIZE;				//扇区地址,将偏移地址除以每个扇区的大小
	secoff=(offaddr%STM_SECTOR_SIZE)/2;		//在扇区内的偏移(2字节)
	secremain=STM_SECTOR_SIZE/2-secoff;		//扇区剩余空间大小   

S T M 32 _ F L A S H _ B A S E = 0 X 8000000 STM32\_FLASH\_BASE = 0X800 0000 STM32_FLASH_BASE=0X8000000
假设写入地址为
W r i t e A d d r = 0 X 8030000 WriteAddr=0X8030000 WriteAddr=0X8030000
那么偏移地址就是
s e c p o s = 0 X 8030000 − 0 X 8000000 = 0 X 0030000 secpos=0X8030000-0X8000000=0X0030000 secpos=0X80300000X8000000=0X0030000


void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)	
{
	u32 secpos;	   //扇区地址
	u16 secoff;	   //扇区内偏移地址(16位字计算)
	u16 secremain; //扇区内剩余地址(16位字计算)	   
 	u16 i;    
	u32 offaddr;   //去掉0X08000000后的地址(也就是相对于基地址的偏移量)
	
	
	if(WriteAddr<STM32_FLASH_BASE|| //小于基地址
		(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))//超出了最大地址
	{
		return;
	 }//非法地址
	
	
	
	FLASH_Unlock();						//解锁
	offaddr=WriteAddr-STM32_FLASH_BASE;		//实际偏移地址.
	secpos=offaddr/STM_SECTOR_SIZE;				//扇区地址,将偏移地址除以每个扇区的大小
	secoff=(offaddr%STM_SECTOR_SIZE)/2;		//在扇区内的偏移(2字节)
	secremain=STM_SECTOR_SIZE/2-secoff;		//扇区剩余空间大小   
	 
	if(NumToWrite<=secremain){ // 如果剩余的扇区空间足够
		secremain=NumToWrite; //?
	}
	while(1) 
	{	
		STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,
									STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
		
		
		for(i=0;i<secremain;i++)//校验数据
		{
			if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除  	  
		}
		
		
		if(i<secremain)//需要擦除
		{
			FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
			for(i=0;i<secremain;i++)//复制
			{
				STMFLASH_BUF[i+secoff]=pBuffer[i];	  
			}
			STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  
		}else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. 				   
		if(NumToWrite==secremain)break;//写入结束了
		else//写入未结束
		{
			secpos++;				//扇区地址增1
			secoff=0;				//偏移位置为0 	 
		   	pBuffer+=secremain;  	//指针偏移
			WriteAddr+=secremain;	//写地址偏移	   
		   	NumToWrite-=secremain;	//字节(16位)数递减
			if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
			else secremain=NumToWrite;//下一个扇区可以写完了
		}	 
	};	
	FLASH_Lock();//上锁
}

存储器地址映射(注意RCT6只有128页)

  • 存储器大小:我们说的flash大小,指的是主存储器的大小
  • 信息块:
    • 启动程序代码,用于存储ST自带的启动程序,用于串口下载代码
    • 用户选择字节一般用于配置读保护和写保护等功能
  • 接口寄存器:用于配置flash读写等
    在这里插入图片描述

B0和B1

在开发板上有两个跳线帽,可以用来切换B0(Boot0)和B1(Boot1),模式如下

B0B1描述
GNDGND从0x8000000运行(主存储器基地址)
3.3GND从信息块运行

flash写

  1. 只有解除写保护才能操作相关寄存器(检查FLASH_CR的LOCK是否解锁)
  2. flash编程时确保没有其他正在进行的编程操作(检查FLASH_SR的BSY位)
  3. 每次编程必须写入半字,否则会出错(设置FLASH_CR寄存器的PG位为1)
  4. flash编程时,写入地址的flash必须是被擦除的,否则无法写入

flash页擦除

  1. 设置CR寄存器的PER为1
  2. 配置AR寄存器选择擦除页
  3. 配置CR寄存器的STRT为1
  4. 等待SR的BSY为0
  5. 读出验证

整片擦除

  1. 设置CR的MER为1
  2. 设置CR的STAT为1
  3. 等待SR的BSY为0
  4. 读出验证

寄存器(具体的参看flash编程手册)

  1. KEYR寄存器写入FPEC解锁键
  2. CR寄存器:锁,开始等
  3. SR寄存器:检查忙
  4. AR寄存器:写地址

flash操作常用库函数

在这里插入图片描述

stm32flash.h

#ifndef __STMFLASH_H__
#define __STMFLASH_H__
#include "sys.h"  

#define STM32_FLASH_SIZE 256 	 		//定义flash大小
#define STM32_FLASH_WREN 1              //允许写
#define STM32_FLASH_BASE 0x08000000 	//STM32 FLASH基地址

u16 STMFLASH_ReadHalfWord(u32 faddr);		  //读半字(2字节)
void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len);	//ָ写指定长度字节
u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len);						//ָ读给定长度字节
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite);		//写
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead);   		//读

//�����
void Test_Write(u32 WriteAddr,u16 WriteData);								   
#endif


stm32flash.c

#include "stmflash.h"
#include "delay.h"

u16 STMFLASH_ReadHalfWord(u32 faddr)//给地址读取半字
{
	return *(vu16*)faddr; 
}

#if STM32_FLASH_WREN

void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)//进行不检查的写
{ 			 		 
	u16 i;
	for(i=0;i<NumToWrite;i++)//写numtowrite个半字
	{
		FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
	    WriteAddr+=2;//写地址+2(由于是半字)
	}  
} 

#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024 //�ֽ�
#else 
#define STM_SECTOR_SIZE	2048
#endif		 

u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];

void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)//写
{
	u32 secpos;
	u16 secoff;
	u16 secremain;
 	u16 i;    
	u32 offaddr;

	if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//在可写空间之外写
	FLASH_Unlock();	//解锁flash
	offaddr=WriteAddr-STM32_FLASH_BASE;//计算偏移地址
	secpos=offaddr/STM_SECTOR_SIZE;	//计算所在sector的地址
	secoff=(offaddr%STM_SECTOR_SIZE)/2;	//计算sector的偏移量
	secremain=STM_SECTOR_SIZE/2-secoff;	//计算sector-sector偏移量

	if(NumToWrite<=secremain)secremain=NumToWrite;
	while(1) 
	{	
		STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);
		for(i=0;i<secremain;i++)
		{
			if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;
		}

		if(i<secremain)
		{
			FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除页
			for(i=0;i<secremain;i++)
			{
				STMFLASH_BUF[i+secoff]=pBuffer[i];	  //写
			}
			STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);
		}else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);
		if(NumToWrite==secremain)break;
		else
		{
			secpos++;
			secoff=0;
		   	pBuffer+=secremain;
			WriteAddr+=secremain;
		   	NumToWrite-=secremain;
			if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;
			else secremain=NumToWrite;
		}	 
	};	
	FLASH_Lock();
}
#endif


void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)//将数据读到缓存数组里
{
	u16 i;
	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);
		ReadAddr+=2;
	}
}



void Test_Write(u32 WriteAddr,u16 WriteData)//测试写一个半字
{
	STMFLASH_Write(WriteAddr,&WriteData,1);
}





主函数调用

#include "delay.h"
#include "sys.h"
#include "key.h"
#include "oled.h"
#include "stmflash.h"
const u8 TEXT_Buffer[]={"STM32 FLASH TEST"};
#define SIZE sizeof(TEXT_Buffer)	 	//数组长度
#define FLASH_SAVE_ADDR  0X08020000 	//设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)
 int main(void)
 {
	u8 datatemp[SIZE];
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
	delay_init();	    	 //延时函数初始化
 	KEY_Init();				//按键初始化
 	OLED_Init();

	while(1)
	{
		if(KEY0)
		{
			OLED_Refresh();
				//OLED_Refresh();
				OLED_ShowString(0,0,"start write",16,1);
				STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);
				OLED_ShowString(0,16,"write finished",16,1);
				OLED_ShowString(0,32,"                ",16,1);
				OLED_Refresh();

		}
		if(!KEY0){
				OLED_Refresh();
				OLED_ShowString(0,0,"start read ",16,1);
				STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE);
				OLED_ShowString(0,16,"read data is    ",16,1);
				OLED_ShowString(0,32,datatemp,16,1);
				OLED_Refresh();
			}
	}
}

学习目标

作业

  1. 阅读flash库
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

小松的STM32教程(14)——内部flash模拟eeprom 的相关文章

随机推荐

  • C语言关键字之联合体

    联合体 union 定义 比如 xff1a typedef union char a 10 int b UN 实验 include lt stdio h gt int main union test char a 10 int b unio
  • 纯C语言创建(带与不带头节点)单链表、头插法、尾插法、删除、遍历、逆转

    include lt stdio h gt include lt stdlib h gt typedef struct Node int data struct Node next Node 创建带头结点的单链表 Node initNode
  • 关于pycharm依赖库 install to user‘s package directory 的排坑日记

    今天基于 unittest 写了一个接口自动化脚本 xff0c 右键 Run 没有问题 但是我想放到我本地的 jenkins xff0c 所以想先以 dos 命令试试 果不其然 xff0c 报错了 害 xff0c 开始排坑 xff08 报错
  • 【洛谷P4180】严格次小生成树

    题目大意 xff1a 给定一个 N 个顶点 xff0c M 条边的带权无向图 xff0c 求该无向图的一个严格次小生成树 引理 xff1a 有至少一个严格次小生成树 xff0c 和最小生成树之间只有一条边的差异 题解 xff1a 通过引理可
  • 有哪些ubuntu上的文件对比的可视化工具?

    在 Ubuntu 上进行文件对比的可视化工具有很多 xff0c 以下是其中一些常见的工具 xff1a Meld xff1a Meld 是一种图形化文件和文件夹比较工具 xff0c 它支持两个或三个文件的比较 Meld 可以高亮显示差异 xf
  • ant design vue pro 面包屑移至最顶上

    ant design vue pro 面包屑移至最顶上显示 xff0c 如图 操作如下 xff1a 1 在src layouts BasicLayout vue 中 lt pro layout 中加入如下代码 lt template v s
  • VMware中ubuntu系统出现花屏和蓝屏的解决方案

    VMware虚拟机中打开的ubuntu系统 xff0c 由于暴力关机或某些原因突然死机 xff0c 关闭VMware软件后 xff0c 再次启动ubuntu系统 xff0c 进入后出现花屏和蓝屏的现象 解决方案一 xff1a 在键盘上同时按
  • Qt多线程总结

    一 Qt下使用线程主要有两种方法 一种是传统的继承QThread类 xff0c 重写run方法 该方法已经落伍了 xff0c 主要原因线程不安全 xff0c 需要自己手动加锁 xff0c 比较麻烦 xff0c 所以推荐使用方法二 定义一个工
  • 关于 vue3运行报错Internal server error: [@vue/compiler-sfc] <script setup> cannot contain ES 的处理方法

    大致的意思就是 script setup 不能使用ES模块导出 其实问题就出在 xff0c 给官方给出的方法混用了 一种是 xff1a lt script gt 标签里面配置 setup 另一种是 xff1a export default
  • sublime text 4 4126 已测可用

    1 打开浏览器进入网站https hexed it 2 打开sublime text4安装目录选择文件sublime text exe 3 搜索80 78 05 00 0f 94 c1更改为c6 40 05 01 48 85 c9 第一个匹
  • docker安装的mysql8修改my.cnf,无法启动容器解决

    参考 xff1a docker修改mysql配置文件后 xff0c 无法启动mysql容器 super ye man的博客 CSDN博客 docker 启动的mysql 配置文件 新手上路 xff0c 使用docker容器必定会遇到一些坑
  • armbian取消休眠去屏保并安装中文输入法

    dpms显示器休眠设置 xff1a 开启 xff1a sudo xset dpms 1 1 2 取消 xff1a sudo xset dpms xset设置屏保 xff1a 设置10秒后进入屏保 xff1a sudo xset s 10 6
  • QT主界面卡死崩溃解决(5种方法)

    声明 本文来自转载 文章作者 张小飞 文章链接 https cryfeifei cn 2020 05 28 qt zhu jie mian qia si de jie jue fang an yi xie ju ti shi xian fa
  • 数字电路(三)最小项和最大项

    逻辑抽象和描述 把实际问题抽象成变量和逻辑函数根据逻辑函数绘制框图使用真值表或者自然语言描述函数表达式 最小项和最大项 最小项和最大项的定义SSOP和 sum 记法最大项 xff1a 下标和真值表行号一样 xff0c 进制转换值要取反 同一
  • 自己写库文件

    自己写库文件 在项目工程文件里写 h文件写在include文件夹里c文件写在src文件里面 在系统文件夹里面写 找到系统的库文件夹 xff08 在json文件里面找 xff09 在src文件夹里面添加h文件和c文件注意新建二级文件可能会出错
  • 数字电路(四)多级输出

    多级电路 什么是多级电路 xff1a 级数大于一个级的电路如何读电路的级数 xff1a 由外向里 xff0c 层层数多级电路的优缺点 xff1a 优点是可以减少门和输入的数量 xff0c 进而减少成本 xff0c 缺点是增加电路的延时如何得
  • echarts 饼图中间自定义显示内容

    做驾驶舱时 xff0c 使用到空心饼图中间显示自定义内容 xff0c 两种方式实现 1 title 进行定位 效果差点 2 graphic 查询 api后 xff0c 最为符合的使用 graphic 代码如下 xff0c 完美实现上图效果
  • 云服务器(一)基本操作

    xshell远程连接云服务器 乌班图 修改密码注意乌班图默认是禁止ssh远程登录的 xff0c 需要进行设置参考此处 VNC图形界面 添加防火墙规则 xff1a VNC使用的是TCP协议 默认端口5901远程ssh连接安装一些必备的包 xf
  • 小松的STM32教程(6)——ADC

    预备 学习目标 学会调用Get Adc Average函数会使能多个ADC通道 概述 ADC输入电压范围 xff1a 2 4V 3 6V xff0c 一般使用0V和3 3V 数据对齐 xff1a 16bit存放12bit的转换数据 xff0
  • 小松的STM32教程(14)——内部flash模拟eeprom

    为什么要用Flash来模拟EEPORM Flash更适合作为程序存储器 xff0c EEPROM更适合作为数据存储器 xff0c 存储大量数据的时候就需要使用EEPROM xff0c 廉价的MCU往往只有FLASH而没有EEPROM xff