重构手环软件架构:实现动态挂载前后台 App 框架,解耦拆分源文件,并引入统一日志宏 xtell.h
This commit is contained in:
182
App/app_alarm.c
Normal file
182
App/app_alarm.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
194
App/app_clock.c
Normal file
194
App/app_clock.c
Normal file
@@ -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; // 其他未被关心的事件交由后台应用或丢弃
|
||||||
|
}
|
||||||
1055
App/app_manager.c
1055
App/app_manager.c
File diff suppressed because it is too large
Load Diff
@@ -3,56 +3,75 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#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 初始化应用程序管理器
|
* @brief 初始化应用管理器
|
||||||
* @details 负责将应用管理器的系统注册表清零,随后逐一注册系统支持的五个前台应用:
|
* @details 将前台活跃槽位与后台的三个槽位全部清空为 NULL。
|
||||||
* ClockApp (待机时钟), MenuApp (主菜单), PairApp (传感器对码),
|
|
||||||
* AlarmApp (传感器警报), SOSApp (主动求救呼叫)。最后默认加载并启动 ClockApp。
|
|
||||||
*/
|
*/
|
||||||
void AppManager_Init(void);
|
void AppManager_Init(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 启动指定的应用程序
|
* @brief 动态将一个应用切换至前台活跃运行
|
||||||
* @param app_id 待启动应用的目标 ID (如 APP_ID_MENU, APP_ID_PAIR)
|
* @param app 目标应用的结构体指针
|
||||||
* @details 会执行完整的切应用生命周期流转:
|
* @return bit 1-切换成功,0-失败
|
||||||
* 1. 检查目标是否合法且包含有效的 OnStart 启动回调。
|
* @details 流程:
|
||||||
* 2. 对当前正在活跃运行的前台应用调用其 onClose(),以关闭该应用的屏幕内容、马达振动或射频特定模式。
|
* 1. 若前台已有应用运行,且该应用未挂载在后台,则对其执行 onClose() 关闭生命周期。
|
||||||
* 3. 将活跃应用指针指向新应用,将其 is_active 和 is_running 标志位置 1。
|
* 2. 将 fg_app 指针指向新的 app。
|
||||||
* 4. 调用新应用的 OnStart() 初始化回调以绘制新页面。
|
* 3. 若该 app 目前并未在后台槽位中运行,则执行其 OnStart() 启动生命周期。
|
||||||
*/
|
*/
|
||||||
void AppManager_StartApp(AppID app_id);
|
bit AppManager_SwitchToForeground(WristbandApp *app);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 向当前活跃的前台应用程序派发事件
|
* @brief 动态将一个应用挂载到后台槽位
|
||||||
* @param evt 要分发派送的系统事件
|
* @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);
|
void AppManager_DispatchEvent(SystemEvent evt);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 在系统主循环中持续轮询运行活跃应用
|
* @brief 在系统主循环中持续轮询轮转前后台所有活跃的应用
|
||||||
* @details 负责从全局注册表中定位到 active_app,并轮询执行其 onRun() 周期回调。
|
* @details 负责轮询前台 fg_app 对应的 onRun(),并遍历轮询后台 bg_apps 数组中所有有效应用的 onRun()。
|
||||||
* 该接口同时集成了【CPU占用率限制机制】:如果在调用 onRun 前后,检测到应用
|
|
||||||
* 占用 CPU 时间超过了 APP_ONRUN_MAX_TICKS (5ms),它将在下一次循环中暂时挂起
|
|
||||||
* 该应用,强制释放本次 CPU 占用以防止界面卡死或影响底层射频解调。
|
|
||||||
*/
|
*/
|
||||||
void AppManager_RunActiveApp(void);
|
void AppManager_RunActiveApp(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取当前前台活跃应用的唯一应用程序 ID
|
* @brief 获取当前前台活跃应用的 ID
|
||||||
* @return AppID 当前活跃前台应用的枚举ID
|
* @return u8 当前活跃前台应用的 ID 编号。若无则返回 255 (0xFF)
|
||||||
*/
|
*/
|
||||||
AppID AppManager_GetActiveAppID(void);
|
u8 AppManager_GetActiveAppID(void);
|
||||||
|
|
||||||
|
// 兼容性接口声明
|
||||||
|
void AppManager_StartApp(u8 app_id);
|
||||||
|
|
||||||
/* ====== 时间编辑状态机交互接口 ====== */
|
// ====== 系统中各独立 App 实例的外包声明 (各 App 内部进行实体定义) ======
|
||||||
|
extern WristbandApp clock_app;
|
||||||
/**
|
extern WristbandApp menu_app;
|
||||||
* @brief 启动时钟数值调整编辑状态机
|
extern WristbandApp pair_app;
|
||||||
* @details 由 MenuApp 的 "HORLOGE" 项被确认点击后触发。用于直接将系统运行状态切入
|
extern WristbandApp alarm_app;
|
||||||
* STATE_SET_TIME,控制灯条常亮蓝色,马达短振反馈,并进入时间参数微调页。
|
extern WristbandApp sos_app;
|
||||||
*/
|
extern WristbandApp rf_monitor_app; // 后台射频监控应用
|
||||||
void Start_Time_Edit(void);
|
|
||||||
|
|
||||||
#endif // __APP_MANAGER_H__
|
#endif // __APP_MANAGER_H__
|
||||||
|
|||||||
187
App/app_menu.c
Normal file
187
App/app_menu.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
231
App/app_pair.c
Normal file
231
App/app_pair.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
106
App/app_rf_monitor.c
Normal file
106
App/app_rf_monitor.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
137
App/app_sos.c
Normal file
137
App/app_sos.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
37
App/clock.c
Normal file
37
App/clock.c
Normal file
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
App/clock.h
Normal file
17
App/clock.h
Normal file
@@ -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__
|
||||||
@@ -68,18 +68,9 @@ typedef enum {
|
|||||||
KEY_EVENT_CONFIRM_LONG // ■ 确认键长按事件 (打开主菜单)
|
KEY_EVENT_CONFIRM_LONG // ■ 确认键长按事件 (打开主菜单)
|
||||||
} KeyEvent;
|
} KeyEvent;
|
||||||
|
|
||||||
/* ====== 事件优先级枚举 ====== */
|
|
||||||
typedef enum {
|
|
||||||
EVENT_PRIORITY_LOW, // 低优先级:系统内部状态更新 (如时钟分钟更新)
|
|
||||||
EVENT_PRIORITY_NORMAL, // 正常优先级:用户按键交互、菜单导航
|
|
||||||
EVENT_PRIORITY_HIGH, // 高优先级:无线传感器入侵警报
|
|
||||||
EVENT_PRIORITY_URGENT // 紧急优先级:SOS 主动求救
|
|
||||||
} EventPriority;
|
|
||||||
|
|
||||||
/* ====== 系统事件结构体定义 ====== */
|
/* ====== 系统事件结构体定义 ====== */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
KeyEvent key_event; // 关联的按键事件类型
|
KeyEvent key_event; // 关联的按键事件类型
|
||||||
EventPriority priority; // 事件的处理优先级
|
|
||||||
u32 extra_data; // 随事件携带的额外数据 (如 24 位射频源地址等)
|
u32 extra_data; // 随事件携带的额外数据 (如 24 位射频源地址等)
|
||||||
u8 extra_size; // 额外数据的有效字节大小 (0~4)
|
u8 extra_size; // 额外数据的有效字节大小 (0~4)
|
||||||
} SystemEvent;
|
} SystemEvent;
|
||||||
|
|||||||
134
App/event.c
134
App/event.c
@@ -2,6 +2,8 @@
|
|||||||
#include "app_manager.h"
|
#include "app_manager.h"
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
#include "clock.h"
|
||||||
|
#include "xtell.h"
|
||||||
|
|
||||||
// 定义事件环形队列的最大深度为 8
|
// 定义事件环形队列的最大深度为 8
|
||||||
#define EVENT_QUEUE_DEPTH 8
|
#define EVENT_QUEUE_DEPTH 8
|
||||||
@@ -64,7 +66,6 @@ SystemEvent EventQueue_Pop(void)
|
|||||||
SystemEvent evt;
|
SystemEvent evt;
|
||||||
// 默认空事件属性
|
// 默认空事件属性
|
||||||
evt.key_event = KEY_EVENT_NONE;
|
evt.key_event = KEY_EVENT_NONE;
|
||||||
evt.priority = EVENT_PRIORITY_LOW;
|
|
||||||
evt.extra_data = 0;
|
evt.extra_data = 0;
|
||||||
evt.extra_size = 0;
|
evt.extra_size = 0;
|
||||||
|
|
||||||
@@ -118,46 +119,17 @@ void Event_Dispatcher_Loop(void)
|
|||||||
inactivity_timer = 0; // 捕获到有效事件,清零休眠计时器,重置闲置超时
|
inactivity_timer = 0; // 捕获到有效事件,清零休眠计时器,重置闲置超时
|
||||||
|
|
||||||
// 串口打印调试信息
|
// 串口打印调试信息
|
||||||
Uart_SendString("[EVENT] key=");
|
XTELL_LOG("[EVENT] key=");
|
||||||
Uart_SendHex8((u8)evt.key_event);
|
XTELL_LOG_HEX8((u8)evt.key_event);
|
||||||
Uart_SendString(", pri=");
|
XTELL_LOG("\r\n");
|
||||||
Uart_SendHex8((u8)evt.priority);
|
|
||||||
Uart_SendString("\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) &&
|
if ((evt.key_event == KEY_EVENT_SOS_CLICK || evt.key_event == KEY_EVENT_SOS_LONG) &&
|
||||||
AppManager_GetActiveAppID() != APP_ID_SOS) {
|
AppManager_GetActiveAppID() != APP_ID_SOS) {
|
||||||
AppManager_StartApp(APP_ID_SOS);
|
AppManager_StartApp(APP_ID_SOS);
|
||||||
return;
|
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);
|
AppManager_DispatchEvent(evt);
|
||||||
}
|
}
|
||||||
@@ -182,89 +154,14 @@ void Timer1_Init(void)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 定时器 1 中断服务程序 (每 1ms 触发一次)
|
* @brief 定时器 1 中断服务程序 (每 1ms 触发一次)
|
||||||
* @details 承担了系统软时钟计算、报警与求救下的马达控制状态机以及 10ms 周期性按键消抖扫描的功能。
|
* @details 承担了系统软时钟计算、10ms 周期性按键消抖扫描的功能。马达等特定业务已剥离至 App 内部。
|
||||||
*/
|
*/
|
||||||
void Timer1_Isr(void) interrupt 3
|
void Timer1_Isr(void) interrupt 3
|
||||||
{
|
{
|
||||||
SystemEvent evt;
|
SystemEvent evt;
|
||||||
|
|
||||||
// 1ms 递增滴答
|
// 1ms 定时更新软时钟
|
||||||
ms_tick++;
|
Clock_IncMS();
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 报警屏幕红边框以 100ms 周期亮灭,进行颜色爆闪指示
|
// 报警屏幕红边框以 100ms 周期亮灭,进行颜色爆闪指示
|
||||||
alarm_flash_flag = (ms_tick / 100) % 2;
|
alarm_flash_flag = (ms_tick / 100) % 2;
|
||||||
@@ -294,12 +191,11 @@ void Timer1_Isr(void) interrupt 3
|
|||||||
evt.extra_data = 0;
|
evt.extra_data = 0;
|
||||||
evt.extra_size = 0;
|
evt.extra_size = 0;
|
||||||
|
|
||||||
// 双键组合判定:▲ 键和 ▼ 键同时按下的消抖与生成
|
// 双键组合判定:▲ 键 and ▼ 键同时按下的消抖与生成
|
||||||
if (raw_up && raw_down) {
|
if (raw_up && raw_down) {
|
||||||
comb_hold++;
|
comb_hold++;
|
||||||
if (comb_hold == 300) { // 持续按下达 3 秒 (300 * 10ms = 3s)
|
if (comb_hold == 300) { // 持续按下达 3 秒 (300 * 10ms = 3s)
|
||||||
evt.key_event = KEY_EVENT_UP_DOWN_COMB;
|
evt.key_event = KEY_EVENT_UP_DOWN_COMB;
|
||||||
evt.priority = EVENT_PRIORITY_NORMAL;
|
|
||||||
EventQueue_Push(evt); // 压入队列
|
EventQueue_Push(evt); // 压入队列
|
||||||
}
|
}
|
||||||
key_up_hold = 0; // 清零单个按键计数,防止双击事件干扰
|
key_up_hold = 0; // 清零单个按键计数,防止双击事件干扰
|
||||||
@@ -312,14 +208,12 @@ void Timer1_Isr(void) interrupt 3
|
|||||||
key_up_hold++;
|
key_up_hold++;
|
||||||
if (key_up_hold == 300) { // 达到 3 秒判定为长按
|
if (key_up_hold == 300) { // 达到 3 秒判定为长按
|
||||||
evt.key_event = KEY_EVENT_UP_LONG;
|
evt.key_event = KEY_EVENT_UP_LONG;
|
||||||
evt.priority = EVENT_PRIORITY_NORMAL;
|
|
||||||
EventQueue_Push(evt);
|
EventQueue_Push(evt);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果松开时按下计数在消抖阈值(20ms)至长按阈值(3s)之间,则判定为一次短按
|
// 如果松开时按下计数在消抖阈值(20ms)至长按阈值(3s)之间,则判定为一次短按
|
||||||
if (key_up_hold >= 2 && key_up_hold < 300) {
|
if (key_up_hold >= 2 && key_up_hold < 300) {
|
||||||
evt.key_event = KEY_EVENT_UP_CLICK;
|
evt.key_event = KEY_EVENT_UP_CLICK;
|
||||||
evt.priority = EVENT_PRIORITY_NORMAL;
|
|
||||||
EventQueue_Push(evt);
|
EventQueue_Push(evt);
|
||||||
}
|
}
|
||||||
key_up_hold = 0; // 释放归零
|
key_up_hold = 0; // 释放归零
|
||||||
@@ -330,13 +224,11 @@ void Timer1_Isr(void) interrupt 3
|
|||||||
key_down_hold++;
|
key_down_hold++;
|
||||||
if (key_down_hold == 300) { // 长按 3 秒
|
if (key_down_hold == 300) { // 长按 3 秒
|
||||||
evt.key_event = KEY_EVENT_DOWN_LONG;
|
evt.key_event = KEY_EVENT_DOWN_LONG;
|
||||||
evt.priority = EVENT_PRIORITY_NORMAL;
|
|
||||||
EventQueue_Push(evt);
|
EventQueue_Push(evt);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (key_down_hold >= 2 && key_down_hold < 300) {
|
if (key_down_hold >= 2 && key_down_hold < 300) {
|
||||||
evt.key_event = KEY_EVENT_DOWN_CLICK;
|
evt.key_event = KEY_EVENT_DOWN_CLICK;
|
||||||
evt.priority = EVENT_PRIORITY_NORMAL;
|
|
||||||
EventQueue_Push(evt);
|
EventQueue_Push(evt);
|
||||||
}
|
}
|
||||||
key_down_hold = 0; // 释放归零
|
key_down_hold = 0; // 释放归零
|
||||||
@@ -347,13 +239,11 @@ void Timer1_Isr(void) interrupt 3
|
|||||||
key_confirm_hold++;
|
key_confirm_hold++;
|
||||||
if (key_confirm_hold == 300) { // 长按 3 秒打开主菜单
|
if (key_confirm_hold == 300) { // 长按 3 秒打开主菜单
|
||||||
evt.key_event = KEY_EVENT_CONFIRM_LONG;
|
evt.key_event = KEY_EVENT_CONFIRM_LONG;
|
||||||
evt.priority = EVENT_PRIORITY_NORMAL;
|
|
||||||
EventQueue_Push(evt);
|
EventQueue_Push(evt);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (key_confirm_hold >= 2 && key_confirm_hold < 300) {
|
if (key_confirm_hold >= 2 && key_confirm_hold < 300) {
|
||||||
evt.key_event = KEY_EVENT_CONFIRM_CLICK;
|
evt.key_event = KEY_EVENT_CONFIRM_CLICK;
|
||||||
evt.priority = EVENT_PRIORITY_NORMAL;
|
|
||||||
EventQueue_Push(evt);
|
EventQueue_Push(evt);
|
||||||
}
|
}
|
||||||
key_confirm_hold = 0; // 释放归零
|
key_confirm_hold = 0; // 释放归零
|
||||||
@@ -362,15 +252,13 @@ void Timer1_Isr(void) interrupt 3
|
|||||||
// SOS 物理求救按键状态机
|
// SOS 物理求救按键状态机
|
||||||
if (raw_sos) {
|
if (raw_sos) {
|
||||||
key_sos_hold++;
|
key_sos_hold++;
|
||||||
if (key_sos_hold == 300) { // 长按 3 秒,发出 URGENT 优先级求救,插入队首
|
if (key_sos_hold == 300) { // 长按 3 秒,发出求救,插入队首
|
||||||
evt.key_event = KEY_EVENT_SOS_LONG;
|
evt.key_event = KEY_EVENT_SOS_LONG;
|
||||||
evt.priority = EVENT_PRIORITY_URGENT;
|
|
||||||
EventQueue_InsertFront(evt);
|
EventQueue_InsertFront(evt);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (key_sos_hold >= 2 && key_sos_hold < 300) { // 短按即呼救,插入队首
|
if (key_sos_hold >= 2 && key_sos_hold < 300) { // 短按即呼救,插入队首
|
||||||
evt.key_event = KEY_EVENT_SOS_CLICK;
|
evt.key_event = KEY_EVENT_SOS_CLICK;
|
||||||
evt.priority = EVENT_PRIORITY_URGENT;
|
|
||||||
EventQueue_InsertFront(evt);
|
EventQueue_InsertFront(evt);
|
||||||
}
|
}
|
||||||
key_sos_hold = 0; // 释放归零
|
key_sos_hold = 0; // 释放归零
|
||||||
|
|||||||
91
App/main.c
91
App/main.c
@@ -5,17 +5,20 @@
|
|||||||
#include "database.h"
|
#include "database.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
#include "clock.h"
|
||||||
|
#include "xtell.h"
|
||||||
#include "../Drivers/rf.h"
|
#include "../Drivers/rf.h"
|
||||||
#include "../Drivers/lcd.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; // 系统整体的当前运行状态,默认为正常时钟待机页
|
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)
|
u8 menu_select = 0; // 主菜单当前选中的行高亮索引 (0~2)
|
||||||
u32 captured_addr = 0; // 配对或报警时,捕获到的传感器 24 位 EV1527 射频物理地址
|
u32 captured_addr = 0; // 配对或报警时,捕获到的传感器 24 位 EV1527 射频物理地址
|
||||||
@@ -46,17 +49,6 @@ volatile u16 comb_hold = 0; // ▲ + ▼ 组合键按下时长累
|
|||||||
* ========================================================================= */
|
* ========================================================================= */
|
||||||
/**
|
/**
|
||||||
* @brief 解析通过串口 1 发送来的键盘及测试模拟指令
|
* @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)
|
void Debug_ProcessCommand(char cmd)
|
||||||
{
|
{
|
||||||
@@ -67,39 +59,32 @@ void Debug_ProcessCommand(char cmd)
|
|||||||
|
|
||||||
if (cmd == 'u') {
|
if (cmd == 'u') {
|
||||||
debug_evt.key_event = KEY_EVENT_UP_CLICK;
|
debug_evt.key_event = KEY_EVENT_UP_CLICK;
|
||||||
debug_evt.priority = EVENT_PRIORITY_NORMAL;
|
|
||||||
EventQueue_Push(debug_evt); // 压入队尾
|
EventQueue_Push(debug_evt); // 压入队尾
|
||||||
Uart_SendString("[UART] KEY_UP Clicked\r\n");
|
XTELL_LOG("[UART] KEY_UP Clicked\r\n");
|
||||||
} else if (cmd == 'U') {
|
} else if (cmd == 'U') {
|
||||||
debug_evt.key_event = KEY_EVENT_UP_LONG;
|
debug_evt.key_event = KEY_EVENT_UP_LONG;
|
||||||
debug_evt.priority = EVENT_PRIORITY_NORMAL;
|
|
||||||
EventQueue_Push(debug_evt);
|
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') {
|
} else if (cmd == 'd') {
|
||||||
debug_evt.key_event = KEY_EVENT_DOWN_CLICK;
|
debug_evt.key_event = KEY_EVENT_DOWN_CLICK;
|
||||||
debug_evt.priority = EVENT_PRIORITY_NORMAL;
|
|
||||||
EventQueue_Push(debug_evt);
|
EventQueue_Push(debug_evt);
|
||||||
Uart_SendString("[UART] KEY_DOWN Clicked\r\n");
|
XTELL_LOG("[UART] KEY_DOWN Clicked\r\n");
|
||||||
} else if (cmd == 'D') {
|
} else if (cmd == 'D') {
|
||||||
debug_evt.key_event = KEY_EVENT_DOWN_LONG;
|
debug_evt.key_event = KEY_EVENT_DOWN_LONG;
|
||||||
debug_evt.priority = EVENT_PRIORITY_NORMAL;
|
|
||||||
EventQueue_Push(debug_evt);
|
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') {
|
} else if (cmd == 's') {
|
||||||
debug_evt.key_event = KEY_EVENT_SOS_CLICK;
|
debug_evt.key_event = KEY_EVENT_SOS_CLICK;
|
||||||
debug_evt.priority = EVENT_PRIORITY_URGENT;
|
|
||||||
EventQueue_InsertFront(debug_evt); // 紧急事件插入队首
|
EventQueue_InsertFront(debug_evt); // 紧急事件插入队首
|
||||||
Uart_SendString("[UART] KEY_SOS Clicked\r\n");
|
XTELL_LOG("[UART] KEY_SOS Clicked\r\n");
|
||||||
} else if (cmd == 'S') {
|
} else if (cmd == 'S') {
|
||||||
debug_evt.key_event = KEY_EVENT_SOS_LONG;
|
debug_evt.key_event = KEY_EVENT_SOS_LONG;
|
||||||
debug_evt.priority = EVENT_PRIORITY_URGENT;
|
|
||||||
EventQueue_InsertFront(debug_evt);
|
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') {
|
} else if (cmd == 'c' || cmd == 'C') {
|
||||||
debug_evt.key_event = KEY_EVENT_UP_DOWN_COMB;
|
debug_evt.key_event = KEY_EVENT_UP_DOWN_COMB;
|
||||||
debug_evt.priority = EVENT_PRIORITY_NORMAL;
|
|
||||||
EventQueue_Push(debug_evt);
|
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') {
|
} else if (cmd == 'a' || cmd == 'A') {
|
||||||
u32 mock_addr = 0x123456; // 设定模拟的报警射频源地址
|
u32 mock_addr = 0x123456; // 设定模拟的报警射频源地址
|
||||||
u8 mock_slot;
|
u8 mock_slot;
|
||||||
@@ -107,48 +92,39 @@ void Debug_ProcessCommand(char cmd)
|
|||||||
if (!Check_Sensor_ID(mock_addr, &mock_slot)) {
|
if (!Check_Sensor_ID(mock_addr, &mock_slot)) {
|
||||||
Add_Sensor_With_Zone(mock_addr, 0, 1);
|
Add_Sensor_With_Zone(mock_addr, 0, 1);
|
||||||
}
|
}
|
||||||
debug_evt.key_event = KEY_EVENT_NONE;
|
mock_rx_addr = mock_addr;
|
||||||
debug_evt.priority = EVENT_PRIORITY_HIGH; // 入侵报警设为 HIGH 优先级
|
mock_rx_data = 0x01;
|
||||||
debug_evt.extra_data = mock_addr;
|
mock_rf_ready = 1; // 触发射频模拟
|
||||||
debug_evt.extra_size = 4;
|
XTELL_LOG("[UART] Mock RF Alarm Triggered\r\n");
|
||||||
EventQueue_InsertFront(debug_evt); // 高优先级插队
|
|
||||||
Uart_SendString("[UART] Mock RF Alarm\r\n");
|
|
||||||
} else if (cmd == 'p' || cmd == 'P') {
|
} else if (cmd == 'p' || cmd == 'P') {
|
||||||
// 只有当手环切入 Pair 模式时,模拟对码信号才有效
|
mock_rx_addr = 0x789ABC;
|
||||||
if (AppManager_GetActiveAppID() == APP_ID_PAIR) {
|
mock_rx_data = 0x01;
|
||||||
debug_evt.key_event = KEY_EVENT_NONE;
|
mock_rf_ready = 1; // 触发射频模拟
|
||||||
debug_evt.priority = EVENT_PRIORITY_NORMAL;
|
XTELL_LOG("[UART] Mock RF Pair Triggered\r\n");
|
||||||
debug_evt.extra_data = 0x789ABC; // 设定模拟对码硬件地址
|
|
||||||
debug_evt.extra_size = 4;
|
|
||||||
EventQueue_Push(debug_evt);
|
|
||||||
Uart_SendString("[UART] Mock RF Pair Signal\r\n");
|
|
||||||
} else {
|
|
||||||
Uart_SendString("[UART] Not in PAIR mode\r\n");
|
|
||||||
}
|
|
||||||
} else if (cmd == '1') {
|
} else if (cmd == '1') {
|
||||||
// 模拟切换至 正常撤防 (NORMAL) 状态并刷新时钟
|
// 模拟切换至 正常撤防 (NORMAL) 状态并刷新时钟
|
||||||
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
|
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
|
||||||
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");
|
XTELL_LOG("[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_CLOCK) {
|
||||||
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");
|
XTELL_LOG("[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_CLOCK) {
|
||||||
current_state = STATE_DISARMED;
|
current_state = STATE_DISARMED;
|
||||||
UI_ShowClockPage(current_state, current_hour, current_min);
|
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') {
|
} else if (cmd == 'z' || cmd == 'Z') {
|
||||||
inactivity_timer = 6000; // 模拟闲置 60 秒超时,强制触发主循环深度休眠
|
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') {
|
} else if (cmd == 't' || cmd == 'T') {
|
||||||
Loopback_Test(); // 调用回环测试验证屏幕
|
Loopback_Test(); // 调用回环测试验证屏幕
|
||||||
RF_Test_Pin(); // 无线射频测试
|
RF_Test_Pin(); // 无线射频测试
|
||||||
@@ -176,20 +152,24 @@ void main(void)
|
|||||||
Delay_ms(50); // 等待电压充盈稳定,避开浪涌
|
Delay_ms(50); // 等待电压充盈稳定,避开浪涌
|
||||||
LCD_Init(); // 初始化 Truly AMOLED 屏幕控制寄存器序列
|
LCD_Init(); // 初始化 Truly AMOLED 屏幕控制寄存器序列
|
||||||
Uart1_Init(); // 初始化串口 1 波特率 115200 供调试日志
|
Uart1_Init(); // 初始化串口 1 波特率 115200 供调试日志
|
||||||
Uart_SendString("Wristband System Initialized!\r\n");
|
XTELL_LOG("Wristband System Initialized!\r\n");
|
||||||
|
|
||||||
Load_Database(); // 从 IAP Flash 第 254 扇区读取所有已绑定的传感器数据至 RAM
|
Load_Database(); // 从 IAP Flash 第 254 扇区读取所有已绑定的传感器数据至 RAM
|
||||||
|
|
||||||
/* 步骤 1: 初始化事件环形缓冲区队列 */
|
/* 步骤 1: 初始化事件环形缓冲区队列 */
|
||||||
EventQueue_Init();
|
EventQueue_Init();
|
||||||
|
|
||||||
/* 步骤 2+5: 初始化应用管理器并登记所有前台应用,默认加载 ClockApp */
|
/* 步骤 2: 初始化应用管理器 */
|
||||||
AppManager_Init();
|
AppManager_Init();
|
||||||
|
|
||||||
|
/* 步骤 3: 动态挂载待机时钟应用到前台,并挂载后台射频监控应用 */
|
||||||
|
AppManager_SwitchToForeground(&clock_app);
|
||||||
|
AppManager_AttachToBackground(&rf_monitor_app);
|
||||||
|
|
||||||
/* 启动 1ms 系统基准定时器 Timer1 */
|
/* 启动 1ms 系统基准定时器 Timer1 */
|
||||||
Timer1_Init();
|
Timer1_Init();
|
||||||
|
|
||||||
Uart_SendString("[SYS] Event-driven framework ready\r\n");
|
XTELL_LOG("[SYS] Event-driven framework ready\r\n");
|
||||||
|
|
||||||
// ====== 主循环 ======
|
// ====== 主循环 ======
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -204,18 +184,19 @@ void main(void)
|
|||||||
Event_Dispatcher_Loop();
|
Event_Dispatcher_Loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 3. 应用调度中心:运行当前处于前台活跃状态的应用(集成了 CPU 运行时间超时挂起保护) */
|
/* 3. 应用调度中心:运行前后台所有已挂载激活的应用 */
|
||||||
AppManager_RunActiveApp();
|
AppManager_RunActiveApp();
|
||||||
|
|
||||||
/* 4. 自动休眠判定:常态无操作闲置达到 60 秒 (6000 * 10ms = 60s) 时,切入低功耗停机休眠 */
|
/* 4. 自动休眠判定:常态无操作闲置达到 60 秒 (6000 * 10ms = 60s) 时,切入低功耗停机休眠 */
|
||||||
if (inactivity_timer >= 6000) {
|
if (inactivity_timer >= 6000) {
|
||||||
inactivity_timer = 0;
|
inactivity_timer = 0;
|
||||||
current_state = STATE_SLEEP; // 系统置为休眠状态
|
current_state = STATE_SLEEP; // 系统置为休眠状态
|
||||||
Enter_Low_Power_Sleep(); // 该函数是阻塞的,MCU 将在此挂起。唤醒后将自动从其后继续执行
|
Enter_Low_Power_Sleep(); // 挂起 MCU
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 唤醒源事件标志,由对应的外部中断 ISR 写入
|
// 唤醒源事件标志,由对应的外部中断 ISR 写入
|
||||||
volatile u8 p0_wakeup_flag = 0; // P0 端口按键唤醒标志
|
volatile u8 p0_wakeup_flag = 0; // P0 端口按键唤醒标志
|
||||||
volatile u8 p2_wakeup_flag = 0; // P2 端口按键唤醒标志
|
volatile u8 p2_wakeup_flag = 0; // P2 端口按键唤醒标志
|
||||||
|
|||||||
22
App/system.c
22
App/system.c
@@ -1,6 +1,8 @@
|
|||||||
#include "system.h"
|
#include "system.h"
|
||||||
#include "rgb.h"
|
#include "rgb.h"
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
|
#include "clock.h"
|
||||||
|
#include "xtell.h"
|
||||||
#include "../Drivers/lcd.h"
|
#include "../Drivers/lcd.h"
|
||||||
#include "../Drivers/rf.h"
|
#include "../Drivers/rf.h"
|
||||||
#include "app_manager.h"
|
#include "app_manager.h"
|
||||||
@@ -268,7 +270,7 @@ void FormatHex(u32 val, char *buf)
|
|||||||
*/
|
*/
|
||||||
void Enter_Low_Power_Sleep(void)
|
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 灯条
|
// 1. 关闭/熄灭 FCOB 灯条
|
||||||
RGB_Send(0, 0, 0, 0, 0, 0);
|
RGB_Send(0, 0, 0, 0, 0, 0);
|
||||||
@@ -343,7 +345,7 @@ void Enter_Low_Power_Sleep(void)
|
|||||||
EA = 1;
|
EA = 1;
|
||||||
|
|
||||||
// 13. 向电源控制寄存器 PCON 写入 0x02,使 MCU 核心进入 Power-Down 模式,CPU 停止运转
|
// 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 位,硬件立刻休眠挂起
|
PCON |= 0x02; // 设定 PD 位,硬件立刻休眠挂起
|
||||||
|
|
||||||
// 唤醒后必须跟 _nop_() 空操作以占位和刷新指令流水线
|
// 唤醒后必须跟 _nop_() 空操作以占位和刷新指令流水线
|
||||||
@@ -388,20 +390,20 @@ void Wakeup_Restore(void)
|
|||||||
// 打印日志,判断并告知调试串口具体是哪个物理源将手环唤醒的
|
// 打印日志,判断并告知调试串口具体是哪个物理源将手环唤醒的
|
||||||
if ((p0_wakeup_flag & 0x0E) || (p2_wakeup_flag & 0x40))
|
if ((p0_wakeup_flag & 0x0E) || (p2_wakeup_flag & 0x40))
|
||||||
{
|
{
|
||||||
Uart_SendString("[WR] Woken by KEY (P0=");
|
XTELL_LOG("[WR] Woken by KEY (P0=");
|
||||||
Uart_SendHex8(p0_wakeup_flag);
|
XTELL_LOG_HEX8(p0_wakeup_flag);
|
||||||
Uart_SendString(", P2=");
|
XTELL_LOG(", P2=");
|
||||||
Uart_SendHex8(p2_wakeup_flag);
|
XTELL_LOG_HEX8(p2_wakeup_flag);
|
||||||
Uart_SendString(")\r\n");
|
XTELL_LOG(")\r\n");
|
||||||
}
|
}
|
||||||
else if ((p2_wakeup_flag & 0x02) || rf_wakeup_flag)
|
else if ((p2_wakeup_flag & 0x02) || rf_wakeup_flag)
|
||||||
{
|
{
|
||||||
rf_wakeup_flag = 1; // 确认标记为射频解调唤醒
|
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
|
else
|
||||||
{
|
{
|
||||||
Uart_SendString("[WR] Woken by TIMER/Other\r\n");
|
XTELL_LOG("[WR] Woken by TIMER/Other\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 【屏幕供电软启动】:
|
// 5. 【屏幕供电软启动】:
|
||||||
@@ -422,7 +424,7 @@ void Wakeup_Restore(void)
|
|||||||
// 9. 更新运行状态为普通时钟,并强制调用时钟界面函数刷屏,返回工作状态
|
// 9. 更新运行状态为普通时钟,并强制调用时钟界面函数刷屏,返回工作状态
|
||||||
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("[SYS] Wakeup restored!\r\n");
|
XTELL_LOG("[SYS] Wakeup restored!\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
24
App/xtell.h
Normal file
24
App/xtell.h
Normal file
@@ -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__
|
||||||
26
Drivers/rf.c
26
Drivers/rf.c
@@ -179,17 +179,13 @@ u16 GetPulseDuration(u8 state, u16 timeout_us)
|
|||||||
return (u16)((u32)GetTimer0_Safe() * 12UL / (MAIN_Fosc / 1000000UL));
|
return (u16)((u32)GetTimer0_Safe() * 12UL / (MAIN_Fosc / 1000000UL));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#include "../App/xtell.h"
|
||||||
* @brief 软件自适应解码接收一帧无线 EV1527 信号
|
|
||||||
* @param out_addr 解码成功后,用于回传物理地址高 20 位的指针
|
// 模拟射频信号全局变量定义
|
||||||
* @param out_data 解码成功后,用于回传指令数据低 4 位的指针
|
volatile u32 mock_rx_addr = 0;
|
||||||
* @return bit 1-解码成功且校验通过,0-捕获失败或脉冲畸变
|
volatile u8 mock_rx_data = 0;
|
||||||
* @note 核心算法:
|
volatile bit mock_rf_ready = 0;
|
||||||
* 1. 等待并寻找合法的同步引导头脉冲 (1T高电平 + 31T低电平,其中低/高比率约为 31,本程序允许比率在 15~48 内变化以抗物理畸变)。
|
|
||||||
* 2. 依据捕获的同步低电平宽度估算基准周期 T = low_time / 31。
|
|
||||||
* 3. 随后依次高精度捕获 24 个数据位的高电平和低电平时间。
|
|
||||||
* 4. 自适应决策:对于每个 bit,如果其高电平时间 raw_h > 低电平时间 raw_l,则判定该位为 1;否则判定该位为 0。
|
|
||||||
*/
|
|
||||||
bit EV1527_Decode(u32 *out_addr, u8 *out_data)
|
bit EV1527_Decode(u32 *out_addr, u8 *out_data)
|
||||||
{
|
{
|
||||||
u16 high_time, low_time;
|
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_h[24]; // 高电平时间寄存数组 (置于 idata 区)
|
||||||
u16 idata raw_l[24]; // 低电平时间寄存数组
|
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. 同步头引导脉冲捕获 (定位上升沿) ======
|
// ====== 1. 同步头引导脉冲捕获 (定位上升沿) ======
|
||||||
{
|
{
|
||||||
u16 wait_cnt = 0;
|
u16 wait_cnt = 0;
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
{"hostname":"DESKTOP-0S8JI7H","username":"xtell"}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"hostname":"DESKTOP-0S8JI7H","username":"xtell"}
|
|
||||||
Reference in New Issue
Block a user