找回密码
 立即注册
查看: 96|回复: 3

STC8G1K08A-8pin控制继电器来定时闭合线路

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 最近打卡:2026-06-08 11:38:46

2

主题

5

回帖

22

积分

新手上路

积分
22
发表于 2026-6-8 11:38:46 | 显示全部楼层 |阅读模式
这是我做的基于STC8G1K08A-8PIN搭配12V继电器来控制一个15V线路的通断定时装置,我尝试采用LVD低压检测,在断电前一刻将定时的数据进行保存,虽然实现了,随时断电再上电都能够接着计时,但是我将定时时间改的久一点,却发现在程序运行途中,会发生继电器突然关闭,但计时还在继续的情况,这是什么原因造成的?就好像电路被干扰了,不知道是程序方面原因还是硬件方面,我硬件方面采用USB转TTL给单片机供5V电,继电器外部12V供电,然后再用一根线连接二者供电的GND,这样就能够通过STC8G1K08A-8PIN来控制继电器1从而控制这条线路的输出。但现在就是测试时它会继电器突然断开,而计时仍在继续。这是我的代码:
#include <STC8G.H>
#include <INTRINS.H>

// ========== 配置参数 ==========
#define TOTAL_TIMEOUT   36000UL
#define DELAY_START     5
#define PROGRAM_VER     0x01

#define EE_BASE         0x0000
#define FLAG_VALID      0xA5

#define ST_WAIT         0
#define ST_RUN          1
#define ST_DONE         2

sbit RELAY = P5^5;

unsigned char sys_state;
unsigned long total_sec;
unsigned int  wait_cnt;
unsigned long ee_last_sec;
bit sector_erased;
bit lvd_triggered;

// ========== 基础串口函数 ==========
void UartChar(unsigned char c)
{
    SBUF = c;
    while(!TI);
    TI = 0;
}

void UartStr(const char *str)
{
    while(*str) UartChar(*str++);
}

void UartNum(unsigned long n)
{
    unsigned char buf[12], i = 0, j;
    if(n == 0) { UartChar('0'); return; }
    while(n > 0) { buf[i++] = '0' + (n % 10); n /= 10; }
    for(j = i; j > 0; j--) UartChar(buf[j - 1]);
}

// ========== IAP底层驱动 ==========
void IAP_Idle(void)
{
    IAP_CONTR = 0; IAP_CMD = 0; IAP_TRIG = 0;
    IAP_ADDRH = 0x80; IAP_ADDRL = 0;
}

unsigned char IAP_Read(int addr)
{
    unsigned char dat;
    IAP_TPS = 11;
    IAP_CONTR = 0x80; IAP_CMD = 1;
    IAP_ADDRL = addr & 0xFF;
    IAP_ADDRH = (addr >> 8) & 0xFF;
    IAP_TRIG = 0x5A; IAP_TRIG = 0xA5;
    _nop_(); _nop_();
    dat = IAP_DATA;
    IAP_Idle();
    return dat;
}

void IAP_Erase(int addr)
{
    IAP_TPS = 11;
    IAP_CONTR = 0x80; IAP_CMD = 3;
    IAP_ADDRL = addr & 0xFF;
    IAP_ADDRH = (addr >> 8) & 0xFF;
    IAP_TRIG = 0x5A; IAP_TRIG = 0xA5;
    _nop_(); _nop_();
    IAP_Idle();
}

void IAP_Write(int addr, unsigned char dat)
{
    IAP_TPS = 11;
    IAP_CONTR = 0x80; IAP_CMD = 2;
    IAP_ADDRL = addr & 0xFF;
    IAP_ADDRH = (addr >> 8) & 0xFF;
    IAP_DATA = dat;
    IAP_TRIG = 0x5A; IAP_TRIG = 0xA5;
    _nop_(); _nop_();
    IAP_Idle();
}

// ========== EEPROM: 主计时数据 ==========
unsigned char CalcChk(unsigned long s)
{
    unsigned char c = PROGRAM_VER ^ FLAG_VALID;
    c ^= (unsigned char)s;
    c ^= (unsigned char)(s >> 8);
    c ^= (unsigned char)(s >> 16);
    c ^= (unsigned char)(s >> 24);
    return c;
}

