单片机/MCU论坛
直播中

夜色妖姬

10年用户 7经验值
擅长:可编程逻辑 51单片机 连接器 51单片机 连接器 51单片机 连接器 测量仪表 51单片机 连接器 51单片机 连接器 51单片机 连接器 51单片机 连接器 存储技术 51单片机 连接器 51单片机 连接器 接口/总线/驱动 51单片机 连接器 51单片机 连接器 51单片机 连接器 控制/MCU 51单片机 连接器 51单片机 连接器 51单片机 连接器 51单片机 连接器
私信 关注
[问答]

关于STC单片机EEPROM断电问题

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;

回帖(3)

武林

2019-10-23 17:42:41

最佳答案

由于程序不完整,不能整体分析,单看main函数是有问题的。在主循环中只要满足if(uart_bit==1)条件,就擦除EEPROM并重写、读取、串口发送。有可能这个操作很频繁(片内EEPROM是有约10万次擦写寿命的),断电时正好在擦除中。所谓擦除就是整个扇区全部写0xff。所以上电后读取的是0xff。正确的做法是把变量number保存在缓存中,不会影响串口及时发送。利用外部中断做断电检测,当电源下降到某值触发外部中断,把缓存中的数据写入EEPROM。再次上电读取EEPROM就不会出错。
1 举报
  • 夜色妖姬: 这个条件就是串口收到正确字符  将number写入EEPROM

李维嘉

2019-10-23 18:35:29
坐等大神解答
举报

王辉

2019-10-23 18:44:29
代码要跑一下看看
1 举报
  • 夜色妖姬: 跑了其他功能都OK   串口发送会立马收到  但是上电时候收到FF   也不是每次都收到FF  大概有一半几率收到正确的数值  百思不得其解

更多回帖

发帖
×
20
完善资料,
赚取积分