打印

一种EEPROM的平衡磨损实现。来帮忙找BUG~~

[复制链接]
825|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
3393018959|  楼主 | 2019-7-6 09:25 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 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/////////////////////////////////////////////////////////////////





EEPROM_Wear.rar

3.71 KB

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

10

帖子

0

粉丝