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
+