STC15F2K60S2内E2PROM应用
1.目的
单片机运行时的数据都存在于RAM(随机存储器)中,在掉电后RAM中的数据是无法保留的,那么怎样使数据在掉电后不丢失呢?这就需要使用内部EEPROM (EEPROM可以擦写100000次)或FLASHROM 等存储器来实现。在传统的单片机系统中,一般是在片外扩展存储器,单片机与存储器之间通过IIC 或SPI 等接口来进行数据通信。这样不光会增加开发成本,同时在程序开发上也要花更多的心思。在STC 单片机中内置了EEPROM(其实是采用ISP/IAP技术读写内部FLASH 来实现EEPROM),正是因为有了IAP,从而可以使单片机可以将数据写入到程序存储器中,使得数据如同烧入的程序一样,掉电不丢失。当然写入数据的区域与程序存储区要分开来,以使程序不会遭到破坏。这样就节省了片外资源,使用起来也更加方便。
下面就详细介绍STC 单片机内置EEPROM 及其使用方法
2.STC15F2K60S2系列单片机的内部结构图
STC15F2K60S2系列单片机的内部结构框图如下图所示,STC15F2K60S2系列单片机中包含中央处理器(CPU)、程序存储器(Flash)、数据存储器(SRAM)、定时器、IO口、高速A/D转换、看门狗、UART高速异步串行通信口1/串行通信口2,CCP/PWM/PCA,一组高速同步串行端口SPI,片内高精度R/C时钟及高可靠复位等模块,STC15F2K60S2系列单片机几乎包含了数据采集和控制中的所有单元模块。
3.IAP及EEPROM新增特殊功能寄存器介绍:
ISP/IAP数据寄存器IAP-DATA
符号 | 地址 | 位地址及符号 | 复位值 | |||||||
IAP-DATA | C2H | MSB |
|
|
|
|
|
| LSB | 1111 1111B |
IAP-ADDRH | C3H |
|
|
|
|
|
|
|
| 0000 0000B |
IAP-ADDRL | C4H |
|
|
|
|
|
|
|
| 0000 0000B |
IAP-CMD | C5H | — | — | — | — | — | — | MS1 | MS0 | XXXX X000B |
IAP-TRIG | C6H |
|
|
|
|
|
|
|
| XXXX XXXXB |
IAP-CONTR | C7H |
IAPEN |
SWBS |
SWRST | CMD_FALL |
— |
WT2 |
WT1 |
WT0 |
0000 X000B |
PCON | 87H | SMOD | SMOD0 | LVDF | POF | GF1 | GF0 | PD | IDL | 0011 0000B |
IAP-DATA:ISP/IAP操作时的数据寄存器。
ISP/ IAP从Flash读出的数据放在此处,向Flash写的数据也放在此处
IAP/ISP地址寄存器IAP-ADDRH和IAP-ADDRL
IAP-ADDRH:ISP/IAP操作时的地址寄存器高八位。
IAP-ADDRL:ISP/IAP操作时的地址寄存器低八位。
IAP/ISP命令寄存器IAP-CMD
IAP/ISP命令寄存器IAP-CMD格式如下:
SFR name | Address | Bit | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
IAP_CMD | C5H | name | - | - | - | - | - | - | MS1 | MS0 |
MS1 | MS0 | 命令/操作 模式选择 |
0 | 0 | Standby 待机模式,无ISP操作 |
0 | 1 | 从用户的应用程序区对“Data Flash/EEPROM区”进行字节读 |
1 | 0 | 从用户的应用程序区对“Data Flash/EEPROM区”进行字节编程 |
1 | 1 | 从用户的应用程序区对“Data Flash/EEPROM区”进行扇区擦除 |
程序在用户应用程序区时,仅可以对数据Flash区(EEPROM)进行字节读/字节编程/扇区擦除
ISP/IAP命令触发寄存器IAP_TRIG
IAP_TRIG:ISP/IAP 操作时的命令模式寄存器。
在IAPEN(IAP_CONTR.7)=1时,对IAP_TRIG先写入5Ah,再写入A5h,ISP/IAP命令才会生效。
ISP/IAP操作完成后,IAP地址高八位寄存器IAP_ADDRH、IAP地址低八位寄存器IAP_ADDRL和IAP命令寄存器IAP_CMD的内容不变。如果接下来要对下一个地址的数据进行ISP/IAP操作,需手动将该地址的高八位和低八位分别写入IAP_ADDRH和IAP_ADDRL寄存器。
每次IAP操作时,都要对对IAP_TRIG先写入5Ah,再写入A5h,ISP/IAP命令才会生效。
在每次触发前,需重新送字节读/字节编程/扇区擦除命令,在命令不改变时,不需重新送命令。
ISP/IAP命令寄存器IAP_CONTR
ISP/IAP控制寄存器IAP_CONTR格式如下:
SFR name | Address | Bit | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
IAP_CONTR | C7H | name | IAPEN | SWBS | SWRST | CMD_FALL | - | WT2 | WT1 | WT0 |
IAPEN:ISP/IAP功能允许为。
0:禁止IAP读/写/擦除Data Flash/EEPROM
1:允许IAP读/写/擦除Data Flash/EEPROM
SWBS:软件选择复位后从用户应用程序区启动(送0),还是从系统ISP监控程序区启动(送1).要与SWRST直接配合才可以实现
SWRST:0:不操作;1:软件控制产生复位,单片机自动复位。
CMD_FALL:如果IAP地址(由IAP地址寄存器IAP_ADDRH和IAP_ADDRL的值决定)指向了非法地址或无效地址,且送了ISP/IAP命令,并对IAP_TRIG送5Ah/A5h触发失败,则CMD_FALL为1,需软件清零。
设置等待时间 | CPU等待时间(多少个CPU工作时钟) | |||||
WT2 | WT1 | WT0 | Read/读(2个时钟) | Program/编程(=55us) | Sector Erase 扇区擦除(=21ms) | 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个钟 | 672384个时钟 | <=30MHz |
工作电压过低判断,此时不要进行EEPROM/IAP操作
PCON:电源控制寄存器
SFR name | Address | Bit | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
PCON | 87H | name | SMOD | SMOD0 | LVDF | POF | DF1 | GF0 | PD | IDL |
LVDF:低压检测标志位,当工作电压Vcc低于低压检测门槛电压时,该位置1.该位要由软件清0当低压检测电路发现工作电压Vcc偏低时,不要进行EEPROM/IAP操作。
5V单片机的低压检测门槛电压:
-40°C | 25°C | 85°C |
4.74 | 4.64 | 4.60 |
4.41 | 4.32 | 4.27 |
4.14 | 4.05 | 4.00 |
3.90 | 3.82 | 3.77 |
3.69 | 3.61 | 3.56 |
3.51 | 3.43 | 3.38 |
3.36 | 3.28 | 3.23 |
3.21 | 3.14 | 3.09 |
3.3V单片机的低压检测门槛电压:
-40°C | 25°C | 85°C |
3.11 | 3.08 | 3.09 |
2.85 | 2.82 | 2.83 |
2.63 | 2.61 | 2.61 |
2.44 | 2.42 | 2.43 |
2.29 | 2.26 | 2.26 |
2.14 | 2.12 | 2.12 |
2.01 | 2.00 | 2.00 |
1.90 | 1.89 | 1.89 |
STC15F2K60S2系列单片机EEPROM空间大小及地址
EEPROM字节数:1K;
扇区数: 2;
用IAP字节读时EEPROM起始扇区首地址: 0000h;
用IAP字节读时EEPROM结束扇区末地址: 03FFh;
用MOVC指令读时EEPROM起始扇区首地址: F000h;
用MOVC指令读时EEPROM结束扇区末地址:F3FFh;
注:512个字节为一个扇区。
4.原理图:
4.1STC15F2K60S2主芯片
4.2显示部分
4.3测试效果
当程序下载完毕,给开发板上电时数码管从000开始显示,随后(005S)断掉电源数秒,然后从新上电,此时数码管从006开始计时。
5.程序
/*********STC系列单片机EEPROM/IAP功能测试程序**************/
/************************主程序********************************/
#include <reg52.h>
#include "E2ROM.c"
code BYTE seven_seg[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92,0x82, 0xf8, 0x80, 0x90};//共阳数码管0--9(0时为有效断)
code BYTE scan_bit[] = {0xfe, 0xfd,0xfb}; //数码管位选 6 5 4 3 2 1
BYTE hour = 0, min = 0, sec = 0;
BYTE cp1, cp2, j; //控制数码管的位选
BYTE flash; //控制小数点闪烁
/***********************中断初始化函数**********************/
void timer0_init(void)
{
TMOD= 0x01; //中断方式1
TH0= 0xf8;
TL0= 0x2f; //对机器脉冲计数,2000个计满溢出引发中断
EA= 1; //开总中断
ET0= 1; //开T0中断
TR0= 1; //启动定时器T0
}
/*******************Timer0中断服务函数**********************/
void timer0_isr(void) interrupt 1
{
TH0= 0xf8; //重新附初值
TL0= 0x2f; //重新附初值
cp1++;
if(cp1>= 250)//半秒
{
cp1= 0;
cp2++;
flash= ~flash; //每半秒取反一次
}
if(cp2>= 2)//一秒
{
cp2= 0;
sec++;
IapEraseSector(0x0000); //擦除第一扇区
IapProgramByte(0x0000,sec); //重新写入数据
}
if(sec>= 60)//当秒为60时分钟开始计数
{
sec= 0;
min++;
IapEraseSector(0x200); //擦除第二扇区
IapProgramByte(0x200,min); //重新写入数据
}
if(min>= 60)//当分钟为60时小时开始计数
{
min= 0;
hour++;
IapEraseSector(0x600); //擦除第三扇区
IapProgramByte(0x600,hour); //重新写入数据
}
if(hour >= 24)hour = 0;//当小时大于24时小时赋值为0
P0= 0xff; //Protues仿真需要消隐
//显示正在走时间
switch(j)
{
case0 : P0 = seven_seg[sec % 10]; break;
case1 : P0 = seven_seg[sec / 10]; break;
case2 : P0 = seven_seg[min % 10] & (0x7f | flash); break;
}
P2= scan_bit[j];
j++;
j%= 3;
}
/*******************主函数**********************/
void main(void)
{
timer0_init();
sec= IapReadByte(0x0000); //从扇区读数据
min= IapReadByte(0x200);
hour= IapReadByte(0x400);
while(1);
}
/*******************E2ROM.C**********************/
#include <reg52.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 0x82 //ifSYSCLK<12MHZ
#define IAP_ADDRESS 0x0000
void IapIdle();
BYTE IapReadByte(WORD addr);
void IapProgramByte(WORD addr, BYTEdat);
void IapEraseSector(WORD addr);
/*******************关闭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)
{
BYTEdat; //数据缓冲区
IAP_CONTR= ENABLE_IAP; //使能IAP
IAP_CMD= CMD_READ; //设置读取命令
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功能
returndat; //返回
}
/**********写一个字节到ISP/IAP/EEPROM区域*************/
void IapProgramByte(WORD addr, BYTEdat)
{
IAP_CONTR= ENABLE_IAP;
IAP_CMD= CMD_PROGRAM;
IAP_ADDRL= addr;
IAP_ADDRH= addr >> 8;
IAP_DATA= dat;
IAP_TRIG= 0x5a;
IAP_TRIG= 0xa5;
_nop_();
IapIdle();
}
/**************扇区擦除***************************/
void IapEraseSector(WORD addr)
{
IAP_CONTR= ENABLE_IAP;
IAP_CMD= CMD_ERASE;
IAP_ADDRL= addr;
IAP_ADDRH= addr >> 8;
IAP_TRIG= 0x5a;
IAP_TRIG= 0xa5;
_nop_();
IapIdle();
}