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
#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
#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();
}