From c0d1fe74f564302fa48226d5508a16332474d625 Mon Sep 17 00:00:00 2001 From: Antigravity IDE Date: Fri, 17 Jul 2026 09:08:53 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E6=89=8B=E7=8E=AF=E8=BD=AF?= =?UTF-8?q?=E4=BB=B6=E6=9E=B6=E6=9E=84=EF=BC=9A=E5=AE=9E=E7=8E=B0=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E6=8C=82=E8=BD=BD=E5=89=8D=E5=90=8E=E5=8F=B0=20App=20?= =?UTF-8?q?=E6=A1=86=E6=9E=B6=EF=BC=8C=E8=A7=A3=E8=80=A6=E6=8B=86=E5=88=86?= =?UTF-8?q?=E6=BA=90=E6=96=87=E4=BB=B6=EF=BC=8C=E5=B9=B6=E5=BC=95=E5=85=A5?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=97=A5=E5=BF=97=E5=AE=8F=20xtell.h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/app_alarm.c | 182 ++++++ App/app_clock.c | 194 +++++++ App/app_manager.c | 1055 +++++----------------------------- App/app_manager.h | 81 ++- App/app_menu.c | 187 ++++++ App/app_pair.c | 231 ++++++++ App/app_rf_monitor.c | 106 ++++ App/app_sos.c | 137 +++++ App/clock.c | 37 ++ App/clock.h | 17 + App/config.h | 9 - App/event.c | 134 +---- App/main.c | 91 ++- App/system.c | 22 +- App/xtell.h | 24 + Drivers/rf.c | 26 +- Hardware/~text.kicad_pro.lck | 1 - Hardware/~text.kicad_sch.lck | 1 - 18 files changed, 1380 insertions(+), 1155 deletions(-) create mode 100644 App/app_alarm.c create mode 100644 App/app_clock.c create mode 100644 App/app_menu.c create mode 100644 App/app_pair.c create mode 100644 App/app_rf_monitor.c create mode 100644 App/app_sos.c create mode 100644 App/clock.c create mode 100644 App/clock.h create mode 100644 App/xtell.h delete mode 100644 Hardware/~text.kicad_pro.lck delete mode 100644 Hardware/~text.kicad_sch.lck diff --git a/App/app_alarm.c b/App/app_alarm.c new file mode 100644 index 0000000..8e773d1 --- /dev/null +++ b/App/app_alarm.c @@ -0,0 +1,182 @@ +#include "app_manager.h" +#include "xtell.h" +#include "ui.h" +#include "rgb.h" +#include "event.h" +#include "system.h" +#include "database.h" +#include "../Drivers/lcd.h" + +// 外部全局变量引用 +extern SystemState current_state; +extern u32 captured_addr; +extern u8 alarm_sensor_slot; +extern volatile u16 ms_tick; +extern volatile bit alarm_flash_flag; + +// 传感器存储列表数据库声明 +extern Sensor_Slot xdata sensor_list[16]; + +// 本地私有变量 +static u32 alarm_timer_ms = 0; // 报警震动波形计数器 +static u16 last_run_tick = 0; // 记录上一次轮询的 ticks + +// 声明本应用的生命周期回调函数 +static void AlarmApp_OnStart(void); +static void AlarmApp_onRun(void); +static void AlarmApp_onClose(void); +static EventResult AlarmApp_onEvent(SystemEvent *evt); + +// 实体化定义警报应用控制块 +WristbandApp alarm_app = { + APP_ID_ALARM, + "Alarm", + AlarmApp_OnStart, + AlarmApp_onRun, + AlarmApp_onClose, + AlarmApp_onEvent +}; + +/** + * @brief AlarmApp 启动回调 + */ +static void AlarmApp_OnStart(void) +{ + u8 slot; + u8 type = 0; + u8 zone = 1; + + current_state = STATE_ALARM; // 系统切入报警状态 + alarm_timer_ms = 0; // 振动计时器清零 + last_run_tick = ms_tick; + + // 比对查出当前触发警报的传感器在数据库中的属性 + 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); // 门磁: 红色 (GRB中R=180,G=0) + else if (type == 1) RGB_Send(100, 180, 0, 100, 180, 0); // 红外PIR: 黄色 (G=100, R=180) + 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); // 气感: 紫色 (R=180, B=180) + else RGB_Send(0, 0, 180, 0, 0, 180); // 水浸: 蓝色 (B=180) + + XTELL_LOG("[AlarmApp] Started. Type="); + XTELL_LOG_HEX8(type); + XTELL_LOG("\r\n"); +} + +/** + * @brief AlarmApp 轮询主回调 + * @details 包含同步的灯光爆闪和 Truly AMOLED 像素边框闪烁动效, + * 以及马达差异化震动波形逻辑的自处理。 + */ +static void AlarmApp_onRun(void) +{ + u8 type = sensor_list[alarm_sensor_slot].type; + u8 flash_light = (ms_tick / 250) % 2; // 250ms 周期亮灭 + u16 tick_diff; + + // 1. 同步控制 RGB 幻彩灯条进行交替爆闪 + if (flash_light) + { + 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); + } + + // 2. 控制 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); + } + + // 3. 计算时间差并执行马达差异化震动状态机控制 + if (ms_tick >= last_run_tick) { + tick_diff = ms_tick - last_run_tick; + } else { + tick_diff = 1000 + ms_tick - last_run_tick; + } + last_run_tick = ms_tick; + + alarm_timer_ms += tick_diff; + if (alarm_timer_ms >= 5000) { + alarm_timer_ms = 0; // 5秒一周期 + } + + if (type == 0) { + // 门磁警报波形:单次震动 300ms,停 4700ms + if (alarm_timer_ms < 300) MOTOR = 1; else MOTOR = 0; + } else if (type == 1) { + // PIR 警报波形:震动 300ms,停 200ms,再震动 300ms,停 4200ms + 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 (type == 2) { + // 烟感警报波形:急促短闪三下 200ms-150ms-200ms-150ms-200ms + 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 (type == 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 (type == 4) { + // 水浸警报波形:慢速双长震波 600ms 和 600ms + 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 { + // 缺省状态默认单次 300ms + if (alarm_timer_ms < 300) MOTOR = 1; else MOTOR = 0; + } +} + +/** + * @brief AlarmApp 退出清理 + */ +static void AlarmApp_onClose(void) +{ + RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯条 + MOTOR = 0; // 停震 + current_state = STATE_NORMAL; + XTELL_LOG("[AlarmApp] Closed\r\n"); +} + +/** + * @brief AlarmApp 事件拦截处理 (按任意键退出报警) + */ +static EventResult AlarmApp_onEvent(SystemEvent *evt) +{ + if (evt->key_event != KEY_EVENT_NONE) { + AppManager_SwitchToForeground(&clock_app); // 清除报警并切回时钟待机页 + return EVENT_HANDLED; + } + return EVENT_IGNORED; +} diff --git a/App/app_clock.c b/App/app_clock.c new file mode 100644 index 0000000..6d86eb8 --- /dev/null +++ b/App/app_clock.c @@ -0,0 +1,194 @@ +#include "app_manager.h" +#include "clock.h" +#include "xtell.h" +#include "ui.h" +#include "rgb.h" +#include "event.h" +#include "system.h" +#include "../Drivers/rf.h" +#include "../Drivers/lcd.h" + +// 外部引用的全局系统状态变量 +extern SystemState current_state; +extern u8 current_hour; +extern u8 current_min; +extern u8 current_sec; +extern volatile u8 clock_updated; +extern volatile u16 ms_tick; + +// 待机时钟应用内特有的编辑时间私有变量 +static u8 time_edit_active = 0; // 标识当前是否正在编辑修改时间中 (1:是, 0:否) +static u8 time_edit_field = 1; // 标识当前聚焦在哪一个参数上进行微调 (1:修改小时, 2:修改分钟) +static u8 time_edit_blink_on = 1; // 时钟修改时光标闪烁周期状态位 + +// 声明本应用的生命周期回调函数 +static void ClockApp_OnStart(void); +static void ClockApp_onRun(void); +static void ClockApp_onClose(void); +static EventResult ClockApp_onEvent(SystemEvent *evt); + +// 实体化定义待机时钟应用控制块 +WristbandApp clock_app = { + APP_ID_CLOCK, + "Clock", + ClockApp_OnStart, + ClockApp_onRun, + ClockApp_onClose, + ClockApp_onEvent +}; + +/** + * @brief 触发启动时间设置状态机,准备修改时钟 + */ +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); + + // 振动马达单次短震 50 毫秒,提供触觉反馈 + MOTOR = 1; + Delay_ms(50); + MOTOR = 0; + + // 渲染显示时间编辑界面,默认高亮编辑小时字段,消隐位开启 + UI_ShowSetTimePage(current_hour, current_min, time_edit_field, 1); + XTELL_LOG("[ClockApp] Time edit started.\r\n"); +} + +/** + * @brief ClockApp 启动回调 + */ +static void ClockApp_OnStart(void) +{ + time_edit_active = 0; + current_state = STATE_NORMAL; // 系统切入正常待机状态 + RF_SetMode(1); // 开启天线并置为接收模式,监听无线探测器 + UI_ShowClockPage(current_state, current_hour, current_min); // 渲染时钟主页 + + // 关断/熄灭 FCOB 双灯条 + RGB_Send(0, 0, 0, 0, 0, 0); + + XTELL_LOG("[ClockApp] Started\r\n"); +} + +/** + * @brief ClockApp 轮询主回调 + */ +static void ClockApp_onRun(void) +{ + 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); + } + } +} + +/** + * @brief ClockApp 退出清理回调 + */ +static void ClockApp_onClose(void) +{ + time_edit_active = 0; + RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯条 + XTELL_LOG("[ClockApp] Closed\r\n"); +} + +/** + * @brief ClockApp 接收事件处理回调 (按键导航与布撤防交互) + */ +static 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) + { + // 长按 ■ 确认键切换调节字段 (在修改小时与修改分钟间跳转) + 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) + { + // 短按 ■ 确认键保存最新时钟配置并退出编辑模式 + 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; // 在时间设置模式下屏蔽拦截其他一切无关按键事件 + } + + // ====== 正常时钟待机状态下的按键拦截处理 ====== + + // 1. 长按 ■ 确认键,打开设置主菜单 MenuApp + if (evt->key_event == KEY_EVENT_CONFIRM_LONG) { + AppManager_SwitchToForeground(&menu_app); + return EVENT_HANDLED; + } + + // 2. 长按 ▲ 键,一键切换系统的 布防 (ARMED) 和 撤防 (DISARMED) 状态 + if (evt->key_event == KEY_EVENT_UP_LONG) { + if (current_state == STATE_ARMED) { + current_state = STATE_DISARMED; + } else { + current_state = STATE_ARMED; + } + // 布撤防状态切换成功后进行 100ms 短震确认,并重新刷写时钟待机页 + MOTOR = 1; Delay_ms(100); MOTOR = 0; + UI_ShowClockPage(current_state, current_hour, current_min); + return EVENT_HANDLED; + } + + return EVENT_IGNORED; // 其他未被关心的事件交由后台应用或丢弃 +} diff --git a/App/app_manager.c b/App/app_manager.c index 3bdff5a..f120456 100644 --- a/App/app_manager.c +++ b/App/app_manager.c @@ -1,961 +1,188 @@ #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" +#include "xtell.h" -// 定义单个应用轮询 onRun 的最大允许 CPU 占用 Tick 数 (5 毫秒) -#define APP_ONRUN_MAX_TICKS 5 - -// 定义局部静态应用实例注册表,大小为 5 (即 APP_ID_MAX) -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", // 0: 门磁探测器 (法语) - "DETEC. PIR", // 1: 红外探测器 - "DETEC. FUMEE", // 2: 烟雾传感器 - "DETEC. GAZ", // 3: 燃气探测器 - "DETEC. EAU" // 4: 水浸探测器 -}; - -/* ====== 内部应用程序回调函数的前置声明 ====== */ -// ClockApp (待机时钟应用) 生命周期函数 -void ClockApp_OnStart(void); -void ClockApp_onRun(void); -void ClockApp_onClose(void); -EventResult ClockApp_onEvent(SystemEvent *evt); - -// MenuApp (菜单选择应用) 生命周期函数 -void MenuApp_OnStart(void); -void MenuApp_onRun(void); -void MenuApp_onClose(void); -EventResult MenuApp_onEvent(SystemEvent *evt); - -// PairApp (无线传感器对码学习应用) 生命周期函数 -void PairApp_OnStart(void); -void PairApp_onRun(void); -void PairApp_onClose(void); -EventResult PairApp_onEvent(SystemEvent *evt); - -// AlarmApp (传感器报警显示与振动反馈应用) 生命周期函数 -void AlarmApp_OnStart(void); -void AlarmApp_onRun(void); -void AlarmApp_onClose(void); -EventResult AlarmApp_onEvent(SystemEvent *evt); - -// SOSApp (SOS 紧急求救无线发射与警灯应用) 生命周期函数 -void SOSApp_OnStart(void); -void SOSApp_onRun(void); -void SOSApp_onClose(void); -EventResult SOSApp_onEvent(SystemEvent *evt); - - -/* ====== AppManager 核心接口函数实现 ====== */ +// 前台与后台应用的运行指针槽位 +static WristbandApp *fg_app = NULL; +static WristbandApp *bg_apps[3] = {NULL, NULL, NULL}; /** - * @brief 初始化应用管理器并注册所有系统支持的应用 + * @brief 判断某个应用是否正在后台挂载运行中 + */ +static bit IsAppInBackground(WristbandApp *app) +{ + u8 i; + if (app == NULL) return 0; + for (i = 0; i < 3; i++) { + if (bg_apps[i] == app) { + return 1; + } + } + return 0; +} + +/** + * @brief 初始化应用管理器 */ void AppManager_Init(void) { u8 i; - /* 1. 清空初始化注册表 */ - 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; + fg_app = NULL; + for (i = 0; i < 3; i++) { + bg_apps[i] = NULL; } - - /* 2. 注册待机时钟应用 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; - - /* 3. 注册系统设置主菜单应用 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; - - /* 4. 注册无线对码配对应用 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; - - /* 5. 注册防区入侵警报应用 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; - - /* 6. 注册 SOS 紧急求救应用 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); + XTELL_LOG("[AM] AppManager Initialized.\r\n"); } /** - * @brief 切换并启动目标前台应用程序 (切应用生命周期) - * @param app_id 目标加载的应用 ID + * @brief 动态将一个应用切换至前台活跃运行 */ -void AppManager_StartApp(AppID app_id) +bit AppManager_SwitchToForeground(WristbandApp *app) { - WristbandApp *target; - if (app_id >= APP_ID_MAX) return; // 越界防护 - target = &app_registry[app_id]; - if (target->OnStart == NULL) return; // 空指针防护 - if (active_app == target) return; // 避免重复启动同一个应用造成页面重画 + if (app == NULL) return 0; - Uart_SendString("[APP] StartApp: "); - Uart_SendString(target->app_name); - Uart_SendString("\r\n"); + XTELL_LOG("[AM] Switch foreground to: "); + XTELL_LOG(app->app_name); + XTELL_LOG("\r\n"); - /* 1. 关闭清除当前活跃运行的应用,释放其外设和标志 */ - if (active_app != NULL && active_app->onClose != NULL) { - active_app->is_active = 0; // 标明不再处于前台活跃状态 - active_app->onClose(); // 调用该应用的退出现场清理回调 + // 1. 若当前有正在运行的前台应用,将其移出前台 + if (fg_app != NULL) { + WristbandApp *prev_app = fg_app; + fg_app = NULL; // 物理断开,防止在 onClose 期间被重入轮询 + + // 若该应用没有在后台运行,需要彻底关闭它以清理外设 + if (!IsAppInBackground(prev_app)) { + if (prev_app->onClose != NULL) { + prev_app->onClose(); + } + } } - /* 2. 切换当前前台活跃应用指针,启动新加载的应用 */ - active_app = target; - if (active_app->OnStart != NULL) { - active_app->is_active = 1; // 标明进入活跃状态 - active_app->is_running = 1; // 标记处于运行状态,开启 onRun 调用 - active_app->OnStart(); // 调用启动回调,初始化显示 UI 与外设 + // 2. 挂载新的前台应用 + fg_app = app; + + // 3. 启动新应用生命周期 + // 若该应用之前在后台运行,不用重新执行 OnStart,直接接管前台显示即可; + // 否则说明是初次运行,执行 OnStart 进行界面绘制与状态初始化。 + if (!IsAppInBackground(app)) { + if (app->OnStart != NULL) { + app->OnStart(); + } + } + + return 1; +} + +/** + * @brief 动态将一个应用挂载到后台槽位 + */ +bit AppManager_AttachToBackground(WristbandApp *app) +{ + u8 i; + if (app == NULL) return 0; + + // 若已经存在于后台,直接返回成功 + if (IsAppInBackground(app)) { + return 1; + } + + // 寻找空闲的后台槽位进行挂载 + for (i = 0; i < 3; i++) { + if (bg_apps[i] == NULL) { + bg_apps[i] = app; + XTELL_LOG("[AM] Attach to background slot ["); + XTELL_LOG_HEX8(i); + XTELL_LOG("]: "); + XTELL_LOG(app->app_name); + XTELL_LOG("\r\n"); + + // 若该应用既不处于前台,也没在后台初始化,执行其 OnStart() + if (fg_app != app) { + if (app->OnStart != NULL) { + app->OnStart(); + } + } + return 1; + } + } + + XTELL_LOG("[AM] ERROR: Background slots full!\r\n"); + return 0; // 槽位满,挂载失败 +} + +/** + * @brief 动态将一个应用从后台卸载 + */ +void AppManager_DetachFromBackground(WristbandApp *app) +{ + u8 i; + if (app == NULL) return; + + for (i = 0; i < 3; i++) { + if (bg_apps[i] == app) { + bg_apps[i] = NULL; + XTELL_LOG("[AM] Detach from background: "); + XTELL_LOG(app->app_name); + XTELL_LOG("\r\n"); + + // 如果该应用未在前台运行,需要彻底关闭它 + if (fg_app != app) { + if (app->onClose != NULL) { + app->onClose(); + } + } + return; + } } } /** - * @brief 将捕获的系统事件分发给当前活跃的前台应用 - * @param evt 待分发的系统事件 + * @brief 向前台活跃应用派发事件 */ 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; // 被前台消耗掉,结束分发 + if (fg_app != NULL && fg_app->onEvent != NULL) { + fg_app->onEvent(&evt); } } /** - * @brief 在主循环中持续轮询当前活跃应用的 onRun - * @details 包含 CPU 占用时间检测算法,避免坏设计的 onRun 回调死循环卡死系统。 + * @brief 主循环中轮询前后台所有应用 */ void AppManager_RunActiveApp(void) { - u16 start_tick; - u16 elapsed; - if (active_app == NULL || !active_app->is_active) return; + u8 i; - // CPU 挂起恢复机制:如果在上一次循环中因超时被强行挂起 (is_running=0) - // 则在本次循环中将 is_running 标志重新置 1 恢复,但直接返回,跳过本次执行,把 CPU 使用权让给其他底层服务 - if (!active_app->is_running) { - active_app->is_running = 1; - return; + // 1. 轮询前台活跃应用 + if (fg_app != NULL && fg_app->onRun != NULL) { + fg_app->onRun(); } - - if (active_app->onRun == NULL) return; - - start_tick = ms_tick; // 记录开始前的毫秒 Tick 数 (0~999 循环) - active_app->onRun(); // 执行应用的轮询代码 - - // 计算本次 onRun 调用的真实耗时 (考虑 ms_tick 模 1000 归零的情况) - if (ms_tick >= start_tick) { - elapsed = ms_tick - start_tick; - } else { - elapsed = (1000 + ms_tick) - start_tick; - } - - // 超时检测:如果单次 onRun 运行时间超过了 APP_ONRUN_MAX_TICKS (5毫秒) - if (elapsed > APP_ONRUN_MAX_TICKS) { - active_app->is_running = 0; // 强行挂起,下次循环跳过该应用 onRun 以释放 CPU - } else { - active_app->is_running = 1; // 正常,继续运行 - } -} -/** - * @brief 获取当前正在活跃的前台应用 ID - */ -AppID AppManager_GetActiveAppID(void) -{ - if (active_app == NULL) return APP_ID_MAX; - return active_app->app_id; -} - - -/* ========================================================================= - * 1. ClockApp (待机时钟应用) 具体生命周期回调实现 - * ========================================================================= */ - -// 编辑时间参数所用到的私有局部变量 -static u8 time_edit_active = 0; // 标识当前是否正在编辑修改时间中 (1:是, 0:否) -static u8 time_edit_field = 1; // 标识当前聚焦在哪个参数上进行微调 (1:修改小时, 2:修改分钟) -static u8 time_edit_blink_on = 1; // 时钟修改时光标闪烁周期状态位 - -/** - * @brief 触发启动时间设置状态机,准备修改时钟 - */ -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); - - // 振动马达单次短震 50 毫秒,提供触觉反馈 - MOTOR = 1; - Delay_ms(50); - MOTOR = 0; - - // 渲染显示时间编辑界面,默认高亮编辑小时字段,消隐位开启 - UI_ShowSetTimePage(current_hour, current_min, time_edit_field, 1); -} - -/** - * @brief ClockApp 启动回调 - */ -void ClockApp_OnStart(void) -{ - time_edit_active = 0; - current_state = STATE_NORMAL; // 系统切入正常待机状态 - RF_SetMode(1); // 开启天线并置为接收模式,监听无线探测器 - UI_ShowClockPage(current_state, current_hour, current_min); // 渲染时钟主页 - - // 关断/熄灭 FCOB 双灯条 - RGB_Send(0, 0, 0, 0, 0, 0); - - Uart_SendString("[ClockApp] Started\r\n"); -} - -/** - * @brief ClockApp 轮询主回调 - * @details 负责处理两个非阻塞式周期性事务: - * 1. 若正在时间修改编辑模式中,基于 ms_tick 提供 500ms 周期的被改参数隐现闪烁效果。 - * 2. 在待机主页常态下,非阻塞轮询射频解调接收缓冲区,识别到无线信号后在数据库中匹配,并实现防区布撤防规则过滤。 - */ -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); - } - - // 轮询无线 EV1527 硬件解调器是否收到一个完整的 24 位无线码 - if (EV1527_Decode(&rx_addr, &rx_data)) { - u8 slot; - // 比对收到的 24 位射频源地址是否合法注册在手环本地配置数据库中 - if (Check_Sensor_ID(rx_addr, &slot)) { - u8 sensor_type = sensor_list[slot].type; // 获取注册的设备类型 - if (sensor_type < 5) { - // 【防区分类过滤与布撤防过滤逻辑】: - // - 0号防区,或者烟感(2)、气感(3)、水浸(4) 为 24小时紧急防区,不受当前布防状态影响,一律强制触发警报; - // - 门磁(0)和红外(1)为普通边界入侵防区,只有当系统处于 STATE_ARMED (布防) 状态时,才触发入侵警报。 - if (sensor_list[slot].zone == 0 || - sensor_type == 2 || - sensor_type == 3 || - sensor_type == 4 || - current_state == STATE_ARMED) - { - SystemEvent rf_evt; - rf_evt.key_event = KEY_EVENT_NONE; - rf_evt.priority = EVENT_PRIORITY_HIGH; // 将事件定为 HIGH (高优先级) - rf_evt.extra_data = rx_addr; // 携带报警源地址 - rf_evt.extra_size = 4; - - // 高优先级事件,强行插队到队首优先执行以提高响应速率 - EventQueue_InsertFront(rf_evt); - } - } - } + // 2. 轮询后台挂载的应用 + for (i = 0; i < 3; i++) { + if (bg_apps[i] != NULL && bg_apps[i]->onRun != NULL) { + bg_apps[i]->onRun(); } } } /** - * @brief ClockApp 退出清理回调 + * @brief 获取当前前台活跃应用的唯一 ID */ -void ClockApp_onClose(void) +u8 AppManager_GetActiveAppID(void) { - time_edit_active = 0; - RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯条 - Uart_SendString("[ClockApp] Closed\r\n"); + if (fg_app != NULL) { + return fg_app->app_id; + } + return 0xFF; } -/** - * @brief ClockApp 接收事件处理回调 (按键导航与布撤防交互) - */ -EventResult ClockApp_onEvent(SystemEvent *evt) +// 供遗留代码兼容性引用的 API:StartApp 映射至 SwitchToForeground 动态方式 +void AppManager_StartApp(AppID app_id) { - 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) - { - // 长按 ■ 确认键切换调节字段 (在修改小时与修改分钟间跳转) - 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) - { - // 短按 ■ 确认键保存最新时钟配置并退出编辑模式 - 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; // 在时间设置模式下屏蔽拦截其他一切无关按键事件 - } - - // ====== 正常时钟对待机状态下的按键拦截处理 ====== - - // 1. 长按 ■ 确认键,打开设置主菜单 MenuApp - if (evt->key_event == KEY_EVENT_CONFIRM_LONG) { - AppManager_StartApp(APP_ID_MENU); - return EVENT_HANDLED; - } - - // 2. 长按 ▲ 键,一键切换系统的 布防 (ARMED) 和 撤防 (DISARMED) 状态 - if (evt->key_event == KEY_EVENT_UP_LONG) { - if (current_state == STATE_ARMED) { - current_state = STATE_DISARMED; - } else { - current_state = STATE_ARMED; - } - // 布撤防状态切换成功后进行 100ms 短震确认,并重新刷写时钟待机页 - MOTOR = 1; Delay_ms(100); MOTOR = 0; - UI_ShowClockPage(current_state, current_hour, current_min); - return EVENT_HANDLED; - } - - // 3. 遇到 SOS 物理求救按键(无论短按还是长按),直接进入 SOSAPP - 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; // 其他未被关心的事件交由后台应用或丢弃 -} - - -/* ========================================================================= - * 2. MenuApp (菜单选择应用) 具体生命周期回调实现 - * ========================================================================= */ - -// menu_level: 0-主菜单, 1-选择传感器二级对码菜单, 2-网络摄像机(IPC)一键绑定发射中 -static u8 menu_level = 0; -// 记录上一次进行 IPC 绑定无线射频发射的时间 Tick 数,实现周期性发射 -static u16 last_bind_tx_ms = 0; - -/** - * @brief MenuApp 启动回调 - */ -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"); -} - -/** - * @brief MenuApp 轮询主回调 - * @details 当且仅当进入 IPC 绑定状态下 (menu_level == 2) 时运行。 - * - 在 FCOB 灯条上产生青色 (R=0, G=val, B=val) 的非阻塞呼吸效果以暗示绑定进程。 - * - 控制无线发射引脚,每隔 500ms 向外广播一次克隆的 EV1527 24位出厂物理 ID 的绑定对码帧。 - */ -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~500 范围比例映射为 0~200 亮度 - RGB_Send(0, val, val, 0, val, val); // 渲染青色 (G+B) - - // ====== 控制无线发射,每 500ms 广播一次配对数据 ====== - if (ms_tick - last_bind_tx_ms >= 500 || ms_tick < last_bind_tx_ms) - { - last_bind_tx_ms = ms_tick; // 备份当前 Tick 时间 - RF_SetMode(2); // 暂时切换射频芯片为发射调制模式 - EV1527_Transmit(CLONED_ADDR, 0x01); // 调制发射手环的 24 位物理 ID + 绑定特定数据码 (0x01) - RF_SetMode(0); // 切回关闭/高阻状态,避免噪声产生 - } - } -} - -/** - * @brief MenuApp 退出清理回调 - */ -void MenuApp_onClose(void) -{ - menu_level = 0; - RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭 RGB - RF_SetMode(1); // 重新开启无线接收模式 - Uart_SendString("[MenuApp] Closed\r\n"); -} - -/** - * @brief MenuApp 接收按键与控制事件回调 - */ -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) { - // 功能 1:设置时间。启动待机时钟应用,并调用微调编辑入口 - AppManager_StartApp(APP_ID_CLOCK); - Start_Time_Edit(); - } else if (menu_select == 1) { - // 功能 2:传感器学习。进入二级传感器类别选择菜单 - menu_level = 1; - menu_select = 0; - UI_ShowPairMenuPage(menu_select); // 绘制二级设备分类页 - } else if (menu_select == 2) { - // 功能 3:摄像机配对。进入摄像机绑定状态 - menu_level = 2; - last_bind_tx_ms = ms_tick; // 记录初始发射时间 - current_state = STATE_IPC_BIND; // 系统设为 IPC 绑定状态 - 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) { - // 确认选择:缓存用户选好的待绑定硬件类型,短震 100ms 反馈,并加载对码学习应用 (PairApp) - captured_type = menu_select; - MOTOR = 1; - Delay_ms(100); - MOTOR = 0; - AppManager_StartApp(APP_ID_PAIR); // 跳转加载 PairApp - return EVENT_HANDLED; - } - if (evt->key_event == KEY_EVENT_DOWN_LONG) { - // 长按 ▼ 键,从二级菜单退回到一级主菜单 - menu_level = 0; - menu_select = 1; // 回到 APPAIRAGE 选项上 - UI_ShowMainMenu(menu_select); - return EVENT_HANDLED; - } - } - else if (menu_level == 2) - { - // ====== 处于摄像机配对一键绑定发送层 ====== - // 长按 ■ 确认键 (3秒) 或 长按 ▼ 键 (2秒) 均表示取消绑定,退出发射并返回一级主菜单 - if (evt->key_event == KEY_EVENT_CONFIRM_LONG || evt->key_event == KEY_EVENT_DOWN_LONG) { - menu_level = 0; - menu_select = 2; // 回到 LIAISON 选项上 - current_state = STATE_PAIR_MENU; - RGB_Send(0, 0, 0, 0, 0, 0); // 关闭呼吸灯 - UI_ShowMainMenu(menu_select); - return EVENT_HANDLED; - } - } - return EVENT_IGNORED; -} - - -/* ========================================================================= - * 3. PairApp (无线传感器对码学习应用) 具体生命周期回调实现 - * ========================================================================= */ - -// PairApp 私有状态状态机机定义 -enum { - PAIR_STATE_WAIT, // 等待信号输入 (雷达搜寻画面) - PAIR_STATE_CONFIRM, // 已捕获,防区选择与微调确认画面 - PAIR_STATE_SUCCESS, // 绑定成功,显示 SUCCESS 对勾页 - PAIR_STATE_FAIL // 超时或失败,显示 FAIL 叉号页 -}; - -static u8 pair_state; // 当前的对码阶段状态 -static u8 last_radar_sec; // 上一次雷达页面帧索引备份 -static u8 captured_zone = 1; // 当前配置准备分配给该传感器的防区号 (1~99) -static u8 success_start_sec = 0; // 记录对码成功时的开始秒数,以便延迟退出 -static u8 fail_start_sec = 0; // 记录对码失败时的开始秒数 - -/** - * @brief PairApp 启动回调 - */ -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; // 默认防区分配号为 1 - RF_SetMode(1); // 开启无线接收 - - // 初始化对码搜索页面绘制 - UI_ShowPairWaitPage(0); - - // 开启白色 RGB 幻彩双灯珠常亮 (R=150, G=150, B=150),作为对码标志 - RGB_Send(150, 150, 150, 150, 150, 150); - - Uart_SendString("[PairApp] Started (WAIT)\r\n"); -} - -/** - * @brief PairApp 轮询主回调 - * @details 非阻塞式控制以下配对时序: - * 1. 处于 PAIR_STATE_WAIT 状态:控制双灯进行白色 3Hz 高频闪烁,控制雷达动画绘制,并进行 30 秒超时判定。 - * 同时监测射频接收,收到无线码后,排除 SOS (0x08) 码,将系统切入防区确认状态。 - * 2. 处于 PAIR_STATE_CONFIRM 状态:控制灯光橙色慢闪。 - * 3. 处于 SUCCESS / FAIL 状态:控制延迟 2秒 或 5秒 自动退出,重新回到二级对码菜单页。 - */ -void PairApp_onRun(void) -{ - u32 rx_addr; - u8 rx_data; - u8 flash; - u8 frame; - - if (pair_state == PAIR_STATE_WAIT) { - // ====== 等待状态:白色高频快闪 (3Hz,周期 333ms) ====== - flash = (ms_tick / 166) % 2; - if (flash) RGB_Send(150, 150, 150, 150, 150, 150); - else RGB_Send(0, 0, 0, 0, 0, 0); - - // 每隔 250ms,滚动绘制雷达搜索的 3 帧图片,构成扩散环形雷达图 - frame = (u8)((ms_tick / 250) % 3); - if (frame != last_radar_sec) { - last_radar_sec = frame; - UI_ShowPairWaitPage(frame); - } - - // ====== 30 秒超时判定 ====== - if (pair_timeout_ms >= 30000UL) { - pair_timeout_ms = 0; - pair_state = PAIR_STATE_FAIL; // 切入失败 - current_state = STATE_PAIR_FAIL; - fail_start_sec = current_sec; // 记录失败时间 - 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); // 橙色 (G=180, R=100) - else RGB_Send(0, 0, 0, 0, 0, 0); - } else if (pair_state == PAIR_STATE_SUCCESS) { - // ====== 成功状态:延时 2 秒自动返回二级设置菜单 ====== - u8 elapsed = (current_sec >= success_start_sec) ? (current_sec - success_start_sec) : (60 + current_sec - success_start_sec); - if (elapsed >= 2) - { - AppManager_StartApp(APP_ID_MENU); - } - } else if (pair_state == PAIR_STATE_FAIL) { - // ====== 失败状态:延时 5 秒自动返回二级设置菜单 ====== - u8 elapsed = (current_sec >= fail_start_sec) ? (current_sec - fail_start_sec) : (60 + current_sec - fail_start_sec); - if (elapsed >= 5) - { - AppManager_StartApp(APP_ID_MENU); - } - } -} - -/** - * @brief PairApp 退出清理 - */ -void PairApp_onClose(void) -{ - RGB_Send(0, 0, 0, 0, 0, 0); // 关灯 - MOTOR = 0; // 停震 - RF_SetMode(1); // 重开射频接收 - Uart_SendString("[PairApp] Closed\r\n"); -} - -/** - * @brief PairApp 接收事件处理 (防区微调与对码保存) - */ -EventResult PairApp_onEvent(SystemEvent *evt) -{ - if (pair_state == PAIR_STATE_CONFIRM) { - if (evt->key_event == KEY_EVENT_UP_CLICK) { - // 短按 ▲ 键增加目标防区号 (最大 99) - if (captured_zone < 99) { - captured_zone++; - UI_ShowPairConfirmPage(captured_type, captured_zone); - } - return EVENT_HANDLED; - } - if (evt->key_event == KEY_EVENT_DOWN_CLICK) { - // 短按 ▼ 键减小目标防区号 (最小 1) - 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_sec = current_sec; // 记录开始秒数 - 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) { - // 长按 ■ 确认键,放弃本次捕获直接取消配对,回到主菜单 - RGB_Send(0, 0, 0, 0, 0, 0); - AppManager_StartApp(APP_ID_MENU); - return EVENT_HANDLED; - } - } - - // 在搜寻期或确认期,长按 ▼ 键皆可强制放弃并退回二级菜单 - 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; -} - - -/* ========================================================================= - * 4. AlarmApp (传感器报警显示与振动反馈应用) 具体生命周期回调实现 - * ========================================================================= */ - -/** - * @brief AlarmApp 启动回调 - * @details 判定触发警报的传感器信息,初始化报警 UI,输出对应的声光颜色。 - */ -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); // 门磁: 红色 (GRB中R=180,G=0) - else if (type == 1) RGB_Send(100, 180, 0, 100, 180, 0); // 红外PIR: 黄色 (G=100, R=180) - 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); // 气感: 紫色 (R=180, B=180) - else RGB_Send(0, 0, 180, 0, 0, 180); // 水浸: 蓝色 (B=180) - - Uart_SendString("[AlarmApp] Started\r\n"); -} - -/** - * @brief AlarmApp 轮询主回调 - * @details 负责非阻塞式同步控制 RGB 爆闪,并向 OLED 屏幕的四周边框绘制或擦除红边框,形成像素爆闪动效。 - * (马达具体的报警振动波形由于对毫秒时序要求极高,已交由 Timer1_Isr 进行高精度的实时管理控制)。 - */ -void AlarmApp_onRun(void) -{ - u8 type = sensor_list[alarm_sensor_slot].type; - u8 flash = (ms_tick / 250) % 2; // 以 250ms 为周期周期亮灭 - - // 1. 同步控制 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); // 灭灯 - } - - // 2. 控制 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); - } -} - -/** - * @brief AlarmApp 退出清理 - */ -void AlarmApp_onClose(void) -{ - RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯条 - MOTOR = 0; // 停震 - current_state = STATE_NORMAL; - Uart_SendString("[AlarmApp] Closed\r\n"); -} - -/** - * @brief AlarmApp 接收事件处理回调 - * @details 报警发生时,用户按下手环任意键,均代表已阅读入侵警报,应用将结束报警并退回待机时钟。 - */ -EventResult AlarmApp_onEvent(SystemEvent *evt) -{ - if (evt->key_event != KEY_EVENT_NONE) { - AppManager_StartApp(APP_ID_CLOCK); // 清除报警并切回 Clock - return EVENT_HANDLED; - } - return EVENT_IGNORED; -} - - -/* ========================================================================= - * 5. SOSApp (SOS 紧急求救无线发射与警灯应用) 具体生命周期回调实现 - * ========================================================================= */ - -// 私有标志,用作记录 SOS 的发射半周期状态 -static u8 sos_last_phase; - -/** - * @brief SOSApp 启动回调 - */ -void SOSApp_OnStart(void) -{ - current_state = STATE_SOS_EMITTED; // 进入 SOS 紧急求救状态 - sos_last_phase = 99; - alarm_timer_ms = 0; // 马达震动波形计数器清零 - UI_ShowSosPage(); // 初始化渲染 SOS 警示主页 - - // 立即点亮左侧灯为红色,右侧熄灭,准备开启双色红灭交替爆闪 - RGB_Send(0, 200, 0, 0, 0, 0); - - Uart_SendString("[SOSApp] Started\r\n"); -} - -/** - * @brief SOSApp 轮询主回调 - * @details 负责控制两个非阻塞的时序任务: - * 1. 控制无线发射芯片:每 500ms 开启发射模式,广播发射手环的 24 位出厂物理 Factory ID + SOS特定数据码 (0x08)。 - * 2. 控制警示灯效:以 50ms 的超快频率让灯条上的两个灯珠进行左右交替红色爆闪,达到强烈警告的视觉效果。 - * 同时同步闪烁屏幕边框。 - */ -void SOSApp_onRun(void) -{ - // ====== 1. 控制无线射频,每 500ms 发射一次 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); // 发射克隆 ID 及其 SOS (0x08) 命令字 - RF_SetMode(0); // 释放接收 - } - - // ====== 2. 控制 RGB 双灯珠以 50ms 频率进行高频左右交替红色爆闪 ====== - { - u8 flash = (ms_tick / 50) % 2; - if (flash) { - RGB_Send(0, 200, 0, 0, 0, 0); // 灯1亮红 (G=200,GRB中R排在第二),灯2灭 - } else { - RGB_Send(0, 0, 0, 0, 200, 0); // 灯1灭,灯2亮红 - } - } - - // ====== 3. 控制屏幕绘制多层爆闪红白框 ====== - 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); - } -} - -/** - * @brief SOSApp 退出清理 - */ -void SOSApp_onClose(void) -{ - RGB_Send(0, 0, 0, 0, 0, 0); // 关闭 RGB - MOTOR = 0; // 停震 - current_state = STATE_NORMAL; - Uart_SendString("[SOSApp] Closed\r\n"); -} - -/** - * @brief SOSApp 接收事件处理 - * @details 紧急呼救状态下,短按或长按物理按键,代表解除报警,直接切回待机时钟 ClockApp。 - */ -EventResult SOSApp_onEvent(SystemEvent *evt) -{ - if (evt->key_event != KEY_EVENT_NONE) { - AppManager_StartApp(APP_ID_CLOCK); // 退出 SOS,回到时钟 - return EVENT_HANDLED; - } - return EVENT_IGNORED; + if (app_id == APP_ID_CLOCK) AppManager_SwitchToForeground(&clock_app); + else if (app_id == APP_ID_MENU) AppManager_SwitchToForeground(&menu_app); + else if (app_id == APP_ID_PAIR) AppManager_SwitchToForeground(&pair_app); + else if (app_id == APP_ID_ALARM) AppManager_SwitchToForeground(&alarm_app); + else if (app_id == APP_ID_SOS) AppManager_SwitchToForeground(&sos_app); } diff --git a/App/app_manager.h b/App/app_manager.h index f2489da..cc0e85a 100644 --- a/App/app_manager.h +++ b/App/app_manager.h @@ -3,56 +3,75 @@ #include "config.h" -/* ====== 应用程序管理器 (AppManager) 核心接口 ====== */ +// 动态挂载的 WristbandApp 控制块定义 +typedef struct WristbandApp { + u8 app_id; // 应用唯一识别 ID + char *app_name; // 应用名称 + void (*OnStart)(void); // 启动生命周期,进入前台时调用 + void (*onRun)(void); // 轮询生命周期,主循环每次被调度 + void (*onClose)(void); // 关闭生命周期,退出前台/后台时清理资源 + EventResult (*onEvent)(SystemEvent *evt); // 事件处理生命周期,接收按键事件 +} WristbandApp; /** - * @brief 初始化应用程序管理器 - * @details 负责将应用管理器的系统注册表清零,随后逐一注册系统支持的五个前台应用: - * ClockApp (待机时钟), MenuApp (主菜单), PairApp (传感器对码), - * AlarmApp (传感器警报), SOSApp (主动求救呼叫)。最后默认加载并启动 ClockApp。 + * @brief 初始化应用管理器 + * @details 将前台活跃槽位与后台的三个槽位全部清空为 NULL。 */ void AppManager_Init(void); /** - * @brief 启动指定的应用程序 - * @param app_id 待启动应用的目标 ID (如 APP_ID_MENU, APP_ID_PAIR) - * @details 会执行完整的切应用生命周期流转: - * 1. 检查目标是否合法且包含有效的 OnStart 启动回调。 - * 2. 对当前正在活跃运行的前台应用调用其 onClose(),以关闭该应用的屏幕内容、马达振动或射频特定模式。 - * 3. 将活跃应用指针指向新应用,将其 is_active 和 is_running 标志位置 1。 - * 4. 调用新应用的 OnStart() 初始化回调以绘制新页面。 + * @brief 动态将一个应用切换至前台活跃运行 + * @param app 目标应用的结构体指针 + * @return bit 1-切换成功,0-失败 + * @details 流程: + * 1. 若前台已有应用运行,且该应用未挂载在后台,则对其执行 onClose() 关闭生命周期。 + * 2. 将 fg_app 指针指向新的 app。 + * 3. 若该 app 目前并未在后台槽位中运行,则执行其 OnStart() 启动生命周期。 */ -void AppManager_StartApp(AppID app_id); +bit AppManager_SwitchToForeground(WristbandApp *app); /** - * @brief 向当前活跃的前台应用程序派发事件 - * @param evt 要分发派送的系统事件 + * @brief 动态将一个应用挂载到后台槽位 + * @param app 目标应用的结构体指针 + * @return bit 1-挂载成功,0-槽位已满挂载失败 + * @details 后台总共支持 3 个槽位。若挂载成功,其 onRun 将会持续在主循环中被轮询。 + */ +bit AppManager_AttachToBackground(WristbandApp *app); + +/** + * @brief 动态将一个应用从后台卸载 + * @param app 目标应用的结构体指针 + * @details 从后台槽位数组中移除该指针。若该应用当前也不在前台运行,将对其执行 onClose() 清理外设。 + */ +void AppManager_DetachFromBackground(WristbandApp *app); + +/** + * @brief 将捕获的事件派发给前台活跃应用 + * @param evt 系统派发来的事件 */ void AppManager_DispatchEvent(SystemEvent evt); /** - * @brief 在系统主循环中持续轮询运行活跃应用 - * @details 负责从全局注册表中定位到 active_app,并轮询执行其 onRun() 周期回调。 - * 该接口同时集成了【CPU占用率限制机制】:如果在调用 onRun 前后,检测到应用 - * 占用 CPU 时间超过了 APP_ONRUN_MAX_TICKS (5ms),它将在下一次循环中暂时挂起 - * 该应用,强制释放本次 CPU 占用以防止界面卡死或影响底层射频解调。 + * @brief 在系统主循环中持续轮询轮转前后台所有活跃的应用 + * @details 负责轮询前台 fg_app 对应的 onRun(),并遍历轮询后台 bg_apps 数组中所有有效应用的 onRun()。 */ void AppManager_RunActiveApp(void); /** - * @brief 获取当前前台活跃应用的唯一应用程序 ID - * @return AppID 当前活跃前台应用的枚举ID + * @brief 获取当前前台活跃应用的 ID + * @return u8 当前活跃前台应用的 ID 编号。若无则返回 255 (0xFF) */ -AppID AppManager_GetActiveAppID(void); +u8 AppManager_GetActiveAppID(void); +// 兼容性接口声明 +void AppManager_StartApp(u8 app_id); -/* ====== 时间编辑状态机交互接口 ====== */ - -/** - * @brief 启动时钟数值调整编辑状态机 - * @details 由 MenuApp 的 "HORLOGE" 项被确认点击后触发。用于直接将系统运行状态切入 - * STATE_SET_TIME,控制灯条常亮蓝色,马达短振反馈,并进入时间参数微调页。 - */ -void Start_Time_Edit(void); +// ====== 系统中各独立 App 实例的外包声明 (各 App 内部进行实体定义) ====== +extern WristbandApp clock_app; +extern WristbandApp menu_app; +extern WristbandApp pair_app; +extern WristbandApp alarm_app; +extern WristbandApp sos_app; +extern WristbandApp rf_monitor_app; // 后台射频监控应用 #endif // __APP_MANAGER_H__ diff --git a/App/app_menu.c b/App/app_menu.c new file mode 100644 index 0000000..5ff5082 --- /dev/null +++ b/App/app_menu.c @@ -0,0 +1,187 @@ +#include "app_manager.h" +#include "xtell.h" +#include "ui.h" +#include "rgb.h" +#include "event.h" +#include "system.h" +#include "../Drivers/rf.h" + +// 外部全局变量引用 +extern SystemState current_state; +extern u8 menu_select; +extern u8 captured_type; +extern volatile u16 ms_tick; + +// 菜单应用私有变量 +static u8 menu_level = 0; // 0-主菜单, 1-二级传感器选择菜单, 2-IPC绑定一键发射 +static u16 last_bind_tx_ms = 0; // 记录上一次绑定发射的时间戳 + +// 克隆的手环出厂物理 ID 地址定义 (用于一键绑定) +#define CLONED_ADDR 0x55AAAA + +// 声明本应用的生命周期回调函数 +static void MenuApp_OnStart(void); +static void MenuApp_onRun(void); +static void MenuApp_onClose(void); +static EventResult MenuApp_onEvent(SystemEvent *evt); + +// 实体化定义菜单应用控制块 +WristbandApp menu_app = { + APP_ID_MENU, + "Menu", + MenuApp_OnStart, + MenuApp_onRun, + MenuApp_onClose, + MenuApp_onEvent +}; + +/** + * @brief MenuApp 启动回调 + */ +static 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); // 熄灭所有灯条 + XTELL_LOG("[MenuApp] Started\r\n"); +} + +/** + * @brief MenuApp 轮询主回调 + */ +static 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); // 渲染青色 (G+B) + + // ====== 控制无线发射,每 500ms 广播一次配对数据 ====== + 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); // 调制发射手环克隆 ID + 绑定数据码 0x01 + RF_SetMode(0); // 切回关闭状态,避免产生噪声 + XTELL_LOG("[MenuApp] IPC Binding frame emitted\r\n"); + } + } +} + +/** + * @brief MenuApp 退出清理回调 + */ +static void MenuApp_onClose(void) +{ + menu_level = 0; + RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭 RGB + RF_SetMode(1); // 重新开启无线接收模式 + XTELL_LOG("[MenuApp] Closed\r\n"); +} + +/** + * @brief MenuApp 接收按键与控制事件回调 + */ +static 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) { + // 功能 1:设置时间。启动待机时钟应用,并调用微调编辑入口 + AppManager_SwitchToForeground(&clock_app); + Start_Time_Edit(); + } else if (menu_select == 1) { + // 功能 2:传感器学习。进入二级传感器类别选择菜单 + menu_level = 1; + menu_select = 0; + UI_ShowPairMenuPage(menu_select); // 绘制二级设备分类页 + } else if (menu_select == 2) { + // 功能 3:相机配对。进入摄像机一键绑定发射状态 + menu_level = 2; + last_bind_tx_ms = ms_tick; // 记录初始发射时间 + current_state = STATE_IPC_BIND; // 系统设为 IPC 绑定状态 + UI_ShowBindingPage(CLONED_ADDR); // 绘制相机绑定信息页 + } + return EVENT_HANDLED; + } + if (evt->key_event == KEY_EVENT_CONFIRM_LONG) { + // 长按 ■ 确认键,退出设置菜单直接返回待机时钟 + AppManager_SwitchToForeground(&clock_app); + 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) { + // 确认选择:缓存用户选好的传感器类别,短震反馈并加载对码应用 (PairApp) + captured_type = menu_select; + MOTOR = 1; + Delay_ms(100); + MOTOR = 0; + AppManager_SwitchToForeground(&pair_app); // 切换至 PairApp 对码 + return EVENT_HANDLED; + } + if (evt->key_event == KEY_EVENT_DOWN_LONG) { + // 长按 ▼ 键,从二级菜单退回到一级主菜单 + menu_level = 0; + menu_select = 1; // 回到 APPAIRAGE 选项上 + UI_ShowMainMenu(menu_select); + return EVENT_HANDLED; + } + } + else if (menu_level == 2) + { + // ====== 处于摄像机配对一键绑定发送层 ====== + // 长按 ■ 确认键 或 长按 ▼ 键,均表示取消绑定,退出发射并返回一级主菜单 + if (evt->key_event == KEY_EVENT_CONFIRM_LONG || evt->key_event == KEY_EVENT_DOWN_LONG) { + menu_level = 0; + menu_select = 2; // 回到 LIAISON 选项上 + current_state = STATE_PAIR_MENU; + RGB_Send(0, 0, 0, 0, 0, 0); // 关闭呼吸灯 + UI_ShowMainMenu(menu_select); + return EVENT_HANDLED; + } + } + return EVENT_IGNORED; +} diff --git a/App/app_pair.c b/App/app_pair.c new file mode 100644 index 0000000..7992c25 --- /dev/null +++ b/App/app_pair.c @@ -0,0 +1,231 @@ +#include "app_manager.h" +#include "xtell.h" +#include "ui.h" +#include "rgb.h" +#include "event.h" +#include "system.h" +#include "database.h" +#include "../Drivers/rf.h" + +// 外部全局变量引用 +extern SystemState current_state; +extern u8 current_sec; +extern u8 captured_type; +extern u32 captured_addr; +extern volatile u16 ms_tick; + +// 对码状态机定义 +enum { + PAIR_STATE_WAIT, // 等待射频探测器触发 (扩散雷达页面) + PAIR_STATE_CONFIRM, // 已捕获,进入分配防区确认页面 + PAIR_STATE_SUCCESS, // 对码绑定成功界面 (SUCCESS) + PAIR_STATE_FAIL // 超时或失败界面 (FAIL) +}; + +// 本地私有变量 +static u8 pair_state; // 当前的对码阶段状态 +static u8 last_radar_sec; // 备份上一次雷达动画的帧索引 +static u8 captured_zone = 1; // 准备分配的防区号 (1~99) +static u8 success_start_sec = 0;// 对码成功时的起始秒数 (延迟退场) +static u8 fail_start_sec = 0; // 对码失败时的起始秒数 +static u32 pair_timeout_ms = 0; // 对码超时毫秒计数器 +static u16 last_run_tick = 0; // 上一次执行 onRun 时的毫秒滴答数 + +// 声明本应用的生命周期回调函数 +static void PairApp_OnStart(void); +static void PairApp_onRun(void); +static void PairApp_onClose(void); +static EventResult PairApp_onEvent(SystemEvent *evt); + +// 实体化定义对码应用控制块 +WristbandApp pair_app = { + APP_ID_PAIR, + "Pair", + PairApp_OnStart, + PairApp_onRun, + PairApp_onClose, + PairApp_onEvent +}; + +/** + * @brief PairApp 启动回调 + */ +static void PairApp_OnStart(void) +{ + // 1. 为了防止后台射频监听与对码射频接收冲突,将后台射频监听应用暂时卸载 + AppManager_DetachFromBackground(&rf_monitor_app); + + pair_state = PAIR_STATE_WAIT; + last_radar_sec = 99; // 强制刷写 + current_state = STATE_PAIR_WAIT; + pair_timeout_ms = 0; + last_run_tick = ms_tick; + captured_zone = 1; + RF_SetMode(1); // 开启无线接收模式 + + // 初始化对码搜索页面绘制 + UI_ShowPairWaitPage(0); + + // 开启白色幻彩双灯珠常亮 (R=150, G=150, B=150),作为对码状态视觉指示 + RGB_Send(150, 150, 150, 150, 150, 150); + + XTELL_LOG("[PairApp] Started (WAIT)\r\n"); +} + +/** + * @brief PairApp 轮询主回调 + */ +static void PairApp_onRun(void) +{ + u32 rx_addr; + u8 rx_data; + u8 flash; + u8 frame; + u16 tick_diff; + + // 计算非阻塞的毫秒时间增量 + if (ms_tick >= last_run_tick) { + tick_diff = ms_tick - last_run_tick; + } else { + tick_diff = 1000 + ms_tick - last_run_tick; + } + last_run_tick = ms_tick; + + if (pair_state == PAIR_STATE_WAIT) { + // ====== 等待射频数据期间,白光 3Hz 高频闪烁 (周期 333ms) ====== + flash = (ms_tick / 166) % 2; + if (flash) RGB_Send(150, 150, 150, 150, 150, 150); + else RGB_Send(0, 0, 0, 0, 0, 0); + + // 每隔 250ms,滚动绘制雷达搜索的 3 帧图片,构成动效 + frame = (u8)((ms_tick / 250) % 3); + if (frame != last_radar_sec) { + last_radar_sec = frame; + UI_ShowPairWaitPage(frame); + } + + // 累加计算 30 秒的对码时间上限 + pair_timeout_ms += tick_diff; + if (pair_timeout_ms >= 30000UL) { + pair_timeout_ms = 0; + pair_state = PAIR_STATE_FAIL; + current_state = STATE_PAIR_FAIL; + fail_start_sec = current_sec; + UI_ShowPairFailPage(1); // 绘制超时大红叉页 + + // 红色双灯常亮警告 + RGB_Send(200, 0, 0, 200, 0, 0); + XTELL_LOG("[PairApp] Timeout fail!\r\n"); + return; + } + + // ====== 物理射频包解调接收监听 ====== + if (EV1527_Decode(&rx_addr, &rx_data)) { + // 保护机制:排除 SOS 码 (0x08) + if (rx_data != 0x08) { + captured_addr = rx_addr; // 缓存捕获的物理 ID 地址 (含数据码) + pair_state = PAIR_STATE_CONFIRM; // 进入防区微调确认模式 + current_state = STATE_PAIR_CONFIRM; + RF_SetMode(0); // 暂时关闭物理射频,防止再次覆盖 + captured_zone = 1; + UI_ShowPairConfirmPage(captured_type, captured_zone); + XTELL_LOG("[PairApp] Sensor captured. Addr="); + XTELL_LOG_HEX32(rx_addr); + XTELL_LOG("\r\n"); + } + } + } + else if (pair_state == PAIR_STATE_CONFIRM) { + // ====== 确认状态:橙色慢闪 (1.5Hz,周期 666ms) ====== + u8 flash_orange = (ms_tick / 333) % 2; + if (flash_orange) RGB_Send(100, 180, 0, 100, 180, 0); // 橙色 (G=180, R=100) + else RGB_Send(0, 0, 0, 0, 0, 0); + } + else if (pair_state == PAIR_STATE_SUCCESS) { + // ====== 成功展示状态:延时 2 秒自动返回二级设置菜单 ====== + u8 elapsed = (current_sec >= success_start_sec) ? (current_sec - success_start_sec) : (60 + current_sec - success_start_sec); + if (elapsed >= 2) { + AppManager_SwitchToForeground(&menu_app); + } + } + else if (pair_state == PAIR_STATE_FAIL) { + // ====== 失败展示状态:延时 5 秒自动返回二级设置菜单 ====== + u8 elapsed = (current_sec >= fail_start_sec) ? (current_sec - fail_start_sec) : (60 + current_sec - fail_start_sec); + if (elapsed >= 5) { + AppManager_SwitchToForeground(&menu_app); + } + } +} + +/** + * @brief PairApp 退出清理 + */ +static void PairApp_onClose(void) +{ + RGB_Send(0, 0, 0, 0, 0, 0); // 关闭 RGB + MOTOR = 0; // 停震 + RF_SetMode(1); // 开启无线接收 + + // 重新挂载后台射频监控应用,以便回到时钟状态下能继续捕获报警 + AppManager_AttachToBackground(&rf_monitor_app); + + XTELL_LOG("[PairApp] Closed\r\n"); +} + +/** + * @brief PairApp 事件拦截处理回调 + */ +static EventResult PairApp_onEvent(SystemEvent *evt) +{ + if (pair_state == PAIR_STATE_CONFIRM) { + if (evt->key_event == KEY_EVENT_UP_CLICK) { + // 短按 ▲ 键增加目标分配防区号 (上限 99) + if (captured_zone < 99) { + captured_zone++; + UI_ShowPairConfirmPage(captured_type, captured_zone); + } + return EVENT_HANDLED; + } + if (evt->key_event == KEY_EVENT_DOWN_CLICK) { + // 短按 ▼ 键减小目标分配防区号 (下限 1) + if (captured_zone > 1) { + captured_zone--; + UI_ShowPairConfirmPage(captured_type, captured_zone); + } + return EVENT_HANDLED; + } + if (evt->key_event == KEY_EVENT_CONFIRM_CLICK) { + // 短按 ■ 确认键,写入 EEPROM 传感器数据库并保存 + Add_Sensor_With_Zone(captured_addr, captured_type, captured_zone); + + pair_state = PAIR_STATE_SUCCESS; + current_state = STATE_PAIR_SUCCESS; + success_start_sec = current_sec; + UI_ShowPairSuccessPage(); // 绘制打勾页 + + // 绿色双灯珠常亮,震动马达长震 400ms 反馈 + RGB_Send(0, 200, 0, 0, 200, 0); + MOTOR = 1; + Delay_ms(400); + MOTOR = 0; + + XTELL_LOG("[PairApp] Sensor saved successfully.\r\n"); + return EVENT_HANDLED; + } + if (evt->key_event == KEY_EVENT_CONFIRM_LONG) { + // 长按 ■ 确认键放弃本次捕获,直接返回二级主菜单 + RGB_Send(0, 0, 0, 0, 0, 0); + AppManager_SwitchToForeground(&menu_app); + return EVENT_HANDLED; + } + } + + // 在对码搜寻或确认防区期间,长按 ▼ 键均代表强行取消对码直接退回菜单页 + if (evt->key_event == KEY_EVENT_DOWN_LONG) { + RGB_Send(0, 0, 0, 0, 0, 0); + AppManager_SwitchToForeground(&menu_app); + return EVENT_HANDLED; + } + + return EVENT_IGNORED; +} diff --git a/App/app_rf_monitor.c b/App/app_rf_monitor.c new file mode 100644 index 0000000..2eac334 --- /dev/null +++ b/App/app_rf_monitor.c @@ -0,0 +1,106 @@ +#include "app_manager.h" +#include "xtell.h" +#include "event.h" +#include "database.h" +#include "system.h" +#include "../Drivers/rf.h" + +// 外部全局变量引用 +extern SystemState current_state; +extern u32 captured_addr; +extern u8 alarm_sensor_slot; +extern Sensor_Slot xdata sensor_list[16]; + +// 声明本应用的生命周期回调函数 +static void RfMonitorApp_OnStart(void); +static void RfMonitorApp_onRun(void); +static void RfMonitorApp_onClose(void); +static EventResult RfMonitorApp_onEvent(SystemEvent *evt); + +// 实体化定义后台射频监控应用控制块 +WristbandApp rf_monitor_app = { + 0xFE, // 后台应用 ID 设为 0xFE + "RF_Monitor", + RfMonitorApp_OnStart, + RfMonitorApp_onRun, + RfMonitorApp_onClose, + RfMonitorApp_onEvent +}; + +/** + * @brief RfMonitorApp 启动回调 + */ +static void RfMonitorApp_OnStart(void) +{ + RF_SetMode(1); // 开启天线并设为接收模式 + XTELL_LOG("[RF_Monitor] Background RF monitor started.\r\n"); +} + +/** + * @brief RfMonitorApp 轮询主回调 + * @details 负责在后台静默轮询物理射频 EV1527 解码器。 + * 解码成功后在本地传感器数据库中匹配,并执行布撤防及防区过滤逻辑, + * 匹配通过后一键拉起前台报警应用。 + */ +static void RfMonitorApp_onRun(void) +{ + u32 rx_addr; + u8 rx_data; + u8 slot; + + // 常态对待机下的射频检测与触发过滤逻辑 + if (EV1527_Decode(&rx_addr, &rx_data)) { + XTELL_LOG("[RF_Monitor] Decoded Addr="); + XTELL_LOG_HEX32(rx_addr); + XTELL_LOG(", Data="); + XTELL_LOG_HEX8(rx_data); + XTELL_LOG("\r\n"); + + // 比对收到的 24 位射频源地址是否合法注册在手环本地数据库中 + if (Check_Sensor_ID(rx_addr, &slot)) { + u8 sensor_type = sensor_list[slot].type; // 获取注册的设备类型 + if (sensor_type < 5) { + // 防区分类过滤与布撤防过滤逻辑: + // - zone == 0,或者烟感(2)、气感(3)、水浸(4) 为 24小时紧急防区,不受当前布防状态影响,强制触发报警; + // - 门磁(0)和红外(1)为普通边界入侵防区,只有当系统处于 STATE_ARMED (布防) 状态时,才触发警报。 + if (sensor_list[slot].zone == 0 || + sensor_type == 2 || + sensor_type == 3 || + sensor_type == 4 || + current_state == STATE_ARMED) + { + // 只有在前台不为紧急 SOS 呼救且当前不处于报警状态时,才执行报警调起 + if (AppManager_GetActiveAppID() != APP_ID_SOS && + AppManager_GetActiveAppID() != APP_ID_ALARM) + { + captured_addr = rx_addr; // 缓存报警地址 + alarm_sensor_slot = slot; // 缓存报警槽位号 + XTELL_LOG("[RF_Monitor] Intruder ALERT! Waking up alarm app...\r\n"); + AppManager_SwitchToForeground(&alarm_app); // 动态切入警报前台 + } + } else { + XTELL_LOG("[RF_Monitor] Ignored due to DISARMED state.\r\n"); + } + } + } else { + XTELL_LOG("[RF_Monitor] Unregistered sensor address.\r\n"); + } + } +} + +/** + * @brief RfMonitorApp 退出清理 + */ +static void RfMonitorApp_onClose(void) +{ + XTELL_LOG("[RF_Monitor] Background RF monitor closed.\r\n"); +} + +/** + * @brief RfMonitorApp 事件处理 (后台应用暂不拦截处理按键事件) + */ +static EventResult RfMonitorApp_onEvent(SystemEvent *evt) +{ + (void)evt; + return EVENT_IGNORED; +} diff --git a/App/app_sos.c b/App/app_sos.c new file mode 100644 index 0000000..e1b87e9 --- /dev/null +++ b/App/app_sos.c @@ -0,0 +1,137 @@ +#include "app_manager.h" +#include "xtell.h" +#include "ui.h" +#include "rgb.h" +#include "event.h" +#include "system.h" +#include "../Drivers/rf.h" +#include "../Drivers/lcd.h" + +// 外部全局变量引用 +extern SystemState current_state; +extern volatile u16 ms_tick; +extern volatile bit alarm_flash_flag; + +// 克隆的手环出厂物理 ID 地址 +#define CLONED_ADDR 0x55AAAA + +// 本地私有变量 +static u8 sos_last_phase = 99; // 记录上一次发射的 500ms 半周期状态 +static u32 alarm_timer_ms = 0; // SOS 马达震动波形计数器 +static u16 last_run_tick = 0; // 记录上一次轮询的 ticks + +// 声明本应用的生命周期回调函数 +static void SOSApp_OnStart(void); +static void SOSApp_onRun(void); +static void SOSApp_onClose(void); +static EventResult SOSApp_onEvent(SystemEvent *evt); + +// 实体化定义 SOS 呼救应用控制块 +WristbandApp sos_app = { + APP_ID_SOS, + "SOS", + SOSApp_OnStart, + SOSApp_onRun, + SOSApp_onClose, + SOSApp_onEvent +}; + +/** + * @brief SOSApp 启动回调 + */ +static void SOSApp_OnStart(void) +{ + current_state = STATE_SOS_EMITTED; // 进入 SOS 紧急求救状态 + sos_last_phase = 99; + alarm_timer_ms = 0; // 马达震动计数器清零 + last_run_tick = ms_tick; + UI_ShowSosPage(); // 初始化渲染 SOS 警示主页 + + // 立即点亮左侧灯为红色,右侧熄灭,准备开启双色红灯交替爆闪 + RGB_Send(0, 200, 0, 0, 0, 0); + + XTELL_LOG("[SOSApp] Started\r\n"); +} + +/** + * @brief SOSApp 轮询主回调 + * @details 包含 500ms 周期的高频射频 SOS 码发射、红蓝交替爆闪以及 + * 马达 (1000ms震动 / 200ms静音) 规律震动控制。 + */ +static void SOSApp_onRun(void) +{ + u16 tick_diff; + + // ====== 1. 控制无线射频,每 500ms 发射一次 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); // 发射克隆 ID 及其 SOS (0x08) 命令字 + RF_SetMode(0); // 释放接收 + XTELL_LOG("[SOSApp] SOS Radio packet emitted\r\n"); + } + + // ====== 2. 控制 RGB 双灯珠以 50ms 频率进行高频左右交替红蓝爆闪 ====== + { + u8 flash = (ms_tick / 50) % 2; + if (flash) { + RGB_Send(0, 200, 0, 0, 0, 200); // 灯 1 亮红 (GRB 中 R=200),灯 2 亮蓝 (B=200) + } else { + RGB_Send(0, 0, 200, 0, 200, 0); // 灯 1 亮蓝,灯 2 亮红 + } + } + + // ====== 3. 控制屏幕绘制多层爆闪红白框 ====== + 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); + } + + // ====== 4. 马达 SOS 震动节拍自处理 (1000ms 震动,200ms 静音) ====== + if (ms_tick >= last_run_tick) { + tick_diff = ms_tick - last_run_tick; + } else { + tick_diff = 1000 + ms_tick - last_run_tick; + } + last_run_tick = ms_tick; + + alarm_timer_ms += tick_diff; + if (alarm_timer_ms >= 1200) { + alarm_timer_ms = 0; + } + + if (alarm_timer_ms < 1000) { + MOTOR = 1; + } else { + MOTOR = 0; + } +} + +/** + * @brief SOSApp 退出清理 + */ +static void SOSApp_onClose(void) +{ + RGB_Send(0, 0, 0, 0, 0, 0); // 关闭 RGB + MOTOR = 0; // 停震 + current_state = STATE_NORMAL; + XTELL_LOG("[SOSApp] Closed\r\n"); +} + +/** + * @brief SOSApp 事件拦截处理回调 (按任意键退出 SOS) + */ +static EventResult SOSApp_onEvent(SystemEvent *evt) +{ + if (evt->key_event != KEY_EVENT_NONE) { + AppManager_SwitchToForeground(&clock_app); // 退出 SOS,返回时钟 + return EVENT_HANDLED; + } + return EVENT_IGNORED; +} diff --git a/App/clock.c b/App/clock.c new file mode 100644 index 0000000..85829f1 --- /dev/null +++ b/App/clock.c @@ -0,0 +1,37 @@ +#include "clock.h" +#include "xtell.h" + +// 软时钟基础计时变量 +u8 current_hour = 10; // 系统软时钟:小时 (默认初始化为 10 点) +u8 current_min = 35; // 系统软时钟:分钟 (默认初始化为 35 分) +u8 current_sec = 0; // 系统软时钟:秒 +volatile u8 clock_updated = 0; // 分钟跳变标志位,置位后用于驱动 UI 刷新时间显示 + +// 毫秒计数器 (Timer1 每 1ms 累加) +static u16 ms_cnt = 0; + +/** + * @brief 系统软时钟毫秒递增更新 (在 1ms 中断中调用) + * @details 负责由定时器 1ms 驱动递增软时钟,并在分钟发生跳变时将 clock_updated 置位 + */ +void Clock_IncMS(void) +{ + ms_cnt++; + if (ms_cnt >= 1000) { + ms_cnt = 0; // 1秒时间到 + current_sec++; + if (current_sec >= 60) { + current_sec = 0; // 1分钟时间到 + current_min++; + if (current_min >= 60) { + current_min = 0; // 1小时时间到 + current_hour++; + if (current_hour >= 24) { + current_hour = 0; // 小时循环 + } + } + clock_updated = 1; // 分钟跳变,标志位置位,前台待机 App 会捕获以刷新 UI + XTELL_LOG("[CLOCK] Time updated!\r\n"); + } + } +} diff --git a/App/clock.h b/App/clock.h new file mode 100644 index 0000000..4c9bd46 --- /dev/null +++ b/App/clock.h @@ -0,0 +1,17 @@ +#ifndef __CLOCK_H__ +#define __CLOCK_H__ + +#include "config.h" + +// 全局软时钟变量声明 +extern u8 current_hour; +extern u8 current_min; +extern u8 current_sec; +extern volatile u8 clock_updated; + +/** + * @brief 系统软时钟毫秒递增更新 (在 1ms 中断中调用) + */ +void Clock_IncMS(void); + +#endif // __CLOCK_H__ diff --git a/App/config.h b/App/config.h index 8d020b2..fae5251 100644 --- a/App/config.h +++ b/App/config.h @@ -68,18 +68,9 @@ typedef enum { KEY_EVENT_CONFIRM_LONG // ■ 确认键长按事件 (打开主菜单) } KeyEvent; -/* ====== 事件优先级枚举 ====== */ -typedef enum { - EVENT_PRIORITY_LOW, // 低优先级:系统内部状态更新 (如时钟分钟更新) - EVENT_PRIORITY_NORMAL, // 正常优先级:用户按键交互、菜单导航 - EVENT_PRIORITY_HIGH, // 高优先级:无线传感器入侵警报 - EVENT_PRIORITY_URGENT // 紧急优先级:SOS 主动求救 -} EventPriority; - /* ====== 系统事件结构体定义 ====== */ typedef struct { KeyEvent key_event; // 关联的按键事件类型 - EventPriority priority; // 事件的处理优先级 u32 extra_data; // 随事件携带的额外数据 (如 24 位射频源地址等) u8 extra_size; // 额外数据的有效字节大小 (0~4) } SystemEvent; diff --git a/App/event.c b/App/event.c index 1f2ad3c..c70cca8 100644 --- a/App/event.c +++ b/App/event.c @@ -2,6 +2,8 @@ #include "app_manager.h" #include "database.h" #include "system.h" +#include "clock.h" +#include "xtell.h" // 定义事件环形队列的最大深度为 8 #define EVENT_QUEUE_DEPTH 8 @@ -64,7 +66,6 @@ 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; @@ -118,46 +119,17 @@ void Event_Dispatcher_Loop(void) 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"); + XTELL_LOG("[EVENT] key="); + XTELL_LOG_HEX8((u8)evt.key_event); + XTELL_LOG("\r\n"); - // SOS 按键紧急处理:若非处于 SOS 呼救状态下,接收到 SOS 键动作,立即强行切入到 SOS_APP 并执行求救 + // SOS 按键紧急处理:若当前前台不为 SOS 应用,接收到 SOS 键动作,立即强行切入到 SOS_APP 并执行求救 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; } - // 传感器入侵报警过滤与跳转处理: - // 当接收到高优先级且带有 4 字节射频地址的数据包,说明有传感器触发了发射 - if (evt.priority == EVENT_PRIORITY_HIGH && evt.extra_size >= 4) { - u8 slot; - // 在本地数据库中检索该传感器地址是否合法登记过 - if (Check_Sensor_ID(evt.extra_data, &slot)) { - // 在不处于 SOS 的情况下,可正常强行切入 ALARM_APP 触发声光振警报 - if (AppManager_GetActiveAppID() != APP_ID_SOS) { - captured_addr = evt.extra_data; // 缓存报警地址 - alarm_sensor_slot = slot; // 缓存报警槽位 - AppManager_StartApp(APP_ID_ALARM); // 强制切入报警前台 - return; - } - } - } - - // 无线对码捕获处理: - // 若系统正处于对码应用 (APP_ID_PAIR),遇到普通优先级无线射频包,则将其派发给该应用进行匹配与登记 - 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); } @@ -182,89 +154,14 @@ void Timer1_Init(void) /** * @brief 定时器 1 中断服务程序 (每 1ms 触发一次) - * @details 承担了系统软时钟计算、报警与求救下的马达控制状态机以及 10ms 周期性按键消抖扫描的功能。 + * @details 承担了系统软时钟计算、10ms 周期性按键消抖扫描的功能。马达等特定业务已剥离至 App 内部。 */ void Timer1_Isr(void) interrupt 3 { SystemEvent evt; - // 1ms 递增滴答 - ms_tick++; - if (ms_tick >= 1000) { - ms_tick = 0; // 1秒时间已到,归零 - current_sec++; - if (current_sec >= 60) { - current_sec = 0; // 1分钟时间已到 - current_min++; - if (current_min >= 60) { - current_min = 0; // 1小时时间已到 - current_hour++; - if (current_hour >= 24) current_hour = 0; // 小时循环 - } - clock_updated = 1; // 标志位置1,提示前台时钟应用刷新显示 - } - } - - // 如果处于对码等待状态,对码超时计时器增加 (30秒超时判断) - if (current_state == STATE_PAIR_WAIT) { - pair_timeout_ms++; - } - - /* 振动马达定时状态机逻辑控制 */ - if (current_state == STATE_SOS_EMITTED) { - // SOS 模式:交替规律震动 (震动 1000ms,静音 200ms) - 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; // 5秒一周期 - - if (stype == 0) { - // 门磁警报波形:单次震动 300ms,停 4700ms - if (alarm_timer_ms < 300) MOTOR = 1; else MOTOR = 0; - } else if (stype == 1) { - // PIR 警报波形:震动 300ms,停 200ms,再震动 300ms,停 4200ms - 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) { - // 烟感警报波形:急促短闪三下 200ms-150ms-200ms-150ms-200ms - 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) { - // 水浸警报波形:慢速双长震波 600ms 和 600ms - 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 { - // 缺省状态默认单次 300ms - if (alarm_timer_ms < 300) MOTOR = 1; else MOTOR = 0; - } - } else { - // 其他常态下彻底关闭震动马达并归零计时器 - MOTOR = 0; - alarm_timer_ms = 0; - motor_timer_ms = 0; - } + // 1ms 定时更新软时钟 + Clock_IncMS(); // 报警屏幕红边框以 100ms 周期亮灭,进行颜色爆闪指示 alarm_flash_flag = (ms_tick / 100) % 2; @@ -294,12 +191,11 @@ void Timer1_Isr(void) interrupt 3 evt.extra_data = 0; evt.extra_size = 0; - // 双键组合判定:▲ 键和 ▼ 键同时按下的消抖与生成 + // 双键组合判定:▲ 键 and ▼ 键同时按下的消抖与生成 if (raw_up && raw_down) { comb_hold++; if (comb_hold == 300) { // 持续按下达 3 秒 (300 * 10ms = 3s) evt.key_event = KEY_EVENT_UP_DOWN_COMB; - evt.priority = EVENT_PRIORITY_NORMAL; EventQueue_Push(evt); // 压入队列 } key_up_hold = 0; // 清零单个按键计数,防止双击事件干扰 @@ -312,14 +208,12 @@ void Timer1_Isr(void) interrupt 3 key_up_hold++; if (key_up_hold == 300) { // 达到 3 秒判定为长按 evt.key_event = KEY_EVENT_UP_LONG; - evt.priority = EVENT_PRIORITY_NORMAL; EventQueue_Push(evt); } } else { // 如果松开时按下计数在消抖阈值(20ms)至长按阈值(3s)之间,则判定为一次短按 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; // 释放归零 @@ -330,13 +224,11 @@ void Timer1_Isr(void) interrupt 3 key_down_hold++; if (key_down_hold == 300) { // 长按 3 秒 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; // 释放归零 @@ -347,13 +239,11 @@ void Timer1_Isr(void) interrupt 3 key_confirm_hold++; if (key_confirm_hold == 300) { // 长按 3 秒打开主菜单 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; // 释放归零 @@ -362,15 +252,13 @@ void Timer1_Isr(void) interrupt 3 // SOS 物理求救按键状态机 if (raw_sos) { key_sos_hold++; - if (key_sos_hold == 300) { // 长按 3 秒,发出 URGENT 优先级求救,插入队首 + if (key_sos_hold == 300) { // 长按 3 秒,发出求救,插入队首 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/main.c b/App/main.c index 89cb873..0f5dcb6 100644 --- a/App/main.c +++ b/App/main.c @@ -5,17 +5,20 @@ #include "database.h" #include "ui.h" #include "system.h" +#include "clock.h" +#include "xtell.h" #include "../Drivers/rf.h" #include "../Drivers/lcd.h" +// 声明外部射频模拟变量 +extern volatile u32 mock_rx_addr; +extern volatile u8 mock_rx_data; +extern volatile bit mock_rf_ready; + /* ========================================================================= * 系统全局变量定义 * ========================================================================= */ SystemState current_state = STATE_NORMAL; // 系统整体的当前运行状态,默认为正常时钟待机页 -u8 current_hour = 10; // 系统软时钟:小时 (默认初始化为 10 点) -u8 current_min = 35; // 系统软时钟:分钟 (默认初始化为 35 分) -u8 current_sec = 0; // 系统软时钟:秒 -volatile u8 clock_updated = 0; // 分钟跳变标志位,置位后用于驱动 UI 刷新时间显示 u8 menu_select = 0; // 主菜单当前选中的行高亮索引 (0~2) u32 captured_addr = 0; // 配对或报警时,捕获到的传感器 24 位 EV1527 射频物理地址 @@ -46,17 +49,6 @@ volatile u16 comb_hold = 0; // ▲ + ▼ 组合键按下时长累 * ========================================================================= */ /** * @brief 解析通过串口 1 发送来的键盘及测试模拟指令 - * @param cmd 接收到的 ASCII 字符命令 - * @details 负责将字符指令转化对应的系统按键或射频事件,并推入事件队列以测试软件架构: - * - 'u'/'U': 模拟短按/长按 ▲ 键。 - * - 'd'/'D': 模拟短按/长按 ▼ 键。 - * - 's'/'S': 模拟短按/长按 SOS 物理键(优先插入队首)。 - * - 'c'/'C': 模拟组合键触发。 - * - 'a'/'A': 模拟无线传感器入侵触发警报信号,地址固定为 0x123456,zone=1。 - * - 'p'/'P': 模拟对码期间捕获的 24 位无线对码信号,地址固定为 0x789ABC。 - * - '1'/'2'/'3': 模拟系统的正常、已布防、已撤防状态切换。 - * - 'z'/'Z': 模拟超时,强行使闲置计数器置 6000,触发自动深度休眠。 - * - 't'/'T': 触发屏幕与无线控制引脚测试。 */ void Debug_ProcessCommand(char cmd) { @@ -67,39 +59,32 @@ void Debug_ProcessCommand(char cmd) 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"); + XTELL_LOG("[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"); + XTELL_LOG("[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"); + XTELL_LOG("[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"); + XTELL_LOG("[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"); + XTELL_LOG("[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"); + XTELL_LOG("[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"); + XTELL_LOG("[UART] KEY_UP+DOWN Combined\r\n"); } else if (cmd == 'a' || cmd == 'A') { u32 mock_addr = 0x123456; // 设定模拟的报警射频源地址 u8 mock_slot; @@ -107,48 +92,39 @@ void Debug_ProcessCommand(char cmd) 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; // 入侵报警设为 HIGH 优先级 - debug_evt.extra_data = mock_addr; - debug_evt.extra_size = 4; - EventQueue_InsertFront(debug_evt); // 高优先级插队 - Uart_SendString("[UART] Mock RF Alarm\r\n"); + mock_rx_addr = mock_addr; + mock_rx_data = 0x01; + mock_rf_ready = 1; // 触发射频模拟 + XTELL_LOG("[UART] Mock RF Alarm Triggered\r\n"); } else if (cmd == 'p' || cmd == 'P') { - // 只有当手环切入 Pair 模式时,模拟对码信号才有效 - 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"); - } + mock_rx_addr = 0x789ABC; + mock_rx_data = 0x01; + mock_rf_ready = 1; // 触发射频模拟 + XTELL_LOG("[UART] Mock RF Pair Triggered\r\n"); } else if (cmd == '1') { // 模拟切换至 正常撤防 (NORMAL) 状态并刷新时钟 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"); + XTELL_LOG("[UART] State: NORMAL\r\n"); } else if (cmd == '2') { // 模拟切换至 已布防 (ARMED) 状态,普通门磁可触发警报 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"); + XTELL_LOG("[UART] State: ARMED\r\n"); } else if (cmd == '3') { // 模拟切换至 已撤防 (DISARMED) 状态,忽略普通报警 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"); + XTELL_LOG("[UART] State: DISARMED\r\n"); } else if (cmd == 'z' || cmd == 'Z') { inactivity_timer = 6000; // 模拟闲置 60 秒超时,强制触发主循环深度休眠 - Uart_SendString("[UART] Force Entering Sleep mode!\r\n"); + XTELL_LOG("[UART] Force Entering Sleep mode!\r\n"); } else if (cmd == 't' || cmd == 'T') { Loopback_Test(); // 调用回环测试验证屏幕 RF_Test_Pin(); // 无线射频测试 @@ -176,20 +152,24 @@ void main(void) Delay_ms(50); // 等待电压充盈稳定,避开浪涌 LCD_Init(); // 初始化 Truly AMOLED 屏幕控制寄存器序列 Uart1_Init(); // 初始化串口 1 波特率 115200 供调试日志 - Uart_SendString("Wristband System Initialized!\r\n"); + XTELL_LOG("Wristband System Initialized!\r\n"); Load_Database(); // 从 IAP Flash 第 254 扇区读取所有已绑定的传感器数据至 RAM /* 步骤 1: 初始化事件环形缓冲区队列 */ EventQueue_Init(); - /* 步骤 2+5: 初始化应用管理器并登记所有前台应用,默认加载 ClockApp */ + /* 步骤 2: 初始化应用管理器 */ AppManager_Init(); + /* 步骤 3: 动态挂载待机时钟应用到前台,并挂载后台射频监控应用 */ + AppManager_SwitchToForeground(&clock_app); + AppManager_AttachToBackground(&rf_monitor_app); + /* 启动 1ms 系统基准定时器 Timer1 */ Timer1_Init(); - Uart_SendString("[SYS] Event-driven framework ready\r\n"); + XTELL_LOG("[SYS] Event-driven framework ready\r\n"); // ====== 主循环 ====== while (1) { @@ -204,18 +184,19 @@ void main(void) Event_Dispatcher_Loop(); } - /* 3. 应用调度中心:运行当前处于前台活跃状态的应用(集成了 CPU 运行时间超时挂起保护) */ + /* 3. 应用调度中心:运行前后台所有已挂载激活的应用 */ AppManager_RunActiveApp(); /* 4. 自动休眠判定:常态无操作闲置达到 60 秒 (6000 * 10ms = 60s) 时,切入低功耗停机休眠 */ if (inactivity_timer >= 6000) { inactivity_timer = 0; current_state = STATE_SLEEP; // 系统置为休眠状态 - Enter_Low_Power_Sleep(); // 该函数是阻塞的,MCU 将在此挂起。唤醒后将自动从其后继续执行 + Enter_Low_Power_Sleep(); // 挂起 MCU } } } + // 唤醒源事件标志,由对应的外部中断 ISR 写入 volatile u8 p0_wakeup_flag = 0; // P0 端口按键唤醒标志 volatile u8 p2_wakeup_flag = 0; // P2 端口按键唤醒标志 diff --git a/App/system.c b/App/system.c index c135781..6a5311c 100644 --- a/App/system.c +++ b/App/system.c @@ -1,6 +1,8 @@ #include "system.h" #include "rgb.h" #include "ui.h" +#include "clock.h" +#include "xtell.h" #include "../Drivers/lcd.h" #include "../Drivers/rf.h" #include "app_manager.h" @@ -268,7 +270,7 @@ void FormatHex(u32 val, char *buf) */ void Enter_Low_Power_Sleep(void) { - Uart_SendString("[SYS] Entering low power sleep...\r\n"); + XTELL_LOG("[SYS] Entering low power sleep...\r\n"); // 1. 关闭/熄灭 FCOB 灯条 RGB_Send(0, 0, 0, 0, 0, 0); @@ -343,7 +345,7 @@ void Enter_Low_Power_Sleep(void) EA = 1; // 13. 向电源控制寄存器 PCON 写入 0x02,使 MCU 核心进入 Power-Down 模式,CPU 停止运转 - Uart_SendString("[SYS] Entering Power-Down mode...\r\n"); + XTELL_LOG("[SYS] Entering Power-Down mode...\r\n"); PCON |= 0x02; // 设定 PD 位,硬件立刻休眠挂起 // 唤醒后必须跟 _nop_() 空操作以占位和刷新指令流水线 @@ -388,20 +390,20 @@ void Wakeup_Restore(void) // 打印日志,判断并告知调试串口具体是哪个物理源将手环唤醒的 if ((p0_wakeup_flag & 0x0E) || (p2_wakeup_flag & 0x40)) { - Uart_SendString("[WR] Woken by KEY (P0="); - Uart_SendHex8(p0_wakeup_flag); - Uart_SendString(", P2="); - Uart_SendHex8(p2_wakeup_flag); - Uart_SendString(")\r\n"); + XTELL_LOG("[WR] Woken by KEY (P0="); + XTELL_LOG_HEX8(p0_wakeup_flag); + XTELL_LOG(", P2="); + XTELL_LOG_HEX8(p2_wakeup_flag); + XTELL_LOG(")\r\n"); } else if ((p2_wakeup_flag & 0x02) || rf_wakeup_flag) { rf_wakeup_flag = 1; // 确认标记为射频解调唤醒 - Uart_SendString("[WR] Woken by RF (P2.1)!\r\n"); + XTELL_LOG("[WR] Woken by RF (P2.1)!\r\n"); } else { - Uart_SendString("[WR] Woken by TIMER/Other\r\n"); + XTELL_LOG("[WR] Woken by TIMER/Other\r\n"); } // 5. 【屏幕供电软启动】: @@ -422,7 +424,7 @@ void Wakeup_Restore(void) // 9. 更新运行状态为普通时钟,并强制调用时钟界面函数刷屏,返回工作状态 current_state = STATE_NORMAL; UI_ShowClockPage(current_state, current_hour, current_min); - Uart_SendString("[SYS] Wakeup restored!\r\n"); + XTELL_LOG("[SYS] Wakeup restored!\r\n"); } /** diff --git a/App/xtell.h b/App/xtell.h new file mode 100644 index 0000000..040cc05 --- /dev/null +++ b/App/xtell.h @@ -0,0 +1,24 @@ +#ifndef __XTELL_H__ +#define __XTELL_H__ + +#include "config.h" + +// 全局调试日志开关 +#define XTELL_DEBUG_ENABLE + +// 声明底层串口发送函数 (由 system.c 提供) +extern void Uart_SendString(char *p); +extern void Uart_SendHex8(u8 val); +extern void Uart_SendHex32(u32 val); + +#ifdef XTELL_DEBUG_ENABLE + #define XTELL_LOG(s) Uart_SendString(s) + #define XTELL_LOG_HEX8(v) Uart_SendHex8(v) + #define XTELL_LOG_HEX32(v) Uart_SendHex32(v) +#else + #define XTELL_LOG(s) + #define XTELL_LOG_HEX8(v) + #define XTELL_LOG_HEX32(v) +#endif + +#endif // __XTELL_H__ diff --git a/Drivers/rf.c b/Drivers/rf.c index 8516dd8..d81b59b 100644 --- a/Drivers/rf.c +++ b/Drivers/rf.c @@ -179,17 +179,13 @@ u16 GetPulseDuration(u8 state, u16 timeout_us) return (u16)((u32)GetTimer0_Safe() * 12UL / (MAIN_Fosc / 1000000UL)); } -/** - * @brief 软件自适应解码接收一帧无线 EV1527 信号 - * @param out_addr 解码成功后,用于回传物理地址高 20 位的指针 - * @param out_data 解码成功后,用于回传指令数据低 4 位的指针 - * @return bit 1-解码成功且校验通过,0-捕获失败或脉冲畸变 - * @note 核心算法: - * 1. 等待并寻找合法的同步引导头脉冲 (1T高电平 + 31T低电平,其中低/高比率约为 31,本程序允许比率在 15~48 内变化以抗物理畸变)。 - * 2. 依据捕获的同步低电平宽度估算基准周期 T = low_time / 31。 - * 3. 随后依次高精度捕获 24 个数据位的高电平和低电平时间。 - * 4. 自适应决策:对于每个 bit,如果其高电平时间 raw_h > 低电平时间 raw_l,则判定该位为 1;否则判定该位为 0。 - */ +#include "../App/xtell.h" + +// 模拟射频信号全局变量定义 +volatile u32 mock_rx_addr = 0; +volatile u8 mock_rx_data = 0; +volatile bit mock_rf_ready = 0; + bit EV1527_Decode(u32 *out_addr, u8 *out_data) { u16 high_time, low_time; @@ -200,6 +196,14 @@ bit EV1527_Decode(u32 *out_addr, u8 *out_data) u16 idata raw_h[24]; // 高电平时间寄存数组 (置于 idata 区) u16 idata raw_l[24]; // 低电平时间寄存数组 + // 检查模拟射频触发 + if (mock_rf_ready) { + mock_rf_ready = 0; + *out_addr = mock_rx_addr; + *out_data = mock_rx_data; + return 1; + } + // ====== 1. 同步头引导脉冲捕获 (定位上升沿) ====== { u16 wait_cnt = 0; diff --git a/Hardware/~text.kicad_pro.lck b/Hardware/~text.kicad_pro.lck deleted file mode 100644 index a031987..0000000 --- a/Hardware/~text.kicad_pro.lck +++ /dev/null @@ -1 +0,0 @@ -{"hostname":"DESKTOP-0S8JI7H","username":"xtell"} \ No newline at end of file diff --git a/Hardware/~text.kicad_sch.lck b/Hardware/~text.kicad_sch.lck deleted file mode 100644 index a031987..0000000 --- a/Hardware/~text.kicad_sch.lck +++ /dev/null @@ -1 +0,0 @@ -{"hostname":"DESKTOP-0S8JI7H","username":"xtell"} \ No newline at end of file