STC15系列單片機(jī)內(nèi)部集成了大容量的EEPROM,與其程序空間是分開(kāi)的。利用ISP/IAP技術(shù)可將內(nèi)部Data Flash當(dāng)EEPROM,擦寫次數(shù)在10W次以上。EEPROM可分為若干個(gè)扇區(qū),每個(gè)扇區(qū)包含512字節(jié)。使用時(shí),建議同一次修改的數(shù)據(jù)放在同一個(gè)扇區(qū),不是同一次修改的數(shù)據(jù)放在不同扇區(qū),不一定要用滿。數(shù)據(jù)存儲(chǔ)器的擦除操作是按扇區(qū)進(jìn)行的。
EEPROM可用于保存一些需要在應(yīng)用中修改并且掉電不易丟失的參數(shù)數(shù)據(jù)。在用戶程序中,可以對(duì)EEPROM進(jìn)行子節(jié)讀/字節(jié)編程/扇區(qū)擦除操作。在工作電壓Vcc偏低時(shí),建議不要進(jìn)行EEPROM/IAP操作。
一、IAP及EEPROM相關(guān)寄存器
1.1 數(shù)據(jù)寄存器IAP_DATA
IAP_DATA是ISP/IAP操作時(shí)的數(shù)據(jù)寄存器。ISP/IAP從Flash讀出的數(shù)據(jù)放在此處,向Flash寫的數(shù)據(jù)也許放在此處。
1.2 地址寄存器IAP_ADDRH和IAP_ADDRL
IAP_ADDRH,ISP/IAP操作時(shí)的地址寄存器高八位。
IAP_ADDRL, ISP/IAP操作時(shí)的地址寄存器高低位。
1.3 命令寄存器IAP_CMD
1.4 命令觸發(fā)寄存器IAP_TRIG
IAP_TRIG是ISP/IAP操場(chǎng)時(shí)的命令觸發(fā)寄存器。在IAPEN(IAP_CONTER.7) = 1時(shí),對(duì)IAP_TRIG先寫入5Ah,再寫入A5h,ISP/IAP命令才會(huì)生效。ISP/IAP操作完成后,IAP地址高八位IAP_ADDRH、IAP地址低八位寄存器IAP_ADDRL和IAP命令寄存器IAP_CMD的內(nèi)容不變。如果接下來(lái)要對(duì)下一個(gè)地址數(shù)據(jù)進(jìn)行ISP/IAP操作,需手動(dòng)將地址的高8位和低8位分別寫入IAP_ADDRH和IAP_ADDRL寄存器。
每次IAP操作時(shí),IAP_TRIG先寫入5Ah,再寫入A5h,ISP/IAP命令才會(huì)生效。每次在觸發(fā)錢,需要重新發(fā)送字節(jié)讀/字節(jié)編程/扇區(qū)擦除命令,在命令不改變時(shí),不需要重新送命令。
1.5 命令寄存器IAP_CONTR
IAPEN:ISP/IAP功能允許位。
0:禁止IAP讀/寫/擦除Data Flash/EEPROM
1:允許IAP讀/寫/擦除Data Flash/EEPROM
SWBS:
0:軟件選擇復(fù)位后從用戶應(yīng)用程序區(qū)啟動(dòng)
1:還是從系統(tǒng)ISP檢測(cè)程序區(qū)啟動(dòng)
SWRST:
0:不操作
1:軟件控制產(chǎn)生復(fù)位,單片機(jī)自動(dòng)復(fù)位
CMD_FAIL:如果IAP地址(由IAP地址寄存器IAP_ADDRH和IAP_ADDRL的值決定)指向了非法地址或者無(wú)效地址,并送了ISP/IAP命令,并對(duì)IAP_TRIG送5Ah/A5h觸發(fā)失敗,則CMD_FAIL為1,需要軟件清零。
WT0~WT2控制等待時(shí)間:
1.6 工作電壓過(guò)低判斷 此時(shí)不需要EEPROM/IAP操作
PCON,電源控制寄存器:
LVDF:低壓檢測(cè)標(biāo)志位,當(dāng)工作電壓Vcc低于低壓檢測(cè)門檻電壓時(shí),該位置1。該位要軟件清0。當(dāng)?shù)蛪簷z測(cè)電路發(fā)現(xiàn)工作電壓Vcc偏低時(shí),不要進(jìn)行EEPROM/IAP操作。
二、STC15單片機(jī)EEPROM空間大小及地址
這里,我們暫時(shí)只關(guān)注STC15W408AS即可。型號(hào)不一樣記得看手冊(cè)。
三、測(cè)試程序
#include "stc15.h"
#include "intrins.h"
void IapIdle();
uchar IapReadByte(uchar addr);
void IapProgramByte(uchar addr, uchar dat);
void IapEraseSector(uchar addr);
#define IAP_ADDRESS 0x0001
void main()
{
uchar ret;
uint i;
P1M0 = 0x02;
P1M1 = 0x00;
P11 = 0; // 先 關(guān)閉LED
// 這里最好加一點(diǎn)延時(shí) 方便查看現(xiàn)象
IapEraseSector(IAP_ADDRESS); // 擦除地址的數(shù)據(jù) 恢復(fù)初始值
IapProgramByte(IAP_ADDRESS,1); // 寫入1
if(IapReadByte(IAP_ADDRESS) == 1)
{
P11 = 1; // 如果讀取的數(shù)據(jù)是1 就點(diǎn)亮LED
}
while(1);
}
// 關(guān)閉IAP 寄存器設(shè)置成默認(rèn)值
void IapIdle()
{
IAP_CONTR = 0; // 禁止IAP讀/寫/擦除Data Flash/EEPROM
IAP_CMD = 0; // 待機(jī) 無(wú)ISP操作
IAP_TRIG = 0; // 清除命令寄存器
IAP_ADDRH = 0x80; // 將地址位設(shè)置到非IAP區(qū)域
IAP_ADDRL = 0;
}
// 從ISP/IAP/EEPROM區(qū)域讀取一字節(jié)
uchar IapReadByte(uchar addr)
{
uchar dat;
IAP_CMD = 0x01; //設(shè)置IAP命令 讀
IAP_ADDRL = addr; //設(shè)置IAP低地址
IAP_ADDRH = addr > > 8; //設(shè)置IAP高地址
IAP_TRIG = 0x5a; //寫觸發(fā)命令(0x5a)
IAP_TRIG = 0xa5; //寫觸發(fā)命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
dat = IAP_DATA; //讀ISP/IAP/EEPROM數(shù)據(jù)
IapIdle(); //關(guān)閉IAP功能
return dat; //返回
}
// 寫一字節(jié)數(shù)據(jù)到ISP/IAP/EEPROM區(qū)域
void IapProgramByte(uchar addr, uchar dat)
{
IAP_CONTR = 0x80; //使能IAP
IAP_CMD = 0x02; //設(shè)置IAP命令 編程
IAP_ADDRL = addr; //設(shè)置IAP低地址
IAP_ADDRH = addr > > 8; //設(shè)置IAP高地址
IAP_DATA = dat; //寫ISP/IAP/EEPROM數(shù)據(jù)
IAP_TRIG = 0x5a; //寫觸發(fā)命令(0x5a)
IAP_TRIG = 0xa5; //寫觸發(fā)命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
IapIdle();
}
// 擦除某一地址的數(shù)據(jù)
void IapEraseSector(uchar addr)
{
IAP_CONTR = 0x80; //使能IAP
IAP_CMD = 0x03; //設(shè)置IAP命令 擦除
IAP_ADDRL = addr; //設(shè)置IAP低地址
IAP_ADDRH = addr > > 8; //設(shè)置IAP高地址
IAP_TRIG = 0x5a; //寫觸發(fā)命令(0x5a)
IAP_TRIG = 0xa5; //寫觸發(fā)命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
IapIdle();
}