STC89C51、52內部都自帶有2K字節的EEPROM,54、55和58都自帶有16K字節的EEPROM,STC單片機是利用IAP技術實現的EEPROM,內部Flash擦寫次數可達100,000 次以上,先來介紹下ISP與IAP的區別和特點。
知識點:ISP與IAP介紹
ISP:In System Programable 是指在系統編程,通俗的講,就是片子已經焊板子上,不用取下,就可以簡單而方便地對其進行編程。比如我們通過電腦給STC單片機下載程序,或給AT89S51單片機下載程序,這就是利用了ISP技術。
IAP:In Application Programable 是指在應用編程,就是片子提供一系列的機制(硬件/軟件上的)當片子在運行程序的時候可以提供一種改變flash數據的方法。通俗點講,也就是說程序自己可以往程序存儲器里寫數據或修改程序。這種方式的典型應用就是用一小段代碼來實現程序的下載,實際上單片機的ISP功能就是通過IAP技術來實現的,即片子在出廠前就已經有一段小的boot程序在里面,片子上電后,開始運行這段程序,當檢測到上位機有下載要求時,便和上位機通信,然后下載數據到存儲區。大家要注意千萬不要嘗試去擦除這段ISP引導程序,否則恐怕以后再也下載不了程序了。
STC單片機內部有幾個專門的特殊功能寄存器負責管理ISP/IAP功能的,見表1。
表1 ISP/IAP相關寄存器列表
名稱 |
地址 |
功能描述 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
復位值 |
ISP_DATA |
E2h |
Flash數據寄存器 |
|
|
|
|
|
|
|
|
1111 1111 |
ISP_ADDRH |
E3h |
Flash高字節地址寄存器 |
|
|
|
|
|
|
|
|
0000 0000 |
ISP_ADDRL |
E4h |
Flash低字節地址寄存器 |
|
|
|
|
|
|
|
|
0000 0000 |
ISP_CMD |
E5h |
Flash命令模式寄存器 |
-- |
-- |
-- |
-- |
-- |
MS2 |
MS1 |
MS0 |
xxxx x000 |
ISP_TRIG |
E6h |
Flash命令觸發寄存器 |
|
|
|
|
|
|
|
|
xxxx xxxx |
ISP_CONTR |
E7h |
ISP/IAP 控制寄存器 |
ISPEN |
SWBS |
SWRST |
-- |
-- |
WT2 |
WT1 |
WT0 |
000x x000 |
ISP_DATA:ISP/IAP操作時的數據寄存器。
ISP/IAP從Flash讀出的數據放在此處,向Flash寫入的數據也需放在此處。
ISP_ADDRH:ISP/IAP操作時的地址寄存器高八位。
ISP_ADDRL:ISP/IAP操作時的地址寄存器低八位。
ISP_CMD:ISP/IAP操作時的命令模式寄存器,須命令觸發寄存器觸發方可生效。命令模式如表2所示。
表2 ISP_CMD寄存器模式設置
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
模式選擇 |
保留 |
命令選擇 |
|
-- |
-- |
-- |
-- |
-- |
0 |
0 |
0 |
待機模式,無ISP操作 |
-- |
-- |
-- |
-- |
-- |
0 |
0 |
1 |
對用戶的應用程序flash區及數據flash區字節讀 |
-- |
-- |
-- |
-- |
-- |
0 |
1 |
0 |
對用戶的應用程序flash區及數據flash區字節編程 |
-- |
-- |
-- |
-- |
-- |
0 |
1 |
1 |
對用戶的應用程序flash區及數據flash區扇區擦除 |
程序在系統ISP程序區時可以對用戶應用程序區/數據Flash區(EEPROM)進行字節讀/字節編程/扇區擦除;程序在用戶應用程序區時,僅可以對數據Flash區(EEPROM)進行字節讀/字節編程/扇區擦除。STC89C51RC/RD+系列單片機出廠時已經固化有ISP引導碼,并設置為上電復位進入ISP程序區,并且出廠時就已完全加密。
ISP_TRIG:ISP/IAP操作時的命令觸發寄存器。
在ISPEN(ISP_CONTR.7) =1時,對ISP_TRIG 先寫入46h,再寫入B9h,ISP/IAP命令才會生效。
STC89C52RC,STC89LE52RC單片機內部可用Data Flash(EEPROM)的地址如表3所示,其它型號單片機請查閱相關資料。
表3 STC89C52RC、STC89LE52RC單片機內部EEPROM地址表
第一扇區 |
第二扇區 |
第三扇區 |
第四扇區 |
起始地址 |
結束地址 |
起始地址 |
結束地址 |
起始地址 |
結束地址 |
起始地址 |
結束地址 |
2000H |
21FFH |
2200H |
23FFH |
2400H |
25FFH |
2600H |
27FFH |
第五扇區 |
第六扇區 |
第七扇區 |
第八扇區 |
起始地址 |
結束地址 |
起始地址 |
結束地址 |
起始地址 |
結束地址 |
起始地址 |
結束地址 |
2800H |
29FFH |
2A00H |
2BFFH |
2C00H |
2DFFH |
2E00H |
2FFFH |
每個扇區為512字節,建議大家在寫程序時,將同一次修改的數據放在同一個扇區,方便修改,因為在執行擦除命令時,一次最少要擦除一個扇區的數據,每次在更新數據前都必須要擦除原數據方可重新寫入新數據,不能直接在原來數據基礎上更新內容。
下面通過一個例子來講解STC系列單片機EEPROM的具體用法。 【例】:在實驗板上實現如下描述,操作STC單片機自帶的EEPROM,存儲一組按秒遞增的二位數據,并且將數據實時顯示在數碼管上,數據每變化一次就往EEPROM中寫入一次,當關閉實驗板電源,再次開啟電源時,從EEPROM中讀取先前存儲的數據,接著遞增顯示。
新建文件part3.4.4.c,程序代碼如下: #include <intrins.h> #include <reg52.h> //52系列單片機頭文件 #define uchar unsigned char #define uint unsigned int #define RdCommand 0x01 //定義ISP的操作命令 #define PrgCommand 0x02 #define EraseCommand 0x03 #define Error 1 #define Ok 0 #define WaitTime 0x01 //定義CPU的等待時間 sfr ISP_DATA=0xe2; //寄存器申明 sfr ISP_ADDRH=0xe3; sfr ISP_ADDRL=0xe4; sfr ISP_CMD=0xe5; sfr ISP_TRIG=0xe6; sfr ISP_CONTR=0xe7; sbit dula=P2^6; //申明U1鎖存器的鎖存端 sbit wela=P2^7; //申明U2鎖存器的鎖存端 uchar code table[]={ 0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71}; uchar num; void delayms(uint xms) { uint i,j; for(i=xms;i>0;i--) //i=xms即延時約xms毫秒 for(j=110;j>0;j--); } void display(uchar shi,uchar ge) //顯示子函數 { dula=1; P0=table[shi]; //送十位段選數據 dula=0; P0=0xff; //送位選數據前關閉所有顯示,防止打開位選鎖存時 wela=1; //原來段選數據通過位選鎖存器造成混亂 P0=0xfe; //送位選數據 wela=0; delayms(5); //延時 dula=1; P0=table[ge]; //送個位段選數據 dula=0; P0=0xff; wela=1; P0=0xfd; wela=0; delayms(5); } /* ================ 打開 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_(); } /* ==================== 字節讀 ======================== */ unsigned char byte_read(unsigned int byte_addr) { ISP_ADDRH = (unsigned char)(byte_addr >> 8);/* 地址賦值 */ ISP_ADDRL = (unsigned char)(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(unsigned int sector_addr) { unsigned int iSectorAddr; iSectorAddr = (sector_addr & 0xfe00); /* 取扇區地址 */ ISP_ADDRH = (unsigned char)(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(unsigned int byte_addr, unsigned char original_data) { ISP_ADDRH = (unsigned char)(byte_addr >> 8); /* 取地址 */ ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff); ISP_CMD = ISP_CMD & 0xf8; /* 清低3位 */ ISP_CMD = ISP_CMD | PrgCommand; /* 寫命令2 */ ISP_DATA = original_data; /* 寫入數據準備 */ ISPgoon(); /* 觸發執行 */ ISP_IAP_disable(); /* 關閉IAP功能 */ } void main() { uchar a,b,num1; TMOD=0x01; //設置定時器0為工作方式1(0000 0001) TH0=(65536-50000)/256; TL0=(65536-50000)%256; EA=1; ET0=1; TR0=1; num1=byte_read(0x2000);//程序開始時讀取EEPROM中數據 if(num1>=60) //防止首次上電時讀取出錯 num1=0; while(1) { if(num>=20) { num=0; num1++; SectorErase(0x2000);//擦除扇區 byte_write(0x2000,num1);//重新寫入數據 if(num1==60) { num1=0; } a=num1/10; b=num1%10; } display(a,b); } } void timer0() interrupt 1 { TH0=(65536-50000)/256; TL0=(65536-50000)%256; num++; } 分析:程序中關健部分已經用注釋加以說明,請讀者編譯程序下載后觀察實際演示效果。
[此貼子已經被作者于2010-4-9 18:27:12編輯過]
|