refactor(app): App-per-screen navigation, remove menu_level entirely
Splits the ad-hoc menu_level state machine into standalone Apps with a uniform parent/child exit convention (long-press CONFIRM -> parent): clock_app renamed to home_app (boot-default), new set_clock_app extracted from home_app's time-edit mode, new ipc_bind_app extracted from menu_app's old level-2 screen, and the sensor-type selection step merged into pair_app as its new initial state. menu_app now only holds the 4-item top-level list (language toggle inline). Also makes both menu_app and pair_app's up/down navigation cyclic (wraps at both ends).
This commit is contained in:
@@ -175,7 +175,7 @@ static void AlarmApp_onClose(void)
|
|||||||
static EventResult AlarmApp_onEvent(SystemEvent *evt)
|
static EventResult AlarmApp_onEvent(SystemEvent *evt)
|
||||||
{
|
{
|
||||||
if (evt->key_event != KEY_EVENT_NONE) {
|
if (evt->key_event != KEY_EVENT_NONE) {
|
||||||
AppManager_SwitchToForeground(&clock_app); // 清除报警并切回时钟待机页
|
AppManager_SwitchToForeground(&home_app); // 清除报警并切回时钟待机页
|
||||||
return EVENT_HANDLED;
|
return EVENT_HANDLED;
|
||||||
}
|
}
|
||||||
return EVENT_IGNORED;
|
return EVENT_IGNORED;
|
||||||
|
|||||||
204
App/app_clock.c
204
App/app_clock.c
@@ -1,204 +0,0 @@
|
|||||||
#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"
|
|
||||||
#include "../Drivers/pcf8563.h"
|
|
||||||
|
|
||||||
// 外部引用的全局系统状态变量
|
|
||||||
extern SystemState current_state;
|
|
||||||
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); // 开启天线并置为接收模式,监听无线探测器
|
|
||||||
|
|
||||||
// 尝试从 PCF8563 RTC 芯片中读取硬件时间 (具备非阻塞防卡死保护)
|
|
||||||
if (PCF8563_Init()) {
|
|
||||||
PCF8563_ReadTime(¤t_hour, ¤t_min, ¤t_sec);
|
|
||||||
XTELL_LOG("[ClockApp] RTC Hardware Synced OK!\r\n");
|
|
||||||
} else {
|
|
||||||
XTELL_LOG("[ClockApp] RTC Hardware Offline, using system clock.\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘制并渲染待机时钟主页面画面
|
|
||||||
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); // 熄灭蓝色指示灯
|
|
||||||
|
|
||||||
// 同步将最新时间 (hour, min, 0秒) 写入 PCF8563 芯片寄存器
|
|
||||||
PCF8563_WriteTime(current_hour, current_min, 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; // 其他未被关心的事件交由后台应用或丢弃
|
|
||||||
}
|
|
||||||
103
App/app_home.c
Normal file
103
App/app_home.c
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#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"
|
||||||
|
#include "../Drivers/pcf8563.h"
|
||||||
|
|
||||||
|
// 外部引用的全局系统状态变量
|
||||||
|
extern SystemState current_state;
|
||||||
|
extern volatile u16 ms_tick;
|
||||||
|
|
||||||
|
// 声明本应用的生命周期回调函数
|
||||||
|
static void HomeApp_OnStart(void);
|
||||||
|
static void HomeApp_onRun(void);
|
||||||
|
static void HomeApp_onClose(void);
|
||||||
|
static EventResult HomeApp_onEvent(SystemEvent *evt);
|
||||||
|
|
||||||
|
// 实体化定义待机时钟主页应用控制块 (开机默认显示的首屏)
|
||||||
|
WristbandApp home_app = {
|
||||||
|
APP_ID_HOME,
|
||||||
|
"Home",
|
||||||
|
HomeApp_OnStart,
|
||||||
|
HomeApp_onRun,
|
||||||
|
HomeApp_onClose,
|
||||||
|
HomeApp_onEvent
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HomeApp 启动回调
|
||||||
|
*/
|
||||||
|
static void HomeApp_OnStart(void)
|
||||||
|
{
|
||||||
|
current_state = STATE_NORMAL; // 系统切入正常待机状态
|
||||||
|
RF_SetMode(1); // 开启天线并置为接收模式,监听无线探测器
|
||||||
|
|
||||||
|
// 尝试从 PCF8563 RTC 芯片中读取硬件时间 (具备非阻塞防卡死保护)
|
||||||
|
if (PCF8563_Init()) {
|
||||||
|
PCF8563_ReadTime(¤t_hour, ¤t_min, ¤t_sec);
|
||||||
|
XTELL_LOG("[HomeApp] RTC Hardware Synced OK!\r\n");
|
||||||
|
} else {
|
||||||
|
XTELL_LOG("[HomeApp] RTC Hardware Offline, using system clock.\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制并渲染待机时钟主页面画面
|
||||||
|
UI_ShowClockPage(current_state, current_hour, current_min);
|
||||||
|
|
||||||
|
// 关断/熄灭 FCOB 双灯条
|
||||||
|
RGB_Send(0, 0, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
XTELL_LOG("[HomeApp] Started\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HomeApp 轮询主回调
|
||||||
|
*/
|
||||||
|
static void HomeApp_onRun(void)
|
||||||
|
{
|
||||||
|
// 软时钟发生分钟改变,需要通知屏幕重画时钟主页
|
||||||
|
if (clock_updated) {
|
||||||
|
clock_updated = 0;
|
||||||
|
UI_ShowClockPage(current_state, current_hour, current_min);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HomeApp 退出清理回调
|
||||||
|
*/
|
||||||
|
static void HomeApp_onClose(void)
|
||||||
|
{
|
||||||
|
RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯条
|
||||||
|
XTELL_LOG("[HomeApp] Closed\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HomeApp 接收事件处理回调 (按键导航与布撤防交互)
|
||||||
|
*/
|
||||||
|
static EventResult HomeApp_onEvent(SystemEvent *evt)
|
||||||
|
{
|
||||||
|
// 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; // 其他未被关心的事件交由后台应用或丢弃
|
||||||
|
}
|
||||||
85
App/app_ipc_bind.c
Normal file
85
App/app_ipc_bind.c
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#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 volatile u16 ms_tick;
|
||||||
|
|
||||||
|
// 本应用私有变量
|
||||||
|
static u16 last_bind_tx_ms = 0; // 记录上一次绑定发射的时间戳
|
||||||
|
|
||||||
|
// 声明本应用的生命周期回调函数
|
||||||
|
static void IpcBindApp_OnStart(void);
|
||||||
|
static void IpcBindApp_onRun(void);
|
||||||
|
static void IpcBindApp_onClose(void);
|
||||||
|
static EventResult IpcBindApp_onEvent(SystemEvent *evt);
|
||||||
|
|
||||||
|
// 实体化定义摄像头一键绑定应用控制块 (从 menu_app 的"LIAISON"选项进入)
|
||||||
|
WristbandApp ipc_bind_app = {
|
||||||
|
APP_ID_IPC_BIND,
|
||||||
|
"IpcBind",
|
||||||
|
IpcBindApp_OnStart,
|
||||||
|
IpcBindApp_onRun,
|
||||||
|
IpcBindApp_onClose,
|
||||||
|
IpcBindApp_onEvent
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief IpcBindApp 启动回调
|
||||||
|
*/
|
||||||
|
static void IpcBindApp_OnStart(void)
|
||||||
|
{
|
||||||
|
last_bind_tx_ms = ms_tick; // 记录初始发射时间
|
||||||
|
current_state = STATE_IPC_BIND; // 系统设为 IPC 绑定状态
|
||||||
|
UI_ShowBindingPage(CLONED_ADDR); // 绘制相机绑定信息页
|
||||||
|
XTELL_LOG("[IpcBindApp] Started\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief IpcBindApp 轮询主回调
|
||||||
|
*/
|
||||||
|
static void IpcBindApp_onRun(void)
|
||||||
|
{
|
||||||
|
// ====== 控制青色双灯珠同步呼吸效果 (呼吸周期 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("[IpcBindApp] Binding frame emitted\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief IpcBindApp 退出清理回调
|
||||||
|
*/
|
||||||
|
static void IpcBindApp_onClose(void)
|
||||||
|
{
|
||||||
|
RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭 RGB
|
||||||
|
XTELL_LOG("[IpcBindApp] Closed\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief IpcBindApp 接收按键事件处理回调
|
||||||
|
*/
|
||||||
|
static EventResult IpcBindApp_onEvent(SystemEvent *evt)
|
||||||
|
{
|
||||||
|
// 长按 ■ 确认键,取消绑定,退出发射并返回主菜单 (父 App)
|
||||||
|
if (evt->key_event == KEY_EVENT_CONFIRM_LONG) {
|
||||||
|
current_state = STATE_PAIR_MENU;
|
||||||
|
AppManager_SwitchToForeground(&menu_app);
|
||||||
|
return EVENT_HANDLED;
|
||||||
|
}
|
||||||
|
return EVENT_IGNORED;
|
||||||
|
}
|
||||||
@@ -180,9 +180,11 @@ u8 AppManager_GetActiveAppID(void)
|
|||||||
// 供遗留代码兼容性引用的 API:StartApp 映射至 SwitchToForeground 动态方式
|
// 供遗留代码兼容性引用的 API:StartApp 映射至 SwitchToForeground 动态方式
|
||||||
void AppManager_StartApp(AppID app_id)
|
void AppManager_StartApp(AppID app_id)
|
||||||
{
|
{
|
||||||
if (app_id == APP_ID_CLOCK) AppManager_SwitchToForeground(&clock_app);
|
if (app_id == APP_ID_HOME) AppManager_SwitchToForeground(&home_app);
|
||||||
else if (app_id == APP_ID_MENU) AppManager_SwitchToForeground(&menu_app);
|
else if (app_id == APP_ID_MENU) AppManager_SwitchToForeground(&menu_app);
|
||||||
|
else if (app_id == APP_ID_SET_CLOCK) AppManager_SwitchToForeground(&set_clock_app);
|
||||||
else if (app_id == APP_ID_PAIR) AppManager_SwitchToForeground(&pair_app);
|
else if (app_id == APP_ID_PAIR) AppManager_SwitchToForeground(&pair_app);
|
||||||
|
else if (app_id == APP_ID_IPC_BIND) AppManager_SwitchToForeground(&ipc_bind_app);
|
||||||
else if (app_id == APP_ID_ALARM) AppManager_SwitchToForeground(&alarm_app);
|
else if (app_id == APP_ID_ALARM) AppManager_SwitchToForeground(&alarm_app);
|
||||||
else if (app_id == APP_ID_SOS) AppManager_SwitchToForeground(&sos_app);
|
else if (app_id == APP_ID_SOS) AppManager_SwitchToForeground(&sos_app);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,9 +67,11 @@ u8 AppManager_GetActiveAppID(void);
|
|||||||
void AppManager_StartApp(AppID app_id);
|
void AppManager_StartApp(AppID app_id);
|
||||||
|
|
||||||
// ====== 系统中各独立 App 实例的外包声明 (各 App 内部进行实体定义) ======
|
// ====== 系统中各独立 App 实例的外包声明 (各 App 内部进行实体定义) ======
|
||||||
extern WristbandApp clock_app;
|
extern WristbandApp home_app;
|
||||||
extern WristbandApp menu_app;
|
extern WristbandApp menu_app;
|
||||||
|
extern WristbandApp set_clock_app;
|
||||||
extern WristbandApp pair_app;
|
extern WristbandApp pair_app;
|
||||||
|
extern WristbandApp ipc_bind_app;
|
||||||
extern WristbandApp alarm_app;
|
extern WristbandApp alarm_app;
|
||||||
extern WristbandApp sos_app;
|
extern WristbandApp sos_app;
|
||||||
extern WristbandApp rf_monitor_app; // 后台射频监控应用
|
extern WristbandApp rf_monitor_app; // 后台射频监控应用
|
||||||
|
|||||||
182
App/app_menu.c
182
App/app_menu.c
@@ -8,40 +8,21 @@
|
|||||||
#include "language.h"
|
#include "language.h"
|
||||||
#include "../Drivers/rf.h"
|
#include "../Drivers/rf.h"
|
||||||
|
|
||||||
// 二级传感器对码菜单中,各类传感器显示名称的字符串 ID,实际文案由 Lang_Get() 按当前语言取出
|
|
||||||
const StringID device_menu_ids[5] = {
|
|
||||||
STR_DEV_PORTE, // 0: 门磁探测器
|
|
||||||
STR_DEV_PIR, // 1: 红外探测器
|
|
||||||
STR_DEV_FUMEE, // 2: 烟雾传感器
|
|
||||||
STR_DEV_GAZ, // 3: 燃气探测器
|
|
||||||
STR_DEV_EAU // 4: 水浸探测器
|
|
||||||
};
|
|
||||||
|
|
||||||
// 外部全局变量引用
|
// 外部全局变量引用
|
||||||
extern SystemState current_state;
|
extern SystemState current_state;
|
||||||
extern u8 menu_select;
|
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; // 记录上一次绑定发射的时间戳
|
|
||||||
|
|
||||||
// 引入外部时钟调节接口
|
|
||||||
extern void Start_Time_Edit(void);
|
|
||||||
|
|
||||||
// 声明本应用的生命周期回调函数
|
// 声明本应用的生命周期回调函数
|
||||||
static void MenuApp_OnStart(void);
|
static void MenuApp_OnStart(void);
|
||||||
static void MenuApp_onRun(void);
|
|
||||||
static void MenuApp_onClose(void);
|
static void MenuApp_onClose(void);
|
||||||
static EventResult MenuApp_onEvent(SystemEvent *evt);
|
static EventResult MenuApp_onEvent(SystemEvent *evt);
|
||||||
|
|
||||||
// 实体化定义菜单应用控制块
|
// 实体化定义菜单应用控制块 (顶层 4 项列表:语言切换/时间设置/传感器配对/摄像头绑定)
|
||||||
WristbandApp menu_app = {
|
WristbandApp menu_app = {
|
||||||
APP_ID_MENU,
|
APP_ID_MENU,
|
||||||
"Menu",
|
"Menu",
|
||||||
MenuApp_OnStart,
|
MenuApp_OnStart,
|
||||||
MenuApp_onRun,
|
NULL,
|
||||||
MenuApp_onClose,
|
MenuApp_onClose,
|
||||||
MenuApp_onEvent
|
MenuApp_onEvent
|
||||||
};
|
};
|
||||||
@@ -51,155 +32,66 @@ WristbandApp menu_app = {
|
|||||||
*/
|
*/
|
||||||
static void MenuApp_OnStart(void)
|
static void MenuApp_OnStart(void)
|
||||||
{
|
{
|
||||||
menu_level = 0;
|
|
||||||
menu_select = 0;
|
menu_select = 0;
|
||||||
current_state = STATE_PAIR_MENU; // 系统运行状态设为对码配置菜单
|
current_state = STATE_PAIR_MENU; // 系统运行状态设为对码配置菜单
|
||||||
RF_SetMode(0); // 关闭射频接收芯片,防止接收杂波干扰屏幕绘制
|
RF_SetMode(0); // 关闭射频接收芯片,防止接收杂波干扰屏幕绘制
|
||||||
UI_ShowMainMenu(menu_select); // 初始化绘制一级主菜单
|
UI_ShowMainMenu(menu_select); // 绘制主菜单
|
||||||
RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭所有灯条
|
RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭所有灯条
|
||||||
XTELL_LOG("[MenuApp] Started\r\n");
|
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 退出清理回调
|
* @brief MenuApp 退出清理回调
|
||||||
*/
|
*/
|
||||||
static void MenuApp_onClose(void)
|
static void MenuApp_onClose(void)
|
||||||
{
|
{
|
||||||
menu_level = 0;
|
|
||||||
RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭 RGB
|
RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭 RGB
|
||||||
RF_SetMode(1); // 重新开启无线接收模式
|
RF_SetMode(1); // 重新开启无线接收模式
|
||||||
XTELL_LOG("[MenuApp] Closed\r\n");
|
XTELL_LOG("[MenuApp] Closed\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief MenuApp 接收按键与控制事件回调
|
* @brief MenuApp 接收按键与控制事件回调 (0-语言切换, 1-HORLOGE, 2-APPAIRAGE, 3-LIAISON)
|
||||||
*/
|
*/
|
||||||
static EventResult MenuApp_onEvent(SystemEvent *evt)
|
static EventResult MenuApp_onEvent(SystemEvent *evt)
|
||||||
{
|
{
|
||||||
if (menu_level == 0)
|
if (evt->key_event == KEY_EVENT_UP_CLICK) {
|
||||||
{
|
// 短按 ▲ 键向上移动焦点,允许循环切换 (到顶绕到底)
|
||||||
// ====== 处于一级主菜单层 (0-语言切换, 1-HORLOGE, 2-APPAIRAGE, 3-LIAISON) ======
|
menu_select = (menu_select == 0) ? 3 : (menu_select - 1);
|
||||||
if (evt->key_event == KEY_EVENT_UP_CLICK) {
|
UI_ShowMainMenu(menu_select);
|
||||||
// 短按 ▲ 键向上移动焦点
|
return EVENT_HANDLED;
|
||||||
if (menu_select > 0) {
|
|
||||||
menu_select--;
|
|
||||||
UI_ShowMainMenu(menu_select);
|
|
||||||
}
|
|
||||||
return EVENT_HANDLED;
|
|
||||||
}
|
|
||||||
if (evt->key_event == KEY_EVENT_DOWN_CLICK) {
|
|
||||||
// 短按 ▼ 键向下移动焦点
|
|
||||||
if (menu_select < 3) {
|
|
||||||
menu_select++;
|
|
||||||
UI_ShowMainMenu(menu_select);
|
|
||||||
}
|
|
||||||
return EVENT_HANDLED;
|
|
||||||
}
|
|
||||||
if (evt->key_event == KEY_EVENT_CONFIRM_CLICK) {
|
|
||||||
// 短按 ■ 确认键,进入选定功能的子页面
|
|
||||||
if (menu_select == 0) {
|
|
||||||
// 功能 0:语言切换。原地在 FR/EN 间切换,立即保存并用新语言重绘当前菜单
|
|
||||||
Language new_lang = (Lang_Current() == LANG_FR) ? LANG_EN : LANG_FR;
|
|
||||||
Lang_Set(new_lang);
|
|
||||||
Save_Database(); // 语言字节与传感器数据库共用同一扇区持久化
|
|
||||||
MOTOR = 1; Delay_ms(50); MOTOR = 0; // 短震反馈,与其它确认操作风格一致
|
|
||||||
UI_ShowMainMenu(menu_select);
|
|
||||||
} else if (menu_select == 1) {
|
|
||||||
// 功能 1:设置时间。启动待机时钟应用,并调用微调编辑入口
|
|
||||||
AppManager_SwitchToForeground(&clock_app);
|
|
||||||
Start_Time_Edit();
|
|
||||||
} else if (menu_select == 2) {
|
|
||||||
// 功能 2:传感器学习。进入二级传感器类别选择菜单
|
|
||||||
menu_level = 1;
|
|
||||||
menu_select = 0;
|
|
||||||
UI_ShowPairMenuPage(menu_select); // 绘制二级设备分类页
|
|
||||||
} else if (menu_select == 3) {
|
|
||||||
// 功能 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)
|
if (evt->key_event == KEY_EVENT_DOWN_CLICK) {
|
||||||
{
|
// 短按 ▼ 键向下移动焦点,允许循环切换 (到底绕到顶)
|
||||||
// ====== 处于二级传感器对码类型菜单层 (0-门磁, 1-PIR, 2-烟感, 3-气感, 4-水浸) ======
|
menu_select = (menu_select == 3) ? 0 : (menu_select + 1);
|
||||||
if (evt->key_event == KEY_EVENT_UP_CLICK) {
|
UI_ShowMainMenu(menu_select);
|
||||||
// 上移焦点
|
return EVENT_HANDLED;
|
||||||
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 = 2; // 回到 APPAIRAGE 选项上
|
|
||||||
UI_ShowMainMenu(menu_select);
|
|
||||||
return EVENT_HANDLED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (menu_level == 2)
|
if (evt->key_event == KEY_EVENT_CONFIRM_CLICK) {
|
||||||
{
|
// 短按 ■ 确认键,进入选定功能的子页面
|
||||||
// ====== 处于摄像机配对一键绑定发送层 ======
|
if (menu_select == 0) {
|
||||||
// 长按 ■ 确认键 或 长按 ▼ 键,均表示取消绑定,退出发射并返回一级主菜单
|
// 功能 0:语言切换。原地在 FR/EN 间切换,立即保存并用新语言重绘当前菜单
|
||||||
if (evt->key_event == KEY_EVENT_CONFIRM_LONG || evt->key_event == KEY_EVENT_DOWN_LONG) {
|
Language new_lang = (Lang_Current() == LANG_FR) ? LANG_EN : LANG_FR;
|
||||||
menu_level = 0;
|
Lang_Set(new_lang);
|
||||||
menu_select = 3; // 回到 LIAISON 选项上
|
Save_Database(); // 语言字节与传感器数据库共用同一扇区持久化
|
||||||
current_state = STATE_PAIR_MENU;
|
MOTOR = 1; Delay_ms(50); MOTOR = 0; // 短震反馈,与其它确认操作风格一致
|
||||||
RGB_Send(0, 0, 0, 0, 0, 0); // 关闭呼吸灯
|
|
||||||
UI_ShowMainMenu(menu_select);
|
UI_ShowMainMenu(menu_select);
|
||||||
return EVENT_HANDLED;
|
} else if (menu_select == 1) {
|
||||||
|
// 功能 1:设置时间。切换到独立的时间设置子 App
|
||||||
|
AppManager_SwitchToForeground(&set_clock_app);
|
||||||
|
} else if (menu_select == 2) {
|
||||||
|
// 功能 2:传感器学习。切换到独立的对码子 App (类型选择是它的第一个状态)
|
||||||
|
AppManager_SwitchToForeground(&pair_app);
|
||||||
|
} else if (menu_select == 3) {
|
||||||
|
// 功能 3:相机配对。切换到独立的摄像头绑定子 App
|
||||||
|
AppManager_SwitchToForeground(&ipc_bind_app);
|
||||||
}
|
}
|
||||||
|
return EVENT_HANDLED;
|
||||||
|
}
|
||||||
|
if (evt->key_event == KEY_EVENT_CONFIRM_LONG) {
|
||||||
|
// 长按 ■ 确认键,退出设置菜单直接返回待机主页
|
||||||
|
AppManager_SwitchToForeground(&home_app);
|
||||||
|
return EVENT_HANDLED;
|
||||||
}
|
}
|
||||||
return EVENT_IGNORED;
|
return EVENT_IGNORED;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,10 +12,21 @@
|
|||||||
extern SystemState current_state;
|
extern SystemState current_state;
|
||||||
extern u8 captured_type;
|
extern u8 captured_type;
|
||||||
extern u32 captured_addr;
|
extern u32 captured_addr;
|
||||||
|
extern u8 menu_select;
|
||||||
extern volatile u16 ms_tick;
|
extern volatile u16 ms_tick;
|
||||||
|
|
||||||
|
// 二级传感器对码菜单中,各类传感器显示名称的字符串 ID,实际文案由 Lang_Get() 按当前语言取出
|
||||||
|
const StringID device_menu_ids[5] = {
|
||||||
|
STR_DEV_PORTE, // 0: 门磁探测器
|
||||||
|
STR_DEV_PIR, // 1: 红外探测器
|
||||||
|
STR_DEV_FUMEE, // 2: 烟雾传感器
|
||||||
|
STR_DEV_GAZ, // 3: 燃气探测器
|
||||||
|
STR_DEV_EAU // 4: 水浸探测器
|
||||||
|
};
|
||||||
|
|
||||||
// 对码状态机定义
|
// 对码状态机定义
|
||||||
enum {
|
enum {
|
||||||
|
PAIR_STATE_SELECT_TYPE, // 选择要学习的传感器类型 (门磁/PIR/烟感/气感/水浸)
|
||||||
PAIR_STATE_WAIT, // 等待射频探测器触发 (扩散雷达页面)
|
PAIR_STATE_WAIT, // 等待射频探测器触发 (扩散雷达页面)
|
||||||
PAIR_STATE_CONFIRM, // 已捕获,进入分配防区确认页面
|
PAIR_STATE_CONFIRM, // 已捕获,进入分配防区确认页面
|
||||||
PAIR_STATE_SUCCESS, // 对码绑定成功界面 (SUCCESS)
|
PAIR_STATE_SUCCESS, // 对码绑定成功界面 (SUCCESS)
|
||||||
@@ -48,13 +59,10 @@ WristbandApp pair_app = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief PairApp 启动回调
|
* @brief 进入雷达搜索等待阶段 (选定传感器类型之后)
|
||||||
*/
|
*/
|
||||||
static void PairApp_OnStart(void)
|
static void PairApp_EnterWait(void)
|
||||||
{
|
{
|
||||||
// 1. 为了防止后台射频监听与对码射频接收冲突,将后台射频监听应用暂时卸载
|
|
||||||
AppManager_DetachFromBackground(&rf_monitor_app);
|
|
||||||
|
|
||||||
pair_state = PAIR_STATE_WAIT;
|
pair_state = PAIR_STATE_WAIT;
|
||||||
last_radar_sec = 99; // 强制刷写
|
last_radar_sec = 99; // 强制刷写
|
||||||
current_state = STATE_PAIR_WAIT;
|
current_state = STATE_PAIR_WAIT;
|
||||||
@@ -69,7 +77,25 @@ static void PairApp_OnStart(void)
|
|||||||
// 开启白色幻彩双灯珠常亮 (R=150, G=150, B=150),作为对码状态视觉指示
|
// 开启白色幻彩双灯珠常亮 (R=150, G=150, B=150),作为对码状态视觉指示
|
||||||
RGB_Send(150, 150, 150, 150, 150, 150);
|
RGB_Send(150, 150, 150, 150, 150, 150);
|
||||||
|
|
||||||
XTELL_LOG("[PairApp] Started (WAIT)\r\n");
|
XTELL_LOG("[PairApp] Entering WAIT\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PairApp 启动回调
|
||||||
|
*/
|
||||||
|
static void PairApp_OnStart(void)
|
||||||
|
{
|
||||||
|
// 为了防止后台射频监听与对码射频接收冲突,将后台射频监听应用暂时卸载
|
||||||
|
AppManager_DetachFromBackground(&rf_monitor_app);
|
||||||
|
|
||||||
|
pair_state = PAIR_STATE_SELECT_TYPE;
|
||||||
|
menu_select = 0;
|
||||||
|
current_state = STATE_PAIR_MENU;
|
||||||
|
RF_SetMode(0); // 选类型阶段暂不需要天线,防止接收杂波干扰屏幕绘制
|
||||||
|
UI_ShowPairMenuPage(menu_select); // 绘制传感器类型选择页
|
||||||
|
RGB_Send(0, 0, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
XTELL_LOG("[PairApp] Started (SELECT_TYPE)\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,14 +168,14 @@ static void PairApp_onRun(void)
|
|||||||
else RGB_Send(0, 0, 0, 0, 0, 0);
|
else RGB_Send(0, 0, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
else if (pair_state == PAIR_STATE_SUCCESS) {
|
else if (pair_state == PAIR_STATE_SUCCESS) {
|
||||||
// ====== 成功展示状态:延时 2 秒自动返回二级设置菜单 ======
|
// ====== 成功展示状态:延时 2 秒自动返回主菜单 ======
|
||||||
u8 elapsed = (current_sec >= success_start_sec) ? (current_sec - success_start_sec) : (60 + current_sec - success_start_sec);
|
u8 elapsed = (current_sec >= success_start_sec) ? (current_sec - success_start_sec) : (60 + current_sec - success_start_sec);
|
||||||
if (elapsed >= 2) {
|
if (elapsed >= 2) {
|
||||||
AppManager_SwitchToForeground(&menu_app);
|
AppManager_SwitchToForeground(&menu_app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (pair_state == PAIR_STATE_FAIL) {
|
else if (pair_state == PAIR_STATE_FAIL) {
|
||||||
// ====== 失败展示状态:延时 5 秒自动返回二级设置菜单 ======
|
// ====== 失败展示状态:延时 5 秒自动返回主菜单 ======
|
||||||
u8 elapsed = (current_sec >= fail_start_sec) ? (current_sec - fail_start_sec) : (60 + current_sec - fail_start_sec);
|
u8 elapsed = (current_sec >= fail_start_sec) ? (current_sec - fail_start_sec) : (60 + current_sec - fail_start_sec);
|
||||||
if (elapsed >= 5) {
|
if (elapsed >= 5) {
|
||||||
AppManager_SwitchToForeground(&menu_app);
|
AppManager_SwitchToForeground(&menu_app);
|
||||||
@@ -177,6 +203,30 @@ static void PairApp_onClose(void)
|
|||||||
*/
|
*/
|
||||||
static EventResult PairApp_onEvent(SystemEvent *evt)
|
static EventResult PairApp_onEvent(SystemEvent *evt)
|
||||||
{
|
{
|
||||||
|
if (pair_state == PAIR_STATE_SELECT_TYPE) {
|
||||||
|
if (evt->key_event == KEY_EVENT_UP_CLICK) {
|
||||||
|
// 短按 ▲ 键上移焦点,允许循环切换 (到顶绕到底)
|
||||||
|
menu_select = (menu_select == 0) ? 4 : (menu_select - 1);
|
||||||
|
UI_ShowPairMenuPage(menu_select);
|
||||||
|
return EVENT_HANDLED;
|
||||||
|
}
|
||||||
|
if (evt->key_event == KEY_EVENT_DOWN_CLICK) {
|
||||||
|
// 短按 ▼ 键下移焦点,允许循环切换 (到底绕到顶)
|
||||||
|
menu_select = (menu_select == 4) ? 0 : (menu_select + 1);
|
||||||
|
UI_ShowPairMenuPage(menu_select);
|
||||||
|
return EVENT_HANDLED;
|
||||||
|
}
|
||||||
|
if (evt->key_event == KEY_EVENT_CONFIRM_CLICK) {
|
||||||
|
// 确认选择:缓存用户选好的传感器类别,短震反馈并进入雷达搜索阶段
|
||||||
|
captured_type = menu_select;
|
||||||
|
MOTOR = 1;
|
||||||
|
Delay_ms(100);
|
||||||
|
MOTOR = 0;
|
||||||
|
PairApp_EnterWait();
|
||||||
|
return EVENT_HANDLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pair_state == PAIR_STATE_CONFIRM) {
|
if (pair_state == PAIR_STATE_CONFIRM) {
|
||||||
if (evt->key_event == KEY_EVENT_UP_CLICK) {
|
if (evt->key_event == KEY_EVENT_UP_CLICK) {
|
||||||
// 短按 ▲ 键增加目标分配防区号 (上限 99)
|
// 短按 ▲ 键增加目标分配防区号 (上限 99)
|
||||||
@@ -212,16 +262,10 @@ static EventResult PairApp_onEvent(SystemEvent *evt)
|
|||||||
XTELL_LOG("[PairApp] Sensor saved successfully.\r\n");
|
XTELL_LOG("[PairApp] Sensor saved successfully.\r\n");
|
||||||
return EVENT_HANDLED;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在对码搜寻或确认防区期间,长按 ▼ 键均代表强行取消对码直接退回菜单页
|
// 在任何阶段,长按 ■ 确认键或长按 ▼ 键都代表放弃/取消对码,统一退回主菜单 (父 App)
|
||||||
if (evt->key_event == KEY_EVENT_DOWN_LONG) {
|
if (evt->key_event == KEY_EVENT_CONFIRM_LONG || evt->key_event == KEY_EVENT_DOWN_LONG) {
|
||||||
RGB_Send(0, 0, 0, 0, 0, 0);
|
RGB_Send(0, 0, 0, 0, 0, 0);
|
||||||
AppManager_SwitchToForeground(&menu_app);
|
AppManager_SwitchToForeground(&menu_app);
|
||||||
return EVENT_HANDLED;
|
return EVENT_HANDLED;
|
||||||
|
|||||||
133
App/app_set_clock.c
Normal file
133
App/app_set_clock.c
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
#include "app_manager.h"
|
||||||
|
#include "clock.h"
|
||||||
|
#include "xtell.h"
|
||||||
|
#include "ui.h"
|
||||||
|
#include "rgb.h"
|
||||||
|
#include "event.h"
|
||||||
|
#include "system.h"
|
||||||
|
#include "../Drivers/pcf8563.h"
|
||||||
|
|
||||||
|
// 外部引用的全局系统状态变量
|
||||||
|
extern SystemState current_state;
|
||||||
|
extern volatile u16 ms_tick;
|
||||||
|
|
||||||
|
// 本应用私有的时间编辑状态
|
||||||
|
static u8 time_edit_field = 1; // 标识当前聚焦在哪一个参数上进行微调 (1:修改小时, 2:修改分钟)
|
||||||
|
static u8 time_edit_blink_on = 1; // 时钟修改时光标闪烁周期状态位
|
||||||
|
|
||||||
|
// 声明本应用的生命周期回调函数
|
||||||
|
static void SetClockApp_OnStart(void);
|
||||||
|
static void SetClockApp_onRun(void);
|
||||||
|
static void SetClockApp_onClose(void);
|
||||||
|
static EventResult SetClockApp_onEvent(SystemEvent *evt);
|
||||||
|
|
||||||
|
// 实体化定义时间设置应用控制块 (从 menu_app 的"HORLOGE"选项进入)
|
||||||
|
WristbandApp set_clock_app = {
|
||||||
|
APP_ID_SET_CLOCK,
|
||||||
|
"SetClock",
|
||||||
|
SetClockApp_OnStart,
|
||||||
|
SetClockApp_onRun,
|
||||||
|
SetClockApp_onClose,
|
||||||
|
SetClockApp_onEvent
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SetClockApp 启动回调
|
||||||
|
*/
|
||||||
|
static void SetClockApp_OnStart(void)
|
||||||
|
{
|
||||||
|
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("[SetClockApp] Started\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SetClockApp 轮询主回调
|
||||||
|
*/
|
||||||
|
static void SetClockApp_onRun(void)
|
||||||
|
{
|
||||||
|
// ====== 时间设置闪烁动画 (周期 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SetClockApp 退出清理回调
|
||||||
|
*/
|
||||||
|
static void SetClockApp_onClose(void)
|
||||||
|
{
|
||||||
|
RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯条
|
||||||
|
XTELL_LOG("[SetClockApp] Closed\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SetClockApp 接收按键事件处理回调
|
||||||
|
*/
|
||||||
|
static EventResult SetClockApp_onEvent(SystemEvent *evt)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// 短按 ■ 确认键保存最新时钟配置,退出到主菜单 (父 App)
|
||||||
|
current_state = STATE_PAIR_MENU;
|
||||||
|
|
||||||
|
// 同步将最新时间 (hour, min, 0秒) 写入 PCF8563 芯片寄存器
|
||||||
|
PCF8563_WriteTime(current_hour, current_min, 0);
|
||||||
|
|
||||||
|
// 退出短振反馈
|
||||||
|
MOTOR = 1; Delay_ms(50); MOTOR = 0;
|
||||||
|
|
||||||
|
AppManager_SwitchToForeground(&menu_app);
|
||||||
|
return EVENT_HANDLED;
|
||||||
|
}
|
||||||
|
return EVENT_HANDLED; // 在时间设置模式下屏蔽拦截其他一切无关按键事件
|
||||||
|
}
|
||||||
@@ -127,7 +127,7 @@ static void SOSApp_onClose(void)
|
|||||||
static EventResult SOSApp_onEvent(SystemEvent *evt)
|
static EventResult SOSApp_onEvent(SystemEvent *evt)
|
||||||
{
|
{
|
||||||
if (evt->key_event != KEY_EVENT_NONE) {
|
if (evt->key_event != KEY_EVENT_NONE) {
|
||||||
AppManager_SwitchToForeground(&clock_app); // 退出 SOS,返回时钟
|
AppManager_SwitchToForeground(&home_app); // 退出 SOS,返回时钟
|
||||||
return EVENT_HANDLED;
|
return EVENT_HANDLED;
|
||||||
}
|
}
|
||||||
return EVENT_IGNORED;
|
return EVENT_IGNORED;
|
||||||
|
|||||||
14
App/config.h
14
App/config.h
@@ -108,12 +108,14 @@ typedef enum {
|
|||||||
|
|
||||||
/* ====== 应用程序 ID 枚举 ====== */
|
/* ====== 应用程序 ID 枚举 ====== */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
APP_ID_CLOCK, // 待机时钟应用程序 (首屏)
|
APP_ID_HOME, // 待机时钟主页应用程序 (开机默认首屏)
|
||||||
APP_ID_MENU, // 系统设置与操作主菜单应用程序
|
APP_ID_MENU, // 系统设置主菜单应用程序 (顶层 4 项列表)
|
||||||
APP_ID_PAIR, // 无线传感器对码学习应用程序
|
APP_ID_SET_CLOCK, // 时间设置应用程序 (menu_app 子应用)
|
||||||
APP_ID_ALARM, // 报警接收与波形反馈应用程序
|
APP_ID_PAIR, // 无线传感器对码学习应用程序 (menu_app 子应用)
|
||||||
APP_ID_SOS, // 紧急呼救与无线发射应用程序
|
APP_ID_IPC_BIND, // 摄像头一键绑定应用程序 (menu_app 子应用)
|
||||||
APP_ID_MAX // 应用程序最大数量上限,用于数组大小
|
APP_ID_ALARM, // 报警接收与波形反馈应用程序
|
||||||
|
APP_ID_SOS, // 紧急呼救与无线发射应用程序
|
||||||
|
APP_ID_MAX // 应用程序最大数量上限,用于数组大小
|
||||||
} AppID;
|
} AppID;
|
||||||
|
|
||||||
/* ====== 应用程序类型枚举 ====== */
|
/* ====== 应用程序类型枚举 ====== */
|
||||||
|
|||||||
14
App/main.c
14
App/main.c
@@ -15,7 +15,7 @@
|
|||||||
SystemState current_state = STATE_NORMAL; // 系统整体的当前运行状态,默认为正常时钟待机页
|
SystemState current_state = STATE_NORMAL; // 系统整体的当前运行状态,默认为正常时钟待机页
|
||||||
|
|
||||||
|
|
||||||
u8 menu_select = 0; // 主菜单当前选中的行高亮索引 (0~2)
|
u8 menu_select = 0; // 当前菜单高亮的选中项索引 (menu_app 0~3,pair_app 传感器类型选择 0~4)
|
||||||
u32 captured_addr = 0; // 配对或报警时,捕获到的传感器 24 位 EV1527 射频物理地址
|
u32 captured_addr = 0; // 配对或报警时,捕获到的传感器 24 位 EV1527 射频物理地址
|
||||||
u8 captured_type = 0; // 正在对码或触发警报的传感器设备类型 (0:门磁, 1:PIR...)
|
u8 captured_type = 0; // 正在对码或触发警报的传感器设备类型 (0:门磁, 1:PIR...)
|
||||||
u8 alarm_sensor_slot = 0; // 触发警报的传感器在 sensor_list 数组中的索引槽位号
|
u8 alarm_sensor_slot = 0; // 触发警报的传感器在 sensor_list 数组中的索引槽位号
|
||||||
@@ -162,21 +162,21 @@ void Debug_ProcessCommand(char cmd)
|
|||||||
}
|
}
|
||||||
} else if (cmd == '1') {
|
} else if (cmd == '1') {
|
||||||
// 模拟切换至 正常撤防 (NORMAL) 状态并刷新时钟
|
// 模拟切换至 正常撤防 (NORMAL) 状态并刷新时钟
|
||||||
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
|
if (AppManager_GetActiveAppID() == APP_ID_HOME) {
|
||||||
current_state = STATE_NORMAL;
|
current_state = STATE_NORMAL;
|
||||||
UI_ShowClockPage(current_state, current_hour, current_min);
|
UI_ShowClockPage(current_state, current_hour, current_min);
|
||||||
}
|
}
|
||||||
Uart_SendString("[UART] State: NORMAL\r\n");
|
Uart_SendString("[UART] State: NORMAL\r\n");
|
||||||
} else if (cmd == '2') {
|
} else if (cmd == '2') {
|
||||||
// 模拟切换至 已布防 (ARMED) 状态,普通门磁可触发警报
|
// 模拟切换至 已布防 (ARMED) 状态,普通门磁可触发警报
|
||||||
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
|
if (AppManager_GetActiveAppID() == APP_ID_HOME) {
|
||||||
current_state = STATE_ARMED;
|
current_state = STATE_ARMED;
|
||||||
UI_ShowClockPage(current_state, current_hour, current_min);
|
UI_ShowClockPage(current_state, current_hour, current_min);
|
||||||
}
|
}
|
||||||
Uart_SendString("[UART] State: ARMED\r\n");
|
Uart_SendString("[UART] State: ARMED\r\n");
|
||||||
} else if (cmd == '3') {
|
} else if (cmd == '3') {
|
||||||
// 模拟切换至 已撤防 (DISARMED) 状态,忽略普通报警
|
// 模拟切换至 已撤防 (DISARMED) 状态,忽略普通报警
|
||||||
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
|
if (AppManager_GetActiveAppID() == APP_ID_HOME) {
|
||||||
current_state = STATE_DISARMED;
|
current_state = STATE_DISARMED;
|
||||||
UI_ShowClockPage(current_state, current_hour, current_min);
|
UI_ShowClockPage(current_state, current_hour, current_min);
|
||||||
}
|
}
|
||||||
@@ -267,7 +267,7 @@ void main(void)
|
|||||||
/* 步骤 1: 初始化事件环形缓冲区队列 */
|
/* 步骤 1: 初始化事件环形缓冲区队列 */
|
||||||
EventQueue_Init();
|
EventQueue_Init();
|
||||||
|
|
||||||
/* 步骤 2+5: 初始化应用管理器并登记所有前台应用,默认加载 ClockApp */
|
/* 步骤 2+5: 初始化应用管理器并登记所有前台应用,默认加载 HomeApp */
|
||||||
AppManager_Init();
|
AppManager_Init();
|
||||||
|
|
||||||
/* 启动并挂载后台射频监控应用,开始侦听无线传感器信号 */
|
/* 启动并挂载后台射频监控应用,开始侦听无线传感器信号 */
|
||||||
@@ -277,7 +277,7 @@ void main(void)
|
|||||||
Timer1_Init();
|
Timer1_Init();
|
||||||
|
|
||||||
/* 开机自动加载并运行待机时钟前台活跃应用 */
|
/* 开机自动加载并运行待机时钟前台活跃应用 */
|
||||||
AppManager_StartApp(APP_ID_CLOCK);
|
AppManager_StartApp(APP_ID_HOME);
|
||||||
|
|
||||||
Uart_SendString("[SYS] Event-driven framework ready\r\n");
|
Uart_SendString("[SYS] Event-driven framework ready\r\n");
|
||||||
|
|
||||||
@@ -309,7 +309,7 @@ void main(void)
|
|||||||
current_state = STATE_SLEEP; // 系统置为休眠状态
|
current_state = STATE_SLEEP; // 系统置为休眠状态
|
||||||
Enter_Low_Power_Sleep(); // 该函数是阻塞的,MCU 将在此挂起。唤醒后将自动从其后继续执行
|
Enter_Low_Power_Sleep(); // 该函数是阻塞的,MCU 将在此挂起。唤醒后将自动从其后继续执行
|
||||||
current_state = STATE_NORMAL; // 唤醒后重新置为正常待机状态
|
current_state = STATE_NORMAL; // 唤醒后重新置为正常待机状态
|
||||||
AppManager_StartApp(APP_ID_CLOCK); // 重新开启时钟应用以刷新画面和背光
|
AppManager_StartApp(APP_ID_HOME); // 重新开启时钟应用以刷新画面和背光
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -375,9 +375,14 @@
|
|||||||
<FilePath>.\App\clock.c</FilePath>
|
<FilePath>.\App\clock.c</FilePath>
|
||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<FileName>app_clock.c</FileName>
|
<FileName>app_home.c</FileName>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<FilePath>.\App\app_clock.c</FilePath>
|
<FilePath>.\App\app_home.c</FilePath>
|
||||||
|
</File>
|
||||||
|
<File>
|
||||||
|
<FileName>app_set_clock.c</FileName>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<FilePath>.\App\app_set_clock.c</FilePath>
|
||||||
</File>
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<FileName>app_menu.c</FileName>
|
<FileName>app_menu.c</FileName>
|
||||||
@@ -389,6 +394,11 @@
|
|||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<FilePath>.\App\app_pair.c</FilePath>
|
<FilePath>.\App\app_pair.c</FilePath>
|
||||||
</File>
|
</File>
|
||||||
|
<File>
|
||||||
|
<FileName>app_ipc_bind.c</FileName>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<FilePath>.\App\app_ipc_bind.c</FilePath>
|
||||||
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<FileName>app_alarm.c</FileName>
|
<FileName>app_alarm.c</FileName>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
|
|||||||
Reference in New Issue
Block a user