杨方 发表于 2026-4-19 12:38:31

ADC+按钮

RTC时钟,ADC掉电检测,PD=1;
之后无法唤醒,外部中断按下,数码管一闪唤醒不了。
另外时钟按键1/3次可以修改数码管。
我把程序附上,亲高师指点,成功后拿辛苦费。

杨方 发表于 2026-4-19 12:41:35

wulin 发表于 2026-4-19 21:16:25

利用芯片自带的掉电中断更合适

//测试条件: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功能
}



杨方 发表于 2026-4-20 19:10:18

谢谢指点。
页: [1]
查看完整版本: ADC+按钮