etec 发表于 2026-5-7 11:24:48

咨询掉电自动存储设计方案

您好,项目使用了STC8H8K64U芯片,现在有一个电能参数,需要再检测到设备掉电时,自动保存一次,请推荐一个比较简便的方案,尽量不增加外部器件,谢谢!

神农鼎 发表于 2026-5-7 13:02:36




完整的系统设计参考原理图@8H8K64U, @32G12K128 - 电源/复位/省电模式/外部晶振/内部时钟/对外时钟输出/PLL-144MHz时钟 国芯人工智能技术交流网站 - AI32位8051交流社区


EEPROM的使用 请 配合比较器做掉电检测 | 唯一正解,电力系统标准应用 - EEPROM/DataFlash 国芯人工智能技术交流网站 - AI32位8051交流社区

xiaobendan 发表于 2026-5-7 13:04:33

啥也不需要,打开低压中断就行了,好用的很

lcwswust 发表于 2026-5-7 14:29:08

程序开始运行时就准备一块擦除后的EEPROM扇区,然后在低压中断里写入EEPROM,这样可以较为快速的保存。
低压检测电压设置到最高,提前测一下掉电时VCC电压下降的曲线,看低压中断到保存参数完成的时间够不够,多留点余量。

etec 发表于 2026-5-7 14:33:19

xiaobendan 发表于 2026-5-7 13:04
啥也不需要,打开低压中断就行了,好用的很

您好,低压中断是哪个中断,能发一下吗?谢谢

wulin 发表于 2026-5-7 16:53:02

etec 发表于 2026-5-7 14:33
您好,低压中断是哪个中断,能发一下吗?谢谢

本示例主要针对频繁掉电保存重要数据设计的方案,由于内置EEPROM有擦除次数寿命限制,本方案可以延寿百倍(理论值)。限制条件是保存的数据不可以等于0xff。因为EEPROM只可写0不可写1,只有擦除扇区才全部置1。

//测试条件:STC8H实验板,MCU型号STC8H3K48S2
//注意:测试本示例时,需在ISP下载时将【允许低压复位(禁止低压中断)】关闭
#include <STC8H.H>
#include <intrins.h>                                //库头文件
#define uint unsigned int                         //宏定义数据类型uint
#define uchar unsigned char                 //宏定义数据类型uchar

#define IAP_ADDRESS 0x0000                        //测试地址

//顺序共阴极数码管段码表,段码a-h顺序接PX0-PX7
uchar code table[]={//共阴数码管段码"0~f-."
                0x3f,0x06,0x5b,0x4f,
                0x66,0x6d,0x7d,0x07,
                0x7f,0x6f,0x77,0x7c,
                0x39,0x5e,0x79,0x71,0x40,0x80};
uchar data dis_buf;            //缓存数组
uintnum,sec;
uchar i;
uintsign;
voidTimer0Init();               //定时器初始化声明
voidIapIdle();                  //关闭IAP/EEPROM
uchar IapRead(uint addr);         //读取EEPROM数据
voidIapProgram(uint addr, uchar dat);//写入EEPROM数据
voidIapErase(uint addr);          //擦除EEPROM扇区

//初始化单片机端口
void McuInit()
{
        P0M0 = 0xff; P0M1 = 0x00;
        P1M0 = 0x00; P1M1 = 0x00;
        P2M0 = 0x00; P2M1 = 0x00;
        P3M0 = 0x00; P3M1 = 0x00;
        P4M0 = 0x00; P4M1 = 0x00;
        P5M0 = 0x00; P5M1 = 0x00;
        P6M0 = 0x00; P6M1 = 0x00;
        P7M0 = 0x00; P7M1 = 0x00;
}
//关闭单片机端口
void McuSleep()
{
        P0M0 = 0x00; P0M1 = 0xff;
        P1M0 = 0x00; P1M1 = 0xff;
        P2M0 = 0x00; P2M1 = 0xff;
        P3M0 = 0x00; P3M1 = 0xff;
        P4M0 = 0x00; P4M1 = 0xff;
        P5M0 = 0x00; P5M1 = 0xff;
        P6M0 = 0x00; P6M1 = 0xff;
        P7M0 = 0x00; P7M1 = 0xff;
}

