/* 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[i]; /* 取数据*/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_DA TA != array[i]){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[i] = ISP_DA TA;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; /* 从头开始*/}}/* -------------------- 数据存入前的准备------------------ *//* 。