STC89C51、52内部都自带有2K字节的EEPROM,54、55和58都自带有16K字节的EEPROM,STC单片机是使用IAP技能完成的EEPROM,内部Flash擦写次数可达100,000 次以上,先来介绍下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单片机内部可用DataFlash(EEPROM)的地址如表3所示,其它类型单片机请查阅相关材料。
表3STC89C52RC、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中读取从前存储的数据,接着递加显现。
#include
#include //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 inTISectorAddr;
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 charoriginal_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++;
}