回答

收藏

[经验] STC单片机EEPROM读写程序

51单片机 51单片机 2966 人阅读 | 0 人回复 | 2013-12-27

/* STC89C54RD+flash空间从0x4000~0xf3ff 90个扇区,每扇区512字节       */
//     #define BaseAddr          0x1000           /*    51rc */
//     #define EndSectoraddr   0x3d00           /*    51rc */
//     #define EndAddr    0x3fff            /*    51rc 12K eeprom   */
       #define BaseAddr          0x4000
       #define EndSectoraddr   0xf200
       #define EndAddr                0xf3ff
       #define UseAddr                0x1000
/* ------------- 定义扇区大小 ------------- */
#define PerSector          512
/* 用户程序需要记忆的数组, 用户实际使用了n-1个数据,数组长度规整到
       2 4 8 16 32 64 */
uchar Ttotal[16]     =
{
       0x55,                           /* 作为判别引导头使用,用户程序请不要修改它 */
       /* 用户保存记忆的数据 */
       0x01,                           /* 用途说明....*/
       0x02,
       0x03,
       0x04,
       0x05,
       0x06,
       0x07,
       0x08,
       0x09,
       0x0a,
       0x0b,
       0x0c,
       0x0d,
       0x0e,
       0x0f,
};
uint        timerForDelay,              /* 专供延时用的变量 */
              i,                                 /* 循环变量                 */
              EepromPtr;                   /* eeprom读写指针      */
