原创 STC单片机内部EEPROM的应用

2008-9-8 07:09 10117 5 5 分类: MCU/ 嵌入式

    ***说明:相关内容来自网上,整理而成。转贴在此主要供参考学习用


单片机运行时的数据都存在于RAM(随机存储器)中,在掉电后RAM 中的数据是无
法保留的,那么怎样使数据在掉电后不丢失呢?这就需要使用EEPROM 或FLASHROM 等
存储器来实现。在传统的单片机系统中,一般是在片外扩展存储器,单片机与存储器之间通
过IIC 或SPI 等接口来进行数据通信。这样不光会增加开发成本,同时在程序开发上也要花
更多的心思。在STC 单片机中内置了EEPROM(其实是采用IAP 技术读写内部FLASH 来
实现EEPROM),这样就节省了片外资源,使用起来也更加方便。下面就详细介绍STC 单
片机内置EEPROM 及其使用方法。
STC 各型号单片机内置的EEPROM 的容量各有不同,见下表:
(内部EEPROM 可以擦写100000 次以上)
上面提到了IAP,它的意思是“在应用编程”,即在程序运行时程序存储器可由程序自
身进行擦写。正是是因为有了IAP,从而可以使单片机可以将数据写入到程序存储器中,使
得数据如同烧入的程序一样,掉电不丢失。当然写入数据的区域与程序存储区要分开来,以
使程序不会遭到破坏。
要使用IAP 功能,与以下几个特殊功能寄存器相关:
ISP_DATA: ISP/IAP 操作时的数据寄存器。
ISP/IAP 从Flash 读出的数据放在此处,向Flash 写的数据也需放在此处
ISP_ADDRH:ISP/IAP 操作时的地址寄存器高八位。
ISP_ADDRL:ISP/IAP 操作时的地址寄存器低八位。
ISP_CMD: ISP/IAP 操作时的命令模式寄存器,须命令触发寄存器触发方可生效。
ISP_TRIG:ISP/IAP 操作时的命令触发寄存器。
当ISPEN(ISP_CONTR.7)=1 时,对ISP_TRIG 先写入0x46,再写入0xb9,ISP/IAP
命令才会生效。
单片机芯片型号起始地址内置EEPROM 容量(每扇区512 字节)
STC89C51RC,STC89LE51RC 0x2000 共八个扇区
STC89C52RC,STC89LE52RC 0x2000 共八个扇区
STC89C54RD+,STC89LE54RD+ 0x8000 共五十八个扇区
STC89C55RD+,STC89LE55RD+ 0x8000 共五十八个扇区
STC89C58RD+,STC89LE58RD+ 0x8000 共五十八个扇区
寄存器标识地址名称7 6 5 4 3 2 1 0 初始值
ISP_DATA 0xE2 ISP/IAP闪存数据寄存器11111111
ISP_ADDRH 0xE3 ISP/IAP 闪存地址高位00000000
ISP_ADDRL 0xE4 ISP/IAP 闪存地址低位00000000
ISP_CMD 0xE5 ISP/IAP闪存命令寄存器MS2
MS1 MS0 xxxxx000
ISP_TRIG 0xE6 ISP/IAP 闪存命令触发xxxxxxxx
ISP_CONTR 0xE7 ISP/IAP 控制寄存器ISPEN SWBS SWRST WT2
WT1 WT0 00xx000
B7 B6 B5 B4 B3 B2 B1 B0 命令/操作模式选择
保留命令选择
- - - - - 0 0 0 待机模式,无ISP/IAP 操作
- - - - - 0 0 1 对用户的应用程序Flash 区及数据Flash 区字节读
- - - - - 0 1 0 对用户的应用程序Flash 区及数据Flash 区字节编程
- - - - - 0 1 1 对用户的应用程序Flash 区及数据Flash 区扇区擦除
ISP_CONTR:ISP/IAP 控制寄存器。
ISPEN:ISP/IAP 功能允许位。0:禁止ISP/IAP 编程改变Flash,1:允许编程改变Flash
SWBS:软件选择从用户主程序区启动(0),还是从ISP 程序区启动(1)。
SWRST:0:不操作,1:产生软件系统复位,硬件自动清零。
ISP_CONTR 中的SWBS 与SWRST 这两个功能位,可以实现单片机的软件启动,并
启动到ISP 区或用户程序区,这在“STC 单片机自动下载”一节,亦有所应用。
如:
ISP_CONTR=0x60? 则可以实现从用户应用程序区软件复位到ISP 程序区开始运行
程序。
ISP_CONTR=0x20? 则可以实现从ISP 程序区软件复位到用户应用程序区开始运行
程序。
用IAP 向Flash 中读写数据,是需要一定的读写时间的,读写数据命令发出后,要等待
一段时间才可以读写成功。这个等待时间就是由WT2、WT1、WT0 与晶体振荡器频率决定
的。
(以上的建议时钟是(WT2、WT1、WT0)取不同的值时的标称时钟,用户系统中的时钟
不要过高,否则可能使操作不稳定。)


 


