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ī)存儲器)中,在掉電后RAM中的數(shù)據(jù)是無法保留的,那么怎樣使數(shù)據(jù)在掉電后不丟失呢?這就需要使用EEPROM或FLASHROM等存儲器來實(shí)現(xiàn)。在傳統(tǒng)的單片機(jī)系統(tǒng)中,一般是在片外擴(kuò)展存儲器,單片機(jī)與存儲器之間通過IIC或SPI等接口來進(jìn)行數(shù)據(jù)通信。這樣不光會增加開發(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的容量各有不同,見下表:
單片機(jī)芯片型號
起始地址
內(nèi)置EEPROM容量(每扇區(qū)512字節(jié))
STC89C51RC,STC89LE51RC
0x2000
共八個(gè)扇區(qū)
STC89C52RC,STC89LE52RC
0x2000
共八個(gè)扇區(qū)
STC89C54RD+,STC89LE54RD+
0x8000
共五十八個(gè)扇區(qū)
STC89C55RD+,STC89LE55RD+
0x8000
共五十八個(gè)扇區(qū)
STC89C58RD+,STC89LE58RD+
0x8000
共五十八個(gè)扇區(qū)
(內(nèi)部EEPROM可以擦寫100000次以上)
上面提到了IAP,它的意思是“在應(yīng)用編程”,即在程序運(yùn)行時(shí)程序存儲器可由程序自身進(jìn)行擦寫。正是是因?yàn)橛辛薎AP,從而可以使單片機(jī)可以將數(shù)據(jù)寫入到程序存儲器中,使得數(shù)據(jù)如同燒入的程序一樣,掉電不丟失。當(dāng)然寫入數(shù)據(jù)的區(qū)域與程序存儲區(qū)要分開來,以使程序不會遭到破壞。
要使用IAP功能,與以下幾個(gè)特殊功能寄存器相關(guān):
寄存器標(biāo)識
地址
名稱
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í)的數(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ā)方可生效。
B7
B6
B5
B4
B3
B2
B1
B0
命令/操作模式選擇
保留
命令選擇
-
-
-
-
-
0
0
0
待機(jī)模式,無ISP/IAP操作
-
-
-
-
-
0
0
1
對用戶的應(yīng)用程序Flash區(qū)及數(shù)據(jù)Flash區(qū)字節(jié)讀
-
-
-
-
-
0
1
0
對用戶的應(yīng)用程序Flash區(qū)及數(shù)據(jù)Flash區(qū)字節(jié)編程
-
-
-
-
-
0
1
1
對用戶的應(yīng)用程序Flash區(qū)及數(shù)據(jù)Flash區(qū)扇區(qū)擦除
ISP_TRIG:ISP/IAP操作時(shí)的命令觸發(fā)寄存器。
當(dāng)ISPEN(ISP_CONTR.7)=1時(shí),對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)復(fù)位,硬件自動清零。
ISP_CONTR中的SWBS與SWRST這兩個(gè)功能位,可以實(shí)現(xiàn)單片機(jī)的軟件啟動,并啟動到ISP區(qū)或用戶程序區(qū),這在“STC單片機(jī)自動下載”一節(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è)置等待時(shí)間
CPU等待時(shí)間(機(jī)器周期)
WT2
WT1
WT0
讀取
編程
扇區(qū)擦除
建議的系統(tǒng)時(shí)鐘
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
(以上的建議時(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ù)給地址,地址不會被自動改變*/
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自動下載
//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);
}