void main()                                                       
{
        McuInit();
        if(IapRead(IAP_ADDRESS)==0xff)//如果没有保存过数据
        {
                IapProgram(IAP_ADDRESS, 0);//扇区首地址写0
                sec=0;
                sign=1;
        }
        else
        {
//                for(i=1;i<12;i++)         //测试写10次
                for(i=1;i<511;i++)      //测试写满510个字节
                {
                        if(IapRead(IAP_ADDRESS+i)==0xff)//如果遇到没有保存数据的单元
                        {
                                sec=IapRead(IAP_ADDRESS+i-1);//读取前一个字节保存的数据
                                sign=i;         //地址缓存
                                break;            //跳出循环
                        }
                }
        }
//        if(sign==11)                  //测试写10次
        if(sign==510)               //如果写满510
        {
                IapErase(IAP_ADDRESS);    //擦除扇区
                IapProgram(IAP_ADDRESS, 0);//首地址写0
                sign=1;
        }
        PCON &= 0xDF;               //清0掉电标志
        ELVD = 1;                     //开低压中断
        EA   = 1;                     //开总中断

        Timer0Init();               //初始化定时器

        while(1)
        {
                if(TF0)                   //查询T0中断请求标志
                {               
                        TF0=0;                //T0中断请求标志清0
                        if(++num>=1000)       //1秒
                        {
                                num=0;                               
                                sec=++sec%250;
                        }
                        dis_buf=table;
                        dis_buf=table;
                        dis_buf=table;
                        P0=0x00;            //段消隐
                        P2=~(0x01<<i);      //送段码
                        P0=dis_buf;      //送位码
                        i=++i%3;            //循环计数
                }//耗时569us
        }
}

void PowerLost() interrupt 6      //低压中断
{
        EA = 0;                     //关闭总中断
        McuSleep();                   //关闭所有端口(停止所有耗电电路)
        IapProgram(IAP_ADDRESS+sign,sec);//写数据到EEPROM
        while((PCON & 0x20) != 0)   //复查低压标志
        {
                PCON &= 0xDF;             //清除低压标志
                _nop_();               
                _nop_();                  //坐等掉电
        }
        IAP_CONTR = 0x20;             //发现是误报,重启单片机,恢复正常工作
}

void Timer0Init(void)             //1毫秒@11.0592MHz
{
        AUXR |= 0x80;               //定时器时钟1T模式
        TMOD &= 0xF0;               //设置定时器模式
        TL0 = 0xCD;                   //设置定时初始值
        TH0 = 0xD4;                   //设置定时初始值
        TF0 = 0;                      //清除TF0标志
        TR0 = 1;                      //定时器0开始计时
}
//关闭IAP/EEPROM
void IapIdle()
{
    IAP_CONTR = 0;                //关闭IAP功能
    IAP_CMD = 0;                  //清除命令寄存器
    IAP_TRIG = 0;               //清除触发寄存器
    IAP_ADDRH = 0x80;             //将地址设置到非IAP区域
    IAP_ADDRL = 0;
}
//读取EEPROM数据
uchar IapRead(uint addr)
{
    uchar dat;

    IAP_CONTR = 0x80;             //使能IAP
    IAP_TPS = 12;               //设置等待参数12MHz
    IAP_CMD = 1;                  //设置IAP读命令
    IAP_ADDRL = addr;             //设置IAP低地址
    IAP_ADDRH = addr >> 8;      //设置IAP高地址
    IAP_TRIG = 0x5a;            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;            //写触发命令(0xa5)
    _nop_();
    dat = IAP_DATA;               //读IAP数据
    IapIdle();                  //关闭IAP功能

    return dat;
}
//写入EEPROM数据
void IapProgram(uint addr, uchar dat)
{
    IAP_CONTR = 0x80;             //使能IAP
    IAP_TPS = 12;               //设置等待参数12MHz
    IAP_CMD = 2;                  //设置IAP写命令
    IAP_ADDRL = addr;             //设置IAP低地址
    IAP_ADDRH = addr >> 8;      //设置IAP高地址
    IAP_DATA = dat;               //写IAP数据
    IAP_TRIG = 0x5a;            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;            //写触发命令(0xa5)
    _nop_();
    IapIdle();                  //关闭IAP功能
}

void IapErase(uint addr)
{
    IAP_CONTR = 0x80;             //使能IAP
    IAP_TPS = 12;               //设置等待参数12MHz
    IAP_CMD = 3;                  //设置IAP擦除命令
    IAP_ADDRL = addr;             //设置IAP低地址
    IAP_ADDRH = addr >> 8;      //设置IAP高地址
    IAP_TRIG = 0x5a;            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;            //写触发命令(0xa5)
    _nop_();                      //
    IapIdle();                  //关闭IAP功能
}



etec 发表于 2026-5-7 17:09:03

好的,谢谢,我看下

xiaobendan 发表于 2026-5-7 17:55:25

etec 发表于 2026-5-7 17:09
好的,谢谢,我看下

下载工具里面设置低压检测电压选最高的

wulin 发表于 2026-5-8 06:18:27

etec 发表于 2026-5-7 17:09
好的,谢谢,我看下

提醒:第100行与第101行的注释颠倒

etec 发表于 2026-5-10 15:46:11

感谢!
页: [1] 2
查看完整版本: 咨询掉电自动存储设计方案