stc单片机EEPROM读写(一)

EEPROM 操作函数:
#define RdCommand 0x01
#define PrgCommand 0x02
#define EraseCommand 0x03
#define Error 1
#define Ok 0
#define WaitTime 0x01
#define PerSector 512
unsigned char xdata Ttotal[512]?
/*


打开ISP,IAP 功能
*/
void ISP_IAP_enable(void)
D7 D6 D5 D4 D3 D2 D1 D0
ISPEN SWBS SWRST - - WT2 WT1 WT0
设置等待时间CPU 等待时间(机器周期)
WT2 WT1 WT0 读取编程扇区擦除建议的系统时钟
0 1 1 6 30 5471 5MHz
0 1 0 11 60 10942 10MHz
0 0 1 22 120 21885 20MHz
0 0 0 43 240 43769 40MHz
{
EA=0?/* 关中断*/
ISP_CONTR|=0x18?/*0001,1000*/
ISP_CONTR|=WaitTime?/*写入硬件延时*/
ISP_CONTR|=0x80?/*ISPEN=1*/
}
/*


关闭ISP,IAP 功能
*/
void ISP_IAP_disable(void)
{
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&=0xf8? /* 清除低3 位*/
ISP_CMD|=RdCommand?/* 写入读命令*/
ISPgoon()?/* 触发执行*/
ISP_IAP_disable()?/* 关闭ISP,IAP 功能*/
return ISP_DATA?/* 返回读到的数据*/
}
/*


扇区擦除
*/
void sectorerase(unsigned int sector_addr)
{
unsigned int iSectorAddr?
iSectorAddr=(sector_addr&0xfe00)?/* 取扇区地址*/
ISP_ADDRH=(unsigned char)(iSectorAddr>>8)?
ISP_ADDRL=0x00?
ISP_CMD&=0xf8?/* 清空低3 位*/
ISP_CMD|=EraseCommand?/* 擦除命令3*/
ISPgoon()?/* 触发执行*/
ISP_IAP_disable()?/* 关闭ISP,IAP 功能*/
}
/*


字节写
*/
void byte_write(unsigned int byte_addr, unsigned char original_data)
{
ISP_ADDRH=(unsigned char)(byte_addr>>8)? /* 取地址*/
ISP_ADDRL=(unsigned char)(byte_addr & 0x00ff)?
ISP_CMD&=0xf8?/* 清低3 位*/
ISP_CMD|=PrgCommand?/* 写命令2*/
ISP_DATA=original_data?/* 写入数据准备*/
ISPgoon()?/* 触发执行*/
ISP_IAP_disable()?/* 关闭IAP 功能*/
}
/*


字节写并校验
*/
unsigned char byte_write_verify(unsigned int byte_addr, unsigned char
original_data)
{
ISP_ADDRH=(unsigned char)(byte_addr>>8)? /* 取地址*/
ISP_ADDRL=(unsigned char)(byte_addr&0xff)?
ISP_CMD&=0xf8?/* 清低3 位*/
ISP_CMD|=PrgCommand?/* 写命令2*/
ISP_DATA=original_data?
ISPgoon()?/* 触发执行*/
/* 开始读,没有在此重复给地址,地址不会被自动改变*/
ISP_DATA=0x00?/* 清数据传递寄存器*/
ISP_CMD&=0xf8?/* 清低3 位*/
ISP_CMD|=RdCommand?/* 读命令1*/
ISP_TRIG=0x46?/* 触发ISP_IAP 命令字节1 */
ISP_TRIG=0xb9?/* 触发ISP_IAP 命令字节2 */
_nop_()?/* 延时*/
ISP_IAP_disable()?/* 关闭IAP 功能*/
if(ISP_DATA==original_data)/* 读写数据校验*/
return Ok?/* 返回校验结果*/
else
return Error?
}
/*


数组写入
*/
unsigned char arraywrite(unsigned int begin_addr, unsigned int len, unsigned char
*array)
{
unsigned int i?
unsigned int in_addr?
/* 判是否是有效范围,此函数不允许跨扇区操作*/
if(len > PerSector)
return Error?
in_addr = begin_addr & 0x01ff?/* 扇区内偏移量*/
if((in_addr+len)>PerSector)
return Error?
in_addr = begin_addr?
/* 逐个写入并校对*/
ISP_IAP_enable()?/* 打开IAP 功能*/
for(i=0?i<len?i++)
{
/* 写一个字节*/
ISP_ADDRH=(unsigned char)(in_addr >> 8)?
ISP_ADDRL=(unsigned char)(in_addr & 0x00ff)?
ISP_DATA=array? /* 取数据*/
ISP_CMD&=0xf8?/* 清低3 位*/
ISP_CMD|=PrgCommand?/* 写命令2 */
ISP_TRIG=0x46?/* 触发ISP_IAP 命令字节1 */
ISP_TRIG=0xb9?/* 触发ISP_IAP 命令字节2 */
_nop_()?
/* 读回来*/
ISP_DATA=0x00?
ISP_CMD&=0xf8?/* 清低3 位*/
ISP_CMD|=RdCommand?/* 读命令1*/
ISP_TRIG=0x46?/* 触发ISP_IAP 命令字节1 */
ISP_TRIG=0xb9?/* 触发ISP_IAP 命令字节2 */
_nop_()?
/* 比较对错*/
if(ISP_DATA!=array)
{
ISP_IAP_disable()?
return Error?
}
in_addr++?/* 指向下一个字节*/
}
ISP_IAP_disable()?
return Ok?
}
/*


扇区读出
*/
/* 程序对地址没有作有效性判断,请调用前事先保证他在规定范围内*/
void arrayread(unsigned int begin_addr, unsigned char len)
{
unsigned int iSectorAddr?
unsigned int i?
iSectorAddr = begin_addr? // & 0xfe00? /* 取扇区地址*/
ISP_IAP_enable()?
for(i=0?i<len?i++)
{
ISP_ADDRH=(unsigned char)(iSectorAddr>>8)?
ISP_ADDRL=(unsigned char)(iSectorAddr & 0x00ff)?
ISP_CMD&=0xf8?/* 清低3 位*/
ISP_CMD|=RdCommand?/* 读命令1*/
ISP_DATA=0?
ISP_TRIG=0x46?/* 触发ISP_IAP 命令字节1 */
ISP_TRIG=0xb9?/* 触发ISP_IAP 命令字节2 */
_nop_()?
Ttotal=ISP_DATA?
iSectorAddr++?
}
ISP_IAP_disable()?/* 关闭IAP 功能*/
}
主函数对EEPROM 操作函数进行调用:
#include <stc51rd.h>
#include <intrins.h>
#include <stc_eeprom.h>
#include <ados.h>
int i?
void delay(unsigned int time)
{
while(time)
?
}
void main()
{
_ADOS(22.1184)?
//ADOS 自动下载
//for(i=0?i<100?i++)
//{
//Ttotal=i?
//}
//arraywrite(0x8000,100,Ttotal)?
/*
第一次运行时向EEPROM 中写入数据
然后再将写入函数注释掉,将先前写
入的数据读出,输出在P2 口上。
*/
arrayread(0x8000,100)?
for(i=0?i<100?i++)
{
P2=~Ttotal?
delay(10000)?
}
while(1)?
}


