- 打卡等级:初来乍到
- 打卡总天数:3
- 最近打卡:2026-06-08 11:38:46
新手上路
- 积分
- 22
|
这是我做的基于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引脚之间增加电容来排除电路中电压异常。求大佬们多指点指点,感谢。
|
|