Files
stc32g128k/App/main.c

232 lines
10 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "config.h"
#include "rgb.h"
#include "event.h"
#include "app_manager.h"
#include "database.h"
#include "ui.h"
#include "system.h"
#include "clock.h"
#include "xtell.h"
#include "../Drivers/rf.h"
#include "../Drivers/lcd.h"
// 声明外部射频模拟变量
extern volatile u32 mock_rx_addr;
extern volatile u8 mock_rx_data;
extern volatile bit mock_rf_ready;
/* =========================================================================
* 系统全局变量定义
* ========================================================================= */
SystemState current_state = STATE_NORMAL; // 系统整体的当前运行状态,默认为正常时钟待机页
u8 menu_select = 0; // 主菜单当前选中的行高亮索引 (0~2)
u32 captured_addr = 0; // 配对或报警时,捕获到的传感器 24 位 EV1527 射频物理地址
u8 captured_type = 0; // 正在对码或触发警报的传感器设备类型 (0:门磁, 1:PIR...)
u8 alarm_sensor_slot = 0; // 触发警报的传感器在 sensor_list 数组中的索引槽位号
// 传感器存储列表数据库,位于单片机外部 XDATA 存储空间,总共 16 个槽位
Sensor_Slot xdata sensor_list[16];
// 全局滴答与定时中断计数变量,位于 volatile 防止被编译器优化
volatile u16 ms_tick = 0; // 定时中断 1ms 基准滴答累加器
volatile u16 inactivity_timer = 0; // 用户闲置计时器,用于无操作判定(单位: 10ms
volatile u32 pair_timeout_ms = 0; // 传感器配对对码超时毫秒计时器 (上限 30000ms 即 30s)
volatile u16 alarm_timer_ms = 0; // 入侵报警马达震动时序计数器
volatile u16 motor_timer_ms = 0; // 独立马达震动时长计数器
volatile bit alarm_flash_flag = 0; // 报警时红框周期性闪烁亮灭标志位
// 物理按键扫描与消抖长按计数器 (10ms 扫描一次)
volatile u16 key_scan_timer = 0; // 10ms 扫描周期分频器
volatile u16 key_up_hold = 0; // ▲ (KEY_UP) 按下时长累加
volatile u16 key_down_hold = 0; // ▼ (KEY_DOWN) 按下时长累加
volatile u16 key_confirm_hold = 0; // ■ (KEY_CONFIRM) 按下时长累加
volatile u16 key_sos_hold = 0; // SOS 按下时长累加
volatile u16 comb_hold = 0; // ▲ + ▼ 组合键按下时长累加
/* =========================================================================
* 串口调试指令解析与事件派发封装
* ========================================================================= */
/**
* @brief 解析通过串口 1 发送来的键盘及测试模拟指令
*/
void Debug_ProcessCommand(char cmd)
{
SystemEvent debug_evt;
inactivity_timer = 0; // 收到指令,立即重置用户的无操作闲置计时器
debug_evt.extra_data = 0;
debug_evt.extra_size = 0;
if (cmd == 'u') {
debug_evt.key_event = KEY_EVENT_UP_CLICK;
EventQueue_Push(debug_evt); // 压入队尾
XTELL_LOG("[UART] KEY_UP Clicked\r\n");
} else if (cmd == 'U') {
debug_evt.key_event = KEY_EVENT_UP_LONG;
EventQueue_Push(debug_evt);
XTELL_LOG("[UART] KEY_UP Long Pressed\r\n");
} else if (cmd == 'd') {
debug_evt.key_event = KEY_EVENT_DOWN_CLICK;
EventQueue_Push(debug_evt);
XTELL_LOG("[UART] KEY_DOWN Clicked\r\n");
} else if (cmd == 'D') {
debug_evt.key_event = KEY_EVENT_DOWN_LONG;
EventQueue_Push(debug_evt);
XTELL_LOG("[UART] KEY_DOWN Long Pressed\r\n");
} else if (cmd == 's') {
debug_evt.key_event = KEY_EVENT_SOS_CLICK;
EventQueue_InsertFront(debug_evt); // 紧急事件插入队首
XTELL_LOG("[UART] KEY_SOS Clicked\r\n");
} else if (cmd == 'S') {
debug_evt.key_event = KEY_EVENT_SOS_LONG;
EventQueue_InsertFront(debug_evt);
XTELL_LOG("[UART] KEY_SOS Long Pressed\r\n");
} else if (cmd == 'c' || cmd == 'C') {
debug_evt.key_event = KEY_EVENT_UP_DOWN_COMB;
EventQueue_Push(debug_evt);
XTELL_LOG("[UART] KEY_UP+DOWN Combined\r\n");
} else if (cmd == 'a' || cmd == 'A') {
u32 mock_addr = 0x123456; // 设定模拟的报警射频源地址
u8 mock_slot;
// 如果本地数据库中没有匹配该射频,则强制登记进 1 号防区
if (!Check_Sensor_ID(mock_addr, &mock_slot)) {
Add_Sensor_With_Zone(mock_addr, 0, 1);
}
mock_rx_addr = mock_addr;
mock_rx_data = 0x01;
mock_rf_ready = 1; // 触发射频模拟
XTELL_LOG("[UART] Mock RF Alarm Triggered\r\n");
} else if (cmd == 'p' || cmd == 'P') {
mock_rx_addr = 0x789ABC;
mock_rx_data = 0x01;
mock_rf_ready = 1; // 触发射频模拟
XTELL_LOG("[UART] Mock RF Pair Triggered\r\n");
} else if (cmd == '1') {
// 模拟切换至 正常撤防 (NORMAL) 状态并刷新时钟
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
current_state = STATE_NORMAL;
UI_ShowClockPage(current_state, current_hour, current_min);
}
XTELL_LOG("[UART] State: NORMAL\r\n");
} else if (cmd == '2') {
// 模拟切换至 已布防 (ARMED) 状态,普通门磁可触发警报
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
current_state = STATE_ARMED;
UI_ShowClockPage(current_state, current_hour, current_min);
}
XTELL_LOG("[UART] State: ARMED\r\n");
} else if (cmd == '3') {
// 模拟切换至 已撤防 (DISARMED) 状态,忽略普通报警
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
current_state = STATE_DISARMED;
UI_ShowClockPage(current_state, current_hour, current_min);
}
XTELL_LOG("[UART] State: DISARMED\r\n");
} else if (cmd == 'z' || cmd == 'Z') {
inactivity_timer = 6000; // 模拟闲置 60 秒超时,强制触发主循环深度休眠
XTELL_LOG("[UART] Force Entering Sleep mode!\r\n");
} else if (cmd == 't' || cmd == 'T') {
Loopback_Test(); // 调用回环测试验证屏幕
RF_Test_Pin(); // 无线射频测试
}
}
/* =========================================================================
* 主函数 (入口)
* ========================================================================= */
void main(void)
{
char cmd;
// ====== STC32G 特殊功能寄存器内核基础初始化 ======
WTST = 0; // 设置程序 Flash 访问等待时间为 0 (最高速运行)
EAXFR = 1; // 允许访问扩展特殊功能寄存器 (XSFR)
CKCON = 0; // 外部总线时钟设定为最快
WDT_CONTR = 0x00; // 关闭看门狗定时器
// ====== 硬件各模块基础初始化 ======
GPIO_Init(); // 配置各个引脚的推挽输出/准双向模式及内部上拉
RF_Init(); // 初始化射频芯片各引脚控制
Delay_ms(500); // 软启动延时,等待供电电容稳定
SGM_SendPulse(27); // 通过脉冲给 SGM3833 供电芯片发指令,稳压开启 LCD 正负电压轨
Delay_ms(50); // 等待电压充盈稳定,避开浪涌
LCD_Init(); // 初始化 Truly AMOLED 屏幕控制寄存器序列
Uart1_Init(); // 初始化串口 1 波特率 115200 供调试日志
XTELL_LOG("Wristband System Initialized!\r\n");
Load_Database(); // 从 IAP Flash 第 254 扇区读取所有已绑定的传感器数据至 RAM
/* 步骤 1: 初始化事件环形缓冲区队列 */
EventQueue_Init();
/* 步骤 2: 初始化应用管理器 */
AppManager_Init();
/* 步骤 3: 动态挂载待机时钟应用到前台,并挂载后台射频监控应用 */
AppManager_SwitchToForeground(&clock_app);
AppManager_AttachToBackground(&rf_monitor_app);
/* 启动 1ms 系统基准定时器 Timer1 */
Timer1_Init();
XTELL_LOG("[SYS] Event-driven framework ready\r\n");
// ====== 主循环 ======
while (1) {
/* 1. 串口非阻塞指令捕获 -> 装配为事件方式入队 */
cmd = Uart_RxChar();
if (cmd != '\0') {
Debug_ProcessCommand(cmd);
}
/* 2. 事件分发总线:循环轮询并处理队列中的所有有效事件 */
while (!EventQueue_IsEmpty()) {
Event_Dispatcher_Loop();
}
/* 3. 应用调度中心:运行前后台所有已挂载激活的应用 */
AppManager_RunActiveApp();
/* 4. 自动休眠判定:常态无操作闲置达到 60 秒 (6000 * 10ms = 60s) 时,切入低功耗停机休眠 */
if (inactivity_timer >= 6000) {
inactivity_timer = 0;
current_state = STATE_SLEEP; // 系统置为休眠状态
Enter_Low_Power_Sleep(); // 挂起 MCU
}
}
}
// 唤醒源事件标志,由对应的外部中断 ISR 写入
volatile u8 p0_wakeup_flag = 0; // P0 端口按键唤醒标志
volatile u8 p2_wakeup_flag = 0; // P2 端口按键唤醒标志
volatile u8 rf_wakeup_flag = 0; // P2.1 射频中断唤醒标志
/* =========================================================================
* STC32G 端口掉电外部中断唤醒中断服务程序 (ISR)
* ========================================================================= */
/**
* @brief Port0 端口掉电中断服务函数 (由 isr_jump.asm 引导定位至此)
* @details 负责处理 P0.1, P0.2, P0.3 按键在休眠模式下的物理触发,并立刻禁用中断防止反复执行。
*/
void Port0_Isr(void) interrupt 13
{
EAXFR = 1; // 使能访问扩展寄存区
p0_wakeup_flag = P0INTF | 0x01;// 读取并备份 P0 中断标志寄存器值 (0x01作安全掩码)
P0INTF = 0x00; // 清除 P0 端口中断悬挂标志位
P0INTE = 0x00; // 【关键保护】:立即关闭 P0 中断允许,防止按键处于低电平期间反复触发 ISR 导致堆栈溢出
}
/**
* @brief Port2 端口掉电中断服务函数 (由 isr_jump.asm 引导定位至此)
* @details 负责处理 P2.6 (SOS按键) 与 P2.1 (射频数据脚) 在休眠模式下的物理跳变唤醒。
*/
void Port2_Isr(void) interrupt 15
{
EAXFR = 1; // 使能扩展寄存区
p2_wakeup_flag = P2INTF | 0x01;// 读取并备份 P2 中断标志寄存区的值
P2INTF = 0x00; // 清除 P2 端口中断悬挂标志位
P2INTE = 0x00; // 立即关闭 P2 中断通道,防止低电平期间反复重入
}