STC單片機(jī)內(nèi)部EEPROM的應(yīng)用 (轉(zhuǎn))2008-05-18 02:47:27| 分類: AVR與C51單片機(jī)技 | 標(biāo)簽: |字號大中小 訂閱 STC單片機(jī)內(nèi)部EEPROM的應(yīng)用 單片機(jī)運(yùn)行時(shí)的數(shù)據(jù)都存在于RAM(隨機(jī)存儲(chǔ)器)中,,在掉電后RAM中的數(shù)據(jù)是無法保留的,那么怎樣使數(shù)據(jù)在掉電后不丟失呢,?這就需要使用EEPROM或FLASHROM等存儲(chǔ)器來實(shí)現(xiàn),。在傳統(tǒng)的單片機(jī)系統(tǒng)中,一般是在片外擴(kuò)展存儲(chǔ)器,,單片機(jī)與存儲(chǔ)器之間通過IIC或SPI等接口來進(jìn)行數(shù)據(jù)通信,。這樣不光會(huì)增加開發(fā)成本,同時(shí)在程序開發(fā)上也要花更多的心思,。在STC單片機(jī)中內(nèi)置了EEPROM(其實(shí)是采用IAP技術(shù)讀寫內(nèi)部FLASH來實(shí)現(xiàn)EEPROM),,這樣就節(jié)省了片外資源,使用起來也更加方便,。下面就詳細(xì)介紹STC單片機(jī)內(nèi)置EEPROM及其使用方法,。 STC各型號單片機(jī)內(nèi)置的EEPROM的容量各有不同,見下表:
(內(nèi)部EEPROM可以擦寫100000次以上) 上面提到了IAP,它的意思是“在應(yīng)用編程”,,即在程序運(yùn)行時(shí)程序存儲(chǔ)器可由程序自身進(jìn)行擦寫,。正是是因?yàn)橛辛薎AP,從而可以使單片機(jī)可以將數(shù)據(jù)寫入到程序存儲(chǔ)器中,,使得數(shù)據(jù)如同燒入的程序一樣,,掉電不丟失。當(dāng)然寫入數(shù)據(jù)的區(qū)域與程序存儲(chǔ)區(qū)要分開來,,以使程序不會(huì)遭到破壞,。 要使用IAP功能,與以下幾個(gè)特殊功能寄存器相關(guān):
ISP_DATA: ISP/IAP操作時(shí)的數(shù)據(jù)寄存器。 ISP/IAP從Flash讀出的數(shù)據(jù)放在此處,,向Flash寫的數(shù)據(jù)也需放在此處 ISP_ADDRH:ISP/IAP操作時(shí)的地址寄存器高八位,。 ISP_ADDRL:ISP/IAP操作時(shí)的地址寄存器低八位。 ISP_CMD: ISP/IAP操作時(shí)的命令模式寄存器,,須命令觸發(fā)寄存器觸發(fā)方可生效,。
ISP_TRIG:ISP/IAP操作時(shí)的命令觸發(fā)寄存器,。 當(dāng)ISPEN(ISP_CONTR.7)=1時(shí),,對ISP_TRIG先寫入0x46,再寫入0xb9,,ISP/IAP命令才會(huì)生效。 ISP_CONTR:ISP/IAP控制寄存器,。
ISPEN:ISP/IAP功能允許位,。0:禁止ISP/IAP編程改變Flash,1:允許編程改變Flash SWBS:軟件選擇從用戶主程序區(qū)啟動(dòng)(0),,還是從ISP程序區(qū)啟動(dòng)(1),。 SWRST:0:不操作,1:產(chǎn)生軟件系統(tǒng)復(fù)位,,硬件自動(dòng)清零,。 ISP_CONTR中的SWBS與SWRST這兩個(gè)功能位,可以實(shí)現(xiàn)單片機(jī)的軟件啟動(dòng),,并啟動(dòng)到ISP區(qū)或用戶程序區(qū),,這在“STC單片機(jī)自動(dòng)下載”一節(jié),亦有所應(yīng)用,。 如: ISP_CONTR=0x60; 則可以實(shí)現(xiàn)從用戶應(yīng)用程序區(qū)軟件復(fù)位到ISP程序區(qū)開始運(yùn)行程序,。 ISP_CONTR=0x20; 則可以實(shí)現(xiàn)從ISP程序區(qū)軟件復(fù)位到用戶應(yīng)用程序區(qū)開始運(yùn)行程序。 用IAP向Flash中讀寫數(shù)據(jù),,是需要一定的讀寫時(shí)間的,,讀寫數(shù)據(jù)命令發(fā)出后,要等待一段時(shí)間才可以讀寫成功,。這個(gè)等待時(shí)間就是由WT2,、WT1、WT0與晶體振蕩器頻率決定的,。
(以上的建議時(shí)鐘是(WT2,、WT1、WT0)取不同的值時(shí)的標(biāo)稱時(shí)鐘,,用戶系統(tǒng)中的時(shí)鐘不要過高,,否則可能使操作不穩(wěn)定。) 以下是具體的實(shí)現(xiàn)代碼:
EEPROM操作函數(shù): #define RdCommand 0x01 #define PrgCommand 0x02 #define EraseCommand 0x03 #define Error 1 #define Ok 0 #define WaitTime 0x01 #define PerSector 512
unsigned char xdata Ttotal[512];
/* --------------------------------------------------------------------- 打開 ISP,IAP 功能 --------------------------------------------------------------------- */ void ISP_IAP_enable(void) { EA=0;/* 關(guān)中斷*/ ISP_CONTR|=0x18;/*0001,1000*/ ISP_CONTR|=WaitTime;/*寫入硬件延時(shí)*/ ISP_CONTR|=0x80;/*ISPEN=1*/ }
/* --------------------------------------------------------------------- 關(guān)閉 ISP,IAP 功能 --------------------------------------------------------------------- */ void ISP_IAP_disable(void) { ISP_CONTR&=0x7f;/* ISPEN = 0 */ ISP_TRIG=0x00; EA=1;/* 開中斷 */ }
/* ---------------------------------------------------------------------- 公用的觸發(fā)代碼 ---------------------------------------------------------------------- */ void ISPgoon(void) { ISP_IAP_enable();/* 打開 ISP,IAP 功能 */ ISP_TRIG=0x46;/* 觸發(fā)ISP_IAP命令字節(jié)1 */ ISP_TRIG=0xb9;/* 觸發(fā)ISP_IAP命令字節(jié)2 */ _nop_(); }
/* ----------------------------------------------------------------------- 字節(jié)讀 ----------------------------------------------------------------------- */ unsigned char byte_read(unsigned int byte_addr) { ISP_ADDRH=(unsigned char)(byte_addr>>8); /* 地址賦值*/ ISP_ADDRL=(unsigned char)(byte_addr&0x00ff);
ISP_CMD&=0xf8; /* 清除低3位 */ ISP_CMD|=RdCommand;/* 寫入讀命令*/
ISPgoon();/* 觸發(fā)執(zhí)行*/ ISP_IAP_disable();/* 關(guān)閉ISP,IAP功能*/
return ISP_DATA;/* 返回讀到的數(shù)據(jù)*/ }
/* ------------------------------------------------------------------------ 扇區(qū)擦除 ------------------------------------------------------------------------ */ void sectorerase(unsigned int sector_addr) { unsigned int iSectorAddr; iSectorAddr=(sector_addr&0xfe00);/* 取扇區(qū)地址*/ ISP_ADDRH=(unsigned char)(iSectorAddr>>8); ISP_ADDRL=0x00;
ISP_CMD&=0xf8;/* 清空低3位*/ ISP_CMD|=EraseCommand;/* 擦除命令3*/
ISPgoon();/* 觸發(fā)執(zhí)行 */ ISP_IAP_disable();/* 關(guān)閉ISP,IAP功能*/ }
/* ------------------------------------------------------------------------------------- 字節(jié)寫 ------------------------------------------------------------------------------------- */ void byte_write(unsigned int byte_addr, unsigned char original_data) { ISP_ADDRH=(unsigned char)(byte_addr>>8); /* 取地址*/ ISP_ADDRL=(unsigned char)(byte_addr & 0x00ff);
ISP_CMD&=0xf8;/* 清低3位*/ ISP_CMD|=PrgCommand;/* 寫命令2*/ ISP_DATA=original_data;/* 寫入數(shù)據(jù)準(zhǔn)備*/
ISPgoon();/* 觸發(fā)執(zhí)行*/ ISP_IAP_disable();/* 關(guān)閉IAP功能*/ }
/* ----------------------------------------------------------------- 字節(jié)寫并校驗(yàn) ----------------------------------------------------------------- */ unsigned char byte_write_verify(unsigned int byte_addr, unsigned char original_data) { ISP_ADDRH=(unsigned char)(byte_addr>>8); /* 取地址*/ ISP_ADDRL=(unsigned char)(byte_addr&0xff);
ISP_CMD&=0xf8;/* 清低3位*/ ISP_CMD|=PrgCommand;/* 寫命令2*/ ISP_DATA=original_data;
ISPgoon();/* 觸發(fā)執(zhí)行*/
/* 開始讀,,沒有在此重復(fù)給地址,,地址不會(huì)被自動(dòng)改變*/ ISP_DATA=0x00;/* 清數(shù)據(jù)傳遞寄存器*/
ISP_CMD&=0xf8;/* 清低3位*/ ISP_CMD|=RdCommand;/* 讀命令1*/
ISP_TRIG=0x46;/* 觸發(fā)ISP_IAP命令字節(jié)1 */ ISP_TRIG=0xb9;/* 觸發(fā)ISP_IAP命令字節(jié)2 */ _nop_();/* 延時(shí)*/
ISP_IAP_disable();/* 關(guān)閉IAP功能*/
if(ISP_DATA==original_data)/* 讀寫數(shù)據(jù)校驗(yàn)*/ return Ok;/* 返回校驗(yàn)結(jié)果*/ else return Error; }
/* -------------------------------------------------------------------------- 數(shù)組寫入 -------------------------------------------------------------------------- */ unsigned char arraywrite(unsigned int begin_addr, unsigned int len, unsigned char *array) { unsigned int i; unsigned int in_addr;
/* 判是否是有效范圍,此函數(shù)不允許跨扇區(qū)操作 */ if(len > PerSector) return Error; in_addr = begin_addr & 0x01ff;/* 扇區(qū)內(nèi)偏移量 */ if((in_addr+len)>PerSector) return Error; in_addr = begin_addr; /* 逐個(gè)寫入并校對 */ ISP_IAP_enable();/* 打開IAP功能 */ for(i=0;i<len;i++) { /* 寫一個(gè)字節(jié) */ ISP_ADDRH=(unsigned char)(in_addr >> 8); ISP_ADDRL=(unsigned char)(in_addr & 0x00ff); ISP_DATA=array[i]; /* 取數(shù)據(jù) */ ISP_CMD&=0xf8;/* 清低3位 */ ISP_CMD|=PrgCommand;/* 寫命令2 */
ISP_TRIG=0x46;/* 觸發(fā)ISP_IAP命令字節(jié)1 */ ISP_TRIG=0xb9;/* 觸發(fā)ISP_IAP命令字節(jié)2 */ _nop_();
/* 讀回來 */ ISP_DATA=0x00;
ISP_CMD&=0xf8;/* 清低3位*/ ISP_CMD|=RdCommand;/* 讀命令1*/
ISP_TRIG=0x46;/* 觸發(fā)ISP_IAP命令字節(jié)1 */ ISP_TRIG=0xb9;/* 觸發(fā)ISP_IAP命令字節(jié)2 */ _nop_();
/* 比較對錯(cuò) */ if(ISP_DATA!=array[i]) { ISP_IAP_disable(); return Error; } in_addr++;/* 指向下一個(gè)字節(jié)*/ } ISP_IAP_disable(); return Ok; }
/* ----------------------------------------------------------------------------- 扇區(qū)讀出 ----------------------------------------------------------------------------- */ /* 程序?qū)Φ刂窙]有作有效性判斷,,請調(diào)用前事先保證他在規(guī)定范圍內(nèi) */ void arrayread(unsigned int begin_addr, unsigned char len) { unsigned int iSectorAddr; unsigned int i; iSectorAddr = begin_addr; // & 0xfe00; /* 取扇區(qū)地址*/
ISP_IAP_enable(); for(i=0;i<len;i++) { ISP_ADDRH=(unsigned char)(iSectorAddr>>8); ISP_ADDRL=(unsigned char)(iSectorAddr & 0x00ff);
ISP_CMD&=0xf8;/* 清低3位*/ ISP_CMD|=RdCommand;/* 讀命令1*/ ISP_DATA=0; ISP_TRIG=0x46;/* 觸發(fā)ISP_IAP命令字節(jié)1 */ ISP_TRIG=0xb9;/* 觸發(fā)ISP_IAP命令字節(jié)2 */ _nop_();
Ttotal[i]=ISP_DATA; iSectorAddr++; } ISP_IAP_disable();/* 關(guān)閉IAP功能*/ }
主函數(shù)對EEPROM操作函數(shù)進(jìn)行調(diào)用: #include <stc51rd.h> #include <intrins.h> #include <stc_eeprom.h> #include <ados.h>
int i;
void delay(unsigned int time) { while(time--); }
void main() { _ADOS(22.1184); //ADOS自動(dòng)下載
//for(i=0;i<100;i++) //{ //Ttotal[i]=i; //} //arraywrite(0x8000,100,Ttotal); /* 第一次運(yùn)行時(shí)向EEPROM中寫入數(shù)據(jù) 然后再將寫入函數(shù)注釋掉,將先前寫 入的數(shù)據(jù)讀出,,輸出在P2口上,。 */ arrayread(0x8000,100); for(i=0;i<100;i++) { P2=~Ttotal[i]; delay(10000); } while(1); } |
|