stc单片机EEPROM读写(二)


sfr isp_data=0xe2;
sfr isp_addrh=0xe3;
sfr isp_addrl=0xe4;
sfr isp_cmd=0xe5;
sfr isp_trig=0xe6;
sfr isp_contr=0xe7;


unsigned char eeprom_read(unsigned int addres);
void eeprom_write(unsigned int address,unsigned char wdata);
void eeprom_eares(unsigned int addres);//扇区擦除。


void eeprom_eares(unsigned int addres)//扇区擦除。
     {unsigned i;
      isp_addrl=addres;     //低位地址
      isp_addrh=addres>>8;  //高位地址
      isp_contr=0x01;
      isp_contr=isp_contr|0x80; //设时间与充ISP操作。
      isp_cmd=0x03;         //扇区命命令
      isp_trig=0x46;        //触发
      isp_trig=0xb9;        //触发启动。
      for(i=0;i<3;i++);
      isp_addrl=0xff;
      isp_addrh=0xff;
      isp_contr=0x00;
      isp_cmd=0x00;
      isp_trig=0x00;
   


          }
void eeprom_write(unsigned int addres,unsigned char write_data)//写数据。
     {unsigned char i;
      isp_data=write_data;   //要写入的数据。
      isp_addrl=addres;     //低位地址
      isp_addrh=addres>>8;  //高位地址
      isp_contr=0x01;
      isp_contr=isp_contr|0x80; //设时间与充ISP操作。
      isp_cmd=0x02;         //写命令
      isp_trig=0x46;        //触发
      isp_trig=0xb9;        //触发启动。
      for(i=0;i<3;i++);
      isp_addrl=0xff;
      isp_addrh=0xff;
      isp_contr=0x00;
      isp_cmd=0x00;
      isp_trig=0x00;


    
       }
