18.1 內(nèi)部 E2PROM 簡(jiǎn)介
單片機(jī)在運(yùn)行時(shí)數(shù)據(jù)均存儲(chǔ)在內(nèi)部 RAM(隨機(jī)存儲(chǔ)器)中,在掉電時(shí)無(wú)法保存數(shù)據(jù)。前面提到過(guò)可以通過(guò)增加外部存儲(chǔ)器 AT24C01 芯片的方式解決,但因?yàn)樾枰黾油獠侩娐?,性?xún)r(jià)比并不高,因此不推薦該方法。STC89C51、52 內(nèi)部都自帶有 2K 字節(jié)的 E2PROM??赏ㄟ^(guò)對(duì) STC 單片機(jī)內(nèi)部的 E2PROM 編程來(lái)實(shí)現(xiàn),這樣節(jié)省了片外資源,使用也比較方便。
STC 單片機(jī)內(nèi)部的 E2PROM 并不是真正的 E2PROM,而是用 DATA FLASH 模擬出來(lái)的,因此操作方法與普通 E2PROM 不同。STC 單片機(jī)內(nèi)部的 E2PROM 采用的是 IAP(在應(yīng)用編程)技術(shù)實(shí)現(xiàn)讀寫(xiě)操作,擦寫(xiě)次數(shù)可達(dá) 100,000 次以上。所謂 IAP 指程序在運(yùn)行時(shí)程序存儲(chǔ)器可有程序本身進(jìn)行擦寫(xiě)。IAP 是相對(duì) ISP 而言的,下面進(jìn)行詳細(xì)的分析。
18.2 ISP 和 IAP 區(qū)別
ISP: In System Programable 是指在系統(tǒng)編程,通俗的講,就是片子已經(jīng)焊板子上,不用取下,就可以簡(jiǎn)單而方便地對(duì)其進(jìn)行編程。比如我們通過(guò)電腦給 STC 單片機(jī)下載程序。
IAP: In Application Programable 是指在應(yīng)用編程,就是片子提供一系列的機(jī)制(硬件/軟件上的)當(dāng)片子在運(yùn)行程序的時(shí)候可以提供一種改變存儲(chǔ)器數(shù)據(jù)的方法。通俗點(diǎn)講,也就是說(shuō)程序自己可以往程序存儲(chǔ)器里寫(xiě)數(shù)據(jù)或修改程序。這種方式的典型應(yīng)用就是用一小段代碼來(lái)實(shí)現(xiàn)程序的下載,實(shí)際上單片機(jī)的 ISP 功能就是通過(guò) IAP 技術(shù)來(lái)實(shí)現(xiàn)的,即片子在出廠前就已經(jīng)有一段小的 boot 程序在里面,片子上電后,開(kāi)始運(yùn)行這段程序,當(dāng)檢測(cè)到上位機(jī)有下載要求時(shí),便和上位機(jī)通信,然后下載程序到程序存儲(chǔ)區(qū)。
以 STC89C52 為例進(jìn)行分析,存儲(chǔ)空間包括 8KB flash 程序存儲(chǔ)空間、512B RAM 數(shù)據(jù)存儲(chǔ)空間、2KB E2PROM 存儲(chǔ)空間。在 51 單片機(jī)中采用的是數(shù)據(jù)和程序存儲(chǔ)地址空間并行的哈佛結(jié)構(gòu),地址分配如下所示:
8KB flash 地址:0——1FFFH
512B RAM 地址:0——0200H
2KB E2PROM 地址:2000H——27FFH
ISP 操作對(duì)象為 8KB flash,IAP 的操作對(duì)象為 2KBE2PROM,IAP 不能對(duì) flash 進(jìn)行讀寫(xiě)操作。IAP 在讀寫(xiě)操作的結(jié)果為,將要寫(xiě)入的值與 E2PROM 中原來(lái)的值進(jìn)行與操作然后將結(jié)果存入。例如在地址 2000H 處第一次成功寫(xiě)入 11010110,第二次寫(xiě)入 00111010,讀出的結(jié)果將會(huì)是這兩個(gè)結(jié)果的相與 0010010,因此如果寫(xiě)入數(shù)據(jù)前該處數(shù)據(jù)不為 FFH,那么寫(xiě)入的數(shù)據(jù)將會(huì)不正確。IAP 的擦除操作的功能就是將數(shù)據(jù)變?yōu)?FFH,但擦除操作是以扇區(qū)為基本操作單位的,STC89C52 的 E2PROM 扇區(qū)地址安排如下表所示。每個(gè)扇區(qū)的大小為 512B。
數(shù)據(jù)存儲(chǔ)操作按照以下步驟進(jìn)行:
1. 寫(xiě)操作之前先將對(duì)應(yīng)扇區(qū)的有效數(shù)據(jù)讀取到 RAM 中暫存(這步不是必須的);
2. 對(duì)整個(gè)扇區(qū)進(jìn)行擦除操作,擦除后該扇區(qū)的數(shù)據(jù)均為 FFH;
3. 將要寫(xiě)入的字節(jié)寫(xiě)入;
4. 將暫存的數(shù)據(jù)寫(xiě)入;
STC 單片機(jī) IAP 程序操作步驟如下:
1. 配置 ISP_CONTR 寄存器,使能第 7 位 ISPEN,讓 ISP_IAP 功能生效,并配置低三位的等待時(shí)間;
2. 寫(xiě)指令:讀/寫(xiě)/擦除,3 個(gè)命令;
3. 賦值 ISP_ADDRH 和 ISP_ADDRL 的地址值,分別為所要操作位置的地址高低位;
4. 關(guān)閉總中斷 EA,因?yàn)橄旅嬉獙?xiě)的 2 個(gè)觸發(fā)指令必須是連續(xù)操作;
5. 執(zhí)行 ISP_IAP 觸發(fā)指令,觸發(fā)后才能進(jìn)行讀寫(xiě);
6. 打開(kāi)總中斷 EA,關(guān)閉 ISP_IAP 功能,清除相關(guān)寄存器。
IAP 及 E2PROM 新增特殊功能寄存器如下圖所示:
1. ISP_DATA:ISP/IAP 數(shù)據(jù)寄存器
ISP/IAP 操作時(shí)的數(shù)據(jù)存儲(chǔ)器,ISP/IAP 從 Flash 讀出來(lái)的數(shù)據(jù)存放在此處,向 Flash 寫(xiě)的數(shù)據(jù)也需要放在此處。
2. ISP_ADDRH/ISP_ADDRL:ISP/IAP 地址寄存器
分別為地址的高、低八位,復(fù)位值為 0x0000。
3. ISP_CMD:ISP/IAP 命令寄存器
MS1 MS0=00
待機(jī)模式,無(wú)數(shù)據(jù)讀寫(xiě)操作;
MS1 MS0=01
從應(yīng)用程序區(qū)對(duì)”Data Flash/E2PROM 區(qū)”進(jìn)行字節(jié)讀命令
MS1 MS0=10
從應(yīng)用程序區(qū)對(duì)”Data Flash/E2PROM 區(qū)”進(jìn)行字節(jié)寫(xiě)命令
MS1 MS0=11
從應(yīng)用程序區(qū)對(duì)”Data Flash/E2PROM 區(qū)”進(jìn)行扇區(qū)擦除命令
4. ISP_TRIG:ISP/IAP 命令觸發(fā)寄存器
在 ISPEN(ISP_CONTR.7)=1 時(shí),對(duì) ISP_TRIG 先寫(xiě)入 0x46,再寫(xiě)入 0xB9,ISP/IAP 功能才會(huì)生效。
5. ISP_CONTR:ISP/IAP 控制寄存器
ISPEN:ISP/IAP 功能允許位。ISPEN=0,禁止 ISP/IAP 讀、寫(xiě)、擦除操作。ISPEN=1,允許 ISP/IAP 讀、寫(xiě)、擦除操作。
SWBS:0 表示,軟件從應(yīng)用程序區(qū)啟動(dòng),1 表示,從系統(tǒng) ISP 監(jiān)控程序區(qū)啟動(dòng)。需與 SWRST 配合使用。
SWRST:0 不操作,1 表示產(chǎn)生軟件系統(tǒng)復(fù)位,硬件自動(dòng)復(fù)位。
SWBS=1,SWRST=1 時(shí),表示在應(yīng)用程序區(qū)軟件復(fù)位并從系統(tǒng) ISP 監(jiān)控程序區(qū)開(kāi)始執(zhí)行程序。SWBS=0,SWRST=1 時(shí),表示在應(yīng)用程序區(qū)軟件復(fù)位并從應(yīng)用程序區(qū)開(kāi)始處執(zhí)行程序。
B2~B0 表示在讀、寫(xiě)、擦除操作過(guò)程中 CPU 插入的等待時(shí)間,推薦選擇如下所示。
18.3 E2PROM 驅(qū)動(dòng)函數(shù)編寫(xiě)
前面已經(jīng)講解了與內(nèi)部 E2PROM 有關(guān)的 6 個(gè)寄存器的功能,下面我們結(jié)合這些寄存器編寫(xiě)驅(qū)動(dòng)函數(shù),因?yàn)樵谡5?reg52.h 中并沒(méi)有對(duì)上述 6 個(gè)特殊功能寄存器進(jìn)行聲明,所以首先得進(jìn)行聲明以及名字字節(jié)定義,如下代碼所示:
/****************特殊功能寄存器聲明****************/
sfr ISP_DATA = 0xE2;
sfr ISP_ADDRH = 0xE3;
sfr ISP_ADDRL = 0xE4;
sfr ISP_CMD = 0xE5;
sfr ISP_TRIG = 0xE6;
sfr ISP_CONTR = 0xE7;
/******************定義命令字節(jié)******************/
#define read_cmd 0x01 //讀命令
#define wirte_cmd 0x02 //寫(xiě)命令
#define erase_cmd 0x03 //擦除命令
/****定義操作等待時(shí)間以及允許IAP操作*******/
#define enable_waitTime 0x82 //系統(tǒng)工作時(shí)鐘< 20MHz 時(shí)
接下來(lái)兩個(gè)函數(shù)分別為關(guān)閉、開(kāi)啟 ISP/IAP 功能函數(shù),以便后續(xù)調(diào)用,如下所示:
void ISP_IAP_disable(void)//關(guān)閉ISP_IAP
{
EA=1;//恢復(fù)中斷
ISP_CONTR = 0x00;
ISP_CMD = 0x00;
ISP_TRIG = 0x00;
}
void ISP_IAP_trigger()//開(kāi)啟
{
EA=0; //下面的2條指令必須連續(xù)執(zhí)行,故關(guān)中斷
ISP_TRIG = 0x46;//送觸發(fā)命令字0x46
ISP_TRIG = 0xB9;//送觸發(fā)命令字0xB9
}
如上所示,在開(kāi)啟功能也成為功能觸發(fā)函數(shù)時(shí)需要關(guān)閉系統(tǒng)中斷 EA,保證命令字 0x46、0xB9 被連續(xù)寫(xiě)入。單片機(jī)對(duì) E2PROM 的操作包括讀、寫(xiě)以及擦除,讀數(shù)據(jù)操作步驟如下所示:
1. 清零數(shù)據(jù)寄存器 ISP_DATA,這一步不是必須的;
2. 向寄存器 ISP_CMD 寫(xiě)入讀數(shù)據(jù)命令;
3. 允許 ISP/IAP,并給出操作等待時(shí)間;
4. 發(fā)送要讀取的目標(biāo)數(shù)據(jù)的存儲(chǔ)地址;
5. 開(kāi)啟 ISP/IAP 功能;
6. 讀出 ISP_DATA 中的數(shù)據(jù)并保存;
7. 關(guān)閉 ISP/IAP 功能;
上面講解的是讀取單個(gè)字節(jié)的步驟,如需讀取多個(gè)字節(jié)的數(shù)據(jù)只需重復(fù)第 4 到第 6 步,讀數(shù)據(jù)函數(shù)如下所示:
void ISP_IAP_readData(uint beginAddr, uchar* pBuf, uint dataSize) //讀取數(shù)據(jù)
{
ISP_DATA=0; //清零,不清也可以
ISP_CMD = read_cmd; //指令:讀取
ISP_CONTR = enable_waitTime;//開(kāi)啟ISP_IAP,并送等待時(shí)間
while(dataSize--) //循環(huán)讀取
{
ISP_ADDRH = (uchar)(beginAddr > > 8); //送地址高字節(jié)
ISP_ADDRL = (uchar)(beginAddr & 0x00ff); //送地址低字節(jié)
ISP_IAP_trigger(); //觸發(fā)
beginAddr++; //地址++
*pBuf++ = ISP_DATA; //將數(shù)據(jù)保存到接收緩沖區(qū)
}
ISP_IAP_disable();//關(guān)閉ISP_IAP功能
}
寫(xiě)數(shù)據(jù)函數(shù)與讀數(shù)據(jù)函數(shù)類(lèi)似,如下所示:
void ISP_IAP_writeData(uint beginAddr,uchar* pDat,uint dataSize) //寫(xiě)數(shù)據(jù)
{
ISP_CONTR = enable_waitTime; //開(kāi)啟ISP_IAP,并送等待時(shí)間
ISP_CMD = wirte_cmd; //送字節(jié)編程命令字
while(dataSize--)
{
ISP_ADDRH = (uchar)(beginAddr > > 8); //送地址高字節(jié)
ISP_ADDRL = (uchar)(beginAddr & 0x00ff);//送地址低字節(jié)
ISP_DATA = *pDat++;//送數(shù)據(jù)
beginAddr++;
ISP_IAP_trigger();//觸發(fā)
}
ISP_IAP_disable(); //關(guān)閉
}
擦除扇區(qū)函數(shù)如下所示:
void ISP_IAP_sectorErase(uint sectorAddr)//扇區(qū)擦除
{
ISP_CONTR = enable_waitTime; //開(kāi)啟ISP_IAP;并送等待時(shí)間
ISP_CMD = erase_cmd; //送扇區(qū)擦除命令字
ISP_ADDRH = (uchar)(sectorAddr > > 8); //送地址高字節(jié)
ISP_ADDRL = (uchar)(sectorAddr & 0X00FF);//送地址低字節(jié)
ISP_IAP_trigger();//觸發(fā)
ISP_IAP_disable();//關(guān)閉ISP_IAP功能
}
值得注意的是:在擦除扇區(qū)函數(shù)中,地址只需在該扇區(qū)范圍內(nèi)即可,不要求發(fā)送該扇區(qū)的首地址。到此我們編寫(xiě)完成了所有函數(shù),因此將函整合到完整的驅(qū)動(dòng)代碼中。"Drive_Eeprom.h "代碼如下:
#ifndef __Eeprom_H__
#define __Eeprom_H__
extern void ISP_IAP_disable(void);//關(guān)閉ISP_IAP
extern void ISP_IAP_trigger();//觸發(fā)
extern void ISP_IAP_readData(unsigned int beginAddr, unsigned char* pBuf, unsigned int dataSize);//讀取數(shù)據(jù)
extern void ISP_IAP_writeData(unsigned int beginAddr,unsigned char* pDat,unsigned int dataSize);//寫(xiě)數(shù)據(jù)
extern void ISP_IAP_sectorErase(unsigned int sectorAddr);//扇區(qū)擦除
#endif
"Drive_Eeprom.c "代碼如下:
#include< reg52.h >
#define uint unsigned int
#define uchar unsigned char
/****************特殊功能寄存器聲明****************/
sfr ISP_DATA = 0xE2;
sfr ISP_ADDRH = 0xE3;
sfr ISP_ADDRL = 0xE4;
sfr ISP_CMD = 0xE5;
sfr ISP_TRIG = 0xE6;
sfr ISP_CONTR = 0xE7;
/******************定義命令字節(jié)******************/
#define read_cmd 0x01 //讀命令
#define wirte_cmd 0x02 //寫(xiě)命令
#define erase_cmd 0x03 //擦除命令
/****定義操作等待時(shí)間以及允許IAP操作*******/
#define enable_waitTime 0x82 //系統(tǒng)工作時(shí)鐘< 20MHz 時(shí)
void ISP_IAP_disable(void)//關(guān)閉ISP_IAP
{
EA=1;//恢復(fù)中斷
ISP_CONTR = 0x00;
ISP_CMD = 0x00;
ISP_TRIG = 0x00;
}
void ISP_IAP_trigger()//觸發(fā)
{
EA=0; //下面的2條指令必須連續(xù)執(zhí)行,故關(guān)中斷
ISP_TRIG = 0x46;//送觸發(fā)命令字0x46
ISP_TRIG = 0xB9;//送觸發(fā)命令字0xB9
}
void ISP_IAP_readData(uint beginAddr, uchar* pBuf, uint dataSize) //讀取數(shù)據(jù)
{
ISP_DATA=0; //清零,不清也可以
ISP_CMD = read_cmd; //指令:讀取
ISP_CONTR = enable_waitTime;//開(kāi)啟ISP_IAP,并送等待時(shí)間
while(dataSize--) //循環(huán)讀取
{
ISP_ADDRH = (uchar)(beginAddr > > 8); //送地址高字節(jié)
ISP_ADDRL = (uchar)(beginAddr & 0x00ff); //送地址低字節(jié)
ISP_IAP_trigger(); //觸發(fā)
beginAddr++; //地址++
*pBuf++ = ISP_DATA; //將數(shù)據(jù)保存到接收緩沖區(qū)
}
ISP_IAP_disable();//關(guān)閉ISP_IAP功能
}
void ISP_IAP_writeData(uint beginAddr,uchar* pDat,uint dataSize) //寫(xiě)數(shù)據(jù)
{
ISP_CONTR = enable_waitTime; //開(kāi)啟ISP_IAP,并送等待時(shí)間
ISP_CMD = wirte_cmd; //送字節(jié)編程命令字
while(dataSize--)
{
ISP_ADDRH = (uchar)(beginAddr > > 8); //送地址高字節(jié)
ISP_ADDRL = (uchar)(beginAddr & 0x00ff);//送地址低字節(jié)
ISP_DATA = *pDat++;//送數(shù)據(jù)
beginAddr++;
ISP_IAP_trigger();//觸發(fā)
}
ISP_IAP_disable(); //關(guān)閉
}
void ISP_IAP_sectorErase(uint sectorAddr)//扇區(qū)擦除
{
ISP_CONTR = enable_waitTime; //開(kāi)啟ISP_IAP;并送等待時(shí)間
ISP_CMD = erase_cmd; //送扇區(qū)擦除命令字
ISP_ADDRH = (uchar)(sectorAddr > > 8); //送地址高字節(jié)
ISP_ADDRL = (uchar)(sectorAddr & 0X00FF);//送地址低字節(jié)
ISP_IAP_trigger();//觸發(fā)
ISP_IAP_disable();//關(guān)閉ISP_IAP功能
}
18.4 E2PROM 應(yīng)用
下面我們下一個(gè)小的應(yīng)用程序來(lái)驗(yàn)證我們驅(qū)動(dòng)函數(shù),函數(shù)實(shí)現(xiàn)的功能為記錄開(kāi)發(fā)板上電的次數(shù)。并把上電的次數(shù),顯示到 1602 液晶顯示器上,主函數(shù)如下圖所示:
#include< reg52.h >
#include"Drive_1602.h"
#include"Drive_Eeprom.h"
#define uchar unsigned char
#define uint unsigned int
sbit DU = P2^7;//數(shù)碼管段選、位選引腳定義
sbit WE = P2^6;
sbit DU_L = P2^3;
uchar pbuf[5] = {0};//數(shù)據(jù)緩沖區(qū)
uchar str[8] = {0};//字符臨時(shí)變量
void main()
{
P3=0;
P0 = 0;//關(guān)閉所有數(shù)碼管
WE = 1;
WE = 0;
DU_L = 0;
Init_1602();//1602初始化
ISP_IAP_readData(0x21f0,pbuf,sizeof(pbuf)); //讀取內(nèi)部存儲(chǔ)器中數(shù)值
pbuf[0]++;
str[0] = pbuf[0]/100 + '0';
str[1] = (pbuf[0]%100)/10 + '0';
str[2] = pbuf[0]%10 + '0';
str[4] = '?';
Disp_1602_str(1,1,str);//顯示上電次數(shù)
ISP_IAP_sectorErase(0x2000); //扇區(qū)擦除,一塊512字節(jié)
ISP_IAP_writeData(0x21f0,pbuf,sizeof(pbuf)); //寫(xiě)EEPROM
while(1);
}
將程序下載到單片機(jī)開(kāi)發(fā)板觀察現(xiàn)象是否與預(yù)想的一致。
18.5 本章小結(jié)
本章詳細(xì)介紹了單片機(jī)內(nèi)部EEPROM的讀寫(xiě)原理及驅(qū)動(dòng)程序的編寫(xiě)。
評(píng)論
查看更多