采用STM32库中的擦写库函数进行基本的擦除操作,读写通过指针实现。
芯片为STM32051,因此一个字为4个字节,每个字节占一个地址,芯片Flash大小64K。
执行读写逻辑为若Flash中读取为空,则写入我们需要的初值,否则读出数据赋给我们的变量。因为写入时需要擦除才可以写入,而单片机有擦写寿命,因此我们采用擦写一定次数(几万次一般都可以接受,ST手册上应该是10万次),然后用记忆页的后一页进行读写以保证长期频繁的擦写使用,这里让单片机的最后3K供记忆功能使用。
工程中遵循在主循环运行之前先读数据的方式来获取记忆值,然后需要记忆时调用写函数即可。
具体实现过程如下:
#define MCUFlashCapacity 64 //MCUFlash大小
#define StartPage 62 //记忆Flash所在的起始页
#define MaxAddrCnt 2 //最大变更页次数(MCUFlashCapacity-StartPage)
#define FlashEraseCnt 15000 //每页擦写次数
#define NumberOfData 16 //16个数据
u32 *WriteStart_Addr; //写操作起始地址
u16 *ReadStart_Addr; //读起始地址
u8 G_AddrCnt_8u; //换页次数
u16 CntValue_u16 = 0; //擦写次数
u16 Peremeter1 = 0; //实验参数
u16 ReadWriteDate[NumberOfData]; //读写数据传递数组
void FlashRead(void)
{
u32 *TempAddr;
static u16 L_AddrCnt_8u = 0;
u16 num;
u16 *data;
u16 *ReadAddrTemp;
static u32 temp_addr;
for(L_AddrCnt_8u = 0;L_AddrCnt_8u < 3;L_AddrCnt_8u++) //检查已经写至第几预留记忆页
{
TempAddr = (u32 *)((0x08000000 + (StartPage-1) * 1024) + L_AddrCnt_8u * 1024);
if((*TempAddr) == 0xFFFFFFFF)//若为空,说明正在使用的页为前一页或还未进行过写操作为第一次操作,当第一次操作时,直接跳出无自增L_AddrCnt_8u为0
{
break;
}
}
num=NumberOfData;
if(L_AddrCnt_8u == 0) //第一个为空:还没写,写入初值
{
data=ReadWriteDate;
WriteStart_Addr = TempAddr;
temp_addr = (u32)WriteStart_Addr;
G_AddrCnt_8u = 0;
/*需要记忆的参数初值设定*/
CntValue_u16=0;
Peremeter1=1;
ReadWriteDate[0] = CntValue_u16;
ReadWriteDate[1] = Peremeter1;
ReadWriteDate[2] = 0x0000;
ReadWriteDate[3] = 0x0000;
ReadWriteDate[4] = 0x0000;
ReadWriteDate[5] = 0x0000;
ReadWriteDate[6] = 0x0000;
ReadWriteDate[7] = 0x0000;
ReadWriteDate[8] = 0x0000;
ReadWriteDate[9] = 0x0000;
ReadWriteDate[10] = 0x0000;
ReadWriteDate[11] = 0x0000;
ReadWriteDate[12] = 0x0000;
ReadWriteDate[13] = 0x0000;
ReadWriteDate[14] = 0x0000;
ReadWriteDate[15] = 0x0000;
/*需要记忆的参数初值设定*/
FLASH_Unlock();
while(num --)
{
FLASH_ProgramHalfWord(temp_addr,*data);//写半字,一次写两个字节
temp_addr += 2;//每8位占一个地址,因此需要自增2个地址
data++;//数据地址执政自增
}
FLASH_Lock();
}
else if(L_AddrCnt_8u < 3) //在前两个记忆页
{
WriteStart_Addr =(u32 *)((u32)TempAddr - 1024); //取前一页的地址为读起始地址
ReadStart_Addr=(u16 *)((u32)TempAddr - 1024); //取前一页的地址为写起始地址
CntValue_u16 = (u16)(*WriteStart_Addr); //由于WriteStart_Addr为u32的指针,指向的数据为u32的数据,而我们保存的数据都是两个字节的,所以需要转化一下
G_AddrCnt_8u = L_AddrCnt_8u - 1;
}
else //L_AddrCnt_8u=3时说明已经在最后一个记忆页
{
WriteStart_Addr = (u32 *)(TempAddr);
ReadStart_Addr=(u16 *)(TempAddr);
CntValue_u16 = (u16)(*WriteStart_Addr);
G_AddrCnt_8u = MaxAddrCnt;
}
/*v读存储的数据v*/
if(CntValue_u16 < (3*FlashEraseCnt)&&L_AddrCnt_8u!=0) //写满3页后不再读取Flash存储值,直接使用初始化值
{
ReadAddrTemp = ReadStart_Addr;
data=ReadWriteDate;
while(num --)
{
*data = *ReadAddrTemp;
ReadAddrTemp++;
data++;
}
CntValue_u16 = ReadWriteDate[0];
Peremeter1 = ReadWriteDate[1];
// = ReadWriteDate[2];
// = ReadWriteDate[3];
// = ReadWriteDate[4];
// = ReadWriteDate[5];
// = ReadWriteDate[6];
// = ReadWriteDate[7];
// = ReadWriteDate[8];
// = ReadWriteDate[9];
// = ReadWriteDate[10];
// = ReadWriteDate[11];
// = ReadWriteDate[12];
// = ReadWriteDate[13];
// = ReadWriteDate[14];
// = ReadWriteDate[15];
}
/*^读存储的数据^*/
}
void FlashWrite(void)
{
u16 num;
u16 *data;
static u32 temp_addr;
data=ReadWriteDate;
CntValue_u16 = (u16)(*WriteStart_Addr);//使用时每次会先读,在读取处已经知晓写的起始地址
num=NumberOfData;
/*需要记忆的参数*/
ReadWriteDate[0] = CntValue_u16+1;
ReadWriteDate[1] = G_MotorGear_16u;
ReadWriteDate[2] = 0x0000;
ReadWriteDate[3] = 0x0000;
ReadWriteDate[4] = 0x0000;
ReadWriteDate[5] = 0x0000;
ReadWriteDate[6] = 0x0000;
ReadWriteDate[7] = 0x0000;
ReadWriteDate[8] = 0x0000;
ReadWriteDate[9] = 0x0000;
ReadWriteDate[10] = 0x0000;
ReadWriteDate[11] = 0x0000;
ReadWriteDate[12] = 0x0000;
ReadWriteDate[13] = 0x0000;
ReadWriteDate[14] = 0x0000;
ReadWriteDate[15] = 0x0000;
/*需要记忆的参数*/
if(CntValue_u16 < ((G_AddrCnt_8u+1)*FlashEraseCnt)) //判断换页
{
temp_addr = (u32)WriteStart_Addr;
FLASH_Unlock();
FLASH_ErasePage((u32)WriteStart_Addr); // 擦除指定的页
while(num --)
{
FLASH_ProgramHalfWord(temp_addr,*data);
temp_addr += 2;//每8位占一个地址
data++;
}
FLASH_Lock();
}
else
{
if((u32)WriteStart_Addr < 0x0800FC00) //在有效范围内,正常切换地址,最后地址写满数据则不在记录
{
WriteStart_Addr = (u32 *)((u32)WriteStart_Addr+1024);
temp_addr = (u32)WriteStart_Addr;
FLASH_Unlock();
FLASH_ErasePage((u32)WriteStart_Addr); // 擦除指定的页
while(num --)
{
FLASH_ProgramHalfWord(temp_addr,*data);
temp_addr += 2;//每8位占一个地址
data++;
}
FLASH_Lock();
}
}
}