先看Flash操作的简单流程
1、确定要写入Flash的首地址(稍后介绍确定地址的方法)
2、解锁Flash
3、对Flash进行操作(写入数据)
4、对Flash重新上锁
但是在实际项目中,直接操作FLASH地址的话,会导致单片机flash寿命大大降低,具体用啥方法呢,在官方给的优化文档中,采用虚拟地址的方法来读写存储数据。
一、falsh模拟eeprom,如何移植到自己的工程中,非常详细的,然后我们在分析虚拟地址,下面是具体操作流程
1、在ST 的网站上下载 AN2594 及例程。
2、在例程中src目录中找到eeprom.c 及inc目录中找到eeprom.h拷贝到自己的工程目录中。
3.、在工程中添加这个eeprom.c
4、在工程中的外设库里添加 stm32f10x_flash.c
5、在eeprom.c 文件中包含 #include "stm32f10x_flash.h"
6、main.c中包含include "eeprom.h"
7、添加数据,在 AN2594中,存入到FLASH的数据是以16位的方式存的。每个数据包含数据本身,还需要存16位的虚拟地址。所以在main.c中需要定义虚地址。:
/* Virtual address defined by the user: 0xFFFF value is prohibited */
uint16_t VirtAddVarTab[NumbOfVar] = {0x5555, 0x6666, 0x7777};
需要多少个16位的数据,将定义在 eeprom.h 中的将NumbOfVar修改成需要的个数。虚地址需要是16位,非0xFFFF,互不重复就可以。
定义数据,如果实际的数据不是16位的,可以定义一个联合来解决。
union {
uint16_t SeatStorage[NumbOfVar+1];
uint8_t SeatByte[NumbOfVar * 2+2];
}Seat;
8、使用时,先初始化
EE_Init();
读出数据
if((0 == EE_ReadVariable(VirtAddVarTab[0], &Seat.SeatStorage[0])) &&
(0 == EE_ReadVariable(VirtAddVarTab[1], &Seat.SeatStorage[1])) &&
(0 == EE_ReadVariable(VirtAddVarTab[2], &Seat.SeatStorage[2])))
// 可以用EE_ReadVariable的返回值是否为0判断FLASH中存储的数据是否有效。
就是把虚拟地址的数据读出到数组SeatStorage中。
二、虚拟地址的具体原理分析
这是STM32模拟EEPROM的使用和优化文档的部分截图
如图中所示,你就明白什么是虚拟地址。
对于EPPROM,读取数据是通过I2C的,传入的是Addr,读出的是数据。这里地址就是物理地址。
对于FLASH模拟EPPROM,我们假设FLASH里面的一块区域0x10000000-0x10001000这4K空间用来模拟,其中数据结构都是
typedef Struct
{
UINT16 data;
UINT16 Address;
}STRUCT_EPPROM;
若在0x10000000处的数据为{0x0001, 0x5555}这里面0x10000000就是物理地址,结合一些逻辑和接口,就可以通过0x5555(结构体中的Address)这个地址找到数/数组,也就是被称为虚拟地址的原因data就是内部存储的数据。
意思就是如果变量是 16 位,则每个变量都占用 32 位( 16 位数据加 16 位虚拟地址),这意味着每次写入新数据时,各个变量分别使用 4 字节的 Flash 。也就是每个变量实际占用四个物理地址,每个 1 KB 页在变满之前可执行 256 次 变量写入。
#define EEPROM_START_ADDRESS ((uint32_t)0x0800F000) /* EEPROM emulation start address:
after 4KByte of used Flash memory */
结束地址就是0x0800FFFF,就是4k的空间
举个实例
在EEPROM.H中修改
/* Variables' number */
#define NumbOfVar ((uint8_t)0x03) 定义要写的 halfword 数量。
用的时候需要定义一个数组
/* Virtual address defined by the user: 0xFFFF value is prohibited */
uint16_t VirtAddVarTab[NumbOfVar];
需要初始化
1.虚拟地址写数据
uint16_t EE_WriteVariable(uint16_t VirtAddress, uint16_t Data); //写VirtAddress 取VirtAddVarTab中内容,相当于编号为0至NumbOfVar-1个数据写入模拟EEPROM中,写入时写入数据紧跟后面写入虚地址VirtAddVarTab(0<=i<NumbOfVar)
相同地址再次写入时不会把上次写的擦掉,而是在模拟EEPROM区尾部未写过的地方再次写入数据、虚地址,读的时候是从尾部开始匹配地址,也就是读取最后一次写的内容。模拟EEPROM区分为2页,如果一页满了把这一页内地址不重复的数据复制到另一页后擦除,2页交替使用。
2.虚拟地址读数据
uint16_t EE_ReadVariable(uint16_t VirtAddress, uint16_t* Data); //读
这个函数返回的是虚拟地址所对应的数据,虚拟地址是作为一个参数被传送的。只有最后更新的被读取。这个函数进入的时候是一个循环,在这个循环内读变量目录到最后一个。如果找不到该地址,读状态变量将返回1,否则就重置表明变量已经被发现并且变量值被返回到Data变量。