EEPROM
EEPROM 简单来说就是存储数据的元器件,断电后数据依然可以被保存。
STC89C51/52 内部自带 2K 字节的 E2PROM
ISP/IAP
- 1.ISP: In system programable 在系统编程
- 2.IAP: In application programable 在应用编程
STC89C52 存储空间有
- a.8KB flash 地址 0-1FFFH
- b.512B RAM 地址 0-0200H
- c.2KB EEPROM 地址 2000H-27FFH
ISP 操作对象为 8KB flash
IAP 操作对象为 2KB EEPROM
EEPROM 分为 4 个扇区每个 512B
操作步骤
扇区
- a. 将想保留的数据存到 RAM
- b. 对扇区进行擦除操作,擦除后扇区数据为 FFH
- c. 写字节到扇区
- d. 将暂存数据写回扇区
寄存器
- a. 配置 ISP_CONTR 寄存器,配置 ISPEN 生效,配置 WT2,WT1,WT0 三位等待时间
- b. 写指令: 读/写/擦三种命令
- c. 赋值 ISP_ADDRH 和 ISP_ADDRL 地址值,分别为操作地址的高低位
- d. 关闭总中断 EA
- e. 执行 ISP_IAP 触发指令,触发后才能进行读写
- f. 打开总中断 EA,关闭 ISP_IAP 功能,清除相关寄存器
-
- ISP_DATA: ISP/IAP 数据寄存器
-
- ISP_ADDRH/ISP_ADDRL: ISP/IAP 地址寄存器
-
- ISP_CMD: ISP/IAP 命令寄存器
- 3.1: MS1 MS0 = 00 待机模式,无法进行读写
- 3.2: MS1 MS0 = 01 从应用程序区对 DATA FLASH/E2PROM 进行读字节命令
- 3.3: MS1 MS0 = 10 从应用程序区对 DATA FLASH/E2PROM 进行写字节命令
- 3.4: MS1 MS0 = 11 从应用程序区对 DATA FLASH/E2PROM 进行擦除命令
- ISP_CMD: ISP/IAP 命令寄存器
-
- ISP_TRIG: ISP/IAP 命令触发器
-
- ISP_CONTR: ISP/IAP 控制寄存器
- 5.1: ISPEN ISP/IAP 功能允许位,ISPEN=0 禁止读写擦除,ISPEN=1 允许读写擦除
- 5.2: SWBS=0 软件从应用程序区启动,SWBS=1 从系统 ISP 监控程序区启动,需与 SWRST 配合使用
- 5.3: SWRST: 0 不操作,1 表示产生软件系统复位,硬件自动复位
- 5.4: SWBS SWRST = 1 1 表示应用程序区软件复位并从系统 ISP 监控程序区开始执行程序
- 5.5: SWBS SWRST = 0 1 表示应用程序区软件复位并从应用程序区开始处执行程序
- 5.6: WT2 WT1 WT0 表示读写擦操作 CPU 插入的等待时间如下图表示
- 5.7
- ISP_CONTR: ISP/IAP 控制寄存器
EEPROM 驱动函数编写
/*
* @Author: cuihaonan
* @Email: devcui@outlook.com
* @Date: 2021-04-08 22:19:28
* @LastEditTime: 2021-04-08 22:34:36
* @LastEditors: cuihaonan
* @Description: Basic description
* @FilePath: /sdcc-include/src/eeprom/include/eeprom.h
* @LICENSE: NONE
*/
#ifndef __EEPROM_H__
#define __EEPROM_H__
// 关闭ISP_IAP
extern void ISP_IAP_disable(void);
// 触发
extern void ISP_IAP_trigger(void);
// 读取数据
extern void ISP_IAP_readData(unsigned int beginAddr, unsigned char *pBuf, unsigned int dataSize);
// 写数据
extern void ISP_IAP_writeData(unsigned int beignAddr, unsigned char *pDat, unsigned int dataSize);
// 扇区擦除
extern void ISP_IAP_sectorErase(unsigned int sectorAddr);
#endif
/*
* @Author: cuihaonan
* @Email: devcui@outlook.com
* @Date: 2021-04-08 22:25:23
* @LastEditTime: 2021-04-08 22:34:26
* @LastEditors: cuihaonan
* @Description: Basic description
* @FilePath: /sdcc-include/src/eeprom/include/eeprom.c
* @LICENSE: NONE
*/
#include "../../../include/STC89xx.h"
#include "./eeprom.h"
// 读
#define read_cmd 0x01
// 写
#define wirte_cmd 0x02
// 擦
#define erase_cmd 0x03
// 等待计时
#define enable_waitTime 0x82
// 关闭ISP_IAP
void ISP_IAP_disable(void)
{
EA = 1;
ISP_CONTR = 0x00;
ISP_CMD = 0x00;
ISP_TRIG = 0x00;
}
// 触发
void ISP_API_trigger(void)
{
EA = 0;
ISP_TRIG = 0x46;
ISP_TRIG = 0xB9;
}
// 读取数据
void ISP_API_readData(unsigned int beginAddr, unsigned char *pBuf, unsigned int dataSize)
{
ISP_DATA = 0;
ISP_CMD = read_cmd;
ISP_CONTR = enable_waitTime;
while (dataSize--)
{
ISP_ADDRH = (unsigned char)(beginAddr >> 8);
ISP_ADDRL = (unsigned char)(beginAddr & 0x00FF);
ISP_IAP_trigger();
beginAddr++;
*pBuf++ = ISP_DATA;
}
ISP_IAP_dsiable();
}
// 写数据
void ISP_IAP_writeData(unsigned int beignAddr, unsigned char *pDat, unsigned int dataSize)
{
ISP_CONTR = enable_waitTime;
ISP_CMD = wirte_cmd;
while (dataSize--)
{
ISP_ADDRH = (unsigned char)(beignAddr >> 8);
ISP_ADDRL = (unsigned char)(beignAddr & 0x00FF);
ISP_DATA = *pDat++;
beignAddr++;
ISP_IAP_trigger();
}
ISP_IAP_dsiable();
}
// 扇区擦除
void ISP_IAP_sectorErase(unsigned int sectorAddr)
{
ISP_CONTR = enable_waitTime;
ISP_CMD = erase_cmd;
ISP_ADDRH = (unsigned char)(sectorAddr >> 8);
ISP_ADDRH = (unsigned char)(sectorAddr & 0x00FF);
ISP_IAP_trigger();
ISP_IAP_disable();
}
EEPROM 应用
下面我们下一个小的应用程序来验证我们驱动函数,函数实现的功能为记录
开发板上电的次数。并把上电的次数,显示到 1602 液晶显示器上
/*
* @Author: cuihaonan
* @Email: devcui@outlook.com
* @Date: 2021-04-08 21:44:53
* @LastEditTime: 2021-04-08 22:39:33
* @LastEditors: cuihaonan
* @Description: Basic description
* @FilePath: /sdcc-include/src/eeprom/main.c
* @LICENSE: NONE
*/
#include "../../include/STC89xx.h"
#include "./include/eeprom.h"
#include "./include/1602.h"
unsigned char pbuf[5] = {0};
unsigned char str[8] = {0};
unsigned char disp[] = "times of PowerOn";
void main()
{
Init_1602();
ISP_IAP_readData(0x21F0, pbuf, sizeof(pbuf));
pbuf[0]++;
str[0] = pbuf[0] / 100 + '0';
str[1] = (pbuf[0] % 100) / 10 + '0';
str[2] = (pbuf[0] % 10) + '0';
str[4] = '0';
Disp_1602_str(1, 1, disp);
Disp_1602_str(2, 6, str);
ISP_IAP_sectorErase(0x2000);
ISP_IAP_writeData(0x21F0, pbuf, sizeof(pbuf));
while(1);
}