STC單片機內(nèi)部EEPROM的應用
STC各型號單片機內(nèi)置的EEPROM的容量各有不同,見下表:
本文引用地址:http://butianyuan.cn/article/201611/316202.htm單片機芯片型號 | 起始地址 | 內(nèi)置EEPROM容量(每扇區(qū)512字節(jié)) |
STC89C51RC,STC89LE51RC | 0x2000 | 共八個扇區(qū) |
STC89C52RC,STC89LE52RC | 0x2000 | 共八個扇區(qū) |
STC89C54RD+,STC89LE54RD+ | 0x8000 | 共五十八個扇區(qū) |
STC89C55RD+,STC89LE55RD+ | 0x8000 | 共五十八個扇區(qū) |
STC89C58RD+,STC89LE58RD+ | 0x8000 | 共五十八個扇區(qū) |
(內(nèi)部EEPROM可以擦寫100000次以上)
上面提到了IAP,它的意思是“在應用編程”,即在程序運行時程序存儲器可由程序自身進行擦寫。正是是因為有了IAP,從而可以使單片機可以將數(shù)據(jù)寫入到程序存儲器中,使得數(shù)據(jù)如同燒入的程序一樣,掉電不丟失。當然寫入數(shù)據(jù)的區(qū)域與程序存儲區(qū)要分開來,以使程序不會遭到破壞。
要使用IAP功能,與以下幾個特殊功能寄存器相關:
寄存器標識 | 地址 | 名稱 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 初始值 |
ISP_DATA | 0xE2 | ISP/IAP閃存數(shù)據(jù)寄存器 | 11111111 | ||||||||
ISP_ADDRH | 0xE3 | ISP/IAP閃存地址高位 | 00000000 | ||||||||
ISP_ADDRL | 0xE4 | ISP/IAP閃存地址低位 | 00000000 | ||||||||
ISP_CMD | 0xE5 | ISP/IAP閃存命令寄存器 | - | - | - | - | - | MS2 | MS1 | MS0 | xxxxx000 |
ISP_TRIG | 0xE6 | ISP/IAP閃存命令觸發(fā) | xxxxxxxx | ||||||||
ISP_CONTR | 0xE7 | ISP/IAP控制寄存器 | ISPEN | SWBS | SWRST | - | - | WT2 | WT1 | WT0 | 00xx000 |
ISP_DATA: ISP/IAP操作時的數(shù)據(jù)寄存器。
ISP/IAP從Flash讀出的數(shù)據(jù)放在此處,向Flash寫的數(shù)據(jù)也需放在此處
ISP_ADDRH:ISP/IAP操作時的地址寄存器高八位。
ISP_ADDRL:ISP/IAP操作時的地址寄存器低八位。
ISP_CMD: ISP/IAP操作時的命令模式寄存器,須命令觸發(fā)寄存器觸發(fā)方可生效。
B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | 命令/操作模式選擇 |
保留 | 命令選擇 | |||||||
- | - | - | - | - | 0 | 0 | 0 | 待機模式,無ISP/IAP操作 |
- | - | - | - | - | 0 | 0 | 1 | 對用戶的應用程序Flash區(qū)及數(shù)據(jù)Flash區(qū)字節(jié)讀 |
- | - | - | - | - | 0 | 1 | 0 | 對用戶的應用程序Flash區(qū)及數(shù)據(jù)Flash區(qū)字節(jié)編程 |
- | - | - | - | - | 0 | 1 | 1 | 對用戶的應用程序Flash區(qū)及數(shù)據(jù)Flash區(qū)扇區(qū)擦除 |
ISP_TRIG:ISP/IAP操作時的命令觸發(fā)寄存器。
當ISPEN(ISP_CONTR.7)=1時,對ISP_TRIG先寫入0x46,再寫入0xb9,ISP/IAP命令才會生效。
ISP_CONTR:ISP/IAP控制寄存器。
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
ISPEN | SWBS | SWRST | - | - | WT2 | WT1 | WT0 |
ISPEN:ISP/IAP功能允許位。0:禁止ISP/IAP編程改變Flash,1:允許編程改變Flash
SWBS:軟件選擇從用戶主程序區(qū)啟動(0),還是從ISP程序區(qū)啟動(1)。
SWRST:0:不操作,1:產(chǎn)生軟件系統(tǒng)復位,硬件自動清零。
ISP_CONTR中的SWBS與SWRST這兩個功能位,可以實現(xiàn)單片機的軟件啟動,并啟動到ISP區(qū)或用戶程序區(qū),這在“STC單片機自動下載”一節(jié),亦有所應用。
如:
ISP_CONTR=0x60; 則可以實現(xiàn)從用戶應用程序區(qū)軟件復位到ISP程序區(qū)開始運行程序。
ISP_CONTR=0x20; 則可以實現(xiàn)從ISP程序區(qū)軟件復位到用戶應用程序區(qū)開始運行程序。
用IAP向Flash中讀寫數(shù)據(jù),是需要一定的讀寫時間的,讀寫數(shù)據(jù)命令發(fā)出后,要等待一段時間才可以讀寫成功。這個等待時間就是由WT2、WT1、WT0與晶體振蕩器頻率決定的。
設置等待時間 | CPU等待時間(機器周期) | |||||
WT2 | WT1 | WT0 | 讀取 | 編程 | 扇區(qū)擦除 | 建議的系統(tǒng)時鐘 |
0 | 1 | 1 | 6 | 30 | 5471 | 5MHz |
0 | 1 | 0 | 11 | 60 | 10942 | 10MHz |
0 | 0 | 1 | 22 | 120 | 21885 | 20MHz |
0 | 0 | 0 | 43 | 240 | 43769 | 40MHz |
(以上的建議時鐘是(WT2、WT1、WT0)取不同的值時的標稱時鐘,用戶系統(tǒng)中的時鐘不要過高,否則可能使操作不穩(wěn)定。)
以下是具體的實現(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
unsignedchar xdata Ttotal[512];
/*
---------------------------------------------------------------------
打開 ISP,IAP功能
---------------------------------------------------------------------
*/
voidISP_IAP_enable(void)
{
EA=0;/*關中斷*/
ISP_CONTR|=0x18;/*0001,1000*/
ISP_CONTR|=WaitTime;/*寫入硬件延時*/
ISP_CONTR|=0x80;/*ISPEN=1*/
}
/*
---------------------------------------------------------------------
關閉 ISP,IAP功能
---------------------------------------------------------------------
*/
voidISP_IAP_disable(void)
{
ISP_CONTR&=0x7f;/* ISPEN = 0 */
ISP_TRIG=0x00;
EA=1;/*開中斷 */
}
/*
----------------------------------------------------------------------
公用的觸發(fā)代碼
----------------------------------------------------------------------
*/
voidISPgoon(void)
{
ISP_IAP_enable();/*打開 ISP,IAP功能 */
ISP_TRIG=0x46;/*觸發(fā)ISP_IAP命令字節(jié)1 */
ISP_TRIG=0xb9;/*觸發(fā)ISP_IAP命令字節(jié)2 */
_nop_();
}
/*
-----------------------------------------------------------------------
字節(jié)讀
-----------------------------------------------------------------------
*/
unsignedchar 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();/*關閉ISP,IAP功能*/
return ISP_DATA;/*返回讀到的數(shù)據(jù)*/
}
/*
------------------------------------------------------------------------
扇區(qū)擦除
------------------------------------------------------------------------
*/
voidsectorerase(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();/*關閉ISP,IAP功能*/
}
/*
-------------------------------------------------------------------------------------
字節(jié)寫
-------------------------------------------------------------------------------------
*/
voidbyte_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ù)準備*/
ISPgoon();/*觸發(fā)執(zhí)行*/
ISP_IAP_disable();/*關閉IAP功能*/
}
/*
-----------------------------------------------------------------
字節(jié)寫并校驗
-----------------------------------------------------------------
*/
unsignedchar 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í)行*/
/*開始讀,沒有在此重復給地址,地址不會被自動改變*/
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_();/*延時*/
ISP_IAP_disable();/*關閉IAP功能*/
if(ISP_DATA==original_data)/*讀寫數(shù)據(jù)校驗*/
return Ok;/*返回校驗結(jié)果*/
else
return Error;
}
/*
--------------------------------------------------------------------------
數(shù)組寫入
--------------------------------------------------------------------------
*/
unsignedchar 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;
/*逐個寫入并校對 */
ISP_IAP_enable();/*打開IAP功能 */
for(i=0;i { /*寫一個字節(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_(); /*比較對錯 */ if(ISP_DATA!=array[i]) { ISP_IAP_disable(); return Error; } in_addr++;/*指向下一個字節(jié)*/ } ISP_IAP_disable(); return Ok; } /* ----------------------------------------------------------------------------- 扇區(qū)讀出 ----------------------------------------------------------------------------- */ /*程序?qū)Φ刂窙]有作有效性判斷,請調(diào)用前事先保證他在規(guī)定范圍內(nèi) */ voidarrayread(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 { 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();/*關閉IAP功能*/ } 主函數(shù)對EEPROM操作函數(shù)進行調(diào)用: #include #include #include #include inti; voiddelay(unsigned int time) { while(time--); } voidmain() { _ADOS(22.1184); //ADOS自動下載 //for(i=0;i<100;i++) //{ //Ttotal[i]=i; //} //arraywrite(0x8000,100,Ttotal); /* 第一次運行時向EEPROM中寫入數(shù)據(jù) 然后再將寫入函數(shù)注釋掉,將先前寫 入的數(shù)據(jù)讀出,輸出在P2口上。 */ arrayread(0x8000,100); for(i=0;i<100;i++) { P2=~Ttotal[i]; delay(10000); } while(1); }
評論