STC单片机 IAP(EEPROM)的使用

STC89C51、52内部都自带有2K字节的EEPROM,54、55和58都自带有16K字节的EEPROM,STC单片机是利用IAP技术实现的EEPROM,内部Flash擦写次数可达100,000 次以上,先来介绍下ISP与IAP的区别和特点。

知识点:ISP与IAP介绍
    ISP:In System Programable 是指在系统编程,通俗的讲,就是片子已经焊板子上,不用取下,就可以简单而方便地对其进行编程。比如我们通过电脑给STC单片机下载程序,或给AT89S51单片机下载程序,这就是利用了ISP技术。
IAP:In Application Programable 是指在应用编程,就是片子提供一系列的机制(硬件/软件上的)当片子在运行程序的时候可以提供一种改变flash数据的方法。通俗点讲,也就是说程序自己可以往程序存储器里写数据或修改程序。这种方式的典型应用就是用一小段代码来实现程序的下载,实际上单片机的ISP功能就是通过IAP技术来实现的,即片子在出厂前就已经有一段小的boot程序在里面,片子上电后,开始运行这段程序,当检测到上位机有下载要求时,便和上位机通信,然后下载数据到存储区。大家要注意千万不要尝试去擦除这段ISP引导程序,否则恐怕以后再也下载不了程序了。
STC单片机内部有几个专门的特殊功能寄存器负责管理ISP/IAP功能的,见表1。
表1 ISP/IAP相关寄存器列表
名称 地址 功能描述 D7 D6 D5 D4 D3 D2 D1 D0 复位值
ISP_DATA E2h Flash数据寄存器                 1111 1111
ISP_ADDRH E3h Flash高字节地址寄存器                 0000 0000
ISP_ADDRL E4h Flash低字节地址寄存器                 0000 0000
ISP_CMD E5h Flash命令模式寄存器 -- -- -- -- -- MS2 MS1 MS0 xxxx x000
ISP_TRIG E6h Flash命令触发寄存器                 xxxx xxxx
ISP_CONTR E7h ISP/IAP 控制寄存器 ISPEN SWBS SWRST -- -- WT2 WT1 WT0 000x x000
ISP_DATA:ISP/IAP操作时的数据寄存器。
ISP/IAP从Flash读出的数据放在此处,向Flash写入的数据也需放在此处。
ISP_ADDRH:ISP/IAP操作时的地址寄存器高八位。
ISP_ADDRL:ISP/IAP操作时的地址寄存器低八位。
ISP_CMD:ISP/IAP操作时的命令模式寄存器,须命令触发寄存器触发方可生效。命令模式如表2所示。
表2 ISP_CMD寄存器模式设置
D7 D6 D5 D4 D3 D2 D1 D0 模式选择
保留 命令选择  
-- -- -- -- -- 0 0 0 待机模式,无ISP操作
-- -- -- -- -- 0 0 1 对用户的应用程序flash区及数据flash区字节读
-- -- -- -- -- 0 1 0 对用户的应用程序flash区及数据flash区字节编程
-- -- -- -- -- 0 1 1 对用户的应用程序flash区及数据flash区扇区擦除
程序在系统ISP程序区时可以对用户应用程序区/数据Flash区(EEPROM)进行字节读/字节编程/扇区擦除;程序在用户应用程序区时,仅可以对数据Flash区(EEPROM)进行字节读/字节编程/扇区擦除。STC89C51RC/RD+系列单片机出厂时已经固化有ISP引导码,并设置为上电复位进入ISP程序区,并且出厂时就已完全加密。
ISP_TRIG:ISP/IAP操作时的命令触发寄存器。
在ISPEN(ISP_CONTR.7) =1时,对ISP_TRIG 先写入46h,再写入B9h,ISP/IAP命令才会生效。
STC89C52RC,STC89LE52RC单片机内部可用Data Flash(EEPROM)的地址如表3所示,其它型号单片机请查阅相关资料。
表3 STC89C52RC、STC89LE52RC单片机内部EEPROM地址表
第一扇区 第二扇区 第三扇区 第四扇区
起始地址 结束地址 起始地址 结束地址 起始地址 结束地址 起始地址 结束地址
2000H 21FFH 2200H 23FFH 2400H 25FFH 2600H 27FFH
第五扇区 第六扇区 第七扇区 第八扇区
起始地址 结束地址 起始地址 结束地址 起始地址 结束地址 起始地址 结束地址
2800H 29FFH 2A00H 2BFFH 2C00H 2DFFH 2E00H 2FFFH
每个扇区为512字节,建议大家在写程序时,将同一次修改的数据放在同一个扇区,方便修改,因为在执行擦除命令时,一次最少要擦除一个扇区的数据(需要提供扇区的首地址),每次在更新数据前都必须要擦除原数据方可重新写入新数据,不能直接在原来数据基础上更新内容。
     注意:上面的是数据存储区的地址,程序存储区地址是从0~1FFF,共8K,程序区只能是ISP编程。
 
