STC15系列单片机内部集成了大容量的EEPROM,其与程序空间是分开的。利用ISP/IAP技术可将内部Date Flash当EEPROM,擦写次数在10万次以上。EEPROM可分为若干个扇区,每个扇区包含512个字节。使用时,建议同一次修改的数据放在同一个扇区,不是同一次修改的数据放在不同的扇区,不一定要用满。数据存储器的擦出操作是按扇区进行的。
EEPROM可用于保存一些需要在应用过程中修改并且掉电不丢失的参数数据。在用户程序中,可以对EEPROM进行字节读/字节编程/扇区擦除操作。在工作电压Vcc偏低时,建议不要进行EEPROM/IAP操作。
图1 STC15F2K60S2系列EEPROM空间大小及地址图
注:以上内容选自STC15系列单片机官方指南书。
通过对STC15系列单片机官方指南书的查阅可知,IAP15系列除外,STC15F2K60S2系列单片机可通过图1的地址空间开始对其内部EEPROM进行数据的读写,而IAP15系列可在用户应用程序区修改用户应用程序区,一般从结束扇区末尾地址开始计算。
后面将会给出STC15系列单片机内部EEPROM的驱动程序,只需要调用EEPROM_Read(UINT16 eeprom_address,UINT8 *data_address,UINT8 length)和EEPROM_Write(UINT16 eeprom_address,UINT8 *data_address,UINT8 length)两个函数即可。
#ifndef _EEPROM_H_
#define _EEPROM_H_
#include "STC15F2K60S2.h"
#include
#ifndef UINT8
#define UINT8 unsigned char
#endif
#ifndef UINT16
#define UINT16 unsigned int
#endif
#ifndef EEPROM_BUFFER_LENGTH
#define EEPROM_BUFFER_LENGTH 128 //数据保护缓冲长度
#endif
#define ISP_STANDBY() ISP_CMD = 0 //ISP空闲命令(禁止)
#define ISP_READ() ISP_CMD = 1 //ISP读出命令
#define ISP_WRITE() ISP_CMD = 2 //ISP写入命令
#define ISP_ERASE() ISP_CMD = 3 //ISP擦除命令
//sfr ISP_TRIG = 0xC6;
#define ISP_TRIG() ISP_TRIG = 0x5A, ISP_TRIG = 0xA5 //ISP触发命令
// 7 6 5 4 3 2 1 0 Reset Value
//sfr IAP_CONTR = 0xC7; IAPEN SWBS SWRST CFAIL - WT2 WT1 WT0 0000,x000 //IAP Control Register
#define ISP_EN (1<<7)
#define ISP_SWBS (1<<6)
#define ISP_SWRST (1<<5)
#define ISP_CMD_FAIL (1<<4)
#define ISP_WAIT_1MHZ 7
#define ISP_WAIT_2MHZ 6
#define ISP_WAIT_3MHZ 5
#define ISP_WAIT_6MHZ 4
#define ISP_WAIT_12MHZ 3
#define ISP_WAIT_20MHZ 2
#define ISP_WAIT_24MHZ 1
#define ISP_WAIT_30MHZ 0
#if (MAIN_Fosc >= 24000000L)
#define ISP_WAIT_FREQUENCY ISP_WAIT_30MHZ
#elif (MAIN_Fosc >= 20000000L)
#define ISP_WAIT_FREQUENCY ISP_WAIT_24MHZ
#elif (MAIN_Fosc >= 12000000L)
#define ISP_WAIT_FREQUENCY ISP_WAIT_20MHZ
#elif (MAIN_Fosc >= 6000000L)
#define ISP_WAIT_FREQUENCY ISP_WAIT_12MHZ
#elif (MAIN_Fosc >= 3000000L)
#define ISP_WAIT_FREQUENCY ISP_WAIT_6MHZ
#elif (MAIN_Fosc >= 2000000L)
#define ISP_WAIT_FREQUENCY ISP_WAIT_3MHZ
#elif (MAIN_Fosc >= 1000000L)
#define ISP_WAIT_FREQUENCY ISP_WAIT_2MHZ
#else
#define ISP_WAIT_FREQUENCY ISP_WAIT_1MHZ
#endif
sfr ISP_DATA = 0xC2;
sfr ISP_ADDRH = 0xC3;
sfr ISP_ADDRL = 0xC4;
sfr ISP_CMD = 0xC5;
sfr ISP_TRIG = 0xC6;
sfr ISP_CONTR = 0xC7;
//定义特殊功能寄存器
void EEPROM_Disable();//关闭eeprom读写
void EEPROM_SectorErase(UINT16 eeprom_address);//扇区擦除
void EEPROM_Read(UINT16 eeprom_address,UINT8 *data_address,UINT8 length);//读数据
void EEPROM_Write(UINT16 eeprom_address,UINT8 *data_address,UINT8 length);//写数据
#endif
#include "EEPROM.h"
//STC系列的内置EEPROM读写程序。
/************************************************************************
*STC15F2K60S2的EEPROM空间地址范围:0x0000 - 0x03FF (IAP字节读取)(1K)
*使用例程:
EEPROM_Read(0x0000,medication_lost_warning_phone_3,12);
EEPROM_Write(0x0000,medication_lost_warning_phone_3,12);
*************************************************************************/
//========================================================================
// 函数: void EEPROM_Disable()
// 描述: 禁止访问ISP/IAP.
// 参数: non.
// 返回: non.
//========================================================================
void EEPROM_Disable()
{
ISP_CONTR = 0; //禁止ISP/IAP操作
ISP_CMD = 0; //去除ISP/IAP命令
ISP_TRIG = 0; //防止ISP/IAP命令误触发
ISP_ADDRH = 0xff; //清0地址高字节
ISP_ADDRL = 0xff; //清0地址低字节,指向非EEPROM区,防止误操作
}
//========================================================================
// 函数: void EEPROM_Read(UINT16 eeprom_address,UINT8 *data_address,UINT8 length)
// 描述: 从指定EEPROM首地址读出n个字节放指定的缓冲.
// 参数: eeprom_address: 读出EEPROM的首地址.
// data_address: 读出数据放缓冲的首地址.
// length: 读出的字节长度.
// 返回: non.
//========================================================================
void EEPROM_Read(UINT16 eeprom_address,UINT8 *data_address,UINT8 length)
{
EA = 0; //禁止中断
ET0 = 0;
ISP_CONTR = (ISP_EN + 3); //设置等待时间,允许ISP/IAP操作,送一次就够
ISP_READ(); //送字节读命令,命令不需改变时,不需重新送命令
do
{
ISP_ADDRH = eeprom_address / 256; //送地址高字节(地址需要改变时才需重新送地址)
ISP_ADDRL = eeprom_address % 256; //送地址低字节
ISP_TRIG(); //先送5AH,再送A5H到ISP/IAP触发寄存器,每次都需要如此
//送完A5H后,ISP/IAP命令立即被触发启动
//CPU等待IAP完成后,才会继续执行程序。
_nop_();
*data_address = ISP_DATA; //读出的数据送往
eeprom_address++;
data_address++;
}while(--length);
EEPROM_Disable();
EA = 1; //重新允许中断
ET0 = 1;
}
//========================================================================
// 函数: void EEPROM_SectorErase(UINT16 eeprom_address)
// 描述: 把指定地址的EEPROM扇区擦除.
// 参数: eeprom_address: 要擦除的扇区EEPROM的地址.
// 返回: non.
//========================================================================
void EEPROM_SectorErase(UINT16 eeprom_address)
{
EA = 0; //禁止中断
ET0 = 0;
//只有扇区擦除,没有字节擦除,512字节/扇区。
//扇区中任意一个字节地址都是扇区地址。
ISP_ADDRH = eeprom_address / 256; //送扇区地址高字节(地址需要改变时才需重新送地址)
ISP_ADDRL = eeprom_address % 256; //送扇区地址低字节
ISP_CONTR = (ISP_EN + 3); //设置等待时间,允许ISP/IAP操作,送一次就够
ISP_ERASE(); //送扇区擦除命令,命令不需改变时,不需重新送命令
ISP_TRIG();
_nop_();
EEPROM_Disable();
EA = 1; //重新允许中断
ET0 = 1;
}
//========================================================================
// 函数: void EEPROM_Write(UINT16 eeprom_address,UINT8 *data_address,UINT8 length)
// 描述: 把缓冲的n个字节写入指定首地址的EEPROM.
// 参数: eeprom_address: 写入EEPROM的首地址.
// data_address: 写入源数据的缓冲的首地址.
// length: 写入的字节长度.
// 返回: non.
//========================================================================
void EEPROM_Write(UINT16 eeprom_address,UINT8 *data_address,UINT8 length)
{
UINT8 sector_location;//待写入数据在扇区内的起始地址
UINT8 xdata data_buffer[EEPROM_BUFFER_LENGTH];
UINT16 i;
sector_location = (UINT8)(eeprom_address & 0x01ff);
eeprom_address &= 0xfe00;
EEPROM_Read(eeprom_address,data_buffer,EEPROM_BUFFER_LENGTH);
for(i=0; i