From 7ebe80241071da9eefc222849a85a3381cc4d454 Mon Sep 17 00:00:00 2001 From: Antigravity IDE Date: Wed, 15 Jul 2026 16:14:30 +0800 Subject: [PATCH] Modularize main.c into rgb, event, database, ui, system, and app_manager modules --- App/app_manager.c | 783 ++++++++++++++++ App/app_manager.h | 16 + App/config.h | 49 + App/database.c | 140 +++ App/database.h | 18 + App/event.c | 309 +++++++ App/event.h | 19 + App/main.c | 2214 ++------------------------------------------- App/rgb.c | 84 ++ App/rgb.h | 12 + App/system.c | 327 +++++++ App/system.h | 31 + App/ui.c | 290 ++++++ App/ui.h | 18 + wristband.uvproj | 30 + 15 files changed, 2216 insertions(+), 2124 deletions(-) create mode 100644 App/app_manager.c create mode 100644 App/app_manager.h create mode 100644 App/database.c create mode 100644 App/database.h create mode 100644 App/event.c create mode 100644 App/event.h create mode 100644 App/rgb.c create mode 100644 App/rgb.h create mode 100644 App/system.c create mode 100644 App/system.h create mode 100644 App/ui.c create mode 100644 App/ui.h diff --git a/App/app_manager.c b/App/app_manager.c new file mode 100644 index 0000000..1d41f47 --- /dev/null +++ b/App/app_manager.c @@ -0,0 +1,783 @@ +#include "app_manager.h" +#include "rgb.h" +#include "event.h" +#include "database.h" +#include "ui.h" +#include "system.h" +#include "../Drivers/rf.h" +#include "../Drivers/lcd.h" + +#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 + +// 定义菜单项定义 +char *code menu_items[5] = { + "DETEC. PORTE", + "DETEC. PIR", + "DETEC. FUMEE", + "DETEC. GAZ", + "DETEC. EAU" +}; + +/* 回调函数前置声明 */ +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; +} diff --git a/App/app_manager.h b/App/app_manager.h new file mode 100644 index 0000000..1113439 --- /dev/null +++ b/App/app_manager.h @@ -0,0 +1,16 @@ +#ifndef __APP_MANAGER_H__ +#define __APP_MANAGER_H__ + +#include "config.h" + +// AppManager 核心接口 +void AppManager_Init(void); +void AppManager_StartApp(AppID app_id); +void AppManager_DispatchEvent(SystemEvent evt); +void AppManager_RunActiveApp(void); +AppID AppManager_GetActiveAppID(void); + +// 时间编辑状态机接口 (由 MenuApp 触发) +void Start_Time_Edit(void); + +#endif diff --git a/App/config.h b/App/config.h index d1edcf4..6f2523f 100644 --- a/App/config.h +++ b/App/config.h @@ -1,10 +1,28 @@ #ifndef __CONFIG_H__ #define __CONFIG_H__ +#include "stc32g.h" +#include "intrins.h" + typedef unsigned char u8; typedef unsigned int u16; typedef unsigned long u32; +/* ====== 硬件引脚定义 (sbit) ====== */ +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) + /* ====== 系统状态 ====== */ typedef enum { STATE_NORMAL, @@ -104,7 +122,38 @@ typedef struct { unsigned char name_gbk[16]; } Sensor_Slot; +/* ====== 全局变量 extern 声明 ====== */ +extern SystemState current_state; +extern u8 current_hour; +extern u8 current_min; +extern u8 current_sec; +extern volatile u8 clock_updated; + +extern u8 menu_select; +extern u32 captured_addr; +extern u8 captured_type; +extern u8 alarm_sensor_slot; + +extern Sensor_Slot xdata sensor_list[16]; +extern char *code sensor_names_fr[5]; + + +extern volatile u16 ms_tick; +extern volatile u16 inactivity_timer; // 无操作闲置计时器 +extern volatile u32 pair_timeout_ms; +extern volatile u16 alarm_timer_ms; +extern volatile u16 motor_timer_ms; +extern volatile bit alarm_flash_flag; + +extern volatile u16 key_scan_timer; +extern volatile u16 key_up_hold; +extern volatile u16 key_down_hold; +extern volatile u16 key_confirm_hold; +extern volatile u16 key_sos_hold; +extern volatile u16 comb_hold; + #define MAIN_Fosc 24000000UL #define CLONED_ADDR 0x37A86UL #endif + diff --git a/App/database.c b/App/database.c new file mode 100644 index 0000000..a776169 --- /dev/null +++ b/App/database.c @@ -0,0 +1,140 @@ +#include "database.h" + +#define IAP_CMD_READ 1 +#define IAP_CMD_WRITE 2 +#define IAP_CMD_ERASE 3 + +// 定义全局常量数组 +char *code sensor_names_fr[5] = { + "PORTE", + "PIR", + "FUMEE", + "GAZ", + "EAU" +}; + +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; +} diff --git a/App/database.h b/App/database.h new file mode 100644 index 0000000..fee12fb --- /dev/null +++ b/App/database.h @@ -0,0 +1,18 @@ +#ifndef __DATABASE_H__ +#define __DATABASE_H__ + +#include "config.h" + +// 底层 IAP 接口 +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); + +#endif diff --git a/App/event.c b/App/event.c new file mode 100644 index 0000000..789df99 --- /dev/null +++ b/App/event.c @@ -0,0 +1,309 @@ +#include "event.h" +#include "app_manager.h" +#include "database.h" +#include "system.h" + +#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; +} + +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); +} + +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; + } + } + } +} diff --git a/App/event.h b/App/event.h new file mode 100644 index 0000000..ab1fc94 --- /dev/null +++ b/App/event.h @@ -0,0 +1,19 @@ +#ifndef __EVENT_H__ +#define __EVENT_H__ + +#include "config.h" + +// 事件队列初始化与操作 +void EventQueue_Init(void); +bit EventQueue_Push(SystemEvent evt); +bit EventQueue_InsertFront(SystemEvent evt); +SystemEvent EventQueue_Pop(void); +bit EventQueue_IsEmpty(void); +bit EventQueue_IsFull(void); +u8 EventQueue_GetCount(void); + +// 事件分发与定时器/按键扫描初始化 +void Event_Dispatcher_Loop(void); +void Timer1_Init(void); + +#endif diff --git a/App/main.c b/App/main.c index 2d6337a..ad70bba 100644 --- a/App/main.c +++ b/App/main.c @@ -1,113 +1,16 @@ -#include "stc32g.h" -#include "intrins.h" #include "config.h" -#include "../Drivers/lcd.h" +#include "rgb.h" +#include "event.h" +#include "app_manager.h" +#include "database.h" +#include "ui.h" +#include "system.h" #include "../Drivers/rf.h" +#include "../Drivers/lcd.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; @@ -136,1884 +39,102 @@ 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) +void Debug_ProcessCommand(char cmd) { - queue_head = 0; - queue_tail = 0; - queue_count = 0; -} + SystemEvent debug_evt; + inactivity_timer = 0; // 调试命令重置闲置计时器 + debug_evt.extra_data = 0; + debug_evt.extra_size = 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); + 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); } - } - 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; + 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) { - 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; + 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 { - if (alarm_timer_ms < 300) MOTOR = 1; else MOTOR = 0; + Uart_SendString("[UART] Not in PAIR mode\r\n"); } - } 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++; - } + } else if (cmd == '1') { + if (AppManager_GetActiveAppID() == APP_ID_CLOCK) { + current_state = STATE_NORMAL; + UI_ShowClockPage(current_state, current_hour, current_min); } - - 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; - } + 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"); } } /* ========================================================================= - * 主函数 (事件驱动框架) + * 主函数 (入口) * ========================================================================= */ - 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; @@ -2046,91 +167,7 @@ void main(void) /* 串口调试命令 → 以事件方式入队 */ 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"); - } + Debug_ProcessCommand(cmd); } /* 步骤3: 事件分发 — 循环处理队列中所有事件 */ @@ -2151,82 +188,12 @@ void main(void) } /* ========================================================================= - * GPIO 初始化 + * STC32G 端口中断 ISR * ========================================================================= */ -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; // 立即禁用,防止低电平保持期间反复触发 @@ -2235,7 +202,6 @@ void Port0_Isr(void) interrupt 13 // 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; // 立即禁用,防止低电平保持期间反复触发 diff --git a/App/rgb.c b/App/rgb.c new file mode 100644 index 0000000..e5e042f --- /dev/null +++ b/App/rgb.c @@ -0,0 +1,84 @@ +#include "rgb.h" + +/* ====== SPI 寄存器定义 (如果 stc32g.h 没定义) ====== */ +#ifndef P_SW1 +sfr P_SW1 = 0xA2; +sfr SPCTL = 0xCE; +sfr SPSTAT = 0xCD; +sfr SPDAT = 0xCF; +#endif + +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; // 开中断 +} diff --git a/App/rgb.h b/App/rgb.h new file mode 100644 index 0000000..5f01d70 --- /dev/null +++ b/App/rgb.h @@ -0,0 +1,12 @@ +#ifndef __RGB_H__ +#define __RGB_H__ + +#include "config.h" + +// SPI 及 RGB 发送初始化 +void SPI_Init(void); + +// 向 FCOB 幻彩灯条发送两颗 LED 的 GRB 数据 +void RGB_Send(u8 g1, u8 r1, u8 b1, u8 g2, u8 r2, u8 b2); + +#endif diff --git a/App/system.c b/App/system.c new file mode 100644 index 0000000..5553bb9 --- /dev/null +++ b/App/system.c @@ -0,0 +1,327 @@ +#include "system.h" +#include "rgb.h" +#include "ui.h" +#include "../Drivers/lcd.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; + + // ===== 配置端口掉电唤醒使能寄存器 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 挂起状态 + 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"); +} diff --git a/App/system.h b/App/system.h new file mode 100644 index 0000000..5c8d61e --- /dev/null +++ b/App/system.h @@ -0,0 +1,31 @@ +#ifndef __SYSTEM_H__ +#define __SYSTEM_H__ + +#include "config.h" + +// GPIO 与系统外设初始化 +void GPIO_Init(void); +void Uart1_Init(void); + +// 延时与时间工具函数 +void Delay_ms(u16 ms); +void Delay10us(void); +void Delay_us(u16 us); +u16 GetTimer0_Safe(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_SendHex16(u16 val); +void Uart_SendHex20(u32 val); +void Uart_SendHex32(u32 val); +void FormatHex(u32 val, char *buf); + +// 电源管理与休眠唤醒 +void Enter_Low_Power_Sleep(void); +void Wakeup_Restore(void); + +#endif diff --git a/App/ui.c b/App/ui.c new file mode 100644 index 0000000..d8d1aa3 --- /dev/null +++ b/App/ui.c @@ -0,0 +1,290 @@ +#include "ui.h" +#include "../Drivers/lcd.h" + +// 引入外部菜单项定义 +extern char *code menu_items[5]; + +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); +} diff --git a/App/ui.h b/App/ui.h new file mode 100644 index 0000000..63696c6 --- /dev/null +++ b/App/ui.h @@ -0,0 +1,18 @@ +#ifndef __UI_H__ +#define __UI_H__ + +#include "config.h" + +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); + +#endif diff --git a/wristband.uvproj b/wristband.uvproj index dec11c3..7a45c41 100644 --- a/wristband.uvproj +++ b/wristband.uvproj @@ -329,6 +329,36 @@ 1 .\App\main.c + + rgb.c + 1 + .\App\rgb.c + + + event.c + 1 + .\App\event.c + + + app_manager.c + 1 + .\App\app_manager.c + + + database.c + 1 + .\App\database.c + + + ui.c + 1 + .\App\ui.c + + + system.c + 1 + .\App\system.c +