以下是自己写的这部分功能代码,因为风格问题,不太喜欢原著代码,感觉自己的还严谨一点。
 
 
/****************************************************************************/
/*                           IAP驱动                                        */
/****************************************************************************/
 
 
/************************************************************************************************************************/
/* ISP/IAP相关寄存器列表 */
/* 名称 地址 功能描述 D7 D6 D5 D4 D3 D2 D1 D0 复位值 */
/* ISP_DATA E2h Flash数据寄存器 1111 1111 */
/* ISP_ADDRH E3h Flash高字节地址寄存器 0000 0000 */
/* ISP_ADDRL E4h Flash低字节地址寄存器 0000 0000 */
/* ISP_CMD E5h Flash命令模式寄存器 -- -- -- -- -- MS2 MS1 MS0 xxxx x000 */
/* ISP_TRIG E6h Flash命令触发寄存器 xxxx xxxx */
/* ISP_CONTR E7h ISP/IAP 控制寄存器 ISPEN SWBS SWRST -- -- WT2 WT1 WT0 000x x000 */
/************************************************************************************************************************/
 
 
/************************************************************************************/
/*  ISP_CMD寄存器模式设置 */
/* D7 D6 D5 D4 D3 D2 D1 D0 模式选择 */
/*  保留 命令选择 */
/* -- -- -- -- -- 0 0 0 待机模式,无ISP操作 */
/* -- -- -- -- -- 0 0 1 对用户的应用程序flash区及数据flash区字节读 */
/* -- -- -- -- -- 0 1 0 对用户的应用程序flash区及数据flash区字节编程 */
/* -- -- -- -- -- 0 1 1 对用户的应用程序flash区及数据flash区扇区擦除 */
/************************************************************************************/
 
/*在ISPEN(ISP_CONTR.7) =1时,对ISP_TRIG 先写入46h,再写入B9h,ISP/IAP命令才会生效。*/
 
 
/* 定义常量 */
#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
 
sfr ISP_DATA = 0xe2;   // Flash数据寄存器
sfr ISP_ADDRH = 0xe3; // Flash高字节地址寄存器
sfr ISP_ADDRL = 0xe4; // Flash低字节地址寄存器
sfr ISP_CMD = 0xe5; // Flash命令模式寄存器
sfr ISP_TRIG = 0xe6; // Flash命令触发寄存器
sfr ISP_CONTR = 0xe7; // ISP/IAP 控制寄存器
 
#define CMD_READ  0x01  // 定义IAP的读字节操作
#define CMD_PRGM  0x02 // 定义IAP的写字节操作
#define CMD_ERASE  0x03  // 定义IAP的擦除扇区操作
 
/*********************** 打开 ISP,IAP 功能 ***********************/
static void ISPIAPEnable(void)
{
  //EA  = 0;        // 关中断
  ISP_CONTR  = ISP_CONTR & 0x18;        // 0001,1000
  ISP_CONTR  = ISP_CONTR | WAIT_TIME;  // 写入硬件延时
  ISP_CONTR  = ISP_CONTR | 0x80;        // ISPEN = 1
}
 
 
/*********************** 关闭 ISP,IAP 功能 ***********************/
static void ISPIAPDisable(void)
{
  ISP_CONTR  = ISP_CONTR & 0x7f;  // ISPEN = 0
  ISP_TRIG  = 0x00;
  //EA    = 1;    // 开中断
}
 
/************************* 触发Flash操作 *************************/
static ActiveOperate(void)
{
  bit eacpy;
  eacpy = EA;
  EA = 0;
  ISPIAPEnable();
  ISP_TRIG = 0x46;   // 触发ISP_IAP命令字节1
  ISP_TRIG = 0xb9;   // 触发ISP_IAP命令字节2
  {UINT8 i=2; while(i--);}
  ISPIAPDisable();
  EA = eacpy;
}
 
 
/**************************** 读一字节 ****************************/
static UINT8 IAPReadByte(const UINT16 uiAddr)
{
  ISP_ADDRH  = (UINT8)(uiAddr >> 8); // 写地址
  ISP_ADDRL  = (UINT8)(uiAddr&0xFF);
  ISP_CMD    = ISP_CMD & 0xf8; // 清低三位
  ISP_CMD    = ISP_CMD | CMD_READ;  // 写入读命令
 
  ActiveOperate(); // 触发执行
  return (ISP_DATA);     // 返回读到的数据
}
 
