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