重复循环利用整个EEPROM区域进行写,巧妙建立默认的索引。外部EEPROM操作接口可实现模块化,可单独建库。完善的写保护机制。

//--------------------------------------------------------
//EEPROM方式的数据管理
//特征字:
//       0xff 空记录
//       0xf0 写锁标志
//       0xf3 有效记录
//       0x0   坏记录
//--------------------------------------------------------
#include "....SystemCom.h"
#include "....SystemMemory.h"

#define FLAG_EMPTYRECORD     0xff         //空记录
#define FLAG_LOCK            0xf0         //写锁标志
#define FLAG_VALIDRECORD     0xf3         //有效记录
#define FLAG_BADRECORD       0x0          //坏记录区

struct EDataBase{
     U8 *pRecordBuffer;               //记录缓冲区开始指针
};
struct EDataBase sEDataBase;

struct InEDataBase{
     U8 mRecordByteSize;              //记录大小,比记录缓冲区大1个字节。(特征字节)
     bool bWriteRequ;                 //写请求

     U8 *pWriteBuffer;                //写EEPROM的数据缓冲区
     bool bWriteBusy;
     U8 mWriteTask;                   //写任务号

     U16 mEepromStartLocation;        //在Eeprom存储起始地址
     U16 mEepromEndLocation;          //在Eeprom存储结束地址
     U16 mCurrentRecordLocation;      //当前数据存储所在位置
     U16 mTemp;

     U8 (*pfEeprom)( U8 mCmd, U16 mId, U8 mData );        //EEPROM操作函数指针
};
struct InEDataBase sInEDataBase;
#define this sInEDataBase

enum EepromCmd{
     eReadU8,
     eWriteU8,
     eReadBusy,
};

#define Eeprom_ReadU8(mId)           (*this.pfEeprom)(eReadU8, mId, NULL)
#define Eeprom_WriteU8(mId,mData)    do{ (*this.pfEeprom)(eWriteU8, mId, mData);}while(0)
#define Eeprom_ReadBusy()            (*this.pfEeprom)(eReadBusy, NULL, NULL)
#define READRECORDFLAG(mId)          Eeprom_ReadU8( InEDataBase_FlagLocation(mId) )
#define WRITERECORDFLAG(mId, mData) Eeprom_WriteU8( InEDataBase_FlagLocation(mId), mData )


//--------------------------------------------------------
//得到特征字位置
//入口:记录的起始位置
//--------------------------------------------------------
U16 InEDataBase_FlagLocation( U16 mRecordLocation )
{
     return mRecordLocation + this.mRecordByteSize - 1;
}

//--------------------------------------------------------
//记录号+1
//返回+1后的记录号
//--------------------------------------------------------
U16 InEDataBase_RecordIdInc( U16 mId )
{
     mId += this.mRecordByteSize;
     if( mId >= this.mEepromEndLocation ){
             mId = this.mEepromStartLocation;
     }
     return mId;
}


//--------------------------------------------------------
//记录号-1
//返回-1后的记录号
//--------------------------------------------------------
U16 InEDataBase_RecordIdDec( U16 mId )
{
     if( mId < (U16)(this.mEepromStartLocation + this.mRecordByteSize) )
     {
         mId = this.mEepromEndLocation - this.mRecordByteSize;
     }
     else {
         mId -= this.mRecordByteSize;
     }
     return mId;
}

//--------------------------------------------------------
//读出数据到缓冲
//--------------------------------------------------------
void InEDataBase_ReadBuffer( void )
{
     U8 i = this.mRecordByteSize;
     U16 mLocation = this.mCurrentRecordLocation;
     U8 *pRecordBuffer = sEDataBase.pRecordBuffer;

     while( --i ){
         *pRecordBuffer++ = Eeprom_ReadU8( mLocation++ );
     }
}

//--------------------------------------------------------
//初始化
//入口:记录字节个数, Eeprom起始地址,Eeprom大小(字节), Eeprom函数操作指针
//--------------------------------------------------------
void EDataBase_Init( U8 mRecordByteSize, U16 mEepromStartLocation, U16 mEepromSize, U8 (*pfEeprom)( U8 mCmd, U16 mId, U8 mData ) )
{
     U16 i,j;
     U8 k;

     Memory_Memset( (U8 *)&this, 0, sizeof(struct InEDataBase) );

     sEDataBase.pRecordBuffer = Memory_Malloc( mRecordByteSize );

     mRecordByteSize++;
     this.pWriteBuffer = Memory_Malloc( mRecordByteSize );
     this.mRecordByteSize = mRecordByteSize;
     this.pfEeprom = pfEeprom;

     this.mEepromStartLocation = mEepromStartLocation;
     this.mEepromEndLocation = mEepromStartLocation + mEepromSize - (mEepromSize % mRecordByteSize);

     //重建索引
     i = this.mEepromStartLocation;

     //必定有个空记录
     while( i < this.mEepromEndLocation )
     {
         k = READRECORDFLAG(i);
         if( ( k == FLAG_LOCK ) || ( k == FLAG_EMPTYRECORD ) )
         {
             //找到空记录,倒推最后一个记录位置
             j = i;
             do{
                 i = InEDataBase_RecordIdDec( i );
                 k = READRECORDFLAG(i);
                 if( k == FLAG_VALIDRECORD ){
                     //找到最近的有效数据
                     goto l_LocationFirst;
                 }
             }
             while( i!= j );

             //没有任何有效数据
             i = this.mEepromStartLocation;
             goto l_LocationFirst;

         }
         else {
             //继续到下个记录
             i = InEDataBase_RecordIdInc( i );
         }
     }
     //无任何数据
     i = this.mEepromStartLocation;

l_LocationFirst:
     this.mCurrentRecordLocation = i;

     //读出数据到缓冲
     InEDataBase_ReadBuffer();
}