/**************************** 写一字节 ****************************/
static void IAPWriteByte(const UINT16 uiAddr, const UINT8 ucData)
{
  ISP_ADDRH  = (UINT8)(uiAddr >> 8); // 写地址
  ISP_ADDRL  = (UINT8)(uiAddr&0xFF);
  ISP_CMD    = ISP_CMD & 0xf8; // 清低三位
  ISP_CMD    = ISP_CMD | CMD_PRGM;  // 写入写命令
  ISP_DATA  = ucData;    // 写入数据准备
 
  ActiveOperate(); // 触发执行
}
 
/**************************** 擦除一扇区 ****************************/
static void IAPEarseSection(const UINT16 uiAddr)
{
  UINT16 uiSecAddr;
 
  uiSecAddr  = (uiAddr & 0xfe00);  // 取扇区地址
  ISP_ADDRH  = (UINT8)(uiSecAddr >> 8); // 写地址
  ISP_ADDRL  = 0x00;
  ISP_CMD  = ISP_CMD & 0xf8;    // 清低三位
  ISP_CMD  = ISP_CMD | CMD_ERASE;  // 写入擦除命令
 
  ActiveOperate(); // 触发执行
}
 
 
------------------------------------------------------------------------------------------------------------------------
 
 

#include<stc12c5410ad.h>//到宏晶网站下载头文件或自己在现有的头文件上加上相应的寄存器定义即可。
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
         
/****************uart init***********/
void UART_inti(void)
{
AUXR=0x40;//定时器1速度是普通8051的12倍,不分频
TMOD=0x20;//定时器1工作在方式2,用来产生波特率
SCON=0x50;//串口工作在方式1,允许接收
TL1=0xF7;//波特率为38400;FB为115200
TH1=0xF7;
PCON=0x00;//SMOD=0
TR1=1;   //产生波特率 
}

void ISP_write(uint ISP_addr,uchar ISP_data)//stc12c5404启始地址2800h ;IAP_CMD 1读,2写,3擦除
{
ISP_DATA=ISP_data; //送数据
ISP_ADDRL=ISP_addr%256;
ISP_ADDRH=ISP_addr/256;//送高低地址
ISP_CONTR=0x83;//IAP ENABLE ,SET CPU WAIT TIME
ISP_CMD=2;//写字节模式
ISP_TRIG=0x46;
ISP_TRIG=0xB9;//触发启动ISP
_nop_();//等待写入
ISP_CONTR=0x00;//禁止ISP/IAp操作
ISP_CMD=0;
ISP_TRIG=0x00;
}

uchar ISP_read(uint ISP_addr)//stc12c5404启始地址2800h ;IAP_CMD 1读,2写,3擦除
{uchar recivedata;
ISP_ADDRL=ISP_addr%256;
ISP_ADDRH=ISP_addr/256;//送高低地址
ISP_CONTR=0x83;//IAP ENABLE ,SET CPU WAIT TIME
ISP_CMD=1;//写字节模式
ISP_TRIG=0x46;
ISP_TRIG=0xB9;//触发启动ISP
_nop_();//等待读
recivedata=ISP_DATA;
ISP_CONTR=0x00;//禁止ISP/IAp操作
ISP_CMD=0;
ISP_TRIG=0x00;
return recivedata;
}

void ISP_erase(uint ISP_addr)//stc12c5404启始地址2800h ;IAP_CMD 1读,2写,3擦除
{ISP_ADDRL=ISP_addr%256;
ISP_ADDRH=ISP_addr/256;//送高低地址
ISP_CONTR=0x83;//IAP ENABLE ,SET CPU WAIT TIME
ISP_CMD=3;//写字节模式
ISP_TRIG=0x46;
ISP_TRIG=0xB9;//触发启动ISP
_nop_();//等待擦除
ISP_CONTR=0x00;//禁止ISP/IAp操作
ISP_CMD=0;
ISP_TRIG=0x00;
}


//主程序只为测试时随意编写的,可以根据需要做相应的修改即可。
void main(void)
{
uchar returndata;
uint textdata=0x2660;
UART_inti();
while(1)
{SBUF=5;
while(!TI);
TI=0;
while(!RI);
RI=0;
ISP_erase(0x2800);
ISP_write(0x2800,textdata%256);
ISP_write(0x2801,textdata/256);
returndata=ISP_read(0x2800);
SBUF=returndata;       
while(!TI);
TI=0;
returndata=ISP_read(0x2801);
SBUF=returndata;
while(!TI);
TI=0;
}

}

//声明下这是应用在STC的单片机中的。不同的型号地址不一样,改下IAP的地址即可。新出的产品像STC12C5A60S2等,触发的命令也不一样ISP_TRIG=0x46;ISP_TRIG=0xB9;也就是这两条要根据手册修改下即可。。

你可能感兴趣的:(ROM)