#include "reg51.h"
#include "intrins.h"
typedef unsigned char BYTE;
typedef unsigned int WORD;
//-----------------------------------------------
sfr IAP_DATA = 0xC2; //IAP數據寄存器
sfr IAP_ADDRH = 0xC3; //IAP地址寄存器高字節
sfr IAP_ADDRL = 0xC4; //IAP地址寄存器低字節
sfr IAP_CMD = 0xC5; //IAP命令寄存器
sfr IAP_TRIG = 0xC6; //IAP命令觸發寄存器
sfr IAP_CONTR = 0xC7; //IAP控制寄存器
#define CMD_IDLE 0 //空閒模式
#define CMD_READ 1 //IAP字節讀命令
#define CMD_PROGRAM 2 //IAP字節編程命令
#define CMD_ERASE 3 //IAP扇區擦除命令
//#define ENABLE_IAP 0x80 //if SYSCLK<30MHz
//#define ENABLE_IAP 0x81 //if SYSCLK<24MHz
#define ENABLE_IAP 0x82 //if SYSCLK<20MHz
//#define ENABLE_IAP 0x83 //if SYSCLK<12MHz
//#define ENABLE_IAP 0x84 //if SYSCLK<6MHz
//#define ENABLE_IAP 0x85 //if SYSCLK<3MHz
//#define ENABLE_IAP 0x86 //if SYSCLK<2MHz
//#define ENABLE_IAP 0x87 //if SYSCLK<1MHz
//測試地址
#define IAP_ADDRESS 0x0400
void Delay(BYTE n);
void IapIdle();
BYTE IapReadByte(WORD addr);
void IapProgramByte(WORD addr, BYTE dat);
void IapEraseSector(WORD addr);
void main()
{
WORD i;
P1 = 0xfe; //1111,1110 系統OK
Delay(10); //延時
IapEraseSector(IAP_ADDRESS); //扇區擦除
for (i=0; i<512; i++) //檢測是否擦除成功(全FF檢測)
{
if (IapReadByte(IAP_ADDRESS+i) != 0xff)
goto Error; //如果出錯,則退出
}
P1 = 0xfc; //1111,1100 擦除成功
Delay(10); //延時
for (i=0; i<512; i++) //編程512字節
{
IapProgramByte(IAP_ADDRESS+i, (BYTE)i);
}
P1 = 0xf8; //1111,1000 編程完成
Delay(10); //延時
for (i=0; i<512; i++) //校驗512字節
{
if (IapReadByte(IAP_ADDRESS+i) != (BYTE)i)
goto Error; //如果校驗錯誤,則退出
}
P1 = 0xf0; //1111,0000 測試完成
while (1);
Error:
P1 &= 0x7f; //0xxx,xxxx IAP操作失敗
while (1);
}
/*----------------------------
軟件延時
----------------------------*/
void Delay(BYTE n)
{
WORD x;
while (n--)
{
x = 0;
while (++x);
}
}
/*----------------------------
關閉IAP
----------------------------*/
void IapIdle()
{
IAP_CONTR = 0; //關閉IAP功能
IAP_CMD = 0; //清除命令寄存器
IAP_TRIG = 0; //清除觸發寄存器
IAP_ADDRH = 0x80; //將地址設置到非IAP區域
IAP_ADDRL = 0;
}
/*----------------------------
從ISP/IAP/EEPROM區域讀取一字節
----------------------------*/
BYTE IapReadByte(WORD addr)
{
BYTE dat; //數據緩衝區
IAP_CONTR = ENABLE_IAP; //使能IAP
IAP_CMD = CMD_READ; //設置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(WORD addr, BYTE dat)
{
IAP_CONTR = ENABLE_IAP; //使能IAP
IAP_CMD = CMD_PROGRAM; //設置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(WORD addr)
{
IAP_CONTR = ENABLE_IAP; //使能IAP
IAP_CMD = CMD_ERASE; //設置IAP命令
IAP_ADDRL = addr; //設置IAP低地址
IAP_ADDRH = addr >> 8; //設置IAP高地址
IAP_TRIG = 0x5a; //寫觸發命令(0x5a)
IAP_TRIG = 0xa5; //寫觸發命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
IapIdle();
}
這是官方的實例,在測試中總是讀不到數據。搞了好長時間也沒有找到問題問題,沒辦法,在官方手冊上查找了參數,發現//測試地址#define IAP_ADDRESS 0x0400錯了,應該是從//測試地址#define IAP_ADDRESS 0x0000開始,改成就可以了,可以寫入數據,關機也可以讀取到數據。
但我操作是隻寫入一個字節,在原程序上改了下,發現還是不能用,從串口反饋過來的數據發現數據不能寫入,每次寫指令都給定了,但數據就是寫不進去,參考別的資料,感覺寫入前都發必須先讀取出來數據,保存。在擦除一次EEPROM,在把改好的數據寫入,把程序寫好,測試從串口反饋出來數據都正常了。
這功能很不錯,不用外加EEPROM芯片,可以實現功能帶記憶,可以存取設定的狀態,在下次在開機,執行上次關機的狀態。 這次也學會怎樣讀寫EEPROM,平時對EEPROM芯片打交道也很多的,一般都是用編程器來讀寫的。總感覺很神密的。下回有空自己寫個來讀取的轉機。