/* --------------- 命令定义 --------------- */
#define RdCommand            0x01       /* 字节读     */
#define PrgCommand           0x02       /* 字节写     */
#define EraseCommand 0x03       /* 扇区擦除 */
/* 定义常量 */
#define Error   1
#define Ok      0
/* 定义Flash对应于20MHz晶振系统的操作等待时间 */
/* 时钟倍频时WaitTime 0x00*/
#define WaitTime   0x01
/* ================ 打开 ISP,IAP 功能 ================= */
void ISP_IAP_enable(void){
       EA  =     0;                                              /* 关中断            */
       ISP_CONTR =       ISP_CONTR & 0x18;       /* 0001,1000 */
       ISP_CONTR =       ISP_CONTR | WaitTime;      /* 写入硬件延时   */
       ISP_CONTR =       ISP_CONTR | 0x80;       /* ISPEN=1           */
}
/* =============== 关闭 ISP,IAP 功能 ================== */
void ISP_IAP_disable(void){
       ISP_CONTR   =     ISP_CONTR & 0x7f;    /* ISPEN = 0 */
       ISP_TRIG      =     0x00;
       EA                =   1;                  /* 开中断 */
}
/* ================ 公用的触发代码 ==================== */
void ISPgoon(void){
       ISP_IAP_enable();               /* 打开 ISP,IAP 功能  */
       ISP_TRIG      =     0x46;             /* 触发ISP_IAP命令字节1       */
       ISP_TRIG      =     0xb9;             /* 触发ISP_IAP命令字节2       */
       _nop_();
}
/* ==================== 字节读 ======================== */
uchar byte_read(uint byte_addr){
       ISP_ADDRH = (uchar)(byte_addr >> 8);    /* 地址赋值   */
       ISP_ADDRL = (uchar)(byte_addr & 0x00ff);
       ISP_CMD   = ISP_CMD    & 0xf8;                 /* 清除低3      */
       ISP_CMD   = ISP_CMD    | RdCommand;       /* 写入读命令      */
       ISPgoon();                                               /* 触发执行          */
       ISP_IAP_disable();                            /* 关闭ISP,IAP功能    */
       return (ISP_DATA);                           /* 返回读到的数据      */
}
/* ================== 扇区擦除 ======================== */
void SectorErase(uint sector_addr){
       uint iSectorAddr;
       iSectorAddr = (sector_addr & 0xfe00); /* 取扇区地址 */
       ISP_ADDRH = (uchar)(iSectorAddr >> 8);
       ISP_ADDRL = 0x00;
       ISP_CMD      = ISP_CMD & 0xf8;                   /* 清空低3      */
       ISP_CMD      = ISP_CMD | EraseCommand;     /* 擦除命令3      */
       ISPgoon();                                               /* 触发执行          */
       ISP_IAP_disable();                            /* 关闭ISP,IAP功能    */
}
/* ==================== 字节写 ======================== */
void byte_write(uint byte_addr, uchar original_data){
       ISP_ADDRH =      (uchar)(byte_addr >> 8);      /* 取地址     */
       ISP_ADDRL =       (uchar)(byte_addr & 0x00ff);
       ISP_CMD      = ISP_CMD & 0xf8;                        /* 清低3   */
       ISP_CMD  = ISP_CMD | PrgCommand;           /* 写命令2    */
       ISP_DATA = original_data;                 /* 写入数据准备   */
       ISPgoon();                                               /* 触发执行          */
       ISP_IAP_disable();                                   /* 关闭IAP功能  */
}
/* =================== 字节写并校验 =================== */
uchar byte_write_verify(uint byte_addr, uchar original_data){
       ISP_ADDRH = (uchar)(byte_addr >> 8);    /* 取地址     */
       ISP_ADDRL = (uchar)(byte_addr & 0xff);
       ISP_CMD  = ISP_CMD & 0xf8;                      /* 清低3   */
       ISP_CMD  = ISP_CMD | PrgCommand;           /* 写命令2    */
       ISP_DATA = original_data;
       ISPgoon();                                               /* 触发执行          */
       /* 开始读,没有在此重复给地址,地址不会被自动改变     */
       ISP_DATA = 0x00;                      /* 清数据传递寄存器   */
       ISP_CMD = ISP_CMD & 0xf8;                        /* 清低3   */
       ISP_CMD = ISP_CMD | RdCommand;                     /* 读命令1    */
       ISP_TRIG      =     0x46;             /* 触发ISP_IAP命令字节1       */
       ISP_TRIG      =     0xb9;             /* 触发ISP_IAP命令字节2 */
       _nop_();                              /* 延时   */
       ISP_IAP_disable();                                   /* 关闭IAP功能  */
       if(ISP_DATA  == original_data){         /* 读写数据校验   */
              return      Ok;                                     /* 返回校验结果   */
       }
       else{
              return      Error;
       }
}
/* ===================== 数组写入 ===================== */
uchar ArrayWrite(uint begin_addr, uint len, uchar *array){
       uint  i;
       uint  in_addr;
       /* 判是否是有效范围,此函数不允许跨扇区操作 */
       if(len > PerSector){
              return Error;
       }
       in_addr = begin_addr & 0x01ff;        /* 扇区内偏移量 */
       if((in_addr + len) > PerSector){
              return Error;
       }
       in_addr = begin_addr;
       /* 逐个写入并校对 */
       ISP_IAP_enable();                             /* 打开IAP功能  */
       for(i = 0; i< len; i++){
              /* 写一个字节 */
              ISP_ADDRH = (uchar)(in_addr >> 8);
              ISP_ADDRL = (uchar)(in_addr & 0x00ff);
              ISP_DATA  = array;                      /* 取数据      */
              ISP_CMD   = ISP_CMD & 0xf8;                    /* 清低3 */
              ISP_CMD   = ISP_CMD | PrgCommand;  /* 写命令2   */
              ISP_TRIG  = 0x46;             /* 触发ISP_IAP命令字节1 */
              ISP_TRIG  = 0xb9;             /* 触发ISP_IAP命令字节2 */
              _nop_();
              /* 读回来 */
              ISP_DATA     =     0x00;
              ISP_CMD  = ISP_CMD & 0xf8;               /* 清低3 */
              ISP_CMD  = ISP_CMD | RdCommand;            /* 读命令1   */
              ISP_TRIG = 0x46;        /* 触发ISP_IAP命令字节1 */
              ISP_TRIG = 0xb9;        /* 触发ISP_IAP命令字节2 */
              _nop_();
              /*  比较对错 */
              if(ISP_DATA != array){
                     ISP_IAP_disable();
                     return Error;
              }
              in_addr++;                                 /* 指向下一个字节      */
       }
       ISP_IAP_disable();
       return      Ok;
}
/* ========================= 扇区读出 ========================= */
/* 程序对地址没有作有效性判断,请调用方事先保证他在规定范围内      */
void ArrayRead(uint begin_addr, uchar len){
//     uchar xdata     data_buffer[];               /* 整个扇区读取缓存区      */
       uint iSectorAddr;
       uint i;
       iSectorAddr = begin_addr;    // & 0xfe00;          /* 取扇区地址     */
       ISP_IAP_enable();
       for(i = 0; i < len; i++){
              ISP_ADDRH =      (uchar)(iSectorAddr >> 8);
              ISP_ADDRL =       (uchar)(iSectorAddr & 0x00ff);
              ISP_CMD   =      ISP_CMD      & 0xf8;                        /* 清低3 */
              ISP_CMD   =      ISP_CMD      | RdCommand;              /* 读命令1   */
              ISP_DATA = 0;
              ISP_TRIG = 0x46;               /* 触发ISP_IAP命令字节1 */
              ISP_TRIG = 0xb9;               /* 触发ISP_IAP命令字节2 */
              _nop_();
              Ttotal  =     ISP_DATA;
              iSectorAddr++;
       }
       ISP_IAP_disable();                                          /* 关闭IAP功能  */
}
/* ==============================================================
eeprom中读取数据
============================================================== */
void DataRestore()
{
       EepromPtr = BaseAddr;                      /* 指向eeprom的起始点     */
       while(EepromPtr < EndAddr)                    /* eeprom的可用区域内 */
       {
              if(byte_read(EepromPtr) == 0x55)/* 找到了上一次有效纪录  */
              {
                     break;                                        /*    寻找完成                     */
              }
              EepromPtr += 0x10;                           /* 指向下一个小区             */
       }
       if(EepromPtr >= EndAddr)                 /* 如果照遍都没有,是新片*/
       {
              EepromPtr = BaseAddr;               /* 指向eeprom的起始点     */
              for(i=0;i<90;i++)
              {
                     SectorErase(EepromPtr+0x200*i);       /* 全部扇区擦除          */
              }
              while(ArrayWrite(EepromPtr, 0x10, Ttotal))       /* 写默认值   */
              {                                                      /* 写入失败才运行的部分   */
                     byte_write(EepromPtr, 0);     /* 该单元已经失效             */
                     if(EepromPtr < EndAddr)
                     {
                            EepromPtr += 0x10;             /* 换一块新的小区             */
                     }
                     else
                     {
                            P1=0;                                  /* 指示芯片内eeprom全坏 */
                            EA= 0;                                /* 不再做任何事                 */
                            while(1);                      /* 死机                               */
                     }
              }
       }
       ArrayRead(EepromPtr, 16);
}
/* ==============================================================
将需要记忆的数据保存到eeprom
============================================================== */
void DataSave()
{
uint  wrPtr;                                                             /* 临时指针          */
NextArea:
       byte_write_verify(EepromPtr, 0);        /* 将原来的标记清除   */
       wrPtr = EepromPtr & 0xfe00;      /* 上一个扇区的起始地址   */
       EepromPtr += 0x10;                                         /* 目标存入地址          */
       /* ------------------ 判断是否启用新的扇区 ---------------- */
       if((EepromPtr & 0x1ff)==0)
       {
              SectorErase(wrPtr);                     /* 将上一个扇区擦除,备用      */
              if(EepromPtr>=EndAddr)            /* 已经用完了最后一个区域      */
              {
                     EepromPtr = BaseAddr;                             /* 从头开始   */
              }
       }
       /* -------------------- 数据存入前的准备 ------------------ */
       /* 。。。。。。。。。。。。。。转移、处理                             */
       Ttotal[0] = 0x55;                                       /* 重申启用标记          */
       if(ArrayWrite(EepromPtr, 0x10, Ttotal))
       {                                                      /* 数据写入,如果有错换一块   */
              goto NextArea;
       }
}
分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /3 下一条