Files
stc32g128k/App/system.c

384 lines
9.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "system.h"
#include "rgb.h"
#include "ui.h"
#include "../Drivers/lcd.h"
#include "../Drivers/rf.h"
#include "database.h"
#include "app_manager.h"
void GPIO_Init(void)
{
IE = 0x00;
IE2 = 0x00;
TCON = 0x00;
TMOD &= 0xF0;
TR0 = 0;
ET0 = 0;
AUXR = 0x00;
INTCLKO = 0x00;
P_SW1 = 0x00;
P_SW2 = 0x80; // 开启 EAXFR=1允许访问 P0PU/P2PU/P0INTE/P2INTE 等扩展 SFR 寄存器
P_SW3 = 0x00;
// 配置 P0.0 (BAT_ADC) 为高阻输入模式
P0M1 |= (1 << 0);
P0M0 &= ~(1 << 0);
// 配置 P0.1 (KEY_UP), P0.2 (KEY_CONFIRM), P0.3 (KEY_DOWN) 为准双向口模式并置 1
P0M1 &= ~((1 << 1) | (1 << 2) | (1 << 3));
P0M0 &= ~((1 << 1) | (1 << 2) | (1 << 3));
KEY_UP = 1;
KEY_CONFIRM = 1;
KEY_DOWN = 1;
// 配置 P2.3 (RGB_DIN) 为推挽输出模式
P2M1 &= ~(1 << 3);
P2M0 |= (1 << 3);
RGB_DIN = 0;
// 配置 P2.5 (MOTOR) 为推挽输出模式
P2M1 &= ~(1 << 5);
P2M0 |= (1 << 5);
MOTOR = 0;
// 配置 P2.6 (KEY_SOS) 为准双向口模式并置 1
P2M1 &= ~(1 << 6);
P2M0 &= ~(1 << 6);
KEY_SOS = 1;
// 配置 P2.2 (SHUT) 为推挽输出模式
P2M1 &= ~(1 << 2);
P2M0 |= (1 << 2);
SHUT = 1; // 默认拉高开启射频接收芯片工作
// 开启按键引脚 P0.1, P0.2, P0.3 和 P2.6 的内部上拉电阻,避免引脚抖动误唤醒
P0PU |= 0x0E;
P2PU |= 0x40;
// 配置 P2.7 (DET) 为高阻输入模式
P2M1 |= (1 << 7);
P2M0 &= ~(1 << 7);
// 配置 P3.0 (TXD), P3.1 (RXD) 和 P3.4 (SGM_CTRL)
P3M1 &= ~((1 << 0) | (1 << 1) | (1 << 4));
P3M0 &= ~(1 << 0);
P3M0 |= ((1 << 1) | (1 << 4));
}
void Delay_ms(u16 ms)
{
u16 i, j;
for (i = 0; i < ms; i++)
for (j = 12000; j > 0; j--);
}
void Delay10us(void)
{
unsigned char data i;
_nop_();
i = 30;
while (--i);
}
u16 GetTimer0_Safe(void)
{
u8 h1, l, h2;
do {
h1 = TH0;
l = TL0;
h2 = TH0;
} while (h1 != h2);
return ((u16)h1 << 8) | l;
}
void Delay_us(u16 us)
{
u16 ticks = (u16)((u32)us * (MAIN_Fosc / 1000000UL) / 12UL);
TL0 = 0;
TH0 = 0;
TR0 = 1;
while (GetTimer0_Safe() < ticks);
TR0 = 0;
}
void Uart1_Init(void)
{
u16 reload = (u16)(65536UL - (MAIN_Fosc / 4 / 115200UL));
SCON = 0x50;
AUXR |= 0x01;
AUXR |= 0x04;
T2L = (u8)reload;
T2H = (u8)(reload >> 8);
AUXR |= 0x10;
TI = 0;
}
void Uart_SendByte(u8 dat)
{
REN = 0;
SBUF = dat;
while (!TI);
TI = 0;
RI = 0;
REN = 1;
}
void Uart_SendString(char *s)
{
REN = 0;
while (*s) {
SBUF = *s++;
while (!TI);
TI = 0;
}
RI = 0;
REN = 1;
}
char Uart_RxChar(void)
{
if (RI) {
char c = SBUF;
RI = 0;
return c;
}
return 0;
}
void Uart_SendHex4(u8 val)
{
val &= 0x0F;
if (val < 10)
Uart_SendByte((u8)('0' + val));
else
Uart_SendByte((u8)('A' + (val - 10)));
}
void Uart_SendHex8(u8 val)
{
Uart_SendHex4(val >> 4);
Uart_SendHex4(val);
}
void Uart_SendHex20(u32 val)
{
Uart_SendHex4((u8)(val >> 16));
Uart_SendHex8((u8)(val >> 8));
Uart_SendHex8((u8)val);
}
void Uart_SendHex32(u32 val)
{
Uart_SendHex8((u8)(val >> 24));
Uart_SendHex8((u8)(val >> 16));
Uart_SendHex8((u8)(val >> 8));
Uart_SendHex8((u8)val);
}
void Uart_SendHex16(u16 val)
{
Uart_SendHex8((u8)(val >> 8));
Uart_SendHex8((u8)val);
}
void FormatHex(u32 val, char *buf)
{
u8 i;
buf[0] = 'A'; buf[1] = 'D'; buf[2] = 'D'; buf[3] = 'R'; buf[4] = ':';
buf[5] = ' '; buf[6] = '0'; buf[7] = 'x';
for (i = 0; i < 5; i++) {
u8 nibble = (val >> (4 * (4 - i))) & 0x0F;
if (nibble < 10)
buf[8 + i] = '0' + nibble;
else
buf[8 + i] = 'A' + (nibble - 10);
}
buf[13] = '\0';
}
void Enter_Low_Power_Sleep(void)
{
// 串口打印进入休眠提示
Uart_SendString("[SYS] Entering low power sleep...\r\n");
// 1. 熄灭 FCOB 双幻彩灯条
RGB_Send(0, 0, 0, 0, 0, 0);
// 2. 向屏幕发送 Display OFF (0x28) 彻底关闭像素发光显示
WriteComm(0x28);
Delay_ms(20);
// 3. AMOLED 屏控制器写入 Sleep In (0x10) 睡眠指令
WriteComm(0x10);
Delay_ms(20);
// 4. 拉低 SGM_CTRL 彻底断开 SGM3833 负压升压芯片的供电
SGM_CTRL = 0;
// 5. 将 AMOLED 控制总线所有 IO 拉低,以防由于 IO 寄生二极管对屏幕倒灌电导致常亮
LCD_CS = 0;
LCD_RST = 0;
LCD_DCX = 0;
LCD_SCL = 0;
LCD_SDI = 0;
// 6. 休眠状态下不能关闭接收芯片,保持 SHUT 为高电平 (1) 工作状态
SHUT = 1;
// 确保开启扩展寄存器访问 (EAXFR = 1),以便能正确配置端口唤醒使能扩展寄存器
P_SW2 |= 0x80;
// 7. 配置 P0.1, P0.2, P0.3 为 低电平触发 唤醒
P0IM1 |= 0x0E;
P0IM0 &= ~0x0E;
P0INTE |= 0x0E;
// 8. 配置 P2.6 (KEY_SOS) 为 低电平触发 唤醒
P2IM1 |= 0x40;
P2IM0 &= ~0x40;
P2INTE |= 0x40;
// 8b. 配置 P2.1 (RF_RX_DATA) 为 低电平触发 唤醒
P2IM1 |= 0x02;
P2IM0 &= ~0x02;
P2INTE |= 0x02;
// ===== 配置端口掉电唤醒使能寄存器 PxWKUE =====
P0WKUE |= 0x0E; // 使能 P0.1/P0.2/P0.3 掉电唤醒
P2WKUE |= 0x42; // 使能 P2.6, P2.1 掉电唤醒
// 9. 不允许外部中断 2 (RF_RX_DATA) 沿触发唤醒源
EX2 = 0;
// 10. 清除端口中断的悬挂/标志位,防误触
P0INTF = 0x00;
P2INTF = 0x00;
// 11. 关闭 Timer1 中断,将 EA 置 0通过硬件唤醒直接向下执行
ET1 = 0;
EA = 0;
// 12. 写入 PCON 掉电模式位,使 MCU 进入深度 Power-Down 挂起状态
while (1) {
Uart_SendString("[SYS] Entering Power-Down mode...\r\n");
PCON |= 0x02; // PD = 1
_nop_();
_nop_();
_nop_();
_nop_();
// 13. 唤醒后直接从这里继续执行,如果是真唤醒,则打破挂起循环
if (Wakeup_Restore() == 1) {
break;
}
}
}
u8 Wakeup_Restore(void)
{
u8 actual_p0;
u8 actual_p2;
u8 decoded = 0;
// 确保 EAXFR=1 才能访问扩展 SFR (P0INTF/P2INTF/P0INTE/P2INTE)
EAXFR = 1;
// 直接读取当前的真实悬挂标志
actual_p0 = P0INTF;
actual_p2 = P2INTF;
// 1. 立即禁用端口中断和唤醒,防止继续触发
P0INTE = 0x00;
P2INTE = 0x00;
P0WKUE = 0x00; // 清除 P0 唤醒允许
P2WKUE = 0x00; // 清除 P2 唤醒允许
P0INTF = 0x00;
P2INTF = 0x00;
EX2 = 0;
// 1b. 关闭掉电唤醒定时器
WKTCH = 0x00;
WKTCL = 0x00;
// [诊断] 打印唤醒来源
if (actual_p0 & 0x0E) {
Uart_SendString("[WR] Woken by KEY P0 (P0INTF=0x");
Uart_SendHex8(actual_p0);
Uart_SendString(")\r\n");
} else if (actual_p2 & 0x40) {
Uart_SendString("[WR] Woken by KEY P2 (P2INTF=0x");
Uart_SendHex8(actual_p2);
Uart_SendString(")\r\n");
} else if (actual_p2 & 0x02) {
Uart_SendString("[WR] Woken by RF P2.1 (P2INTF=0x");
Uart_SendHex8(actual_p2);
Uart_SendString(")\r\n");
} else {
Uart_SendString("[WR] Woken by TIMER/OTHER\r\n");
}
// 3. 处理射频唤醒:判断是否真的有传感器报警
if ((actual_p2 & 0x02) && !(actual_p0 & 0x0E) && !(actual_p2 & 0x40)) {
u8 i;
u32 rx_addr;
u8 rx_data;
// 全速尝试解调,由于 EA 此时仍为 0不受滴答定时器干扰
for (i = 0; i < 6; i++) {
if (EV1527_Decode(&rx_addr, &rx_data)) {
u8 slot;
if (Check_Sensor_ID(rx_addr, &slot)) {
u8 sensor_type = sensor_list[slot].type;
if (sensor_type < 5) {
// 只有在防区开启 (zone == 0) 或处于 STATE_ARMED (警戒) 状态下才触发
if (sensor_list[slot].zone == 0 || current_state == STATE_ARMED) {
captured_addr = rx_addr;
alarm_sensor_slot = slot;
decoded = 1;
break;
}
}
}
}
Delay_ms(20);
}
if (!decoded) {
// 只是噪声或未配对的遥控器,无视,不亮屏直接返回 0 让主休眠循环继续挂起(保持 EA = 0
Uart_SendString("[WR] RF false alarm/noise. Re-sleeping.\r\n");
return 0;
}
Uart_SendString("[WR] RF Alarm Decoded successfully!\r\n");
}
// 4. 到这里说明是真唤醒(或者是按键,或者是有效的射频警报解码成功)
// 开启射频接收芯片工作
SHUT = 1;
// 拉高 SGM_CTRL 并发送脉冲使能 SGM3833 升压
SGM_CTRL = 1;
SGM_SendPulse(27);
// 等待负压电轨充分稳定
Delay_ms(200);
// 重做屏控制器 RM69310 寄存器组的初始化
LCD_Init();
// 重置闲置倒计时
inactivity_timer = 0;
// 根据唤醒源决定显示什么
if (decoded) {
// 如果是有效的射频入侵触发,强制切到 Alarm 状态并启动应用
AppManager_StartApp(APP_ID_ALARM);
} else {
// 如果是按键触发唤醒,显示主时间界面
current_state = STATE_NORMAL;
UI_ShowClockPage(current_state, current_hour, current_min);
}
Uart_SendString("[SYS] Wakeup restored!\r\n");
return 1; // 返回 1 表示真唤醒
}