unsigned long EE_Read(void)
{
    unsigned long s;
    unsigned char v, f, c;

    v = IAP_Read(EE_BASE);
    f = IAP_Read(EE_BASE + 1);
    s  = (unsigned long)IAP_Read(EE_BASE + 2);
    s |= (unsigned long)IAP_Read(EE_BASE + 3) << 8;
    s |= (unsigned long)IAP_Read(EE_BASE + 4) << 16;
    s |= (unsigned long)IAP_Read(EE_BASE + 5) << 24;
    c = IAP_Read(EE_BASE + 6);

    if(v != PROGRAM_VER || f != FLAG_VALID || c != CalcChk(s) || s > TOTAL_TIMEOUT)
        return 0xFFFFFFFF;

    ee_last_sec = s;
    return s;
}

void EE_Write_Fast(unsigned long s)
{
    IAP_Write(EE_BASE, PROGRAM_VER);
    IAP_Write(EE_BASE + 1, FLAG_VALID);
    IAP_Write(EE_BASE + 2, (unsigned char)s);
    IAP_Write(EE_BASE + 3, (unsigned char)(s >> 8));
    IAP_Write(EE_BASE + 4, (unsigned char)(s >> 16));
    IAP_Write(EE_BASE + 5, (unsigned char)(s >> 24));
    IAP_Write(EE_BASE + 6, CalcChk(s));
}

// ========== 系统初始化 ==========
void SysInit(void)
{
    P0M0 = 0x00; P0M1 = 0x00;
    P1M0 = 0x00; P1M1 = 0x00;
    P2M0 = 0x00; P2M1 = 0x00;
    P3M0 = 0x00; P3M1 = 0x00;
    P5M0 = 0x00; P5M1 = 0x00;

    SCON = 0x50;
    AUXR = 0x00;
    TMOD = 0x20;
    TH1 = 0xFD; TL1 = 0xFD; TR1 = 1;
    ES = 1;

    TMOD &= 0xF0; TMOD |= 0x01;
    AUXR &= 0x7F;
    TL0 = 0x00; TH0 = 0x4C;
    TF0 = 0; ET0 = 1; EA = 1;
}

// ========== 主函数 ==========
void main(void)
{
    unsigned long loaded_sec, last_sec;

    SysInit();
    RELAY = 0;
    sys_state = ST_WAIT;
    wait_cnt = 0;
    last_sec = 0;
    sector_erased = 0;
    lvd_triggered = 0;

    // 初始化低压检测
    PCON &= 0xDF;
    ELVD = 1;
    EA = 1;

    // 读取主计时
    loaded_sec = EE_Read();
    if(loaded_sec == 0xFFFFFFFF)
    {
        UartStr("\r\n=== Cherokee Timer ===\r\n");
        UartStr("[INIT] New chip.\r\n");
        IAP_Erase(EE_BASE);
        EE_Write_Fast(0);
        loaded_sec = 0;
    }

    total_sec = loaded_sec;

    UartStr("\r\n=== Cherokee Timer ===\r\n");
    UartStr("[LOAD] ");
    UartNum(total_sec);
    UartStr("s\r\n");

    if(total_sec >= TOTAL_TIMEOUT)
    {
        sys_state = ST_DONE;
        RELAY = 0;
        UartStr("[DONE]\r\n");
    }
    else
    {
        sys_state = ST_WAIT;
        RELAY = 0;
        UartStr("[WAIT]\r\n");
        TF0 = 0; TR0 = 1;
    }

    // 主循环
    while(1)
    {
        // 主循环守护:如果运行时继电器意外断开,立刻重新吸合
        if(sys_state == ST_RUN && RELAY == 0)
        {
            RELAY = 1;
        }

        // 如果LVD被触发过且系统还在运行,说明是干扰,重新擦除扇区
        if(lvd_triggered && sys_state == ST_RUN)
        {
            lvd_triggered = 0;
            sector_erased = 0;
        }

        // 提前擦除扇区,为掉电时的快速写入做准备
        if(sys_state == ST_RUN && sector_erased == 0)
        {
            IAP_Erase(EE_BASE);
            sector_erased = 1;
        }

        // 每秒输出计时信息
        if(sys_state == ST_RUN && total_sec != last_sec)
        {
            last_sec = total_sec;
            UartStr("T:");
            UartNum(total_sec);
            UartStr("/");
            UartNum(TOTAL_TIMEOUT);
            UartStr(" R:");
            UartNum(TOTAL_TIMEOUT - total_sec);
            UartStr("s\r\n");
        }
    }
}