//--------------------------------------------------------
//写数据存储进EEPROM
//--------------------------------------------------------
void EDataBase_FlushBuffer( void )
{
     this.bWriteRequ = true;
}

//--------------------------------------------------------
//循环
//--------------------------------------------------------
void EDataBase_Loop( void )
{
     if( this.bWriteBusy && ( Eeprom_ReadBusy() == false ) ){
             //检查是否写完
             switch( this.mWriteTask ){
                 case 0: //擦除新记录区,首先找到有效空块
                     this.mCurrentRecordLocation = InEDataBase_RecordIdInc( this.mCurrentRecordLocation );
                     if ( READRECORDFLAG( this.mCurrentRecordLocation ) == FLAG_BADRECORD )
                     {
                         //是处于坏块上,退出,下次进入时到下个记录位置
                         break;
                     }
                     this.mTemp = this.mCurrentRecordLocation;
                     this.mWriteTask = 1;
                     break;

                 case 1: //擦除当前有效空块后的空块,保证当前有效空块写入数据后至少有一个有效空块.(初始建立索引需要)
                     this.mTemp = InEDataBase_RecordIdInc( this.mTemp );
                     if ( READRECORDFLAG( this.mTemp ) == FLAG_BADRECORD )
                     {
                         //是处于坏块上,退出,下次进入时到下个记录位置
                         break;
                     }

                     //写空区标志
                     WRITERECORDFLAG( this.mTemp, FLAG_EMPTYRECORD );
                     this.mWriteTask = 2;
                     break;


                 case 2: //检查是否空区建立
                     if ( READRECORDFLAG( this.mTemp ) != FLAG_EMPTYRECORD )
                     {
                         //失败
                         WRITERECORDFLAG( this.mTemp, FLAG_BADRECORD );
                         this.mWriteTask = 1;
                         break;
                     }
                     else {
                         //开始写数据.首先锁标志
                         WRITERECORDFLAG( this.mCurrentRecordLocation, FLAG_LOCK );
                         this.mWriteTask = 3;
                     }
                     break;

                 case 3:
                     if ( READRECORDFLAG( this.mCurrentRecordLocation ) != FLAG_LOCK )
                     {
                         //失败
                         WRITERECORDFLAG( this.mCurrentRecordLocation, FLAG_BADRECORD );
                         this.mWriteTask = 0;
                     }
                     else {
                         //开始写数据
                         this.mTemp = 0;
                         this.mWriteTask = 4;
                     }
                     break;

                 case 4: //写全部数据
                     Eeprom_WriteU8( this.mTemp + this.mCurrentRecordLocation, this.pWriteBuffer[ this.mTemp ] );
                     if( ++this.mTemp == (this.mRecordByteSize - 1) ){
                         //写完数据
                         this.mWriteTask = 5;
                     }
                     break;

                 case 5: //校验数据
                     this.mTemp = 0;
                     while( this.mTemp != (this.mRecordByteSize - 1) )
                     {
                         if( this.pWriteBuffer[ this.mTemp ] != Eeprom_ReadU8( this.mTemp + this.mCurrentRecordLocation ) )
                         {
                             //失败
                             WRITERECORDFLAG( this.mTemp, FLAG_BADRECORD );
                             this.mWriteTask = 0;
                             break;
                         }
                         this.mTemp++;
                     }
                     this.mWriteTask = 6;
                     break;

                 case 6: //写完成标志
                     WRITERECORDFLAG( this.mCurrentRecordLocation, FLAG_VALIDRECORD );
                     this.mWriteTask = 7;
                     break;

                 case 7: //校验写完成标志
                     if( READRECORDFLAG( this.mCurrentRecordLocation ) != FLAG_VALIDRECORD )
                     {
                         //标志错误,失败
                         WRITERECORDFLAG( this.mTemp, FLAG_BADRECORD );
                         this.mWriteTask = 0;
                         break;
                     }
                     else {
                         //完成
                         this.bWriteBusy = false;
                     }
                     break;
             }
     }
     else {
         //空闲下检查是否有写请求
         if( this.bWriteRequ == true ){
             //开始写
             this.mWriteTask = 0;
             this.bWriteRequ = false;
             this.bWriteBusy = true;

             Memory_MemCopy( this.pWriteBuffer, sEDataBase.pRecordBuffer, this.mRecordByteSize - 1 );
         }
     }
}