unsigned char eeprom_read(unsigned int addres)
     {unsigned char i,z;
      isp_addrl=addres;     //低位地址
      isp_addrh=addres>>8;  //高位地址
      isp_contr=0x01;
      isp_contr=isp_contr|0x80; //设时间与充ISP操作。
      isp_cmd=0x01;         //写命令
      isp_trig=0x46;        //触发
      isp_trig=0xb9;        //触发启动。
      for(i=0;i<3;i++);
      isp_addrl=0xff;
      isp_addrh=0xff;
      isp_contr=0x00;
      isp_cmd=0x00;
      isp_trig=0x00;
      z="isp"_data;
      return(z);
    
    
     }


stc单片机EEPROM读写(三)

;;;     内部EEPROM读写定义
ISP_DATA   EQU  0E2H    ;写入读出数据寄存器.
ISP_ADDRH  EQU  0E3H    ;地址寄存器高8位
ISP_ADDRL  EQU  0E4H    ;地址寄存器低8位
ISP_CMD    EQU  0E5H    ;命令模式寄存器
ISP_TRIG   EQU  0E6H    ;命令触发寄存器
ISP_CONTR  EQU  0E7H    ;ISP/IAP控制寄存器.
ISP_IAP_BYTE_READ EQU 1 ;字节读
ISP_IAP_BYTE_PROGRAM EQU 2 ;字节编程,要空才能写
ISP_IAP_SECTOR_ERASE EQU 3 ;扇区擦除,
WAIT_TIME   EQU  1         ;20M以下为1
BYTE_ADDR_HIGH EQU  60H    ;高位地址
BYTE_ADDR_LOW  EQU  61H    ;低位地址
BYTE_WRITE_DATA EQU 62H    ;要写入的数据


