本帖最后由 3393018959 于 2019-7-6 09:30 编辑
/*--------------------------------------------------------------------------
EEPROM平衡磨损管理系统V1.2
该系统可管理64--256byte大小的EEPROM,需用户定义宏EEPROM_BYTE_SIZE
该系统可自定义管理块数量,要提升约n倍寿命,则建议管理(EEPROM_BYTE_SIZE/8/n)个块
至少要预留1个空闲块。即满足(EEPROM_BYTE_SIZE/8) - ((EEPROM_USE_NUM-1)/8+1) - EEPROM_USE_NUM >= 1;
块地址寄存器需从0x00挨个定义使用,即0x00管理块0,0x01管理块1.....
每块分配8个字节数据
用户需在.c文件中实现函数
void EE_Write(u8 data,u8 add)//传入写入的值、地址。将值写入地址
u8 EE_Read (u8 add)//传入地址,读出该地址的值并返回
该系统的实现基本思路为:EEPROM的前面 EEPROM_USE_NUM 个地址为地址导引区,其中存储着真实数据块存放的地址。
MapAddress数组是EEPROM使用情况映射表,每一个位代表1个块,为1则代表该块已被占用,为0则代表该块空闲
通过u8 EEPROM_Init(EEPROM_Typedef *EEPROM_temp, u8 EEADDR_HEAD_n)函数初始化地址及数据后
再通过void EEPROM_MapInit(void)函数初始化映射表。
每一次调用u8 EEPROM_Write(EEPROM_Typedef *EEPROM_temp, u8 EEADDR_HEAD_n)函数后,当前数据存放地址的擦写计数器自加1,
当自加到256次溢出时,寻找该地址之后的新地址,并将数据迁移到新地址,将地址导引区中的旧地址值更新。实现跳址存储。
使用实例:
#include "EEPROM_Wear.h"
EEPROM_Typedef EEPROM_Group1;//定义一个EEPROM_Typedef类型的结构体。
int main(void)
{
u32 PowerUseTime;//记录运行总时间,单位分钟
//首次上电,初始化数据块1并分配初始地址,
EEPROM_Init(&EEPROM_Group1,EEADDR_HEAD_1);
EEPROM_MapInit();//初始化映射表
EEPROM_Read(&EEPROM_Group1,EEADDR_HEAD_1);//读出块1的数据到结构体EEPROM_Group_1
//合成之前总时间
PowerUseTime = (u16)EEPROM_Group1.data3<<24|(u16)EEPROM_Group1.data2<<16|(u16)EEPROM_Group1.data1<<8|(u16)EEPROM_Group1.data0;
if(PowerUseTime > 525600)//上电总时间达到1年
{
while(1)//报废原程序,切换成另外一个程序
{
//比如LED灯闪烁。或者自毁啥的
P10 = 1;
delay();
P10 = 0;
delay();
}
}
//正常代码
while(1)
{
if(Timer1s > 59)//1分钟时间到
{
Timer1s = 0;
PowerUseTime++;//分钟+1
EEPROM_Group1.data3 = (PowerUseTime&0xff000000)>>24;
EEPROM_Group1.data2 = (PowerUseTime&0x00ff0000)>>16;
EEPROM_Group1.data1 = (PowerUseTime&0x0000ff00)>>8;
EEPROM_Group1.data0 = PowerUseTime&0x000000ff;
EEPROM_Write(&EEPROM_Group1,EEADDR_HEAD_1);//写入修改后的时间
printf("PowerUseTime = %u\r\n",PowerUseTime);//打印上电运行总时间
}
}
}
--------------------------------------------------------------------------*/
#ifndef __EEPROM_WEAR_H_
#define __EEPROM_WEAR_H_
typedef unsigned char u8;
//最大支持8块。每块大小8字节
//每块最多管理6个字节数据
#define EEPROM_BYTE_SIZE 256 //定义EEPROM大小
#define EEPROM_MAP_SIZE (EEPROM_BYTE_SIZE>>6)
#define EEPROM_USE_NUM 8 //使用块数量
//8个EEPROM块地址寄存器地址//类似指针
#define EEADDR_HEAD_1 0x00
#define EEADDR_HEAD_2 0x01
#define EEADDR_HEAD_3 0x02
#define EEADDR_HEAD_4 0x03
#define EEADDR_HEAD_5 0x04
#define EEADDR_HEAD_6 0x05
#define EEADDR_HEAD_7 0x06
#define EEADDR_HEAD_8 0x07
#define EEPROM_USE_MARK 0X44 //使用标志
//块结构
typedef struct EEPROM_t
{
u8 useFlag; //使用标志
u8 dataEraseCnt;//数据擦写计数器
u8 data0; //数据0
u8 data1; //数据0
u8 data2; //数据1
u8 data3; //数据2
u8 data4; //数据3
u8 Xor; //异或校验
}EEPROM_Typedef;
void Find_Nextaddress(u8 *address);
void EE_Write(u8 data,u8 add);
u8 EE_Read (u8 add);
u8 EEPROM_Write(EEPROM_Typedef *EEPROM_temp, u8 EEADDR_HEAD_n);
u8 EEPROM_Read(EEPROM_Typedef *EEPROM_temp, u8 EEADDR_HEAD_n);
u8 EEPROM_Init(EEPROM_Typedef *EEPROM_temp, u8 EEADDR_HEAD_n);
void EEPROM_MapInit(void);
#endif
///////////////////////////////////END///////////////////////////////////////////////////////
/*EEPROM_Wear.C文件*/
#include "EEPROM_Wear.h"
#include "iic_file.h"
//实现写EEPROM一字节
void EE_Write(u8 data,u8 add)
{
register_double_ADD_iic_Nbyte_write( IIC_MAIN_SYS ,0xA0,(u16)add,1 ,&data);
}
//实现读EEPROM一字节
u8 EE_Read (u8 add)
{
u8 temp;
register_double_ADD_iic_Nbyte_read( IIC_MAIN_SYS,0xA0 ,(u16)add ,1 ,&temp);
return temp;
}
//EEPROM地址使用情况映射表
u8 MapAddress[EEPROM_MAP_SIZE]={0};
/************************************************
函数原型 : u8 EEPROM_Init(EEPROM_Typedef *EEPROM_temp, u8 EEADDR_HEAD_n)
功 能 : 根据传入的结构体EEPROM_temp中的内容初始化EEADDR_HEAD_n区
参 数 : 1、EEPROM_temp,初始化的内容,其中EEPROM_temp->useFlag的值为EEPROM_USE_MARK是强制初始化(调用则初始化),否则为首次上电初始化
返 回 值 : 1:成功。0:失败
作 者 : ZH
*************************************************/
u8 EEPROM_Init(EEPROM_Typedef *EEPROM_temp, u8 EEADDR_HEAD_n)
{
u8 tempAddress = 0;
tempAddress = EE_Read(EEADDR_HEAD_n);//读地址
//如果传入的参数EEPROM_temp->useFlag为EEPROM_USE_MARK,则强制初始化
if(EEPROM_temp->useFlag != EEPROM_USE_MARK){//否则根据地址中的标志是否使用来确定是否初始化
EEPROM_temp->useFlag = EE_Read(tempAddress + 0);//读是否被使用
}
if(EEPROM_temp->useFlag != (EEPROM_USE_MARK + EEADDR_HEAD_n))//未使用、则初始化该结构体
{
EE_Write((EEADDR_HEAD_n<<3)+8 , EEADDR_HEAD_n);//分配初始地址
tempAddress = EE_Read(EEADDR_HEAD_n);//读地址
EEPROM_temp->useFlag = (EEPROM_USE_MARK + EEADDR_HEAD_n);
EE_Write(EEPROM_temp->useFlag , tempAddress + 0);//写入对应块使用标志
EE_Write(EEPROM_temp->dataEraseCnt , tempAddress + 1);
EE_Write(EEPROM_temp->data0 , tempAddress + 2);
EE_Write(EEPROM_temp->data1 , tempAddress + 3);
EE_Write(EEPROM_temp->data2 , tempAddress + 4);
EE_Write(EEPROM_temp->data3 , tempAddress + 5);
EE_Write(EEPROM_temp->data4 , tempAddress + 6);
EE_Write(EEPROM_temp->Xor , tempAddress + 7);
return 1;
}
return 0;
}
/************************************************
函数原型 : void EEPROM_MapInit(void)
功 能 : 分配块初始地址,初始化MapAddress映射表
参 数 : 无
返 回 值 : 无
作 者 : ZH
*************************************************/
void EEPROM_MapInit(void)
{
u8 EEPROM_Contrl_Addr[8],i = 0;
for(i = 0;i < ((EEPROM_USE_NUM-1)/8+1);i++)
{
MapAddress[0] |= (0x80>>i);//标记引导区
}
for(i = 0; i < EEPROM_USE_NUM;i++)//读所有块当前对应的存放地址
{
if(EE_Read(i) == 0){
EE_Write((i<<3)+8 , i);//分配初始地址
}
EEPROM_Contrl_Addr = EE_Read(i);
MapAddress[EEPROM_Contrl_Addr >> 6] |= (0x80 >> ((EEPROM_Contrl_Addr >> 3) % 8) ); //并且在映射表中标记该地址已使用
}
}
/************************************************
函数原型 : void Find_Nextaddress(u8 *address)
功 能 : 传入正在使用的地址
找到该地址之后的第一个空闲地址
标记该空闲地址为使用并返回该地址值
参 数 : u8 *address//当前使用地址
返 回 值 : u8 *address//新分配地址
作 者 : ZH
*************************************************/
void Find_Nextaddress(u8 *address)
{
u8 i,j;
//从旧地址映射位置开始找
i = (*address>>6);
j = (*address>>3)%8+1;
while(1)
{
for(;i < EEPROM_MAP_SIZE;i++)
{
for(;j < 8;j++)
{
if(!(MapAddress & (0x80 >> j)))//找到其后未使用过的块
{
*address = ((i<<3)+j)<<3;
MapAddress |= (0x80 >> (j));
return;
}
}
j = 0;
}
//该地址之后的地址都被使用,
i = 0;j = 0; //从头找
}
// MapAddress[0] |= 0x80 >> (((EEPROM_USE_NUM-1)>>3)+1);
// *address = (((EEPROM_USE_NUM-1)>>3)+1)<<3;
}
/************************************************
函数原型 : u8 EEPROM_Read(EEPROM_Typedef *EEPROM_temp, u8 EEADDR_HEAD_n)
功 能 : 将块EEADDR_HEAD_n地址指向的地址中的内容通过指针EEPROM_temp整块读取
参 数 : 1、EEPROM_Typedef *EEPROM_temp:用来存放读取的数据
2、u8 EEADDR_HEAD_n。要读取的块,其中n = {0,7}。
返 回 值 : //该数据属于数据块EEADDR_HEAD_n,并且数据异或校验正确,则返回1.
作 者 : ZH
*************************************************/
u8 EEPROM_Read(EEPROM_Typedef *EEPROM_temp, u8 EEADDR_HEAD_n)
{
u8 tempAddress = 0;
//读出该块的地址
tempAddress = EE_Read(EEADDR_HEAD_n);
//根据该块的地址读出所有数据
EEPROM_temp->useFlag = EE_Read(tempAddress + 0);
EEPROM_temp->dataEraseCnt = EE_Read(tempAddress + 1);
EEPROM_temp->data0 = EE_Read(tempAddress + 2);
EEPROM_temp->data1 = EE_Read(tempAddress + 3);
EEPROM_temp->data2 = EE_Read(tempAddress + 4);
EEPROM_temp->data3 = EE_Read(tempAddress + 5);
EEPROM_temp->data4 = EE_Read(tempAddress + 6);
EEPROM_temp->Xor = EE_Read(tempAddress + 7);
return !((EEPROM_temp->useFlag - EEPROM_USE_MARK)^EEADDR_HEAD_n^\
EEPROM_temp->data0 ^ EEPROM_temp->data1^\
EEPROM_temp->data2 ^ EEPROM_temp->data3^\
EEPROM_temp->data4 ^ EEPROM_temp->Xor);
}
/************************************************
函数原型 : u8 EEPROM_Write(EEPROM_Typedef *EEPROM_temp, u8 EEADDR_HEAD_n)
功 能 : 检查块EEADDR_HEAD_n指向的空间是否连续写入256次。
如果该空间已经连续写入256次,则使EEADDR_HEAD_n指向新的空间。
将该空间擦写计数器自加1.
接着将EEPROM_temp中的内容写入该空间
参 数 : 1、EEPROM_Typedef *EEPROM_temp:要写入的数据
2、EEADDR_HEAD_n:操作的块
返 回 值 : //数据异或校验正确返回1
作 者 : ZH
*************************************************/
u8 EEPROM_Write(EEPROM_Typedef *EEPROM_temp, u8 EEADDR_HEAD_n)
{
u8 tempAddressOld = 0,tempAddressNew = 0;
//读出该块的地址
tempAddressOld = EE_Read(EEADDR_HEAD_n);
tempAddressNew = tempAddressOld;
//擦写次数+1
//如果为0,即擦写次数达到256次
if(!(++EEPROM_temp->dataEraseCnt)){
EEPROM_temp->dataEraseCnt = 0;
EE_Write(0 , tempAddressOld + 0);//释放使用标记
EE_Write(0 , tempAddressOld + 1);//清理擦写计数
Find_Nextaddress(&tempAddressNew);//更新地址
MapAddress[tempAddressOld >> 6] &= ~(0x80 >> ((tempAddressOld >> 3) % 8));//释放旧地址
EE_Write(tempAddressNew,EEADDR_HEAD_n);//把新地址写入对应的地址管理区
EE_Write(EEPROM_USE_MARK + EEADDR_HEAD_n , tempAddressNew + 0);//写入对应块使用标志
}
//写入数据
EE_Write(EEPROM_temp->dataEraseCnt , tempAddressNew + 1);
EE_Write(EEPROM_temp->data0 , tempAddressNew + 2);
EE_Write(EEPROM_temp->data1 , tempAddressNew + 3);
EE_Write(EEPROM_temp->data2 , tempAddressNew + 4);
EE_Write(EEPROM_temp->data3 , tempAddressNew + 5);
EE_Write(EEPROM_temp->data4 , tempAddressNew + 6);
EE_Write(EEPROM_temp->Xor , tempAddressNew + 7);
//数据异或校验正确
return !(EEPROM_temp->data0 ^ EEPROM_temp->data1^\
EEPROM_temp->data2 ^ EEPROM_temp->data3^\
EEPROM_temp->Xor);
}///////////////////////////////////////////END/////////////////////////////////////////////////////////////////
|
|