本帖最后由 407871699 于 2021-3-20 16:40 编辑
最近研究了STC8G的片上EEPROM,比片外ROM方便多了。
但刚开始使用STC官网下载的例程时候老报错,还是不熟悉啊。
然后和官方的手册上的例程比较:手册提供了3个函数,写1个字节函数void IapProgramByte(WORD addr, BYTE dat),读1个字节函数BYTE IapReadByte(WORD addr),擦除扇区函数void IapEraseSector(WORD addr)。 手册上这3个函数不方便与主函数进行数据交换,还要在编数据交换函数;因此结合手册例程,把官网下载的EEPROM例程改动了一下,编成EEPROM.H,就可以直接调用
片上EEPROM写入时,重点是必须先用扇区擦除,否则写入的数值错误。
单片机源程序如下:
#include<STC8xxxx.h> //建议到官网下载,则包含sfr IAP_TPS = 0xF5;
//sfr IAP_TPS = 0xF5; //这个是STC8新增的寄存器,按CPU频率计算等待时间
#define EEPROM_ADD_0 0x0000 //EROM ADDRESS 起始地址,大家可以根据需要改存储位置,但是要符合MCU手册
#define MAIN_Fosc 11059200L //定义MCU频率;这句可以放在初始化配置中或主程序中
#define IAP_EN (1<<7) //启动IAP;等价于IAP_EN=0X80;
#define IAP_ENABLE() IAP_CONTR = IAP_EN; IAP_TPS = MAIN_Fosc / 1000000 //使能+等待
void DisableEEPROM(void) //禁止IAP操作
{
IAP_CONTR = 0; //禁止IAP操作
IAP_CMD = 0; //去除IAP命令
IAP_TRIG = 0; //防止IAP命令误触发
IAP_ADDRH = 0xff; //清0地址高字节
IAP_ADDRL = 0xff; //清0地址低字节,指向非EEPROM区,防止误操作
}
void EEPROM_Trig(void) //触发EEPROM
{
F0 = EA; //保存全局中断
EA = 0; //禁止中断, 避免触发命令无效
IAP_TRIG = 0x5A; //先送5AH,再送A5H到IAP触发寄存器,每次都需要如此
IAP_TRIG = 0xA5; //送完A5H后,IAP命令立即被触发启动//CPU等待IAP完成后,才会继续执行程序。
_nop_(); _nop_(); //2个空指令等待一下
EA = F0; //恢复全局中断
}
// 参数: EE_address: 读取EEPROM的首地址.
// DataAddress: 读取数据后给到 数组首地址.
// number: 读取的字节长度.
void EEPROM_read_n(unsigned int EE_address,unsigned char *DataAddress,unsigned int number)
{
IAP_ENABLE(); //设置等待时间,允许IAP操作,送一次就够
IAP_CMD = 1; //=1读;送字节读命令,命令不需改变时,不需重新送命令
do
{
IAP_ADDRH = EE_address / 256; //送地址高字节(地址需要改变时才需重新送地址)
IAP_ADDRL = EE_address % 256; //送地址低字节
EEPROM_Trig(); //触发EEPROM操作
*DataAddress = IAP_DATA; //读出的数据送往
EE_address++;
DataAddress++;
}while(--number);
DisableEEPROM();
}
void EEPROM_SectorErase(unsigned int EE_address)
{
IAP_ENABLE(); //设置等待时间,允许IAP操作,送一次就够
IAP_CMD = 3; //宏调用, =3,送扇区擦除命令,命令不需改变时,不需重新送命令
//只有扇区擦除,没有字节擦除,512字节/扇区。
//扇区中任意一个字节地址都是扇区地址。
IAP_ADDRH = EE_address / 256; //送扇区地址高字节(地址需要改变时才需重新送地址)
IAP_ADDRL = EE_address % 256; //送扇区地址低字节
EEPROM_Trig(); //触发EEPROM操作
DisableEEPROM(); //禁止EEPROM操作
}
// 参数: EE_address: 写入EEPROM的首地址.
// DataAddress: 写入源数据的缓冲的首地址. 就是从数组读取数据,
// number: 写入的字节长度.
void EEPROM_write_n(unsigned int EE_address,unsigned char *DataAddress,unsigned int number)
{
IAP_ENABLE(); //设置等待时间,允许IAP操作,送一次就够
IAP_CMD = 2; //宏调用, =2,送字节写命令
do
{
IAP_ADDRH = EE_address / 256; //送地址高字节(地址需要改变时才需重新送地址)
IAP_ADDRL = EE_address % 256; //送地址低字节
IAP_DATA = *DataAddress; //送数据到IAP_DATA,只有数据改变时才需重新送
EEPROM_Trig(); //触发EEPROM操作
EE_address++; //下一个地址
DataAddress++; //下一个数据
}while(--number); //直到结束
DisableEEPROM();
}
调用举例:(建议定义一个数组)
unsigned char ROM_num[6]; //存储数据的数组,最后与EEPROM进行数据交换
EEPROM_read_n(EEPROM_ADD_0,ROM_num,6); //读取ROM信息到数组
void _write_ROM() //编制专门的写入函数
{
EEPROM_SectorErase(EEPROM_ADD_0); //必须先擦除数据(由MCU自动,512字节被擦除),之后再写入,才能写正确
ROM_num[0]=test_1/256; //将4个测试数值分别给到临时交换数组。
ROM_num[1]=test_1%256;
ROM_num[2]=test_2; //test_1、3是16位的数据(2个字节);
ROM_num[3]=test_3/256;
ROM_num[4]=test_3%256;
ROM_num[5]=test_4; //test_2、test_4是单字节数据(8位)
EEPROM_write_n(EEPROM_ADD_0,ROM_num,6); //从数组写如到ROM中
}
|