;***********************内部EEPROM操作*************************
READ_EEPROM:MOV ISP_ADDRH,ISP_ADDRH   ;送高地址
            MOV ISP_ADDRL,ISP_ADDRL   ;送低地址
            MOV ISP_CONTR,#01h        ;设置等等待时间
            ORL ISP_CONTR,#80h        ;允许ISP/IAP操作
            MOV ISP_CMD,#01h          ;送读命令
            MOV ISP_TRIG,#46H         ;触发
            MOV ISP_TRIG,#0B9H        ;触发启动.
            NOP
            NOP
            MOV ISP_CONTR,#00H
            MOV ISP_CMD,#00H
            MOV ISP_TRIG,#00H
            MOV ISP_ADDRH,#0FFH
            MOV ISP_ADDRL,#0FFH
            MOV 6AH,ISP_DATA   ;读出的数据放到6AH单元中。


            RET
WRITE_EARES: MOV WDT,#34H
             CLR EA                      ;关中断
             MOV ISP_ADDRH,ISP_ADDRH   ;送高地址
             MOV ISP_ADDRL,ISP_ADDRL   ;送低地址
             MOV ISP_CONTR,#1h           ;设置等等待时间
             ORL ISP_CONTR,#10000000B    ;允许ISP/IAP操作
             MOV ISP_CMD,#3h             ;送扇区命令
             MOV ISP_TRIG,#46H           ;触发
             MOV ISP_TRIG,#0B9H          ;触发启动.
             NOP
             NOP
             MOV ISP_CONTR,#00H
             MOV ISP_CMD,#00H
             MOV ISP_TRIG,#00H
             MOV ISP_ADDRH,#0FFH
             MOV ISP_ADDRL,#0FFH
             SETB EA
             RET
WRITE_EEPROM:MOV WDT,#34H
             CLR EA
             MOV ISP_DATA,BYTE_WRITE_DATA ;要写入的数据
             MOV ISP_ADDRH,ISP_ADDRH      ;送高地址
             MOV ISP_ADDRL,ISP_ADDRL      ;送低地址
             MOV ISP_CONTR,#1h            ;设置等等待时间
             ORL ISP_CONTR,#10000000B     ;允许ISP/IAP操作
             MOV ISP_CMD,#02h             ;送写命令
             MOV ISP_TRIG,#46H            ;触发
             MOV ISP_TRIG,#0B9H           ;触发启动.
             NOP
             NOP
             MOV ISP_CONTR,#00H
             MOV ISP_CMD,#00H
             MOV ISP_TRIG,#00H
             MOV ISP_ADDRH,#0FFH
             MOV ISP_ADDRL,#0FFH
             SETB EA                      ;开中断


             RET



STC单片机内部FLASH读写程序
电子伙伴 发表于 2006-4-11 16:26:00



/********************************************************************
//模 块 名:STC单片机内部FLASH读写程序
//创 建 者:电子伙伴     日期:2006-4-11 16:13
//修 改 者:                      日期:
//功能描述:
//其他说明:参考宏晶提供的C源代码再减肥一下
//芯片型号: STC89C58RD 50MHz晶振,双倍数振荡频率 单指令周期为0.12us
//配    置:
//版    本:V1.0


本程序功能调试通过
  
 注: 本程序只供学习使用,未经作者允许,不能用于其它任何用途
********************************************************************/
//#i nclude "STC89C51.h"
//#i nclude <intrins.h>


/* 定义常量 */
#define ERROR   0
#define OK      1


/* 定义Flash 操作等待时间 */
#define WAIT_TIME  0x00    //mcu clock 40mhz
//#define WAIT_TIME  0x01    //mcu clock 20mhz
//#define WAIT_TIME  0x02    //mcu clock 10mhz
//#define WAIT_TIME  0x03    //mcu clock 5mhz



