/* 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; } } |