EEPROM用的也比较多了,之前写代码也是可以使用的,但是这次发现有些问题,写进数据后断电再上电读取正常数值,不做任何操作再断电再上电,读取就是FF,再断再上又正常。可能个人对STC的EEPROM了解的不够深刻,没完全摸清它的脾气……
芯片采用STC12C5A60S2,供电采用7.4直流锂电经过7805后供电,硬件上有滤波器件。先附上改动后代码 (在我其他的代码里亲测可用)。
//EEPROM.h----------------------------------------------------------------------
#include
sfr IAP_DATA = 0xC2; //IAP数据寄存器
sfr IAP_ADDRH = 0xC3; //IAP地址寄存器高字节
sfr IAP_ADDRL = 0xC4; //IAP地址寄存器低字节
sfr IAP_CMD = 0xC5; //IAP命令寄存器
sfr IAP_TRIG = 0xC6; //IAP命令触发寄存器
sfr IAP_CONTR = 0xC7; //IAP控制寄存器
#define ENABLE_IAP 0x82 //if SYSCLK<12MHz
/*----------------------------
关闭IAP
----------------------------*/
void IapIdle()
{
IAP_CONTR = 0; //关闭IAP功能
IAP_CMD = 0; //清除命令寄存器
IAP_TRIG = 0; //清除触发寄存器
IAP_ADDRH = 0x80; //将地址设置到非IAP区域
IAP_ADDRL = 0;
}
/*----------------------------
从ISP/IAP/EEPROM区域读取一字节
----------------------------*/
unsigned char IapReadByte(unsigned int addr)
{
unsigned char datt; //数据缓冲区
IAP_CONTR = ENABLE_IAP; //使能IAP
IAP_CMD = 1; //设置IAP命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
datt = IAP_DATA; //读ISP/IAP/EEPROM数据
IapIdle(); //关闭IAP功能
return datt; //返回
}
/*----------------------------
写一字节数据到ISP/IAP/EEPROM区域
----------------------------*/
void IapProgramByte(unsigned int addr, unsigned char dat1)
{
IAP_CONTR = ENABLE_IAP; //使能IAP
IAP_CMD = 2; //设置IAP命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_DATA = dat1; //写ISP/IAP/EEPROM数据
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
IapIdle();
}
/*----------------------------
扇区擦除
----------------------------*/
void IapEraseSector(unsigned int addr)
{
IAP_CONTR = ENABLE_IAP; //使能IAP
IAP_CMD = 3; //设置IAP命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
IapIdle();
}
//main.c-----------------------------------------------------------------------
void Uart_SendData(uchar dat)//发送一个Byte数据
{
while (uart_busy);
uart_busy = 1;
SBUF = dat;
}
void SendString(uchar ub,uchar uc,uchar ud)//发送字符串
{
uart_n4=(ub+uc+ud)%256;
_nop_();_nop_();_nop_();
Uart_SendData(0xEF);
Uart_SendData(ub);
Uart_SendData(uc);
Uart_SendData(ud);
Uart_SendData(uart_n4);
Uart_SendData(0xFE);
}
void main()
{
nb1 = IapReadByte(0x0000);
SendString(nb1,0,0);
while(1)
{
if(uart_bit==1)
{
IapEraseSector(0x0000);_nop_();_nop_();_nop_();
IapProgramByte(0x0000,number);_nop_();_nop_();
nb1 = IapReadByte(0x0000);
SendString(nb1,0,0);
uart_bit=0;
}
}
}
PS:已省略部分函数,比如delay();uart_init();void uart() interrupt 4;
2019-10-23 17:42:41
最佳答案
由于程序不完整,不能整体分析,单看main函数是有问题的。在主循环中只要满足if(uart_bit==1)条件,就擦除EEPROM并重写、读取、串口发送。有可能这个操作很频繁(片内EEPROM是有约10万次擦写寿命的),断电时正好在擦除中。所谓擦除就是整个扇区全部写0xff。所以上电后读取的是0xff。正确的做法是把变量number保存在缓存中,不会影响串口及时发送。利用外部中断做断电检测,当电源下降到某值触发外部中断,把缓存中的数据写入EEPROM。再次上电读取EEPROM就不会出错。
由于程序不完整,不能整体分析,单看main函数是有问题的。在主循环中只要满足if(uart_bit==1)条件,就擦除EEPROM并重写、读取、串口发送。有可能这个操作很频繁(片内EEPROM是有约10万次擦写寿命的),断电时正好在擦除中。所谓擦除就是整个扇区全部写0xff。所以上电后读取的是0xff。正确的做法是把变量number保存在缓存中,不会影响串口及时发送。利用外部中断做断电检测,当电源下降到某值触发外部中断,把缓存中的数据写入EEPROM。再次上电读取EEPROM就不会出错。
1
举报
-
夜色妖姬:
这个条件就是串口收到正确字符 将number写入EEPROM