/* 定义几个数据存储的位置 *//* MCU: STC89C58RD */
#define HOUR  0x8000
#define MINUTE 0x8001



/* 打开 ISP,IAP 功能 */
void ISP_EN(void)
{
 EA = 0;               //  关中断   
 ISP_CONTR = ISP_CONTR & 0x18;        // 0001,1000
 ISP_CONTR = ISP_CONTR | WAIT_TIME;
 ISP_CONTR = ISP_CONTR | 0x80;        // 1000,0000
}


/* 关闭 ISP,IAP 功能 */
void ISP_DI(void)
{
 ISP_CONTR = ISP_CONTR & 0x7f;     // 0111,1111
 ISP_TRIG = 0x00;
 EA   =   1;                    // 开中断
}


/* 字节读 *//* 程序执行时间15us,指令周期为0.12us */
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;         // 1111,1000
 ISP_CMD  = ISP_CMD | 0x01;          // 0000,0001  字节读数据存储区


 ISP_EN();


 ISP_TRIG = 0x46;
 ISP_TRIG = 0xb9;
 _nop_();


 ISP_DI();
 return (ISP_DATA);
}


/* 扇区擦除 *//* 程序执行时间10ms,指令周期为0.12us */
void Sector_erase(unsigned int Sector_addr)
{
 unsigned char addre;
 addre   = (unsigned char)(Sector_addr >> 8);  // 1111,1110,0000,0000; 取扇区地址
 ISP_ADDRH = addre&0xfe;
 ISP_ADDRL = 0x00;
 
 ISP_CMD = ISP_CMD & 0xf8;       // 1111,1000
 ISP_CMD = ISP_CMD | 0x03;       // 0000,0011  扇区擦除数据存储区
 
 ISP_EN();
 
 ISP_TRIG = 0x46;               // 触发ISP_IAP命令
 ISP_TRIG = 0xb9;              // 触发ISP_IAP命令
  _nop_();


 ISP_DI();
}


/* 字节编程 *//* 程序执行时间64us,指令周期为0.12us */
void Byte_program(unsigned int byte_addr, unsigned char DataBuf)
{
 ISP_ADDRH = (unsigned char)(byte_addr >> 8);
 ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);


 ISP_CMD  = ISP_CMD & 0xf8;         // 1111,1000
 ISP_CMD  = ISP_CMD | 0x02;      // 0000,0010  字节编程数据存储区
 ISP_DATA = DataBuf;


 ISP_EN();
 
 ISP_TRIG = 0x46;               // 触发ISP_IAP命令
 ISP_TRIG = 0xb9;               // 触发ISP_IAP命令
 _nop_();


 ISP_DI();
}


/* 字节编程并校验 */
unsigned char ByteProgramVerify(unsigned int byte_addr, unsigned char DataBuf)
{
 ISP_ADDRH = (unsigned char)(byte_addr >> 8);
 ISP_ADDRL = (unsigned char)(byte_addr & 0x00ff);


 ISP_CMD  = ISP_CMD & 0xf8;          // 1111,1000
 ISP_CMD  = ISP_CMD | 0x02;      // 0000,0010  字节编程数据存储区
 ISP_DATA = DataBuf;


 ISP_EN();


 ISP_TRIG = 0x46;
 ISP_TRIG = 0xb9;
 _nop_();


 ISP_DATA = 0x00;


 ISP_CMD  = ISP_CMD & 0xf8;          // 1111,1000
 ISP_CMD  = ISP_CMD | 0x01;          // 0000,0001


 ISP_TRIG = 0x46;               // 触发ISP_IAP命令
 ISP_TRIG = 0xb9;               // 触发ISP_IAP命令
 _nop_();


 ISP_DI();


 if(ISP_DATA==DataBuf) return OK;
 else return ERROR;
}


 

文章评论0条评论)

登录后参与讨论
我要评论
0
5
关闭 站长推荐上一条 /2 下一条