// ========== 定时器0中断(50ms) ==========
void T0_ISR(void) interrupt 1
{
    static unsigned char tick = 0;

    TL0 = 0x00; TH0 = 0x4C;
    tick++;
    if(tick < 20) return;
    tick = 0;

    if(sys_state == ST_WAIT)
    {
        wait_cnt++;
        if(wait_cnt >= DELAY_START)
        {
            sys_state = ST_RUN;
            RELAY = 1;
            UartStr("[ON]\r\n");
        }
    }
    else if(sys_state == ST_RUN)
    {
        total_sec++;
        if(total_sec >= TOTAL_TIMEOUT)
        {
            sys_state = ST_DONE;
            RELAY = 0;
            TR0 = 0;
            IAP_Erase(EE_BASE);
            EE_Write_Fast(total_sec);
            UartStr("[DONE]\r\n");
        }
    }
}

// ========== 低压检测中断(掉电保护) ==========
void LVD_ISR(void) interrupt 6
{
    unsigned char i;

    RELAY = 1;

    // 冷静期确认:连续3次检测到低压标志才认为是真掉电
    for(i = 0; i < 3; i++)
    {
        if(!(PCON & 0x20))
        {
            // 低压标志消失,说明是干扰
            lvd_triggered = 1;
            PCON &= 0xDF;
            EA = 1;
            return;
        }
        PCON &= 0xDF;
    }

    // 确认是真掉电,执行紧急保存
    EA = 0;
    if(sys_state == ST_RUN)
    {
        EE_Write_Fast(total_sec);
    }

    // 等待彻底掉电或电压恢复
    while((PCON & 0x20) != 0)
    {
        PCON &= 0xDF;
        _nop_(); _nop_();
    }
    IAP_CONTR = 0x20;
}

// ========== 串口中断 ==========
void UART_ISR(void) interrupt 4
{
    if(RI) RI = 0;
    if(TI) TI = 0;
}
虽然增加了一些保护机制,但还是没用,请大佬们帮我看看是问题出在了哪里?是程序上还是硬件上需要改进,我询问AI,它说可能继电器线圈出现电磁干扰导致触发LVD低压检测,是让我在单片机VCC引脚和GND引脚之间增加电容来排除电路中电压异常。求大佬们多指点指点,感谢。
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:316
  • 最近打卡:2026-07-22 06:56:11
已绑定手机

70

主题

874

回帖

3225

积分

论坛元老

DIY玩家

积分
3225
发表于 2026-6-8 12:01:41 | 显示全部楼层
独立供电的系统
为何连接二者供电的GND
永怀这学徒心
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:153
  • 最近打卡:2026-07-21 14:44:21
已绑定手机

5

主题

356

回帖

6446

积分

论坛元老

积分
6446
发表于 2026-6-8 12:12:23 | 显示全部楼层
主循环是不是卡死了
回复

使用道具 举报 送花

  • 打卡等级:初来乍到
  • 打卡总天数:3
  • 最近打卡:2026-06-08 11:38:46

2

主题

5

回帖

22

积分

新手上路

积分
22
发表于 2026-6-8 13:19:00 | 显示全部楼层
供地是因为不这样,线圈无法闭合,之前我没有这样,但就是闭合不了,后来我试着供地发现可以。
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-7-22 10:32 , Processed in 0.084824 second(s), 54 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表