當(dāng)在程序運(yùn)行的過程中你希望修改某個(gè)變量并且此變量的值在掉電以后不丟失,那么你就可以采用將變量數(shù)據(jù)寫入EEPROM的方式來實(shí)現(xiàn)。
什么是EEPROM,即Electrically Erasable ProgrammableRead_Only Memory首先它是一種存儲(chǔ)器,并且可以通過高電壓來進(jìn)行反復(fù)擦寫的存儲(chǔ)器。具有掉電數(shù)據(jù)不丟失的特點(diǎn)。比如常用的24C系列,93C系列的器件。一般這種器件采用I2C的方式與單片機(jī)進(jìn)行通訊,對(duì)于這種通訊方式及器件的應(yīng)用另作總結(jié)。這里主要總結(jié)一下,STC12C5204AD芯片內(nèi)部包含的EEPROM的應(yīng)用方法。
STC12C5201AD系列單片機(jī)內(nèi)部集成了EEPROM是與程序空間分開的,利用ISP/IAP技術(shù)可將內(nèi)部data flash當(dāng)EEPROM,擦寫10萬次以上。
EEPROM可分為若干個(gè)扇區(qū),每個(gè)扇區(qū)包含512字節(jié)。
使用時(shí)建議同一次修改的數(shù)據(jù)放在同一個(gè)扇區(qū),不是同一次修改的數(shù)據(jù)放在不同的扇區(qū),不一定要用滿。數(shù)據(jù)存儲(chǔ)器的擦除操作是按扇區(qū)進(jìn)行的。
在程序中可對(duì)EEPROM進(jìn)行字節(jié)讀寫/字節(jié)編程/扇區(qū)擦除操作。在工作電壓Vcc偏低時(shí),建議不要進(jìn)行EEPROM/IAP操作。以免發(fā)生數(shù)據(jù)錯(cuò)誤。
應(yīng)用的步驟
1、 聲明與EEPROM相關(guān)的寄存器
2、 編寫EEPROM初始化函數(shù)
3、 編寫字節(jié)擦除函數(shù)
4、 編寫字節(jié)編程函數(shù)
5、 編寫字節(jié)讀取函數(shù)
6、 在需要讀取EEPROM字節(jié)內(nèi)容時(shí)直接調(diào)用字節(jié)讀取函數(shù)即可
7、 在需要進(jìn)行寫EEPROM字節(jié)時(shí),先調(diào)用字節(jié)擦除函數(shù),將字節(jié)內(nèi)容擦除成FFH后,在調(diào)用字節(jié)編程函數(shù),將數(shù)據(jù)寫入到EEPROM的地址單元中。
與EEPROM應(yīng)用相關(guān)的寄存器
|
符號(hào)
|
描述
|
地址
|
位地址及符號(hào)
|
復(fù)位值
|
|||||||
|
IAP_DATA
|
ISP/IAP flash data register
|
C2H
|
|
|
|
|
|
|
|
|
1111 1111B
|
|
IAP_ADDRH
|
ISP/IAP flash address high
|
C3H
|
|
|
|
|
|
|
|
|
0000 0000B
|
|
IAP_ADDRL
|
ISP/IAP flash address low
|
C4H
|
|
|
|
|
|
|
|
|
|
|
IAP_CMD
|
ISP/IAP flash command register
|
C5H
|
|
|
|
|
|
|
MS1
|
MS0
|
|
|
IAP_TRIG
|
ISP/IAP flash command trigger
|
C6H
|
|
|
|
|
|
|
|
|
xxxxxxxxxB
|
|
IAP_CONTR
|
ISP/IAP control register
|
C7H
|
IAPEN
|
SWBS
|
SWRST
|
CMD_FAIL
|
|
WT2
|
WT1
|
WT0
|
0000X000B
|
|
PCON
|
Power control
|
87H
|
SOMD
|
SMOD0
|
LVDF
|
POF
|
GF1
|
GF0
|
PD
|
IDL
|
00110000B
|
1、IAP_DATA:ISP/IAP數(shù)據(jù)寄存器
ISP/IAP操作時(shí)的數(shù)據(jù)寄存器。
ISP/IAP從FlASH讀出的數(shù)據(jù)存放此處,向flash寫的數(shù)據(jù)也需要放在此處。
2、 IAP_ADDRH和IAP_ADDRL :IAP/ISP地址寄存器
3、 IAP_CMD:ISP/IAP命令寄存器
|
MS1
|
MS0
|
命令/操作 模式選擇
|
|
0
|
0
|
Standby 待機(jī)模式,無ISP操作
|
|
0
|
1
|
從用戶程序區(qū)對(duì)“data flash /EEprom區(qū)”進(jìn)行字節(jié)讀
|
|
1
|
0
|
從用戶的應(yīng)用程序區(qū)對(duì)“data flash/eeprom區(qū)”進(jìn)行字節(jié)編寫
|
|
1
|
1
|
從用戶的應(yīng)用程序區(qū)對(duì)“data flash/eeprom區(qū)”進(jìn)行扇區(qū)擦除
|
4、IAP_TRIG:ISP/IAP命令觸發(fā)寄存器
在IAPEN(IAP_CONTR.7)=1時(shí),對(duì)IAP——trig先寫入5AH,在寫入A5H,ISP\IAP命令才會(huì)生效
ISP\IAP操作完成后,IAP地址高8位寄存器IAP_ADDRH、IAP地址低8位寄存器IAP_ADDRL 和IAP命令寄存器IAP_CMD的內(nèi)容不變。如果接下來要對(duì)下一個(gè)地址的數(shù)據(jù)進(jìn)行IAP/ISP操作,需手動(dòng)將該地址的高8位和低8位分別寫入IAP_ADDRH和IAP_ADDRL寄存器。
每次IAP操作時(shí),都要對(duì)IAP_TRIG先寫入5AH,再寫入A5H,ISP/IAP命令才會(huì)生效。
5、IAP_CONTR:ISP\IAP控制寄存器
|
SFR name
|
Address
|
Bit
|
B7
|
B6
|
B5
|
B4
|
B3
|
B2
|
B1
|
B0
|
|
IAP_CONTR
|
C7H
|
Name
|
IAPEN
|
SWBS
|
SWRST
|
CMD_FAIL
|
_
|
WT2
|
WT1
|
WT0
|
IAPEN:ISP/IAP功能允許位:0:禁止IAP讀/寫/擦除 data flash /eeprom
1:允許IAP讀/寫/擦除 data flash /eeprom
SWBW:軟件選擇從用戶應(yīng)用程序區(qū)啟動(dòng)(送0),還是從系統(tǒng)ISP監(jiān)控程序啟動(dòng)(送1)。
要與SWRST直接配合使用才可以實(shí)現(xiàn)
SWRST:0:不操作;1:產(chǎn)生軟件系統(tǒng)復(fù)位,硬件自動(dòng)復(fù)位。
CMD_FAIL:如果送了ISP/IAP命令,并對(duì)IAP_TRIG送5AH/A5H觸發(fā)失敗,則為1,需由軟件清零。
在用戶應(yīng)用程序區(qū)(AP區(qū))軟件復(fù)位并從用戶應(yīng)用程序區(qū)(AP區(qū))開始執(zhí)行程序。
MOV IAP_CONTR,#00100000B;SWBS=0(選擇AP區(qū)),SWRST=1(軟復(fù)位)
在用戶應(yīng)用程序區(qū)(AP區(qū))軟件復(fù)位并從系統(tǒng)ISP監(jiān)控程序區(qū)開始執(zhí)行程序
MOV IAP_CONTR,#01100000B;SWBS=1(選擇ISP區(qū)),SWRST=1(軟復(fù)位)
在系統(tǒng)ISP監(jiān)控程序區(qū)軟件復(fù)位并從用戶應(yīng)用程序區(qū)(AP區(qū))開始執(zhí)行程序
MOV IAP_CONTR,#00100000B;SWBS=0(選擇AP區(qū))SWRST=1(軟復(fù)位)
在系統(tǒng)ISP監(jiān)控程序區(qū)軟件復(fù)位并從系統(tǒng)ISP監(jiān)控程序區(qū)開始執(zhí)行程序。
MOV IAP_CONTR,#01100000B;SWBS=1(選擇ISP區(qū)),SWRST=1(軟復(fù)位)
設(shè)置等待時(shí)間
|
設(shè)置等待時(shí)間
|
CPU等待時(shí)間(多少個(gè)CPU工作時(shí)鐘)
|
||||||
|
WT2
|
WT1
|
WT0
|
Read/讀
(2個(gè)時(shí)鐘)
|
Program/編程(=55us)
|
Sector erase
扇區(qū)擦除
=21us
|
Recommended system clock
跟等待參數(shù)對(duì)應(yīng)的推薦系統(tǒng)時(shí)鐘
|
|
|
1
|
1
|
1
|
2個(gè)時(shí)鐘
|
55個(gè)時(shí)鐘
|
21012個(gè)時(shí)鐘
|
<=1MHz
|
|
|
1
|
1
|
0
|
2個(gè)時(shí)鐘
|
110個(gè)時(shí)鐘
|
42024個(gè)時(shí)鐘
|
<=2MHz
|
|
|
1
|
0
|
1
|
2個(gè)時(shí)鐘
|
165個(gè)時(shí)鐘
|
63036個(gè)時(shí)鐘
|
<=3MHz
|
|
|
1
|
0
|
0
|
2個(gè)時(shí)鐘
|
330個(gè)時(shí)鐘
|
126072個(gè)時(shí)鐘
|
<=6MHz
|
|
|
0
|
1
|
1
|
2個(gè)時(shí)鐘
|
660個(gè)時(shí)鐘
|
252144個(gè)時(shí)鐘
|
<=12MHz
|
|
|
0
|
1
|
0
|
2個(gè)時(shí)鐘
|
1100個(gè)時(shí)鐘
|
420240個(gè)時(shí)鐘
|
<=20MHz
|
|
|
0
|
0
|
1
|
2個(gè)時(shí)鐘
|
1320個(gè)時(shí)鐘
|
504288個(gè)時(shí)鐘
|
<=24MHz
|
|
|
0
|
0
|
0
|
2個(gè)時(shí)鐘
|
1760個(gè)時(shí)鐘
|
672348個(gè)時(shí)鐘
|
<=30MHz
|
|
12c系列單片機(jī)內(nèi)部EEPROM選型一覽表
|
型號(hào)
|
字節(jié)數(shù)(eeprom)
|
扇區(qū)數(shù)
|
起始扇區(qū)首地址
|
結(jié)束扇區(qū)末尾地址
|
|
STC12C5201AD/PWM
|
2K
|
4
|
0000h
|
07ffh
|
|
STC12C5202AD/PWM
|
2k
|
4
|
0000h
|
07ffh
|
|
STC12C5203AD/PWM
|
2k
|
4
|
0000h
|
07ffh
|
|
STC12C5204AD/PWM
|
1k
|
2
|
0000h
|
03ffh
|
|
STC12C5205AD/PWM
|
1k
|
2
|
0000h
|
03ffh
|
大建議:
1、 同一次修改的數(shù)據(jù)放在同一個(gè)扇區(qū)中不是同一次修改的數(shù)據(jù)放在另外的扇區(qū)就不須讀出保護(hù)。
2、 如果一個(gè)扇區(qū)只用一個(gè)字節(jié),那就是真正的EEPROM,STC單片機(jī)的Data flash 比外部EEPROM要快很多讀一個(gè)字節(jié)/編程一個(gè)字節(jié)大概是2個(gè)時(shí)鐘/55微秒。
3、 如果在一個(gè)扇區(qū)中存放了大量的數(shù)據(jù),某次只需要修改其中的一個(gè)字節(jié)或一部分字節(jié)時(shí),則另外的不需要修改的數(shù)據(jù)須先讀出放在STC單片機(jī)的RAM中,然后擦除整個(gè)扇區(qū),再將需要保留的數(shù)據(jù)和需修改的數(shù)據(jù)按字節(jié)逐字節(jié)寫回該扇區(qū)中(只有字節(jié)寫命令,無連續(xù)字節(jié)寫命令)。這時(shí)每個(gè)扇區(qū)使用的字節(jié)數(shù)是使用的越少越方便(不需要讀出一大堆需保留數(shù)據(jù))。
常見問題:
1、 IAP指令完成后,地址是否會(huì)自動(dòng)” 加1”或“減1”?
不會(huì)
2、 送5A和A5觸發(fā)之后下一次IAP命令是否還需要送5A和A5觸發(fā)?
是,一定要。
STC12C5201AD/PWM單片機(jī)內(nèi)部EEPROM地址表
|
第一扇區(qū)
|
第二扇區(qū)
|
第三扇區(qū)
|
第四扇區(qū)
|
每個(gè)扇區(qū)
512字節(jié)
建議同一次修改的數(shù)據(jù)放在同一個(gè)扇區(qū),不是同一次修改的數(shù)據(jù)放在不同的扇區(qū),不必用滿,當(dāng)然也可以用滿。
|
||||
|
起始地址
|
結(jié)束地址
|
起始地址
|
結(jié)束地址
|
起始地址
|
結(jié)束地址
|
起始地址
|
結(jié)束地址
|
|
|
0000h
|
1FFH
|
200H
|
3FFH
|
400H
|
5FFH
|
600H
|
7FFH
|
|
|
第五扇區(qū)
|
第六扇區(qū)
|
第七扇區(qū)
|
第八扇區(qū)
|
|||||
|
起始地址
|
結(jié)束地址
|
起始地址
|
結(jié)束地址
|
起始地址
|
結(jié)束地址
|
起始地址
|
結(jié)束地址
|
|
|
800H
|
9FFH
|
A00H
|
BFFH
|
C00H
|
DFFH
|
E00H
|
FFFH
|
|
|
第九扇區(qū)
|
第十扇區(qū)
|
第十一扇區(qū)
|
第十二扇區(qū)
|
|||||
|
起始地址
|
結(jié)束地址
|
起始地址
|
結(jié)束地址
|
起始地址
|
結(jié)束地址
|
起始地址
|
結(jié)束地址
|
|
|
1000H
|
11FFH
|
1200H
|
13FFH
|
1400H
|
15FFH
|
1600H
|
17FFH
|
|
下面就舉一個(gè)例子來說明一下EEPROM的應(yīng)用
程序功能:
三個(gè)按鍵分別是,開關(guān)、增大、減小。通過7段數(shù)碼管將鍵值顯示出來(1-9)。
并將開關(guān)鍵關(guān)閉前的鍵值保存在EEPROM中,系統(tǒng)再次上電時(shí)顯示上次關(guān)閉前的鍵值。
#include<reg52.h>//頭文件
#include”intrins.h”
/******宏定義*************/
#define uintunsigned int//用unsigned int 代替unsigned int
#define ucharunsigned char//用uchar 替代unsigned char
/*********位定義**************/
sbit SW=P3^2;//開關(guān)
sbit INC=P3^3;//增大按鍵
sbit DEC=P3^4;//減小按鍵
sbitDATA=P1^5;//595數(shù)據(jù)流
sbitSHIFT=P1^6;//595移位寄存器
sbitSTORAGE=P1^7;//595存儲(chǔ)寄存器
sbitBEEP=P1^4;//蜂鳴器
/數(shù)碼管顯示代碼
uchar codenum[]={0x01,0xf3,0x89,0xa1,0xb2,0xa4,0x84,0xf1,0x80,0x20} ; //0-9代碼
//變量聲明
bit kai=0,biaozhi=0;
uchar MA,dat;
/*定義與EEPROM相關(guān)的特殊功能寄存器*/
sfrIAP_DATA=0XC2;//FLASH data register
sfrIAP_ADDRH=0XC3;//FLASH address high
sfrIAP_ADDRL=0XC4;//FLASH address low
sfrIAP_CMD=0XC5;//FLASH command register
sfrIAP_TRIG=0XC6;//FLSH command trigger
sfrIAP_CONTR=0XC7;//flash control register
/*定義IAP/ISP/EEPROM命令*/
#define CMD_IDLE0//stand_by
#define CMD_READ1//byte_read
#defineCMD_PROGRAM 2//byte_program
#defineCMD_ERASE 3//sector_erase
/*定義與EEPROM相關(guān)的寄存器 */
//#defineENABLE_IAP 0X80 //if sysclk<30MHz
//#defineENABLE_IAP 0X81 //if sysclk<24MHz
//#defineENABLE_IAP 0X82 //if sysclk<20MHz
#defineENABLE_IAP 0X83 //if sysclk<12MHz//定義控制寄存器
//#defineENABLE_IAP 0X84 //if sysclk<6MHz
//#defineENABLE_IAP 0X85 //if sysclk<3MHz
//#define ENABLE_IAP 0X86 //if sysclk<2MHz
//#defineENABLE_IAP 0X87 //if sysclk<1MHz
/*定義EEPROM 的起始地址*/
#defineIAP_ADDRESS 0X0000
/****初始化函數(shù)**********/
Void iapidle()
{
IAP_CONTR=0;//關(guān)閉IAP功能
IAP_CMD=0;//CMD寄存器初始化
IAP_TRIG=0;//清空觸發(fā)寄存器
IAP_ADDRH=0X80;//數(shù)據(jù)指針指向非EEPROM區(qū)
IAP_ADDRL=0;//CLEAR IAP address to preventmisuse
}
/*讀EEPROM的一個(gè)字節(jié)地址的內(nèi)容
輸入:地址
輸出:EEPROM字節(jié)數(shù)據(jù)
*/
uchar iapreadbyte(uint addr)
{
IAP_CONTR=ENABLE_IAP;//設(shè)置IAP控制寄存器
IAP_CMD=CMD_READ;//設(shè)置IAP的命令寄存器為讀狀態(tài)
IAP_ADDRL=addr;//設(shè)置EERPROM的低8位地址
IAP_ADDRH=addr>>8;
IAP_TRIG=0X5A;
IAP_TRIG=0XA5;
_nop_();
dat=IAP_DATA;
iapidle();
return dat;
}
/*寫一個(gè)字節(jié)的IAP/ISP/EEPROM空間
輸入: 字節(jié)地址
要寫入的數(shù)據(jù)
Void iapprogrambyte(uint addr,uchar dat)
{
IAP_CONTR=ENABLE_IAP;//打開IAP功能并設(shè)置等待時(shí)間
IAP_CMD=CMD_PROGRAM;//設(shè)置寫命令
IAP_ADDRL=addr;//設(shè)置字節(jié)地址低8位
IAP_ADDRH=addr>>8;//設(shè)置字節(jié)地址的高8位
IAP_DATA=dat;//寫內(nèi)容
IAP_TRIG=0X5A;//發(fā)送觸發(fā)命令1
IAP_TRIG=0XA5;//發(fā)送觸發(fā)命令2
_nop_();//等待直到寫操作完成
Iapidle();
}
/*扇區(qū)擦除
輸入:地址
Void iaperasesector(uint addr)
{
IAP_CONTR=ENABLE_IAP;//開IAP功能并設(shè)置等待時(shí)間
IAP_CMD=CMD_ERASE;//設(shè)置擦除命令
IAP_ADDRL=addr;
IAP_ADDRH=addr>>8;
IAP_TRIG=0x5a;
IAP_TRIG=0XA5;
_nop_();
Iapidle();
}
/********寫595函數(shù)*******************/
void write_595(uchar x)
{
uchar j;
for(j=0;j<8;j++)
{
x=x<<1;
SHIFT=0;
_nop_();
_nop_();
_nop_();
DATA=CY;
SHIFT=1;
_nop_();
_nop_();
_nop_();
SHIFT=0;
}
}
/********595輸出函數(shù)函數(shù)*******************/
void out_595(void)
{
STORAGE=0;
_nop_();
_nop_();
STORAGE=1;
_nop_();
_nop_();
STORAGE=0;
}
/*軟件延時(shí) */
void delay(uchar t)
{ uchar x;
while(t--)
{
for(x=0;x<250;x++)
{
_nop_();
_nop_();
_nop_();
_nop_();
}
}
}
/******按鍵檢測(cè)函數(shù)**********************/
void keycheck(void)
{
if(SW==0)//判斷開關(guān)按鍵是否按下
{delay(10);//抗干擾
if(SW==0)//確實(shí)按下
{kai=~kai;
}
while(!SW)//一直按下
{BEEP=1;}//蜂鳴器響
BEEP=0;//松開按鍵,蜂鳴器關(guān)閉
}
if(INC==0&kai==1&MA<9)//如果處于開的狀態(tài)并且數(shù)字小于9則按下增大鍵執(zhí)行
{
delay(10);
if(INC==0&kai&MA<9)
{MA++;}
while(!INC)
{BEEP=1;}
BEEP=0;
}
if(DEC==0&kai&MA>1)
{
delay(10);
if(DEC==0&kai&MA>1)
{MA--;}
while(!DEC)
{BEEP=1;}
BEEP=0;
}
if(kai==1) //如果電源打開了,則將電源開的狀態(tài)標(biāo)志置1
{biaozhi=1;}
if(biaozhi==1&kai==0)//判斷電源打開后被關(guān)閉,目的是只在開關(guān)鍵關(guān)閉時(shí),寫一次EEPROM,避免不停的擦寫EEPROM
{ biaozhi=0;
iaperasesector(0x00);
programbyte(0x00,MA);
// 執(zhí)行EEPROM寫程
}
}
//主函數(shù)
Void main(void )
{
BEEP=0; //關(guān)閉蜂鳴器
iapreadbyte(0x00);//讀出EEPROM的值
MA=dat;
if(MA<1|MA>9)//如果讀出的值不在1-9范圍內(nèi)則強(qiáng)制為5.
{MA=5;}
while(1)
{
keycheck();//執(zhí)行按鍵掃描程序
write_595(num[MA]);
write_595(num[MA]);
out_595();
}
}
這只是一個(gè)簡單的讀寫一個(gè)字節(jié)的簡單測(cè)試程序,對(duì)于扇區(qū)擦除的結(jié)果也沒有進(jìn)行驗(yàn)證。
想要驗(yàn)證需要在加一段代碼。具體請(qǐng)參照STC的數(shù)據(jù)手冊(cè)。
