在我们应用开发时,经常会有一些程序运行参数需要保存,如一些修正系数。这些数据的特点是:数量少而且不需要经常修改,但又不能定义为常量,因为每台设备可能不一样而且在以后还有修改的可能。将这类数据存在指定的位置,需要修改时直接修改存储位置的数值,需要使用时则直接读取,会是一种方便的做法。考虑到这些数据量比较少,使用专门的存储单元既不经济,也没有必要,而stm32f103内部的flash容量较大,而且st的库函数中还提供了基本的flash操作函数,实现起来也比较方便。
以大容量产品stm32f103zet为例,其flash容量达到512k,可以将其中一部分用作数据存储。如下是大容量的flash组织模式:
stm32的闪存模块由:主存储器、信息块和闪存存储器接口寄存器等 3 部分组成。
1)主存储器,该部分用来存放代码和数据常数(如 const 类型的数据)。对于大容量产品,其被划分为 256 页,每页 2k 字节。注意,小容量和中容量产品则每页只有 1k 字节。从上图可以看出主存储器的起始地址就是0x08000000, b0、b1 都接 gnd 的时候,就是从 0x08000000开始运行代码的。
2)信息块,该部分分为 2 个小部分,其中启动程序代码,是用来存储 st 自带的启动程序,用于串口下载代码,当 b0 接 v3.3,b1 接 gnd 的时候,运行的就是这部分代码。用户选择字节,则一般用于配置写保护、读保护等功能,本章不作介绍。
3)闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制机构。对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(fpec)管理;编程与擦除的高电压由内部产生。
在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行;既在进行写或擦除操作时,不能进行代码或数据的读取操作。
根据上面的flash组织模式,我们可以根据自己的使用方便来作相应的定义。因为大容量每个扇区定义为2k,而小容量和中容量都定义为1k,所以我们做如下宏定义:
#define flash_size 512 //所选mcu的flash容量大小(单位为k)
#if flash_size《256
#define sector_size 1024 //字节#else
#define sector_size 2048 //字节#endif
虽然st的库函数比较全面,但都是基本操作,为了使用方面,根据我们自己的需要对其进行再次封装。
对于读操作相对比较简单,内置闪存模块可以在通用地址空间直接寻址,就像读取变量一样。
//从指定地址开始读取多个数据void flash_readmoredata(uint32_t startaddress,uint16_t *readdata,uint16_t counttoread)
{
uint16_t dataindex;
for(dataindex=0;dataindex《counttoread;dataindex++)
{
readdata[dataindex]=flash_readhalfword(startaddress+dataindex*2);
}
}
//读取指定地址的半字(16位数据)uint16_t flash_readhalfword(uint32_t address)
{
return *(__io uint16_t*)address;
}
//读取指定地址的全字(32位数据)uint32_t flash_readword(uint32_t address)
{
uint32_t temp1,temp2;
temp1=*(__io uint16_t*)address;
temp2=*(__io uint16_t*)(address+2);
return (temp2《《16)+temp1;
}
对于写操作相对来说要复杂得多,写操作包括对用户数据的写入和擦除。为了防止误操作还有写保护锁。但这些基本的操作st的库函数已经为我们写好了,我们只需要调用即可。
stm32复位后,fpec模块是被保护的,只有在写保护被解除后,我们才能操作相关寄存器。stm32闪存的编程每次必须写入16位,任何不是半字的操作都会造成错误。如下图是flash写的过程:
stm32的flash在编程的时候,也必须要求其写入地址的flash 是被擦除了的(也就是其值必须是0xffff),否则无法写入。flash的擦除要求必须整页擦除,所以也必须整页写入,否则可能会丢失数据。如下图是flash页擦除过程:
如下为flash全擦除过程,
根据以上图示我们便写数据写入函数如下:
//从指定地址开始写入多个数据void flash_writemoredata(uint32_t startaddress,uint16_t *writedata,uint16_t counttowrite)
{
if(startaddress《flash_base||((startaddress+counttowrite*2)》=(flash_base+1024*flash_size)))
{
return;//非法地址 }
flash_unlock(); //解锁写保护
uint32_t offsetaddress=startaddress-flash_base; //计算去掉0x08000000后的实际偏移地址
uint32_t sectorposition=offsetaddress/sector_size; //计算扇区地址,对于stm32f103vet6为0~255
uint32_t sectorstartaddress=sectorposition*sector_size+flash_base; //对应扇区的首地址
flash_erasepage(sectorstartaddress);//擦除这个扇区
uint16_t dataindex;
for(dataindex=0;dataindex《counttowrite;dataindex++)
{
flash_programhalfword(startaddress+dataindex*2,writedata[dataindex]);
}
flash_lock();//上锁写保护
}
在擦除之前应该将页面上的数据读取出来与要写入的数据合并,待擦除后再写入,但这样数据量很大(大容量是2k一个扇区),所以考虑到是少量数据存储,所以每次都将全部数据同时写入,简化操作,也减少数据处理量。经测试以上程序写入和读出数据均正确,可以实现内部flash的读写操作。
MAX66020 ISO/IEC 14443 B型1Kb存储器
红外感应开关
PCB阻抗设计主要类型及影响因素
无需电池的物联网设备说明了什么
桥田快换产品在超声波焊接工艺的应用
stm32f103zet6如何识别flash大小
舵机对比伺服电机,步进电机有哪些优点?
5G的发展对于我国科技和经济发展来说是难得的机遇
几种最基本的运算放大电路设计
蓝牙室内定位与UWB融合组网解决方案的说明
ABB正式成立工业自动化事业部中国技术中心
未来物联网发展的五大趋势
WiSA与瑞昱半导体(Realtek)合作开发并推出一款5GHz多通道沉浸式音频模块
贸泽电子荣获MEAN WELL颁发的年度最佳分销商奖
戴尔已经移除受影响的BIOS 携手Intel推送新版本
云计算关键技术与研究问题
企业可参考借鉴AI在医疗领域的成功经验
AMD做减法VS英特尔做加法,Fabless大战IDM?
白酒甲醇快速检测仪器有哪些特点
比亚迪车载充电机(OBC)选用国产碳化硅拥有更强竞争力