Files
stc32g128k/App/main.c
2026-07-15 15:57:37 +08:00

2243 lines
68 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 "stc32g.h"
#include "intrins.h"
#include "config.h"
#include "../Drivers/lcd.h"
#include "../Drivers/rf.h"
/* =========================================================================
* 硬件引脚定义
* ========================================================================= */
sbit MOTOR = P2^5; // 振动马达控制端 (推挽输出,与 SPI SCLK 物理共享引脚)
sbit KEY_UP = P0^1; // 侧边按键 ▲ (高阻输入 + 上拉)
sbit KEY_CONFIRM = P0^2; // 侧边按键 ■ (高阻输入 + 上拉)
sbit KEY_DOWN = P0^3; // 侧边按键 ▼ (高阻输入 + 上拉)
sbit KEY_SOS = P2^6; // 物理 SOS 求救按键 (高阻输入 + 上拉)
sbit RGB_DIN = P2^3; // FCOB 幻彩灯条 DIN 控制线
sbit SHUT = P2^2; // LR690L 射频芯片休眠/工作控制引脚 (低使能开启工作)
sbit SGM_CTRL = P3^4; // SGM3833 PMIC 屏幕负压供电使能脚
sbit LCD_RST = P1^0; // Truly AMOLED 屏幕复位脚 (RESET)
sbit LCD_DCX = P1^1; // Truly AMOLED 屏幕数据/指令选择脚 (D/C)
sbit LCD_SDI = P1^4; // Truly AMOLED 屏幕 SPI 数据脚 (MOSI)
sbit LCD_SCL = P1^5; // Truly AMOLED 屏幕 SPI 时钟脚 (SCLK)
sbit LCD_CS = P1^6; // Truly AMOLED 屏幕 SPI 片选脚 (CS)
/* ====== 硬件 SPI 寄存器定义 ====== */
sfr P_SW1 = 0xA2;
sfr SPCTL = 0xCE;
sfr SPSTAT = 0xCD;
sfr SPDAT = 0xCF;
/* ====== RGB 幻彩灯条驱动函数 ====== */
void SPI_Init(void)
{
P_SW1 = (P_SW1 & ~0x0C) | 0x04; // 映射 SPI 至第二组 (P2.2~P2.5)
SPCTL = 0xD1; // SSIG=1, SPEN=1, MSTR=1, SPR0=1 (3.0 MHz)
SPSTAT = 0xC0; // 清除标志
}
void Encode_Byte(u8 val, u8 *out)
{
u32 spi_val = 0;
u8 i;
for (i = 0; i < 8; i++)
{
spi_val <<= 3;
if (val & (0x80 >> i))
{
spi_val |= 6; // 1码 -> 110
}
else
{
spi_val |= 4; // 0码 -> 100
}
}
out[0] = (u8)(spi_val >> 16);
out[1] = (u8)(spi_val >> 8);
out[2] = (u8)spi_val;
}
void RGB_Send(u8 g1, u8 r1, u8 b1, u8 g2, u8 r2, u8 b2)
{
u8 idata spi_buf[18];
u8 i;
// 转码 3-bit 编码
Encode_Byte(g1, &spi_buf[0]);
Encode_Byte(r1, &spi_buf[3]);
Encode_Byte(b1, &spi_buf[6]);
Encode_Byte(g2, &spi_buf[9]);
Encode_Byte(r2, &spi_buf[12]);
Encode_Byte(b2, &spi_buf[15]);
EA = 0; // 关中断
// 临时将 P2.5 (MOTOR) 配置为高阻输入以隔离时钟信号
P2M1 |= (1 << 5);
P2M0 &= ~(1 << 5);
SPI_Init(); // 启动 SPI
for (i = 0; i < 18; i++)
{
SPDAT = spi_buf[i];
while (!(SPSTAT & 0x80)); // 等待发送完毕
SPSTAT = 0xC0;
}
SPCTL = 0; // 关闭 SPI
// 配置 P2.3 (RGB_DIN) 为推挽输出并拉低 100us 做 Reset
P2M1 &= ~(1 << 3);
P2M0 |= (1 << 3);
RGB_DIN = 0;
for (i = 0; i < 200; i++)
{
_nop_(); _nop_(); _nop_();
}
// 恢复 P2.5 (MOTOR) 为推挽输出以进行振动控制
P2M1 &= ~(1 << 5);
P2M0 |= (1 << 5);
MOTOR = 0;
EA = 1; // 开中断
}
/* =========================================================================
* 系统全局变量
* ========================================================================= */
SystemState current_state = STATE_NORMAL;
u8 current_hour = 10;
u8 current_min = 35;
u8 current_sec = 0;
volatile u8 clock_updated = 0;
u8 menu_select = 0;
u32 captured_addr = 0;
u8 captured_type = 0;
u8 alarm_sensor_slot = 0;
Sensor_Slot xdata sensor_list[16];
volatile u16 ms_tick = 0;
volatile u16 inactivity_timer = 0; // 无操作闲置计时器
volatile u32 pair_timeout_ms = 0;
volatile u16 alarm_timer_ms = 0;
volatile u16 motor_timer_ms = 0;
volatile bit alarm_flash_flag = 0;
volatile u16 key_scan_timer = 0;
volatile u16 key_up_hold = 0;
volatile u16 key_down_hold = 0;
volatile u16 key_confirm_hold = 0;
volatile u16 key_sos_hold = 0;
volatile u16 comb_hold = 0;
/* =========================================================================
* 辅助映射表及前置声明
* ========================================================================= */
char *code sensor_names_fr[5] = {
"PORTE",
"PIR",
"FUMEE",
"GAZ",
"EAU"
};
char *code default_names[5] = {
"PORTE",
"PIR",
"FUMEE",
"GAZ",
"EAU"
};
u8 code default_zones[5] = {1, 1, 0, 0, 0};
char *code menu_items[5] = {
"DETEC. PORTE",
"DETEC. PIR",
"DETEC. FUMEE",
"DETEC. GAZ",
"DETEC. EAU"
};
void GPIO_Init(void);
void Timer1_Init(void);
u16 GetTimer0_Safe(void);
void Delay_us(u16 us);
void Delay_ms(u16 ms);
void Uart1_Init(void);
void Uart_SendByte(u8 dat);
void Uart_SendString(char *s);
char Uart_RxChar(void);
void Uart_SendHex4(u8 val);
void Uart_SendHex8(u8 val);
void Uart_SendHex20(u32 val);
void Uart_SendHex32(u32 val);
void Uart_SendHex16(u16 val);
void FormatHex(u32 val, char *buf);
void IAP_Disable(void);
u8 IAP_ReadByte(u16 addr);
void IAP_WriteByte(u16 addr, u8 dat);
void IAP_EraseSector(u16 addr);
void Load_Database(void);
void Save_Database(void);
void Add_Sensor_With_Zone(u32 addr, u8 type, u8 zone);
bit Check_Sensor_ID(u32 addr, u8 *out_slot_index);
void UI_ShowClockPage(SystemState state, u8 hour, u8 min);
void UI_ShowPairMenuPage(u8 selected_index);
void UI_ShowPairWaitPage(u8 frame_index);
void UI_ShowPairConfirmPage(u8 type, u8 zone_index);
void UI_ShowPairSuccessPage(void);
void UI_ShowPairFailPage(u8 is_timeout);
void UI_ShowAlarmPage(u8 type, u8 zone_index);
void UI_ShowSosPage(void);
void UI_ShowBindingPage(u32 addr);
void UI_ShowMainMenu(u8 selected_index);
void UI_ShowSetTimePage(u8 hour, u8 min, u8 selection, u8 blink_on);
void Start_Time_Edit(void);
bit EV1527_Decode(u32 *out_addr, u8 *out_data);
void RF_SetMode(u8 mode);
void EV1527_Transmit(u32 addr, u8 dat);
void EventQueue_Init(void);
bit EventQueue_Push(SystemEvent evt);
bit EventQueue_InsertFront(SystemEvent evt);
SystemEvent EventQueue_Pop(void);
bit EventQueue_IsEmpty(void);
void AppManager_Init(void);
void AppManager_StartApp(AppID app_id);
void AppManager_DispatchEvent(SystemEvent evt);
void AppManager_RunActiveApp(void);
AppID AppManager_GetActiveAppID(void);
void Add_Sensor_With_Zone(u32 addr, u8 type, u8 zone);
/* =========================================================================
* 步骤1+4: 事件环形队列 (SystemEvent 环形缓冲区, 深度8)
* ========================================================================= */
#define EVENT_QUEUE_DEPTH 8
static SystemEvent idata event_queue[EVENT_QUEUE_DEPTH];
static u8 queue_head = 0;
static u8 queue_tail = 0;
static u8 queue_count = 0;
void EventQueue_Init(void)
{
queue_head = 0;
queue_tail = 0;
queue_count = 0;
}
bit EventQueue_Push(SystemEvent evt)
{
if (queue_count >= EVENT_QUEUE_DEPTH) return 0;
EA = 0;
event_queue[queue_tail] = evt;
queue_tail = (queue_tail + 1) % EVENT_QUEUE_DEPTH;
queue_count++;
EA = 1;
return 1;
}
bit EventQueue_InsertFront(SystemEvent evt)
{
if (queue_count >= EVENT_QUEUE_DEPTH) return 0;
EA = 0;
queue_head = (queue_head - 1 + EVENT_QUEUE_DEPTH) % EVENT_QUEUE_DEPTH;
event_queue[queue_head] = evt;
queue_count++;
EA = 1;
return 1;
}
SystemEvent EventQueue_Pop(void)
{
SystemEvent evt;
evt.key_event = KEY_EVENT_NONE;
evt.priority = EVENT_PRIORITY_LOW;
evt.extra_data = 0;
evt.extra_size = 0;
if (queue_count == 0) return evt;
EA = 0;
evt = event_queue[queue_head];
queue_head = (queue_head + 1) % EVENT_QUEUE_DEPTH;
queue_count--;
EA = 1;
return evt;
}
bit EventQueue_IsEmpty(void)
{
return (queue_count == 0) ? 1 : 0;
}
bit EventQueue_IsFull(void)
{
return (queue_count >= EVENT_QUEUE_DEPTH) ? 1 : 0;
}
u8 EventQueue_GetCount(void)
{
return queue_count;
}
/* =========================================================================
* 步骤2+5: App 框架与 AppManager
* ========================================================================= */
#define APP_ONRUN_MAX_TICKS 5
static WristbandApp app_registry[APP_ID_MAX];
static WristbandApp *active_app = NULL;
#define DEFAULT_APP_ID APP_ID_CLOCK
/* 回调函数前置声明 */
void ClockApp_OnStart(void);
void ClockApp_onRun(void);
void ClockApp_onClose(void);
EventResult ClockApp_onEvent(SystemEvent *evt);
void MenuApp_OnStart(void);
void MenuApp_onRun(void);
void MenuApp_onClose(void);
EventResult MenuApp_onEvent(SystemEvent *evt);
void PairApp_OnStart(void);
void PairApp_onRun(void);
void PairApp_onClose(void);
EventResult PairApp_onEvent(SystemEvent *evt);
void AlarmApp_OnStart(void);
void AlarmApp_onRun(void);
void AlarmApp_onClose(void);
EventResult AlarmApp_onEvent(SystemEvent *evt);
void SOSApp_OnStart(void);
void SOSApp_onRun(void);
void SOSApp_onClose(void);
EventResult SOSApp_onEvent(SystemEvent *evt);
/* ====== AppManager 核心接口 ====== */
void AppManager_Init(void)
{
u8 i;
/* 清零注册表 */
for (i = 0; i < APP_ID_MAX; i++) {
app_registry[i].app_id = APP_ID_MAX;
app_registry[i].app_type = APP_TYPE_FOREGROUND;
app_registry[i].app_name = "";
app_registry[i].OnStart = NULL;
app_registry[i].onRun = NULL;
app_registry[i].onClose = NULL;
app_registry[i].onEvent = NULL;
app_registry[i].is_active = 0;
app_registry[i].is_running = 0;
}
/* 注册 ClockApp */
app_registry[APP_ID_CLOCK].app_id = APP_ID_CLOCK;
app_registry[APP_ID_CLOCK].app_type = APP_TYPE_FOREGROUND;
app_registry[APP_ID_CLOCK].app_name = "Clock";
app_registry[APP_ID_CLOCK].OnStart = ClockApp_OnStart;
app_registry[APP_ID_CLOCK].onRun = ClockApp_onRun;
app_registry[APP_ID_CLOCK].onClose = ClockApp_onClose;
app_registry[APP_ID_CLOCK].onEvent = ClockApp_onEvent;
/* 注册 MenuApp */
app_registry[APP_ID_MENU].app_id = APP_ID_MENU;
app_registry[APP_ID_MENU].app_type = APP_TYPE_FOREGROUND;
app_registry[APP_ID_MENU].app_name = "Menu";
app_registry[APP_ID_MENU].OnStart = MenuApp_OnStart;
app_registry[APP_ID_MENU].onRun = MenuApp_onRun;
app_registry[APP_ID_MENU].onClose = MenuApp_onClose;
app_registry[APP_ID_MENU].onEvent = MenuApp_onEvent;
/* 注册 PairApp */
app_registry[APP_ID_PAIR].app_id = APP_ID_PAIR;
app_registry[APP_ID_PAIR].app_type = APP_TYPE_FOREGROUND;
app_registry[APP_ID_PAIR].app_name = "Pair";
app_registry[APP_ID_PAIR].OnStart = PairApp_OnStart;
app_registry[APP_ID_PAIR].onRun = PairApp_onRun;
app_registry[APP_ID_PAIR].onClose = PairApp_onClose;
app_registry[APP_ID_PAIR].onEvent = PairApp_onEvent;
/* 注册 AlarmApp */
app_registry[APP_ID_ALARM].app_id = APP_ID_ALARM;
app_registry[APP_ID_ALARM].app_type = APP_TYPE_FOREGROUND;
app_registry[APP_ID_ALARM].app_name = "Alarm";
app_registry[APP_ID_ALARM].OnStart = AlarmApp_OnStart;
app_registry[APP_ID_ALARM].onRun = AlarmApp_onRun;
app_registry[APP_ID_ALARM].onClose = AlarmApp_onClose;
app_registry[APP_ID_ALARM].onEvent = AlarmApp_onEvent;
/* 注册 SOSApp */
app_registry[APP_ID_SOS].app_id = APP_ID_SOS;
app_registry[APP_ID_SOS].app_type = APP_TYPE_FOREGROUND;
app_registry[APP_ID_SOS].app_name = "SOS";
app_registry[APP_ID_SOS].OnStart = SOSApp_OnStart;
app_registry[APP_ID_SOS].onRun = SOSApp_onRun;
app_registry[APP_ID_SOS].onClose = SOSApp_onClose;
app_registry[APP_ID_SOS].onEvent = SOSApp_onEvent;
AppManager_StartApp(DEFAULT_APP_ID);
}
void AppManager_StartApp(AppID app_id)
{
WristbandApp *target;
if (app_id >= APP_ID_MAX) return;
target = &app_registry[app_id];
if (target->OnStart == NULL) return;
if (active_app == target) return;
Uart_SendString("[APP] StartApp: ");
Uart_SendString(target->app_name);
Uart_SendString("\r\n");
/* 关闭当前活跃应用 */
if (active_app != NULL && active_app->onClose != NULL) {
active_app->is_active = 0;
active_app->onClose();
}
/* 切换并启动新应用 */
active_app = target;
if (active_app->OnStart != NULL) {
active_app->is_active = 1;
active_app->is_running = 1;
active_app->OnStart();
}
}
void AppManager_DispatchEvent(SystemEvent evt)
{
EventResult result = EVENT_IGNORED;
if (active_app != NULL && active_app->is_active && active_app->onEvent != NULL) {
result = active_app->onEvent(&evt);
if (result == EVENT_HANDLED) return;
}
}
void AppManager_RunActiveApp(void)
{
u16 start_tick;
u16 elapsed;
if (active_app == NULL || !active_app->is_active) return;
if (!active_app->is_running) return;
if (active_app->onRun == NULL) return;
start_tick = ms_tick;
active_app->onRun();
elapsed = ms_tick - start_tick;
if (elapsed > APP_ONRUN_MAX_TICKS) {
active_app->is_running = 0;
} else {
active_app->is_running = 1;
}
}
AppID AppManager_GetActiveAppID(void)
{
if (active_app == NULL) return APP_ID_MAX;
return active_app->app_id;
}
/* =========================================================================
* ClockApp 回调实现与时间编辑状态机
* ========================================================================= */
static u8 time_edit_active = 0;
static u8 time_edit_field = 1;
static u8 time_edit_blink_on = 1;
void Start_Time_Edit(void)
{
time_edit_active = 1;
time_edit_field = 1;
time_edit_blink_on = 1;
current_state = STATE_SET_TIME;
// FCOB 幻彩灯条蓝色常亮 (R=0, G=0, B=180)
RGB_Send(0, 0, 180, 0, 0, 180);
// 马达短振 50ms
MOTOR = 1;
Delay_ms(50);
MOTOR = 0;
UI_ShowSetTimePage(current_hour, current_min, time_edit_field, 1);
}
void ClockApp_OnStart(void)
{
time_edit_active = 0;
current_state = STATE_NORMAL;
RF_SetMode(1);
UI_ShowClockPage(current_state, current_hour, current_min);
// 启动时熄灭灯条
RGB_Send(0, 0, 0, 0, 0, 0);
Uart_SendString("[ClockApp] Started\r\n");
}
void ClockApp_onRun(void)
{
u32 rx_addr;
u8 rx_data;
if (time_edit_active)
{
// 500ms 周期控制闪烁
u8 blink = (ms_tick / 500) % 2;
if (blink != time_edit_blink_on)
{
time_edit_blink_on = blink;
UI_ShowSetTimePage(current_hour, current_min, time_edit_field, time_edit_blink_on);
}
}
else
{
if (clock_updated) {
clock_updated = 0;
UI_ShowClockPage(current_state, current_hour, current_min);
}
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) {
if (sensor_list[slot].zone == 0 || current_state == STATE_ARMED) {
SystemEvent rf_evt;
rf_evt.key_event = KEY_EVENT_NONE;
rf_evt.priority = EVENT_PRIORITY_HIGH;
rf_evt.extra_data = rx_addr;
rf_evt.extra_size = 4;
EventQueue_InsertFront(rf_evt);
}
}
}
}
}
}
void ClockApp_onClose(void)
{
time_edit_active = 0;
RGB_Send(0, 0, 0, 0, 0, 0);
Uart_SendString("[ClockApp] Closed\r\n");
}
EventResult ClockApp_onEvent(SystemEvent *evt)
{
if (time_edit_active)
{
if (evt->key_event == KEY_EVENT_UP_CLICK)
{
if (time_edit_field == 1) {
current_hour = (current_hour + 1) % 24;
} else {
current_min = (current_min + 1) % 60;
}
UI_ShowSetTimePage(current_hour, current_min, time_edit_field, 1);
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_DOWN_CLICK)
{
if (time_edit_field == 1) {
current_hour = (current_hour == 0) ? 23 : (current_hour - 1);
} else {
current_min = (current_min == 0) ? 59 : (current_min - 1);
}
UI_ShowSetTimePage(current_hour, current_min, time_edit_field, 1);
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_CONFIRM_LONG)
{
// 长按 Confirm 键跳转字段
if (time_edit_field == 1) {
time_edit_field = 2;
} else {
time_edit_field = 1;
}
UI_ShowSetTimePage(current_hour, current_min, time_edit_field, 1);
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_CONFIRM_CLICK)
{
// 短按 Confirm 键保存退出
time_edit_active = 0;
current_state = STATE_NORMAL;
RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭
// 退出短振
MOTOR = 1; Delay_ms(50); MOTOR = 0;
UI_ShowClockPage(current_state, current_hour, current_min);
return EVENT_HANDLED;
}
return EVENT_HANDLED; // 吞掉其他按键
}
if (evt->key_event == KEY_EVENT_CONFIRM_LONG) {
AppManager_StartApp(APP_ID_MENU);
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_UP_LONG) {
if (current_state == STATE_ARMED) {
current_state = STATE_DISARMED;
} else {
current_state = STATE_ARMED;
}
// 切换状态短震确认
MOTOR = 1; Delay_ms(100); MOTOR = 0;
UI_ShowClockPage(current_state, current_hour, current_min);
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_SOS_CLICK || evt->key_event == KEY_EVENT_SOS_LONG) {
AppManager_StartApp(APP_ID_SOS);
return EVENT_HANDLED;
}
return EVENT_IGNORED;
}
/* =========================================================================
* MenuApp 回调实现
* ========================================================================= */
static u8 menu_level = 0; // 0:主菜单, 1:传感器对码二级菜单, 2:IPC绑定中
static u16 last_bind_tx_ms = 0;
void MenuApp_OnStart(void)
{
menu_level = 0;
menu_select = 0;
current_state = STATE_PAIR_MENU;
RF_SetMode(0);
UI_ShowMainMenu(menu_select);
RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭灯条
Uart_SendString("[MenuApp] Started (Main Menu)\r\n");
}
void MenuApp_onRun(void)
{
if (menu_level == 2)
{
// 绑定中:控制青色呼吸灯 (周期1000ms双灯同步)
u16 breath = ms_tick % 1000;
u8 val = (breath < 500) ? (breath * 2 / 5) : ((1000 - breath) * 2 / 5); // 范围 0~200
RGB_Send(0, val, val, 0, val, val);
// 每 500ms 发射一次绑定信号 (出厂 Factory ID)
if (ms_tick - last_bind_tx_ms >= 500 || ms_tick < last_bind_tx_ms)
{
last_bind_tx_ms = ms_tick;
RF_SetMode(2);
EV1527_Transmit(CLONED_ADDR, 0x01);
RF_SetMode(0);
}
}
}
void MenuApp_onClose(void)
{
menu_level = 0;
RGB_Send(0, 0, 0, 0, 0, 0);
RF_SetMode(1);
Uart_SendString("[MenuApp] Closed\r\n");
}
EventResult MenuApp_onEvent(SystemEvent *evt)
{
if (menu_level == 0)
{
// 主菜单层 (0-HORLOGE, 1-APPAIRAGE, 2-LIAISON)
if (evt->key_event == KEY_EVENT_UP_CLICK) {
if (menu_select > 0) {
menu_select--;
UI_ShowMainMenu(menu_select);
}
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_DOWN_CLICK) {
if (menu_select < 2) {
menu_select++;
UI_ShowMainMenu(menu_select);
}
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_CONFIRM_CLICK) {
if (menu_select == 0) {
// 启动时钟 App 并立即进入编辑模式
AppManager_StartApp(APP_ID_CLOCK);
Start_Time_Edit();
} else if (menu_select == 1) {
// 进入二级传感器对码菜单
menu_level = 1;
menu_select = 0;
UI_ShowPairMenuPage(menu_select);
} else if (menu_select == 2) {
// 进入摄像机绑定
menu_level = 2;
last_bind_tx_ms = ms_tick;
current_state = STATE_IPC_BIND;
UI_ShowBindingPage(CLONED_ADDR);
}
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_CONFIRM_LONG) {
AppManager_StartApp(APP_ID_CLOCK);
return EVENT_HANDLED;
}
}
else if (menu_level == 1)
{
// 传感器对码选择菜单 (0-门磁, 1-PIR, 2-烟感, 3-气感, 4-水浸)
if (evt->key_event == KEY_EVENT_UP_CLICK) {
if (menu_select > 0) {
menu_select--;
UI_ShowPairMenuPage(menu_select);
}
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_DOWN_CLICK) {
if (menu_select < 4) {
menu_select++;
UI_ShowPairMenuPage(menu_select);
}
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_CONFIRM_CLICK) {
// 保存当前选定的类型到 captured_type短震 100ms 并跳转到 PairApp 对码搜索
captured_type = menu_select;
MOTOR = 1;
Delay_ms(100);
MOTOR = 0;
AppManager_StartApp(APP_ID_PAIR);
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_DOWN_LONG) {
// 返回主菜单
menu_level = 0;
menu_select = 1;
UI_ShowMainMenu(menu_select);
return EVENT_HANDLED;
}
}
else if (menu_level == 2)
{
// 绑定中:长按 Confirm 键 (3秒) 或 长按 DOWN 键 (2秒) 退出返回主菜单
if (evt->key_event == KEY_EVENT_CONFIRM_LONG || evt->key_event == KEY_EVENT_DOWN_LONG) {
menu_level = 0;
menu_select = 2;
current_state = STATE_PAIR_MENU;
RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭
UI_ShowMainMenu(menu_select);
return EVENT_HANDLED;
}
}
return EVENT_IGNORED;
}
/* =========================================================================
* PairApp 回调实现
* ========================================================================= */
enum {
PAIR_STATE_WAIT,
PAIR_STATE_CONFIRM,
PAIR_STATE_SUCCESS,
PAIR_STATE_FAIL
};
static u8 pair_state;
static u8 last_radar_sec;
static u8 captured_zone = 1;
static u32 success_start_ms = 0;
static u32 fail_start_ms = 0;
void PairApp_OnStart(void)
{
pair_state = PAIR_STATE_WAIT;
last_radar_sec = 99;
current_state = STATE_PAIR_WAIT;
pair_timeout_ms = 0;
captured_zone = 1;
RF_SetMode(1);
// 初始化对码搜寻状态显示
UI_ShowPairWaitPage(0);
// 快闪白色 RGB (双灯)
RGB_Send(150, 150, 150, 150, 150, 150);
Uart_SendString("[PairApp] Started (WAIT)\r\n");
}
void PairApp_onRun(void)
{
u32 rx_addr;
u8 rx_data;
u8 flash;
u8 frame;
if (pair_state == PAIR_STATE_WAIT) {
// 白色 3Hz 快闪 (周期 333ms, 亮 166ms)
flash = (ms_tick / 166) % 2;
if (flash) RGB_Send(150, 150, 150, 150, 150, 150);
else RGB_Send(0, 0, 0, 0, 0, 0);
frame = (u8)((ms_tick / 250) % 3);
if (frame != last_radar_sec) {
last_radar_sec = frame;
UI_ShowPairWaitPage(frame);
}
if (pair_timeout_ms >= 30000UL) {
pair_timeout_ms = 0;
pair_state = PAIR_STATE_FAIL;
current_state = STATE_PAIR_FAIL;
fail_start_ms = ms_tick;
UI_ShowPairFailPage(1);
// 红色常亮
RGB_Send(200, 0, 0, 200, 0, 0);
Uart_SendString("[PairApp] Timeout\r\n");
return;
}
if (EV1527_Decode(&rx_addr, &rx_data)) {
// 排除 SOS 数据码 (0x08 代表 SOS 求救,不用于常规传感器配对)
if (rx_data != 0x08)
{
captured_addr = rx_addr;
pair_state = PAIR_STATE_CONFIRM;
current_state = STATE_PAIR_CONFIRM;
RF_SetMode(0);
captured_zone = 1; // 初始为防区 1
UI_ShowPairConfirmPage(captured_type, captured_zone);
Uart_SendString("[PairApp] Sensor captured\r\n");
}
}
} else if (pair_state == PAIR_STATE_CONFIRM) {
// 橙色慢闪 (1.5Hz, 周期 666ms)
u8 flash = (ms_tick / 333) % 2;
if (flash) RGB_Send(100, 180, 0, 100, 180, 0); // 橙色
else RGB_Send(0, 0, 0, 0, 0, 0);
} else if (pair_state == PAIR_STATE_SUCCESS) {
// 2 秒后自动返回二级菜单
if (ms_tick - success_start_ms >= 2000 || ms_tick < success_start_ms)
{
AppManager_StartApp(APP_ID_MENU);
}
} else if (pair_state == PAIR_STATE_FAIL) {
// 5 秒后自动返回二级菜单
if (ms_tick - fail_start_ms >= 5000 || ms_tick < fail_start_ms)
{
AppManager_StartApp(APP_ID_MENU);
}
}
}
void PairApp_onClose(void)
{
RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯光
MOTOR = 0;
RF_SetMode(1);
Uart_SendString("[PairApp] Closed\r\n");
}
EventResult PairApp_onEvent(SystemEvent *evt)
{
if (pair_state == PAIR_STATE_CONFIRM) {
if (evt->key_event == KEY_EVENT_UP_CLICK) {
if (captured_zone < 99) {
captured_zone++;
UI_ShowPairConfirmPage(captured_type, captured_zone);
}
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_DOWN_CLICK) {
if (captured_zone > 1) {
captured_zone--;
UI_ShowPairConfirmPage(captured_type, captured_zone);
}
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_CONFIRM_CLICK) {
// 保存防区配对并显示成功反馈
Add_Sensor_With_Zone(captured_addr, captured_type, captured_zone);
pair_state = PAIR_STATE_SUCCESS;
current_state = STATE_PAIR_SUCCESS;
success_start_ms = ms_tick;
UI_ShowPairSuccessPage();
// 绿色常亮且马达长振 400ms
RGB_Send(0, 200, 0, 0, 200, 0);
MOTOR = 1;
Delay_ms(400);
MOTOR = 0;
return EVENT_HANDLED;
}
if (evt->key_event == KEY_EVENT_CONFIRM_LONG) {
// 长按 Confirm 取消返回主菜单
RGB_Send(0, 0, 0, 0, 0, 0);
AppManager_StartApp(APP_ID_MENU);
return EVENT_HANDLED;
}
}
// 搜寻状态或确认状态下长按 DOWN 退出
if (evt->key_event == KEY_EVENT_DOWN_LONG) {
RGB_Send(0, 0, 0, 0, 0, 0);
AppManager_StartApp(APP_ID_MENU);
return EVENT_HANDLED;
}
return EVENT_IGNORED;
}
/* =========================================================================
* AlarmApp 回调实现
* ========================================================================= */
void AlarmApp_OnStart(void)
{
u8 slot;
u8 type = 0;
u8 zone = 1;
current_state = STATE_ALARM;
alarm_timer_ms = 0;
alarm_flash_flag = 0;
if (Check_Sensor_ID(captured_addr, &slot)) {
type = sensor_list[slot].type;
zone = sensor_list[slot].zone;
alarm_sensor_slot = slot;
}
// 初始化显示警报界面
UI_ShowAlarmPage(type, zone);
// 首次亮起防区对应颜色
if (type == 0) RGB_Send(0, 180, 0, 0, 180, 0); // 门磁: 红色 (R=180)
else if (type == 1) RGB_Send(100, 180, 0, 100, 180, 0); // PIR: 黄色
else if (type == 2) RGB_Send(180, 0, 0, 180, 0, 0); // 烟感: 绿色 (G=180)
else if (type == 3) RGB_Send(0, 180, 180, 0, 180, 180); // 气感: 紫色
else RGB_Send(0, 0, 180, 0, 0, 180); // 水浸: 蓝色
Uart_SendString("[AlarmApp] Started\r\n");
}
void AlarmApp_onRun(void)
{
u8 type = sensor_list[alarm_sensor_slot].type;
u8 flash = (ms_tick / 250) % 2; // 250ms 亮灭周期
// 同步控制 RGB 幻彩灯闪烁
if (flash)
{
if (type == 0) RGB_Send(0, 180, 0, 0, 180, 0);
else if (type == 1) RGB_Send(100, 180, 0, 100, 180, 0);
else if (type == 2) RGB_Send(180, 0, 0, 180, 0, 0);
else if (type == 3) RGB_Send(0, 180, 180, 0, 180, 180);
else RGB_Send(0, 0, 180, 0, 0, 180);
}
else
{
RGB_Send(0, 0, 0, 0, 0, 0);
}
// Truly AMOLED 粗红框闪烁
if (alarm_flash_flag) {
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
LCD_DrawRectBorder(6, 6, 108, 228, COLOR_RED);
} else {
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_BLACK);
LCD_DrawRectBorder(6, 6, 108, 228, COLOR_BLACK);
}
}
void AlarmApp_onClose(void)
{
RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯条
MOTOR = 0;
current_state = STATE_NORMAL;
Uart_SendString("[AlarmApp] Closed\r\n");
}
EventResult AlarmApp_onEvent(SystemEvent *evt)
{
// 任意键按下或长按 ➔ 清除报警返回时钟
if (evt->key_event != KEY_EVENT_NONE) {
AppManager_StartApp(APP_ID_CLOCK);
return EVENT_HANDLED;
}
return EVENT_IGNORED;
}
/* =========================================================================
* SOSApp 回调实现
* ========================================================================= */
static u8 sos_last_phase;
void SOSApp_OnStart(void)
{
current_state = STATE_SOS_EMITTED;
sos_last_phase = 99;
alarm_timer_ms = 0;
UI_ShowSosPage();
// 立即启动双灯交替爆闪
RGB_Send(0, 200, 0, 0, 0, 0);
Uart_SendString("[SOSApp] Started\r\n");
}
void SOSApp_onRun(void)
{
// 每 500ms 发射一次求救射频信号 (CLONED_ADDR 伴随 SOS 数据码 0x08)
u8 sec_phase = (u8)((ms_tick / 500) % 2);
if (sec_phase != sos_last_phase) {
sos_last_phase = sec_phase;
RF_SetMode(2);
EV1527_Transmit(CLONED_ADDR, 0x08);
RF_SetMode(0);
}
// 50ms 周期控制双灯交替红色闪烁
{
u8 flash = (ms_tick / 50) % 2;
if (flash) {
RGB_Send(0, 200, 0, 0, 0, 0); // 灯1亮红灯2灭
} else {
RGB_Send(0, 0, 0, 0, 200, 0); // 灯1灭灯2亮红
}
}
// Truly AMOLED 边框闪烁 (红粗边框 + 白细边框)
if (alarm_flash_flag) {
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
LCD_DrawRectBorder(5, 5, 110, 230, COLOR_RED);
LCD_DrawRectBorder(8, 8, 104, 224, COLOR_WHITE);
} else {
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_BLACK);
LCD_DrawRectBorder(5, 5, 110, 230, COLOR_BLACK);
LCD_DrawRectBorder(8, 8, 104, 224, COLOR_BLACK);
}
}
void SOSApp_onClose(void)
{
RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯条
MOTOR = 0;
current_state = STATE_NORMAL;
Uart_SendString("[SOSApp] Closed\r\n");
}
EventResult SOSApp_onEvent(SystemEvent *evt)
{
// 短按或长按任意键 ➔ 退出求救状态返回待机时钟
if (evt->key_event != KEY_EVENT_NONE) {
AppManager_StartApp(APP_ID_CLOCK);
return EVENT_HANDLED;
}
return EVENT_IGNORED;
}
/* =========================================================================
* 延时与时间获取辅助函数
* ========================================================================= */
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;
}
/* =========================================================================
* STC32G 闪存 IAP (EEPROM) 读写
* ========================================================================= */
#define IAP_CMD_READ 1
#define IAP_CMD_WRITE 2
#define IAP_CMD_ERASE 3
void IAP_Disable(void)
{
IAP_CONTR = 0;
IAP_CMD = 0;
IAP_TRIG = 0;
IAP_ADDRH = 0xff;
IAP_ADDRL = 0xff;
}
u8 IAP_ReadByte(u16 addr)
{
IAP_CONTR = 0x80;
IAP_TPS = (u8)(MAIN_Fosc / 1000000UL);
IAP_CMD = IAP_CMD_READ;
IAP_ADDRL = (u8)addr;
IAP_ADDRH = (u8)(addr >> 8);
IAP_ADDRE = 0;
IAP_TRIG = 0x5a;
IAP_TRIG = 0xa5;
_nop_(); _nop_(); _nop_(); _nop_();
IAP_Disable();
return IAP_DATA;
}
void IAP_WriteByte(u16 addr, u8 dat)
{
IAP_CONTR = 0x80;
IAP_TPS = (u8)(MAIN_Fosc / 1000000UL);
IAP_CMD = IAP_CMD_WRITE;
IAP_ADDRL = (u8)addr;
IAP_ADDRH = (u8)(addr >> 8);
IAP_ADDRE = 0;
IAP_DATA = dat;
IAP_TRIG = 0x5a;
IAP_TRIG = 0xa5;
_nop_(); _nop_(); _nop_(); _nop_();
IAP_Disable();
}
void IAP_EraseSector(u16 addr)
{
IAP_CONTR = 0x80;
IAP_TPS = (u8)(MAIN_Fosc / 1000000UL);
IAP_CMD = IAP_CMD_ERASE;
IAP_ADDRL = (u8)addr;
IAP_ADDRH = (u8)(addr >> 8);
IAP_ADDRE = 0;
IAP_TRIG = 0x5a;
IAP_TRIG = 0xa5;
_nop_(); _nop_(); _nop_(); _nop_();
IAP_Disable();
}
void Load_Database(void)
{
u16 addr = 0;
u8 *ptr = (u8 *)&sensor_list;
u16 size = sizeof(sensor_list);
u16 i;
for (i = 0; i < size; i++) {
ptr[i] = IAP_ReadByte(addr++);
}
}
void Save_Database(void)
{
u16 addr = 0;
u8 *ptr = (u8 *)&sensor_list;
u16 size = sizeof(sensor_list);
u16 i;
IAP_EraseSector(0);
for (i = 0; i < size; i++) {
IAP_WriteByte(addr++, ptr[i]);
}
}
void Add_Sensor_With_Zone(u32 addr, u8 type, u8 zone)
{
u8 i;
u8 slot_to_use = 15;
for (i = 0; i < 16; i++) {
if (sensor_list[i].is_used == 0x01) {
u32 existing_addr = ((u32)sensor_list[i].addr[0] << 16) |
((u32)sensor_list[i].addr[1] << 8) |
sensor_list[i].addr[2];
if (existing_addr == addr) {
slot_to_use = i;
break;
}
} else {
slot_to_use = i;
break;
}
}
sensor_list[slot_to_use].is_used = 0x01;
sensor_list[slot_to_use].addr[0] = (u8)(addr >> 16);
sensor_list[slot_to_use].addr[1] = (u8)(addr >> 8);
sensor_list[slot_to_use].addr[2] = (u8)addr;
sensor_list[slot_to_use].type = type;
sensor_list[slot_to_use].zone = zone;
for (i = 0; i < 15; i++) {
sensor_list[slot_to_use].name_gbk[i] = sensor_names_fr[type][i];
if (sensor_names_fr[type][i] == '\0') break;
}
sensor_list[slot_to_use].name_gbk[15] = '\0';
Save_Database();
}
bit Check_Sensor_ID(u32 addr, u8 *out_slot_index)
{
u8 i;
for (i = 0; i < 16; i++) {
if (sensor_list[i].is_used == 0x01) {
u32 slot_addr = ((u32)sensor_list[i].addr[0] << 16) |
((u32)sensor_list[i].addr[1] << 8) |
sensor_list[i].addr[2];
if (slot_addr == addr) {
*out_slot_index = i;
return 1;
}
}
}
return 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';
}
/* =========================================================================
* UI 页面渲染函数
* ========================================================================= */
void UI_ShowClockPage(SystemState state, u8 hour, u8 min)
{
char time_str[6];
LCD_Clear(COLOR_BLACK);
// 左上角锁图标
if (state == STATE_ARMED)
{
LCD_DrawRectBorder(10, 23, 20, 15, COLOR_RED);
LCD_FillRect(15, 15, 2, 8, COLOR_RED);
LCD_FillRect(17, 15, 10, 2, COLOR_RED);
LCD_FillRect(25, 15, 2, 8, COLOR_RED);
}
else
{
LCD_DrawRectBorder(10, 23, 20, 15, COLOR_GREEN);
LCD_FillRect(15, 15, 2, 8, COLOR_GREEN);
LCD_FillRect(17, 15, 8, 2, COLOR_GREEN);
LCD_FillRect(25, 17, 2, 6, COLOR_GREEN);
}
// 右上角电量
LCD_ShowString(62, 24, "85%", COLOR_GRAY, COLOR_BLACK);
LCD_DrawMonoBitmap(92, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK);
// 中间时间
time_str[0] = '0' + (hour / 10);
time_str[1] = '0' + (hour % 10);
time_str[2] = ':';
time_str[3] = '0' + (min / 10);
time_str[4] = '0' + (min % 10);
time_str[5] = '\0';
LCD_ShowString16x32Centered(100, time_str, COLOR_WHITE, COLOR_BLACK);
// AM/PM
LCD_ShowStringCentered(150, "AM", COLOR_WHITE, COLOR_BLACK);
// 日期 (法语)
LCD_ShowStringCentered(195, "VEN 10-07", COLOR_GRAY, COLOR_BLACK);
}
void UI_ShowPairMenuPage(u8 selected_index)
{
LCD_Clear(COLOR_BLACK);
LCD_ShowStringCentered(30, "MENU APPAI.", COLOR_AMBER, COLOR_BLACK);
if (selected_index > 0) {
LCD_ShowStringCentered(78, menu_items[selected_index - 1], COLOR_GRAY, COLOR_BLACK);
}
LCD_DrawRectBorder(8, 110, 104, 28, COLOR_CYAN);
LCD_ShowStringCentered(118, menu_items[selected_index], COLOR_CYAN, COLOR_BLACK);
if (selected_index < 4) {
LCD_ShowStringCentered(158, menu_items[selected_index + 1], COLOR_GRAY, COLOR_BLACK);
}
}
void UI_ShowPairWaitPage(u8 frame_index)
{
LCD_Clear(COLOR_BLACK);
LCD_ShowStringCentered(35, "APPAIRAGE", COLOR_GRAY, COLOR_BLACK);
if (frame_index == 0)
LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_radar_32x32_f1, COLOR_CYAN, COLOR_BLACK);
else if (frame_index == 1)
LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_radar_32x32_f2, COLOR_CYAN, COLOR_BLACK);
else
LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_radar_32x32_f3, COLOR_CYAN, COLOR_BLACK);
LCD_ShowStringCentered(165, "RECHERCHE", COLOR_WHITE, COLOR_BLACK);
LCD_ShowStringCentered(200, "DECLENCH. CAPT", COLOR_CYAN, COLOR_BLACK);
}
void UI_ShowPairConfirmPage(u8 type, u8 zone_index)
{
char zone_str[3];
LCD_Clear(COLOR_BLACK);
LCD_ShowStringCentered(32, "v RECU", COLOR_GREEN, COLOR_BLACK);
if (type == 0) {
LCD_DrawMonoBitmap(44, 55, 32, 32, bmp_door_32x32, COLOR_WHITE, COLOR_BLACK);
} else if (type == 2 || type == 3) {
LCD_DrawMonoBitmap(44, 55, 32, 32, bmp_siren_32x32, COLOR_WHITE, COLOR_BLACK);
} else {
LCD_DrawMonoBitmap(44, 55, 32, 32, bmp_confirm_lock_32x32, COLOR_WHITE, COLOR_BLACK);
}
LCD_ShowStringCentered(105, sensor_names_fr[type], COLOR_WHITE, COLOR_BLACK);
LCD_DrawRectBorder(15, 140, 90, 44, COLOR_CYAN);
zone_str[0] = '0' + (zone_index / 10);
zone_str[1] = '0' + (zone_index % 10);
zone_str[2] = '\0';
LCD_ShowString16x32(44, 146, zone_str, COLOR_CYAN, COLOR_BLACK);
LCD_ShowStringCentered(215, "CONFIRMER", COLOR_GRAY, COLOR_BLACK);
}
void UI_ShowPairSuccessPage(void)
{
LCD_Clear(COLOR_BLACK);
LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_checkmark_32x32, COLOR_GREEN, COLOR_BLACK);
LCD_ShowStringCentered(145, "SUCCES", COLOR_GREEN, COLOR_BLACK);
LCD_ShowStringCentered(180, "ENREGISTRE", COLOR_GRAY, COLOR_BLACK);
}
void UI_ShowPairFailPage(u8 is_timeout)
{
LCD_Clear(COLOR_BLACK);
LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_cross_32x32, COLOR_RED, COLOR_BLACK);
LCD_ShowStringCentered(145, "ECHEC", COLOR_RED, COLOR_BLACK);
if (is_timeout) {
LCD_ShowStringCentered(180, "DELAI DEPASSE", COLOR_GRAY, COLOR_BLACK);
} else {
LCD_ShowStringCentered(180, "TYPE INCORRECT", COLOR_GRAY, COLOR_BLACK);
}
}
void UI_ShowAlarmPage(u8 type, u8 zone_index)
{
char zone_info[20];
LCD_Clear(COLOR_BLACK);
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
LCD_DrawRectBorder(6, 6, 108, 228, COLOR_RED);
if (type == 0) {
LCD_DrawMonoBitmap(44, 50, 32, 32, bmp_door_32x32, COLOR_RED, COLOR_BLACK);
} else if (type == 2 || type == 3) {
LCD_DrawMonoBitmap(44, 50, 32, 32, bmp_siren_32x32, COLOR_RED, COLOR_BLACK);
} else {
LCD_DrawMonoBitmap(44, 50, 32, 32, bmp_confirm_lock_32x32, COLOR_RED, COLOR_BLACK);
}
{
u8 len = 0;
char *p = sensor_names_fr[type];
while(*p) {
zone_info[len++] = *p++;
}
zone_info[len++] = ' ';
zone_info[len++] = '0' + (zone_index / 10);
zone_info[len++] = '0' + (zone_index % 10);
zone_info[len] = '\0';
}
LCD_ShowStringCentered(135, zone_info, COLOR_RED, COLOR_BLACK);
LCD_ShowStringCentered(175, "INTRUSION!", COLOR_WHITE, COLOR_BLACK);
}
void UI_ShowSosPage(void)
{
LCD_Clear(COLOR_BLACK);
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
LCD_DrawRectBorder(5, 5, 110, 230, COLOR_RED);
LCD_DrawRectBorder(8, 8, 104, 224, COLOR_WHITE);
// 警灯组合绘制
LCD_FillRect(44, 45, 32, 2, COLOR_RED);
LCD_FillRect(48, 47, 24, 12, COLOR_RED);
LCD_FillRect(40, 59, 40, 4, COLOR_GRAY);
LCD_FillRect(57, 51, 6, 6, COLOR_WHITE);
LCD_FillRect(58, 36, 4, 6, COLOR_RED);
LCD_FillRect(46, 38, 4, 4, COLOR_RED);
LCD_FillRect(70, 38, 4, 4, COLOR_RED);
LCD_ShowStringCentered(120, "SOS", COLOR_RED, COLOR_BLACK);
LCD_ShowStringCentered(150, "ENVOI", COLOR_RED, COLOR_BLACK);
LCD_ShowStringCentered(195, "APPEL...", COLOR_WHITE, COLOR_BLACK);
}
void UI_ShowBindingPage(u32 addr)
{
char id_str[20];
LCD_Clear(COLOR_BLACK);
// 摄像机组合绘制
LCD_DrawRectBorder(36, 46, 32, 26, COLOR_CYAN);
LCD_FillRect(44, 56, 6, 6, COLOR_CYAN);
LCD_FillRect(68, 48, 2, 22, COLOR_CYAN);
LCD_FillRect(70, 50, 2, 18, COLOR_CYAN);
LCD_FillRect(72, 52, 2, 14, COLOR_CYAN);
LCD_ShowStringCentered(120, "LIAISON", COLOR_WHITE, COLOR_BLACK);
LCD_ShowStringCentered(155, "ENVOI EN COURS", COLOR_CYAN, COLOR_BLACK);
id_str[0] = 'I'; id_str[1] = 'D'; id_str[2] = ':'; id_str[3] = ' ';
id_str[4] = '0'; id_str[5] = 'x';
{
u8 val1 = (u8)(addr >> 16);
u8 val2 = (u8)(addr >> 8);
u8 val3 = (u8)addr;
u8 nibble = val1 >> 4;
id_str[6] = (nibble < 10) ? ('0' + nibble) : ('A' + (nibble - 10));
nibble = val1 & 0x0F;
id_str[7] = (nibble < 10) ? ('0' + nibble) : ('A' + (nibble - 10));
nibble = val2 >> 4;
id_str[8] = (nibble < 10) ? ('0' + nibble) : ('A' + (nibble - 10));
nibble = val2 & 0x0F;
id_str[9] = (nibble < 10) ? ('0' + nibble) : ('A' + (nibble - 10));
nibble = val3 >> 4;
id_str[10] = (nibble < 10) ? ('0' + nibble) : ('A' + (nibble - 10));
nibble = val3 & 0x0F;
id_str[11] = (nibble < 10) ? ('0' + nibble) : ('A' + (nibble - 10));
id_str[12] = '\0';
}
LCD_ShowStringCentered(205, id_str, COLOR_GRAY, COLOR_BLACK);
}
void UI_ShowMainMenu(u8 selected_index)
{
LCD_Clear(COLOR_BLACK);
LCD_FillRect(58, 20, 4, 2, COLOR_GRAY);
LCD_FillRect(56, 22, 8, 2, COLOR_GRAY);
LCD_FillRect(54, 24, 12, 2, COLOR_GRAY);
if (selected_index == 0)
{
LCD_DrawRectBorder(44, 70, 32, 32, COLOR_CYAN);
LCD_FillRect(59, 76, 2, 10, COLOR_CYAN);
LCD_FillRect(59, 85, 8, 2, COLOR_CYAN);
LCD_ShowStringCentered(150, "1. HORLOGE", COLOR_WHITE, COLOR_BLACK);
}
else if (selected_index == 1)
{
LCD_DrawMonoBitmap(44, 70, 32, 32, bmp_radar_32x32_f3, COLOR_CYAN, COLOR_BLACK);
LCD_ShowStringCentered(150, "2. APPAIRAGE", COLOR_WHITE, COLOR_BLACK);
}
else if (selected_index == 2)
{
LCD_DrawRectBorder(44, 74, 20, 16, COLOR_CYAN);
LCD_FillRect(52, 80, 4, 4, COLOR_CYAN);
LCD_FillRect(64, 76, 2, 12, COLOR_CYAN);
LCD_FillRect(66, 78, 2, 8, COLOR_CYAN);
LCD_FillRect(68, 80, 2, 4, COLOR_CYAN);
LCD_ShowStringCentered(150, "3. LIAISON", COLOR_WHITE, COLOR_BLACK);
}
LCD_FillRect(54, 210, 12, 2, COLOR_GRAY);
LCD_FillRect(56, 212, 8, 2, COLOR_GRAY);
LCD_FillRect(58, 214, 4, 2, COLOR_GRAY);
}
void UI_ShowSetTimePage(u8 hour, u8 min, u8 selection, u8 blink_on)
{
char time_str[6];
LCD_Clear(COLOR_BLACK);
LCD_DrawRectBorder(10, 23, 20, 15, COLOR_GREEN);
LCD_FillRect(15, 15, 2, 8, COLOR_GREEN);
LCD_FillRect(17, 15, 8, 2, COLOR_GREEN);
LCD_FillRect(25, 17, 2, 6, COLOR_GREEN);
LCD_ShowString(62, 24, "85%", COLOR_GRAY, COLOR_BLACK);
LCD_DrawMonoBitmap(92, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK);
if (selection == 1 && !blink_on)
{
time_str[0] = ' ';
time_str[1] = ' ';
}
else
{
time_str[0] = '0' + (hour / 10);
time_str[1] = '0' + (hour % 10);
}
time_str[2] = '\0';
LCD_ShowString16x32(12, 100, time_str, COLOR_CYAN, COLOR_BLACK);
LCD_ShowString16x32(48, 98, ":", COLOR_WHITE, COLOR_BLACK);
if (selection == 2 && !blink_on)
{
time_str[0] = ' ';
time_str[1] = ' ';
}
else
{
time_str[0] = '0' + (min / 10);
time_str[1] = '0' + (min % 10);
}
time_str[2] = '\0';
LCD_ShowString16x32(64, 100, time_str, COLOR_CYAN, COLOR_BLACK);
LCD_ShowStringCentered(150, "AM", COLOR_WHITE, COLOR_BLACK);
LCD_ShowStringCentered(195, "VEN 10-07", COLOR_GRAY, COLOR_BLACK);
}
void Wakeup_Restore(void);
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 为 低电平触发 唤醒
// PxIM1=1, PxIM0=0 → 低电平触发
P0IM1 |= 0x0E;
P0IM0 &= ~0x0E;
P0INTE |= 0x0E;
// 8. 配置 P2.6 (KEY_SOS) 为 低电平触发 唤醒
P2IM1 |= 0x40;
P2IM0 &= ~0x40;
P2INTE |= 0x40;
// ===== 关键新增:配置端口掉电唤醒使能寄存器 PxWKUE =====
// STC32G 在进入 Power-Down (STOP) 模式后,除了开启端口中断外,
// 必须使能对应的 PxWKUE 寄存器,硬件唤醒通路才能生效!
P0WKUE |= 0x0E; // 使能 P0.1/P0.2/P0.3 掉电唤醒
P2WKUE |= 0x40; // 使能 P2.6 掉电唤醒
// 9. 不允许外部中断 2 (RF_RX_DATA) 沿触发唤醒源
EX2 = 0;
// 10. 清除端口中断的悬挂/标志位,防误触
P0INTF = 0x00;
P2INTF = 0x00;
// 11. 关闭 Timer1 中断,保持 EA=1 让端口中断能唤醒
ET1 = 0;
EA = 1;
// 12. 写入 PCON 掉电模式位,使 MCU 进入深度 Power-Down 挂起状态
// CPU 在此停止,直到端口低电平中断唤醒
Uart_SendString("[SYS] Entering Power-Down mode...\r\n");
PCON |= 0x02; // PD = 1
_nop_();
_nop_();
_nop_();
_nop_();
// 13. 唤醒并执行完 ISR 后CPU 从这里继续执行
Wakeup_Restore();
}
void Wakeup_Restore(void)
{
u8 p0_flag;
u8 p2_flag;
// 确保 EAXFR=1 才能访问扩展 SFR (P0INTF/P2INTF/P0INTE/P2INTE)
EAXFR = 1;
// [诊断] 在清除标志位之前先读取,判断唤醒来源
p0_flag = P0INTF;
p2_flag = P2INTF;
// 1. 立即禁用端口中断和唤醒,防止继续触发
P0INTE = 0x00;
P2INTE = 0x00;
P0WKUE = 0x00; // 清除 P0 唤醒允许
P2WKUE = 0x00; // 清除 P2 唤醒允许
P0INTF = 0x00;
P2INTF = 0x00;
EX2 = 0;
// 1b. 关闭掉电唤醒定时器
WKTCH = 0x00;
WKTCL = 0x00;
// 2. 重新开启全局中断与 Timer1 中断
EA = 1;
ET1 = 1;
// [诊断] 打印唤醒来源
if (p0_flag & 0x0E) {
Uart_SendString("[WR] Woken by KEY P0 (P0INTF=0x");
Uart_SendHex8(p0_flag);
Uart_SendString(")\r\n");
} else if (p2_flag & 0x40) {
Uart_SendString("[WR] Woken by KEY P2 (P2INTF=0x");
Uart_SendHex8(p2_flag);
Uart_SendString(")\r\n");
} else {
Uart_SendString("[WR] Woken by TIMER (no key flag)\r\n");
}
// 3. 保持 SHUT 开启射频接收芯片工作
SHUT = 1;
// 4. 拉高 SGM_CTRL 并发送脉冲使能 SGM3833 升压
SGM_CTRL = 1;
SGM_SendPulse(27);
// 5. 等待负压电轨充分稳定
Delay_ms(200);
// 6. 重做屏控制器 RM69310 寄存器组的初始化
LCD_Init();
// 7. 重置闲置倒计时
inactivity_timer = 0;
// 8. 直接绘制时钟界面(绕过 AppManager 早期返回保护)
current_state = STATE_NORMAL;
UI_ShowClockPage(current_state, current_hour, current_min);
Uart_SendString("[SYS] Wakeup restored!\r\n");
}
// 移除了冲突的中断服务函数,采用更健壮的 EA=0 挂起直接继续执行唤醒序列
/* =========================================================================
* 步骤3: 事件分发路由器 (Event_Dispatcher_Loop)
* ========================================================================= */
void Event_Dispatcher_Loop(void)
{
SystemEvent evt = EventQueue_Pop();
if (evt.key_event == KEY_EVENT_NONE && evt.extra_size == 0) {
return;
}
inactivity_timer = 0; // 收到有效按键或无线事件,重置闲置计时器
Uart_SendString("[EVENT] key=");
Uart_SendHex8((u8)evt.key_event);
Uart_SendString(", pri=");
Uart_SendHex8((u8)evt.priority);
Uart_SendString("\r\n");
// SOS 按键:仅在当前不处于 SOS 求救模式时触发进入 SOS 状态,否则作为普通按键进行事件分发
if ((evt.key_event == KEY_EVENT_SOS_CLICK || evt.key_event == KEY_EVENT_SOS_LONG) &&
AppManager_GetActiveAppID() != APP_ID_SOS) {
AppManager_StartApp(APP_ID_SOS);
return;
}
// 传感器入侵警报:当前不处于 SOS 求救状态时方可跳转到报警界面
if (evt.priority == EVENT_PRIORITY_HIGH && evt.extra_size >= 4) {
u8 slot;
if (Check_Sensor_ID(evt.extra_data, &slot)) {
if (AppManager_GetActiveAppID() != APP_ID_SOS) {
captured_addr = evt.extra_data;
alarm_sensor_slot = slot;
AppManager_StartApp(APP_ID_ALARM);
return;
}
}
}
// 无线对码捕获:处于 PAIR 模式下时,将射频事件分发给 PairApp 处理并退出分发
if (evt.priority == EVENT_PRIORITY_NORMAL && evt.extra_size >= 4) {
captured_addr = evt.extra_data;
captured_type = menu_select;
if (AppManager_GetActiveAppID() == APP_ID_PAIR) {
AppManager_DispatchEvent(evt);
return;
}
}
AppManager_DispatchEvent(evt);
}
/* =========================================================================
* 定时器 1 (1ms 中断服务 + 按键消抖状态机)
* ========================================================================= */
void Timer1_Init(void)
{
AUXR |= 0x40;
TMOD &= 0x0F;
{
u16 reload = (u16)(65536UL - (MAIN_Fosc / 1000UL));
TL1 = (u8)reload;
TH1 = (u8)(reload >> 8);
}
ET1 = 1;
TR1 = 1;
EA = 1;
}
void Timer1_Isr(void) interrupt 3
{
SystemEvent evt;
ms_tick++;
if (ms_tick >= 1000) {
ms_tick = 0;
current_sec++;
if (current_sec >= 60) {
current_sec = 0;
current_min++;
if (current_min >= 60) {
current_min = 0;
current_hour++;
if (current_hour >= 24) current_hour = 0;
}
clock_updated = 1;
}
}
if (current_state == STATE_PAIR_WAIT) {
pair_timeout_ms++;
}
/* 振动马达定时状态机(仅控制马达,不再控制已被挪用/废弃的 LED */
if (current_state == STATE_SOS_EMITTED) {
alarm_timer_ms++;
if (alarm_timer_ms >= 1200) alarm_timer_ms = 0;
if (alarm_timer_ms < 1000) MOTOR = 1; else MOTOR = 0;
} else if (current_state == STATE_ALARM) {
u8 stype = sensor_list[alarm_sensor_slot].type;
alarm_timer_ms++;
if (alarm_timer_ms >= 5000) alarm_timer_ms = 0;
if (stype == 0) {
// 门磁警报振动波形
if (alarm_timer_ms < 300) MOTOR = 1; else MOTOR = 0;
} else if (stype == 1) {
// PIR 警报振动波形
if (alarm_timer_ms < 300) MOTOR = 1;
else if (alarm_timer_ms >= 300 && alarm_timer_ms < 500) MOTOR = 0;
else if (alarm_timer_ms >= 500 && alarm_timer_ms < 800) MOTOR = 1;
else MOTOR = 0;
} else if (stype == 2) {
// 烟感警报振动波形
if (alarm_timer_ms < 200) MOTOR = 1;
else if (alarm_timer_ms >= 200 && alarm_timer_ms < 350) MOTOR = 0;
else if (alarm_timer_ms >= 350 && alarm_timer_ms < 550) MOTOR = 1;
else if (alarm_timer_ms >= 550 && alarm_timer_ms < 700) MOTOR = 0;
else if (alarm_timer_ms >= 700 && alarm_timer_ms < 900) MOTOR = 1;
else MOTOR = 0;
} else if (stype == 3) {
// 气感警报振动波形
if (alarm_timer_ms < 200) MOTOR = 1;
else if (alarm_timer_ms >= 200 && alarm_timer_ms < 300) MOTOR = 0;
else if (alarm_timer_ms >= 300 && alarm_timer_ms < 500) MOTOR = 1;
else if (alarm_timer_ms >= 500 && alarm_timer_ms < 600) MOTOR = 0;
else if (alarm_timer_ms >= 600 && alarm_timer_ms < 800) MOTOR = 1;
else if (alarm_timer_ms >= 800 && alarm_timer_ms < 900) MOTOR = 0;
else if (alarm_timer_ms >= 900 && alarm_timer_ms < 1100) MOTOR = 1;
else MOTOR = 0;
} else if (stype == 4) {
// 水浸警报振动波形
if (alarm_timer_ms < 600) MOTOR = 1;
else if (alarm_timer_ms >= 600 && alarm_timer_ms < 900) MOTOR = 0;
else if (alarm_timer_ms >= 900 && alarm_timer_ms < 1500) MOTOR = 1;
else MOTOR = 0;
} else {
if (alarm_timer_ms < 300) MOTOR = 1; else MOTOR = 0;
}
} else {
MOTOR = 0;
alarm_timer_ms = 0;
motor_timer_ms = 0;
}
alarm_flash_flag = (ms_tick / 100) % 2;
/* 按键消抖扫描 (每10ms) */
key_scan_timer++;
if (key_scan_timer >= 10) {
u8 raw_up = !KEY_UP;
u8 raw_down = !KEY_DOWN;
u8 raw_confirm = !KEY_CONFIRM;
u8 raw_sos = !KEY_SOS;
key_scan_timer = 0;
// 如果检测到有任何按键按下,清零闲置计时器;否则在非休眠非警报下累加
if (raw_up || raw_down || raw_confirm || raw_sos) {
inactivity_timer = 0;
} else {
if (current_state != STATE_SLEEP && current_state != STATE_ALARM && current_state != STATE_SOS_EMITTED) {
inactivity_timer++;
}
}
evt.key_event = KEY_EVENT_NONE;
evt.extra_data = 0;
evt.extra_size = 0;
if (raw_up && raw_down) {
comb_hold++;
if (comb_hold == 300) {
evt.key_event = KEY_EVENT_UP_DOWN_COMB;
evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(evt);
}
key_up_hold = 0;
key_down_hold = 0;
} else {
comb_hold = 0;
if (raw_up) {
key_up_hold++;
if (key_up_hold == 300) {
evt.key_event = KEY_EVENT_UP_LONG;
evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(evt);
}
} else {
if (key_up_hold >= 2 && key_up_hold < 300) {
evt.key_event = KEY_EVENT_UP_CLICK;
evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(evt);
}
key_up_hold = 0;
}
if (raw_down) {
key_down_hold++;
if (key_down_hold == 300) {
evt.key_event = KEY_EVENT_DOWN_LONG;
evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(evt);
}
} else {
if (key_down_hold >= 2 && key_down_hold < 300) {
evt.key_event = KEY_EVENT_DOWN_CLICK;
evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(evt);
}
key_down_hold = 0;
}
if (raw_confirm) {
key_confirm_hold++;
if (key_confirm_hold == 300) {
evt.key_event = KEY_EVENT_CONFIRM_LONG;
evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(evt);
}
} else {
if (key_confirm_hold >= 2 && key_confirm_hold < 300) {
evt.key_event = KEY_EVENT_CONFIRM_CLICK;
evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(evt);
}
key_confirm_hold = 0;
}
if (raw_sos) {
key_sos_hold++;
if (key_sos_hold == 300) {
evt.key_event = KEY_EVENT_SOS_LONG;
evt.priority = EVENT_PRIORITY_URGENT;
EventQueue_InsertFront(evt);
}
} else {
if (key_sos_hold >= 2 && key_sos_hold < 300) {
evt.key_event = KEY_EVENT_SOS_CLICK;
evt.priority = EVENT_PRIORITY_URGENT;
EventQueue_InsertFront(evt);
}
key_sos_hold = 0;
}
}
}
}
/* =========================================================================
* 主函数 (事件驱动框架)
* ========================================================================= */
void main(void)
{
u8 last_led_r = 1;
u8 last_led_g = 1;
u8 last_led_b = 1;
u8 last_motor = 0;
char cmd;
WTST = 0;
EAXFR = 1;
CKCON = 0;
WDT_CONTR = 0x00;
GPIO_Init();
RF_Init();
Delay_ms(500);
SGM_SendPulse(27);
Delay_ms(50);
LCD_Init();
Uart1_Init();
Uart_SendString("Wristband System Initialized!\r\n");
Load_Database();
/* 步骤1: 初始化事件队列 */
EventQueue_Init();
/* 步骤2+5: 初始化应用管理器 */
AppManager_Init();
/* 启动1ms定时器中断 */
Timer1_Init();
Uart_SendString("[SYS] Event-driven framework ready\r\n");
while (1) {
/* 串口调试命令 → 以事件方式入队 */
cmd = Uart_RxChar();
if (cmd != '\0') {
SystemEvent debug_evt;
inactivity_timer = 0; // 调试命令重置闲置计时器
debug_evt.extra_data = 0;
debug_evt.extra_size = 0;
if (cmd == 'u') {
debug_evt.key_event = KEY_EVENT_UP_CLICK;
debug_evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(debug_evt);
Uart_SendString("[UART] KEY_UP Clicked\r\n");
} else if (cmd == 'U') {
debug_evt.key_event = KEY_EVENT_UP_LONG;
debug_evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(debug_evt);
Uart_SendString("[UART] KEY_UP Long Pressed\r\n");
} else if (cmd == 'd') {
debug_evt.key_event = KEY_EVENT_DOWN_CLICK;
debug_evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(debug_evt);
Uart_SendString("[UART] KEY_DOWN Clicked\r\n");
} else if (cmd == 'D') {
debug_evt.key_event = KEY_EVENT_DOWN_LONG;
debug_evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(debug_evt);
Uart_SendString("[UART] KEY_DOWN Long Pressed\r\n");
} else if (cmd == 's') {
debug_evt.key_event = KEY_EVENT_SOS_CLICK;
debug_evt.priority = EVENT_PRIORITY_URGENT;
EventQueue_InsertFront(debug_evt);
Uart_SendString("[UART] KEY_SOS Clicked\r\n");
} else if (cmd == 'S') {
debug_evt.key_event = KEY_EVENT_SOS_LONG;
debug_evt.priority = EVENT_PRIORITY_URGENT;
EventQueue_InsertFront(debug_evt);
Uart_SendString("[UART] KEY_SOS Long Pressed\r\n");
} else if (cmd == 'c' || cmd == 'C') {
debug_evt.key_event = KEY_EVENT_UP_DOWN_COMB;
debug_evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(debug_evt);
Uart_SendString("[UART] KEY_UP+DOWN Combined\r\n");
} else if (cmd == 'a' || cmd == 'A') {
u32 mock_addr = 0x123456;
u8 mock_slot;
if (!Check_Sensor_ID(mock_addr, &mock_slot)) {
Add_Sensor_With_Zone(mock_addr, 0, 1);
}
debug_evt.key_event = KEY_EVENT_NONE;
debug_evt.priority = EVENT_PRIORITY_HIGH;
debug_evt.extra_data = mock_addr;
debug_evt.extra_size = 4;
EventQueue_InsertFront(debug_evt);
Uart_SendString("[UART] Mock RF Alarm\r\n");
} else if (cmd == 'p' || cmd == 'P') {
if (AppManager_GetActiveAppID() == APP_ID_PAIR) {
debug_evt.key_event = KEY_EVENT_NONE;
debug_evt.priority = EVENT_PRIORITY_NORMAL;
debug_evt.extra_data = 0x789ABC;
debug_evt.extra_size = 4;
EventQueue_Push(debug_evt);
Uart_SendString("[UART] Mock RF Pair Signal\r\n");
} else {
Uart_SendString("[UART] Not in PAIR mode\r\n");
}
} else if (cmd == '1') {
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
current_state = STATE_NORMAL;
UI_ShowClockPage(current_state, current_hour, current_min);
}
Uart_SendString("[UART] State: NORMAL\r\n");
} else if (cmd == '2') {
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
current_state = STATE_ARMED;
UI_ShowClockPage(current_state, current_hour, current_min);
}
Uart_SendString("[UART] State: ARMED\r\n");
} else if (cmd == '3') {
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
current_state = STATE_DISARMED;
UI_ShowClockPage(current_state, current_hour, current_min);
}
Uart_SendString("[UART] State: DISARMED\r\n");
} else if (cmd == 'z' || cmd == 'Z') {
inactivity_timer = 1000; // 模拟闲置超时,强制触发休眠
Uart_SendString("[UART] Force Entering Sleep mode!\r\n");
}
}
/* 步骤3: 事件分发 — 循环处理队列中所有事件 */
while (!EventQueue_IsEmpty()) {
Event_Dispatcher_Loop();
}
/* 步骤5: 运行活跃应用 (含CPU占用控制) */
AppManager_RunActiveApp();
/* 步骤6: 超时自动进入深度休眠 */
if (inactivity_timer >= 1000) {
inactivity_timer = 0;
current_state = STATE_SLEEP;
Enter_Low_Power_Sleep();
}
}
}
/* =========================================================================
* GPIO 初始化
* ========================================================================= */
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));
}
// =========================================================================
// STC32G 端口中断 ISR
// 说明STC32G 的 Port0 (向量 37) / Port2 (向量 39) 超过 Keil C251
// 支持的 0~31 范围,无法直接用 interrupt 37/39 声明。
// 因此通过 App/isr_jump.asm 在硬件绝对向量地址处放置 LJMP
// 将 Port0 向量 → interrupt 13 入口 (0x006BH)
// 将 Port2 向量 → interrupt 15 入口 (0x007BH)
// 以此实现 Keil C251 可正确编译链接的中断服务函数。
// =========================================================================
// Port0 中断服务函数 (由 isr_jump.asm 从向量 37 @ 0x012BH 跳转至此)
void Port0_Isr(void) interrupt 13
{
// 关键P0INTF/P0INTE 是 far 扩展 SFR写入前必须确保 EAXFR=1
EAXFR = 1;
P0INTF = 0x00; // 清除 P0 端口中断悬挂标志
P0INTE = 0x00; // 立即禁用,防止低电平保持期间反复触发
}
// Port2 中断服务函数 (由 isr_jump.asm 从向量 39 @ 0x013BH 跳转至此)
void Port2_Isr(void) interrupt 15
{
// 关键P2INTF/P2INTE 是 far 扩展 SFR写入前必须确保 EAXFR=1
EAXFR = 1;
P2INTF = 0x00; // 清除 P2 端口中断悬挂标志
P2INTE = 0x00; // 立即禁用,防止低电平保持期间反复触发
}