当在程序运行的过程中你希望修改某个变量并且此变量的值在掉电以后不丢失,那么你就可以采用将变量数据写入EEPROM的方式来实现。
什么是EEPROM,即Electrically Erasable ProgrammableRead_Only Memory首先它是一种存储器,并且可以通过高电压来进行反复擦写的存储器。具有掉电数据不丢失的特点。比如常用的24C系列,93C系列的器件。一般这种器件采用I2C的方式与单片机进行通讯,对于这种通讯方式及器件的应用另作总结。这里主要总结一下,STC12C5204AD芯片内部包含的EEPROM的应用方法。
STC12C5201AD系列单片机内部集成了EEPROM是与程序空间分开的,利用ISP/IAP技术可将内部data flash当EEPROM,擦写10万次以上。
EEPROM可分为若干个扇区,每个扇区包含512字节。
使用时建议同一次修改的数据放在同一个扇区,不是同一次修改的数据放在不同的扇区,不一定要用满。数据存储器的擦除操作是按扇区进行的。
在程序中可对EEPROM进行字节读写/字节编程/扇区擦除操作。在工作电压Vcc偏低时,建议不要进行EEPROM/IAP操作。以免发生数据错误。
应用的步骤
1、 声明与EEPROM相关的寄存器
2、 编写EEPROM初始化函数
3、 编写字节擦除函数
4、 编写字节编程函数
5、 编写字节读取函数
6、 在需要读取EEPROM字节内容时直接调用字节读取函数即可
7、 在需要进行写EEPROM字节时,先调用字节擦除函数,将字节内容擦除成FFH后,在调用字节编程函数,将数据写入到EEPROM的地址单元中。
与EEPROM应用相关的寄存器
符号
|
描述
|
地址
|
位地址及符号
|
复位值
|
|||||||
IAP_DATA
|
ISP/IAP flash data register
|
C2H
|
|
|
|
|
|
|
|
|
1111 1111B
|
IAP_ADDRH
|
ISP/IAP flash address high
|
C3H
|
|
|
|
|
|
|
|
|
0000 0000B
|
IAP_ADDRL
|
ISP/IAP flash address low
|
C4H
|
|
|
|
|
|
|
|
|
|
IAP_CMD
|
ISP/IAP flash command register
|
C5H
|
|
|
|
|
|
|
MS1
|
MS0
|
|
IAP_TRIG
|
ISP/IAP flash command trigger
|
C6H
|
|
|
|
|
|
|
|
|
xxxxxxxxxB
|
IAP_CONTR
|
ISP/IAP control register
|
C7H
|
IAPEN
|
SWBS
|
SWRST
|
CMD_FAIL
|
|
WT2
|
WT1
|
WT0
|
0000X000B
|
PCON
|
Power control
|
87H
|
SOMD
|
SMOD0
|
LVDF
|
POF
|
GF1
|
GF0
|
PD
|
IDL
|
00110000B
|
1、IAP_DATA:ISP/IAP数据寄存器
ISP/IAP操作时的数据寄存器。
ISP/IAP从FlASH读出的数据存放此处,向flash写的数据也需要放在此处。
2、 IAP_ADDRH和IAP_ADDRL :IAP/ISP地址寄存器
3、 IAP_CMD:ISP/IAP命令寄存器
MS1
|
MS0
|
命令/操作 模式选择
|
0
|
0
|
Standby 待机模式,无ISP操作
|
0
|
1
|
从用户程序区对“data flash /EEprom区”进行字节读
|
1
|
0
|
从用户的应用程序区对“data flash/eeprom区”进行字节编写
|
1
|
1
|
从用户的应用程序区对“data flash/eeprom区”进行扇区擦除
|
4、IAP_TRIG:ISP/IAP命令触发寄存器
在IAPEN(IAP_CONTR.7)=1时,对IAP——trig先写入5AH,在写入A5H,ISP\IAP命令才会生效
ISP\IAP操作完成后,IAP地址高8位寄存器IAP_ADDRH、IAP地址低8位寄存器IAP_ADDRL 和IAP命令寄存器IAP_CMD的内容不变。如果接下来要对下一个地址的数据进行IAP/ISP操作,需手动将该地址的高8位和低8位分别写入IAP_ADDRH和IAP_ADDRL寄存器。
每次IAP操作时,都要对IAP_TRIG先写入5AH,再写入A5H,ISP/IAP命令才会生效。
5、IAP_CONTR:ISP\IAP控制寄存器
SFR name
|
Address
|
Bit
|
B7
|
B6
|
B5
|
B4
|
B3
|
B2
|
B1
|
B0
|
IAP_CONTR
|
C7H
|
Name
|
IAPEN
|
SWBS
|
SWRST
|
CMD_FAIL
|
_
|
WT2
|
WT1
|
WT0
|
IAPEN:ISP/IAP功能允许位:0:禁止IAP读/写/擦除 data flash /eeprom
1:允许IAP读/写/擦除 data flash /eeprom
SWBW:软件选择从用户应用程序区启动(送0),还是从系统ISP监控程序启动(送1)。
要与SWRST直接配合使用才可以实现
SWRST:0:不操作;1:产生软件系统复位,硬件自动复位。
CMD_FAIL:如果送了ISP/IAP命令,并对IAP_TRIG送5AH/A5H触发失败,则为1,需由软件清零。
在用户应用程序区(AP区)软件复位并从用户应用程序区(AP区)开始执行程序。
MOV IAP_CONTR,#00100000B;SWBS=0(选择AP区),SWRST=1(软复位)
在用户应用程序区(AP区)软件复位并从系统ISP监控程序区开始执行程序
MOV IAP_CONTR,#01100000B;SWBS=1(选择ISP区),SWRST=1(软复位)
在系统ISP监控程序区软件复位并从用户应用程序区(AP区)开始执行程序
MOV IAP_CONTR,#00100000B;SWBS=0(选择AP区)SWRST=1(软复位)
在系统ISP监控程序区软件复位并从系统ISP监控程序区开始执行程序。
MOV IAP_CONTR,#01100000B;SWBS=1(选择ISP区),SWRST=1(软复位)
设置等待时间
设置等待时间
|
CPU等待时间(多少个CPU工作时钟)
|
||||||
WT2
|
WT1
|
WT0
|
Read/读
(2个时钟)
|
Program/编程(=55us)
|
Sector erase
扇区擦除
=21us
|
Recommended system clock
跟等待参数对应的推荐系统时钟
|
|
1
|
1
|
1
|
2个时钟
|
55个时钟
|
21012个时钟
|
<=1MHz
|
|
1
|
1
|
0
|
2个时钟
|
110个时钟
|
42024个时钟
|
<=2MHz
|
|
1
|
0
|
1
|
2个时钟
|
165个时钟
|
63036个时钟
|
<=3MHz
|
|
1
|
0
|
0
|
2个时钟
|
330个时钟
|
126072个时钟
|
<=6MHz
|
|
0
|
1
|
1
|
2个时钟
|
660个时钟
|
252144个时钟
|
<=12MHz
|
|
0
|
1
|
0
|
2个时钟
|
1100个时钟
|
420240个时钟
|
<=20MHz
|
|
0
|
0
|
1
|
2个时钟
|
1320个时钟
|
504288个时钟
|
<=24MHz
|
|
0
|
0
|
0
|
2个时钟
|
1760个时钟
|
672348个时钟
|
<=30MHz
|
|
12c系列单片机内部EEPROM选型一览表
型号
|
字节数(eeprom)
|
扇区数
|
起始扇区首地址
|
结束扇区末尾地址
|
STC12C5201AD/PWM
|
2K
|
4
|
0000h
|
07ffh
|
STC12C5202AD/PWM
|
2k
|
4
|
0000h
|
07ffh
|
STC12C5203AD/PWM
|
2k
|
4
|
0000h
|
07ffh
|
STC12C5204AD/PWM
|
1k
|
2
|
0000h
|
03ffh
|
STC12C5205AD/PWM
|
1k
|
2
|
0000h
|
03ffh
|
大建议:
1、 同一次修改的数据放在同一个扇区中不是同一次修改的数据放在另外的扇区就不须读出保护。
2、 如果一个扇区只用一个字节,那就是真正的EEPROM,STC单片机的Data flash 比外部EEPROM要快很多读一个字节/编程一个字节大概是2个时钟/55微秒。
3、 如果在一个扇区中存放了大量的数据,某次只需要修改其中的一个字节或一部分字节时,则另外的不需要修改的数据须先读出放在STC单片机的RAM中,然后擦除整个扇区,再将需要保留的数据和需修改的数据按字节逐字节写回该扇区中(只有字节写命令,无连续字节写命令)。这时每个扇区使用的字节数是使用的越少越方便(不需要读出一大堆需保留数据)。
常见问题:
1、 IAP指令完成后,地址是否会自动” 加1”或“减1”?
不会
2、 送5A和A5触发之后下一次IAP命令是否还需要送5A和A5触发?
是,一定要。
STC12C5201AD/PWM单片机内部EEPROM地址表
第一扇区
|
第二扇区
|
第三扇区
|
第四扇区
|
每个扇区
512字节
建议同一次修改的数据放在同一个扇区,不是同一次修改的数据放在不同的扇区,不必用满,当然也可以用满。
|
||||
起始地址
|
结束地址
|
起始地址
|
结束地址
|
起始地址
|
结束地址
|
起始地址
|
结束地址
|
|
0000h
|
1FFH
|
200H
|
3FFH
|
400H
|
5FFH
|
600H
|
7FFH
|
|
第五扇区
|
第六扇区
|
第七扇区
|
第八扇区
|
|||||
起始地址
|
结束地址
|
起始地址
|
结束地址
|
起始地址
|
结束地址
|
起始地址
|
结束地址
|
|
800H
|
9FFH
|
A00H
|
BFFH
|
C00H
|
DFFH
|
E00H
|
FFFH
|
|
第九扇区
|
第十扇区
|
第十一扇区
|
第十二扇区
|
|||||
起始地址
|
结束地址
|
起始地址
|
结束地址
|
起始地址
|
结束地址
|
起始地址
|
结束地址
|
|
1000H
|
11FFH
|
1200H
|
13FFH
|
1400H
|
15FFH
|
1600H
|
17FFH
|
下面就举一个例子来说明一下EEPROM的应用
程序功能:
三个按键分别是,开关、增大、减小。通过7段数码管将键值显示出来(1-9)。
并将开关键关闭前的键值保存在EEPROM中,系统再次上电时显示上次关闭前的键值。
#include<reg52.h>//头文件
#include”intrins.h”
/******宏定义*************/
#define uintunsigned int//用unsigned int 代替unsigned int
#define ucharunsigned char//用uchar 替代unsigned char
/*********位定义**************/
sbit SW=P3^2;//开关
sbit INC=P3^3;//增大按键
sbit DEC=P3^4;//减小按键
sbitDATA=P1^5;//595数据流
sbitSHIFT=P1^6;//595移位寄存器
sbitSTORAGE=P1^7;//595存储寄存器
sbitBEEP=P1^4;//蜂鸣器
/数码管显示代码
uchar codenum[]={0x01,0xf3,0x89,0xa1,0xb2,0xa4,0x84,0xf1,0x80,0x20} ; //0-9代码
//变量声明
bit kai=0,biaozhi=0;
uchar MA,dat;
/*定义与EEPROM相关的特殊功能寄存器*/
sfrIAP_DATA=0XC2;//FLASH data register
sfrIAP_ADDRH=0XC3;//FLASH address high
sfrIAP_ADDRL=0XC4;//FLASH address low
sfrIAP_CMD=0XC5;//FLASH command register
sfrIAP_TRIG=0XC6;//FLSH command trigger
sfrIAP_CONTR=0XC7;//flash control register
/*定义IAP/ISP/EEPROM命令*/
#define CMD_IDLE0//stand_by
#define CMD_READ1//byte_read
#defineCMD_PROGRAM 2//byte_program
#defineCMD_ERASE 3//sector_erase
/*定义与EEPROM相关的寄存器 */
//#defineENABLE_IAP 0X80 //if sysclk<30MHz
//#defineENABLE_IAP 0X81 //if sysclk<24MHz
//#defineENABLE_IAP 0X82 //if sysclk<20MHz
#defineENABLE_IAP 0X83 //if sysclk<12MHz//定义控制寄存器
//#defineENABLE_IAP 0X84 //if sysclk<6MHz
//#defineENABLE_IAP 0X85 //if sysclk<3MHz
//#define ENABLE_IAP 0X86 //if sysclk<2MHz
//#defineENABLE_IAP 0X87 //if sysclk<1MHz
/*定义EEPROM 的起始地址*/
#defineIAP_ADDRESS 0X0000
/****初始化函数**********/
Void iapidle()
{
IAP_CONTR=0;//关闭IAP功能
IAP_CMD=0;//CMD寄存器初始化
IAP_TRIG=0;//清空触发寄存器
IAP_ADDRH=0X80;//数据指针指向非EEPROM区
IAP_ADDRL=0;//CLEAR IAP address to preventmisuse
}
/*读EEPROM的一个字节地址的内容
输入:地址
输出:EEPROM字节数据
*/
uchar iapreadbyte(uint addr)
{
IAP_CONTR=ENABLE_IAP;//设置IAP控制寄存器
IAP_CMD=CMD_READ;//设置IAP的命令寄存器为读状态
IAP_ADDRL=addr;//设置EERPROM的低8位地址
IAP_ADDRH=addr>>8;
IAP_TRIG=0X5A;
IAP_TRIG=0XA5;
_nop_();
dat=IAP_DATA;
iapidle();
return dat;
}
/*写一个字节的IAP/ISP/EEPROM空间
输入: 字节地址
要写入的数据
Void iapprogrambyte(uint addr,uchar dat)
{
IAP_CONTR=ENABLE_IAP;//打开IAP功能并设置等待时间
IAP_CMD=CMD_PROGRAM;//设置写命令
IAP_ADDRL=addr;//设置字节地址低8位
IAP_ADDRH=addr>>8;//设置字节地址的高8位
IAP_DATA=dat;//写内容
IAP_TRIG=0X5A;//发送触发命令1
IAP_TRIG=0XA5;//发送触发命令2
_nop_();//等待直到写操作完成
Iapidle();
}
/*扇区擦除
输入:地址
Void iaperasesector(uint addr)
{
IAP_CONTR=ENABLE_IAP;//开IAP功能并设置等待时间
IAP_CMD=CMD_ERASE;//设置擦除命令
IAP_ADDRL=addr;
IAP_ADDRH=addr>>8;
IAP_TRIG=0x5a;
IAP_TRIG=0XA5;
_nop_();
Iapidle();
}
/********写595函数*******************/
void write_595(uchar x)
{
uchar j;
for(j=0;j<8;j++)
{
x=x<<1;
SHIFT=0;
_nop_();
_nop_();
_nop_();
DATA=CY;
SHIFT=1;
_nop_();
_nop_();
_nop_();
SHIFT=0;
}
}
/********595输出函数函数*******************/
void out_595(void)
{
STORAGE=0;
_nop_();
_nop_();
STORAGE=1;
_nop_();
_nop_();
STORAGE=0;
}
/*软件延时 */
void delay(uchar t)
{ uchar x;
while(t--)
{
for(x=0;x<250;x++)
{
_nop_();
_nop_();
_nop_();
_nop_();
}
}
}
/******按键检测函数**********************/
void keycheck(void)
{
if(SW==0)//判断开关按键是否按下
{delay(10);//抗干扰
if(SW==0)//确实按下
{kai=~kai;
}
while(!SW)//一直按下
{BEEP=1;}//蜂鸣器响
BEEP=0;//松开按键,蜂鸣器关闭
}
if(INC==0&kai==1&MA<9)//如果处于开的状态并且数字小于9则按下增大键执行
{
delay(10);
if(INC==0&kai&MA<9)
{MA++;}
while(!INC)
{BEEP=1;}
BEEP=0;
}
if(DEC==0&kai&MA>1)
{
delay(10);
if(DEC==0&kai&MA>1)
{MA--;}
while(!DEC)
{BEEP=1;}
BEEP=0;
}
if(kai==1) //如果电源打开了,则将电源开的状态标志置1
{biaozhi=1;}
if(biaozhi==1&kai==0)//判断电源打开后被关闭,目的是只在开关键关闭时,写一次EEPROM,避免不停的擦写EEPROM
{ biaozhi=0;
iaperasesector(0x00);
programbyte(0x00,MA);
// 执行EEPROM写程
}
}
//主函数
Void main(void )
{
BEEP=0; //关闭蜂鸣器
iapreadbyte(0x00);//读出EEPROM的值
MA=dat;
if(MA<1|MA>9)//如果读出的值不在1-9范围内则强制为5.
{MA=5;}
while(1)
{
keycheck();//执行按键扫描程序
write_595(num[MA]);
write_595(num[MA]);
out_595();
}
}
这只是一个简单的读写一个字节的简单测试程序,对于扇区擦除的结果也没有进行验证。
想要验证需要在加一段代码。具体请参照STC的数据手册。