本存储代码占用Ram资源极少,不占用Flash用于存储数据的空间,采用存储空间循环使用方法达到延长flash使用寿命,还可用于E2PROM,外扩Flash等平台,不受数据结构类型限制。注意需要不掉电后备寄存器来保存当前正使用的存储数据地址和当前页数据存储个数的未使用量,大家也可根据自己需要稍作修改以适应不同的平台。
一、源代码:
//头文件,根据需要修改
#define FlashDAT_StarDress 0X08050000//Flash用于存储数据起始地址
#define FlashDAT_Count 5//存储区占用多少页
#define FlashPagesize 2048//页大小(具体平台) 檫除单位 2K
#define Flash_SmalSize_W 2//flash最小写入单位(具体平台)
#define Flash_SmalSize_R 2//flash最小读单位
//第1个Size_Write--本页是否使用 数据从第2个Size_Write开始存储
/*
本函数用于对某个数据源的存储空间进行初始化,不需要初始化的成员就不用初始化,这些成员将在初始化函数中进行初始化,函数指针成员指向的函数需要根据具体存储平台来实现。
*/
typedef struct s_FlashSave //支持8位或16位数据读写 跨存储平台
{
my_u8 *pName;//数据名
my_u8 *pDat;//要存储的数据源 数据结构类型不限
my_u8 BkUpDatStar;//后备区数据起始寄存器
my_u8 DatSize;//不初始化 数据大小(以最小写入字节数为单位) (存储数据结构大小/Flash_SmalSize_W 如果取余不为0则再+ Flash_SmalSize_W)//一个数据占用存储空间大小
my_u16 DatPageCount;//本数据源占用了几页 单位
my_u16 pDatPage;//不初始化 当前运行占用页(上电推算)
my_u16 pDatCcount;//不初始化 当前运行页数据剩余保存量(后备区保存)
my_u32 DatSavStar;//保存本数据源的起始地址(防掉电 手动指定) 第一个字节=1,表示本页被占用 =2坏页,=3存储有数据
my_u32 pDatDress;//数据保存的当前运行地址 (后备区保存)
--------------以下函数需要根据具体平台来实现---------------------
void (*pFunFlash_W)(my_u32 Dress,my_u8 *pDate);//写数据 可给16位数据
my_u16 (*pFunFlash_R)(my_u32 Dress);//读数据
my_u8 (*pFunFlash_Erase)(my_u32 PageDress);//檫除整页 檫除成功返回1 失败返回0
void (*pFunBkUpDat_W)(my_u8 BkUp_DRx,my_u16 Dat);//写数据到后备寄存器
my_u16 (*pFunBkUpDat_R)(my_u8 BkUp_DRx);//读后备寄存器
void (*pFunDatOut)(my_u16*);//数据输出函数
}sFlashSave;
/*
初始化函数,DatSize数据源数据结构大小,可用sizeo计算
*/
void Flash_DATSave_Init(sFlashSave *psFlashSave,my_u8 DatSize)//上电初始化 分配存储空间 DatSize数据源数据结构大小
{
my_u32 pDress = psFlashSave->DatSavStar;//
my_u16 n = 0;
my_u16 date = 3;
psFlashSave->DatSize = DatSize/Flash_SmalSize_W;//一次写入Flash_SmalSize_W字节
if(DatSize%Flash_SmalSize_W) psFlashSave->DatSize ++;
psFlashSave->pDatCcount = (FlashPagesize -Flash_SmalSize_W)/(psFlashSave->DatSize *Flash_SmalSize_W);
psFlashSave->pDatPage = 1;
if(psFlashSave->pFunBkUpDat_R(psFlashSave->BkUpDatStar)||
psFlashSave->pFunBkUpDat_R(psFlashSave->BkUpDatStar +1))//上电从后备寄存器恢复写地址
{
psFlashSave->pDatDress = psFlashSave->pFunBkUpDat_R(psFlashSave->BkUpDatStar);
psFlashSave->pDatDress |= psFlashSave->pFunBkUpDat_R(psFlashSave->BkUpDatStar +1) <<16;
psFlashSave->pDatCcount = psFlashSave->pFunBkUpDat_R(psFlashSave->BkUpDatStar +2);
psFlashSave->pDatPage = (psFlashSave->pDatDress -psFlashSave->DatSavStar)/FlashPagesize +1;
return;
}
//第一次上电
if((pDress +psFlashSave->DatPageCount*FlashPagesize) >=(FlashDAT_StarDress +FlashDAT_Count*FlashPagesize))//超出数据存储空间
{
psFlashSave->pDatDress = 0;//无存储空间
return;
}
//*********************************************************************************
n = psFlashSave->DatPageCount;
// FLASH_Unlock;//flash解锁
while(n--)//檫除存储页 以判断存储页是否损坏
{
if(n >psFlashSave->DatPageCount)
{
psFlashSave->pDatDress = 0;//存储区损坏
return;
}
if(psFlashSave->pFunFlash_Erase(pDress))
{
psFlashSave->pFunFlash_W(pDress,(my_u8*)&date);//占用标识
date = 1;//占用 3有数据
}
else
{
date = 2;//无效
psFlashSave->pFunFlash_W(pDress,(my_u8*)&date);//檫除失败
}
pDress += FlashPagesize;//下一页
}
// FLASH_lock;//flash加锁
psFlashSave->pDatDress = psFlashSave->DatSavStar +Flash_SmalSize_W;
}
/*
本函数用于写数据到存储区,每次数据存储后更新后备寄存器中数据的存储地址和本页剩余存储个数
*/
void WriteDatFlash(sFlashSave *psFlashSave)//写数据
{
my_u16 n = 0;
my_u16 date = 1;
if((!psFlashSave->DatPageCount)||(!psFlashSave->pDatDress)) return;//没分配存储空间
// FLASH_Unlock;//flash解锁
if(!psFlashSave->pDatCcount)//当前页空间不足
{
while(psFlashSave->pDatPage++)
{
n ++;
if(n >psFlashSave->DatPageCount)
{
printf("%s-Flash存储空间损坏!",psFlashSave->pName);
return;
}
if(psFlashSave->pDatPage > psFlashSave->DatPageCount)
psFlashSave->pDatPage = 1;
psFlashSave->pDatDress =psFlashSave->DatSavStar +(psFlashSave->pDatPage -1) *FlashPagesize;//下一页
if(((my_u8)psFlashSave->pFunFlash_R(psFlashSave->pDatDress)) !=2)
{
if(psFlashSave->pFunFlash_Erase(psFlashSave->pDatDress))//檫除成功
{
date = 3;//表示存储有数据
psFlashSave->pFunFlash_W(psFlashSave->pDatDress,(my_u8*)&date);//占用标识
psFlashSave->pDatDress += Flash_SmalSize_W;
psFlashSave->pDatCcount = (FlashPagesize -Flash_SmalSize_W)/(psFlashSave->DatSize *Flash_SmalSize_W);
break;
}
else
{
date = 2;
psFlashSave->pFunFlash_W(psFlashSave->pDatDress,(my_u8*)&date);//檫除失败
}
}
}
}
n = psFlashSave->DatSize;
while(n--)
{
psFlashSave->pFunFlash_W(psFlashSave->pDatDress,psFlashSave->pDat);//写数据 写函数映射
if(Flash_SmalSize_W ==2)//2个字节
{
psFlashSave->pDatDress += 2;
psFlashSave->pDat += 2;
}
else
{
psFlashSave->pDatDress ++;
psFlashSave->pDat ++;
}
}
psFlashSave->pDat -= psFlashSave->DatSize *Flash_SmalSize_W;//退回到数据源
psFlashSave->pDatCcount --;
// FLASH_lock;//flash加锁
psFlashSave->pFunBkUpDat_W(psFlashSave->BkUpDatStar,psFlashSave->pDatDress);//低16位地址
psFlashSave->pFunBkUpDat_W(psFlashSave->BkUpDatStar +1,psFlashSave->pDatDress >>16);//高16位地址
psFlashSave->pFunBkUpDat_W(psFlashSave->BkUpDatStar +2,psFlashSave->pDatCcount);//写数据到后备寄存器
}
/*
读最新数据到某处,如上位机或其它存储设备,有函数指针指向的输出函数而定; RCount =0表示读出所有数据,否则读出 RCount个数据,先读以前的数据再读最近一个存储的数据
*/
void ReadDatFlash(sFlashSave *psFlashSave,my_u32 RCount)//读数据 从头读到尾 Count = 0则读出全部数据 否则读最新的Count个数据(先读以前的)
{
my_u32 pDatStarDess = psFlashSave->DatSavStar;//数据起始页地址
my_u32 pDatCount =0;//数据量
my_u32 PagDatCount =(FlashPagesize -Flash_SmalSize_W)/(psFlashSave->DatSize *Flash_SmalSize_W);//1页存储数据量
my_u8 DatSize = psFlashSave->DatSize;
my_u16 pageN = 1;
my_u16 DatOut =0;
//下一页有数据则下一页为数据起始页
//下一页没数据则第一页为数据起始页
//Count大于实际数据存储数量,以实际数据存储数量为准
//Count小于实际数据存储数量,以Count数量为准
//Count = 0则读出全部数据
//--------------- 得到数据存储的起始地址pDatStarDess ------------------------------
if((!psFlashSave->DatPageCount)||(!psFlashSave->pDatDress)) return;//无存储空间
pageN = psFlashSave->pDatPage;
if(RCount ==0)//读出全部数据 输出方式 先旧后新
{
pDatCount = PagDatCount;//预设
while(1)
{
if((++pageN) >psFlashSave->DatPageCount)
pageN = 1;
pDatStarDess = psFlashSave->DatSavStar +(pageN -1)*FlashPagesize;//指向页首
if(pageN == psFlashSave->pDatPage)//找回到自己
{
if(psFlashSave->pDatCcount ==PagDatCount) return;//没有数据
break;//没有找到有效的前页
}
if(((my_u8)psFlashSave->pFunFlash_R(pDatStarDess)) == 3)
break;
}
}
else//读最新的RCount个数据 输出方式 先旧后新
{
pDatCount = PagDatCount -psFlashSave->pDatCcount;//本页数据量
if(RCount <= pDatCount)//本页内
{
pDatStarDess = psFlashSave->pDatDress - RCount *(psFlashSave->DatSize *Flash_SmalSize_W) -Flash_SmalSize_W;
pDatCount = RCount;
}
else
{
pDatCount = RCount -pDatCount;
while(1)
{
if((--pageN) ==0)
pageN = psFlashSave->DatPageCount;
pDatStarDess = psFlashSave->DatSavStar +(pageN -1)*FlashPagesize;//指向页首 因为页尾肯能有多余的垃圾空间(存不下一个数据)
if(pageN == psFlashSave->pDatPage)
break;//没有找到有效的前页
if((((my_u8)psFlashSave->pFunFlash_R(pDatStarDess)) == 1)&&(pageN == psFlashSave->DatPageCount))//没有数据
{
pDatCount = PagDatCount;//预设 因为返回找
pDatStarDess = psFlashSave->DatSavStar;
pageN =1;
while(1)
{
if((pageN == psFlashSave->pDatPage)&&(psFlashSave->pDatCcount ==PagDatCount))//找回到自己
return;//没有数据
else if(((my_u8)psFlashSave->pFunFlash_R(pDatStarDess)) ==3)//有数据
break;
pageN ++;
pDatStarDess += FlashPagesize;
}
break;
}
if(((my_u8)psFlashSave->pFunFlash_R(pDatStarDess)) ==3)//有数据
{
if(pDatCount <=PagDatCount)
{
pDatStarDess += (PagDatCount -pDatCount) *(psFlashSave->DatSize *Flash_SmalSize_W);
break;
}
else
{
pDatCount -= PagDatCount;
pDatStarDess -= FlashPagesize;
}
}
}
}
}
//-------------------------------读数据--------------------------------
pDatStarDess += Flash_SmalSize_W;//去除页标识
printf("%s-数据:",psFlashSave->pName);
while(1)//读出最新数据
{
if((pageN == psFlashSave->pDatPage)&&((RCount ==0)||(RCount > (PagDatCount -psFlashSave->pDatCcount))))
pDatCount = PagDatCount -psFlashSave->pDatCcount;
while(pDatCount --)
{
DatSize = psFlashSave->DatSize;
while(DatSize--)//数据单位大小
{
DatOut = psFlashSave->pFunFlash_R(pDatStarDess);//读数据 读函数映射
psFlashSave->pFunDatOut(&DatOut);
if(Flash_SmalSize_R ==2)//读2字节
pDatStarDess += 2;
else
pDatStarDess ++;
}
printf("\n");//调试用 便于查看数据 换行
}
if(pageN == psFlashSave->pDatPage) return;//读出结束
while(1)
{
if((++pageN) > psFlashSave->DatPageCount)
pageN =1;
pDatStarDess = psFlashSave->DatSavStar +(pageN -1)*FlashPagesize;
if(((my_u8)psFlashSave->pFunFlash_R(pDatStarDess)) ==3) //有效页
{
pDatCount = PagDatCount;
pDatStarDess += Flash_SmalSize_W;break; //去除页标识
}
}
}
}
二、使用方法:使用环境,Keil5,stm32f103vct6,HAL库
1.根据存储平台实现需要的读写函数
a.写片上Flash函数
void WriteFlash1(my_u32 Dress,my_u8 *pDate)//写数据 写函数映射
{
HAL_FLASH_Unlock();//flash解锁
if(Flash_SmalSize_W ==1)//写1个字节
*(my_u32 *)Dress = *pDate;
if(Flash_SmalSize_W ==2)//写2个字节
HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD,Dress,((*(pDate+1))<<8)+(*pDate));//16位
HAL_FLASH_Lock();//flash加锁
}
b.读片上Flash函数
my_u16 ReadFlash1(my_u32 Dress)//读数据 读函数映射
{
if(Flash_SmalSize_W ==1)//1个字节
return *(my_u8*)Dress;
if(Flash_SmalSize_W ==2)//2个字节
return *(my_u16*)Dress;
}
c.檫除片上Flash页 檫除成功返回1 失败返回0
my_u8 FlashSect_Erase1(my_u32 PageDress)//檫除整页
{
my_u32 PageError =0;//无用
HAL_FLASH_Unlock();//flash解锁
FLASH_EraseInitTypeDef EraFLASH;
EraFLASH.NbPages = 1;//单次檫除1页
EraFLASH.PageAddress = PageDress;//檫除起始地址
EraFLASH.TypeErase = FLASH_TYPEERASE_PAGES;//按页檫除
PageError = HAL_FLASHEx_Erase(&EraFLASH,&PageError);
HAL_FLASH_Lock();//flash加锁
if(PageError == HAL_OK)
return 1;//檫除成功
else
return 0;//檫除失败
}
d.写后备寄存器
void WritBkUpDat1(my_u8 BkUp_DRx,my_u16 Dat)//写数据到后备寄存器
{
HAL_RTCEx_BKUPWrite(&hrtc,BkUp_DRx,Dat);
}
e.读后备寄存器
my_u16 ReadBkUpDat1(my_u8 BkUp_DRx)//读后备寄存器
{
return HAL_RTCEx_BKUPRead(&hrtc,BkUp_DRx);
}
f.数据输出
void DatOupt(my_u16 *date)//数据输出到电脑端调试助手
{
// HAL_UART_Transmit(&huart1,(my_u8*)date,2,10);
printf("%x",*date);//调试用 不能打印数据0
}
g.测试用的数据源
typedef struct s_DatTset
{
my_u8 date;
my_u16 Count;
my_u32 Tim;
}sDatTset;
2.配置片上Flash存储数据的空间,如下图
2.创建一个sFlashSave类型的变量,并赋值,如下图
3.测试结果如下图
跨页存储,如下图 ,地址080507FA地址开始行为本页最后的6个字节存储空间,不够一个数据存储,则数据存入到下一页,后面的03 00为一页的页标识,表示本页存储有数据