Files
stc32g128k/App/event.c
edisondeng 2a83079007 refactor(event): replace global 60s inactivity timer with per-second tick event
Adds KEY_EVENT_SECOND_TICK, generated once per real second from
Clock_IncMS() via a new second_tick_flag and pushed through the normal
event queue, so every foreground App's onEvent() receives it and
decides independently whether to act. Removes the old global
inactivity_timer (60s sleep from anywhere) entirely. HomeApp now
accumulates its own idle-second counter and sleeps after 10s idle
specifically while on the home screen; MenuApp's existing 10s
auto-close is converted from onRun() polling to the same tick+reset
pattern for consistency. Any non-tick key event resets each app's own
counter to 0.
2026-07-31 18:49:03 +08:00

269 lines
9.2 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 "event.h"
#include "app_manager.h"
#include "database.h"
#include "system.h"
#include "xtell.h"
#include "../Drivers/adc.h"
// 定义事件环形队列的最大深度为 8
#define EVENT_QUEUE_DEPTH 8
// 环形队列存储数组 (存放在单片机 xdata 拓展 RAM区中)
static SystemEvent xdata event_queue[EVENT_QUEUE_DEPTH];
static u8 queue_head = 0; // 队首读指针
static u8 queue_tail = 0; // 队尾写指针
static u8 queue_count = 0; // 当前队列内有效事件总数
/**
* @brief 初始化/清空事件队列
*/
void EventQueue_Init(void)
{
queue_head = 0;
queue_tail = 0;
queue_count = 0;
}
/**
* @brief 将普通事件压入队尾
* @param evt 输入的系统事件
* @return bit 1-成功0-失败
*/
bit EventQueue_Push(SystemEvent evt)
{
if (queue_count >= EVENT_QUEUE_DEPTH) return 0; // 队列满,丢弃事件
EA = 0; // 关闭全局中断,保护临界区操作的原子性
event_queue[queue_tail] = evt; // 存入队尾
queue_tail = (queue_tail + 1) % EVENT_QUEUE_DEPTH; // 尾指针向后移动,回环
queue_count++; // 有效计数加 1
EA = 1; // 恢复全局中断
return 1;
}
/**
* @brief 将高优先级/紧急事件强行插到队首
* @param evt 输入的系统事件
* @return bit 1-成功0-失败
*/
bit EventQueue_InsertFront(SystemEvent evt)
{
if (queue_count >= EVENT_QUEUE_DEPTH) return 0; // 队列满,插队失败
EA = 0; // 屏蔽中断
// 头指针向前移动一位(带回环防溢出)
queue_head = (queue_head - 1 + EVENT_QUEUE_DEPTH) % EVENT_QUEUE_DEPTH;
event_queue[queue_head] = evt; // 存入腾出的队首位置
queue_count++; // 计数加 1
EA = 1; // 恢复中断
return 1;
}
/**
* @brief 从队首弹出一个缓冲事件
* @return SystemEvent 返回弹出的事件。若空返回空闲事件
*/
SystemEvent EventQueue_Pop(void)
{
SystemEvent evt;
// 默认空事件属性
evt.key_event = KEY_EVENT_NONE;
evt.priority = EVENT_PRIORITY_NORMAL;
evt.extra_data = 0;
evt.extra_size = 0;
if (queue_count == 0) return evt; // 队空,直接返回空闲事件
EA = 0; // 屏蔽中断
evt = event_queue[queue_head]; // 读取队首数据
queue_head = (queue_head + 1) % EVENT_QUEUE_DEPTH; // 头指针向后移动,回环
queue_count--; // 有效计数减 1
EA = 1; // 恢复中断
return evt;
}
/**
* @brief 判断队列是否为空
*/
bit EventQueue_IsEmpty(void)
{
return (queue_count == 0) ? 1 : 0;
}
/**
* @brief 判断队列是否已满
*/
bit EventQueue_IsFull(void)
{
return (queue_count >= EVENT_QUEUE_DEPTH) ? 1 : 0;
}
/**
* @brief 获取当前队列中的事件总数
*/
u8 EventQueue_GetCount(void)
{
return queue_count;
}
/**
* @brief 事件分发器核心轮询逻辑
* @details 逐一处理队列中的事件,并根据系统当前的运行模式进行拦截、跳转或下发派发。
*/
void Event_Dispatcher_Loop(void)
{
SystemEvent evt = EventQueue_Pop(); // 弹出一个事件
// 如果是无事发生的空事件,直接退出
if (evt.key_event == KEY_EVENT_NONE && evt.extra_size == 0) {
return;
}
// 串口打印调试信息 (整秒滴答事件每秒都会触发一次,不打印以免刷屏)
if (evt.key_event != KEY_EVENT_SECOND_TICK) {
XTELL_LOG("[EVENT] key=");
XTELL_LOG_HEX8((u8)evt.key_event);
XTELL_LOG("\r\n");
}
// SOS 按键紧急处理:若当前前台不为 SOS 应用,接收到 SOS 键动作,立即强行切入到 SOS_APP 并执行求救
if ((evt.key_event == KEY_EVENT_SOS_CLICK || evt.key_event == KEY_EVENT_SOS_LONG) &&
AppManager_GetActiveAppID() != APP_ID_SOS) {
AppManager_StartApp(APP_ID_SOS);
return;
}
// 关机事件处理:捕获到长按关机指令,直接调用 Power_Off 关断电源
if (evt.key_event == KEY_EVENT_POWER_OFF) {
Power_Off();
return;
}
// 常规事件(如普通的按键点击)直接透传分发给当前处于前台活跃的应用
AppManager_DispatchEvent(evt);
}
/**
* @brief 主循环按键扫描与消抖处理函数
* @details 由主循环每 10ms 检查并执行,完全在主线程中运行,绝无中断重入冲突。
*/
void Event_KeyScan_Poll(void)
{
SystemEvent evt;
// 读取按键的电平信号 (KEY_UP, KEY_CONFIRM 为低电平有效)
u8 raw_up = !KEY_UP;
u8 raw_down = ADC_IsKeyDown(); // KEY_DOWN 通过 P0.3 ADC 通道判定 (常态 2.0V, 按下 3.7V)
u8 raw_confirm = !KEY_CONFIRM;
u8 raw_sos = !KEY_SOS; // 恢复 SOS 物理按键电平检测
key_scan_timer = 0; // 重置扫描分频计数器
// 【SOS 按键电平实时诊断】
{
static u8 last_raw_sos = 99;
if (raw_sos != last_raw_sos) {
last_raw_sos = raw_sos;
Uart_SendString("[KEY-DIAG] KEY_SOS (P2.6) logic raw: ");
Uart_SendByte(raw_sos ? '1' : '0');
Uart_SendString(", Physical GPIO Pin: ");
Uart_SendByte(KEY_SOS ? '1' : '0');
Uart_SendString("\r\n");
}
}
// 按键调试诊断:若按键 ADC 值有变化或处于高电平,串口输出真实 ADC 值
{
u16 down_val = ADC_ReadFiltered(ADC_CHANNEL_KEY_DOWN);
if (down_val > 2800) {
Uart_SendString("[KEY-DIAG] KEY_DOWN pressed! ADC Raw: ");
Uart_SendByte((u8)('0' + (down_val / 1000) % 10));
Uart_SendByte((u8)('0' + (down_val / 100) % 10));
Uart_SendByte((u8)('0' + (down_val / 10) % 10));
Uart_SendByte((u8)('0' + (down_val % 10)));
Uart_SendString("\r\n");
}
}
// 默认将派发事件重置为空
evt.key_event = KEY_EVENT_NONE;
evt.priority = EVENT_PRIORITY_NORMAL;
evt.extra_data = 0;
evt.extra_size = 0;
// 双键组合判定:▲ 键 and ▼ 键同时按下的消抖与生成
if (raw_up && raw_down) {
comb_hold++;
if (comb_hold == 300) { // 持续按下达 3 秒 (300 * 10ms = 3s)
evt.key_event = KEY_EVENT_UP_DOWN_COMB;
EventQueue_Push(evt); // 压入队列
}
key_up_hold = 0; // 清零单个按键计数,防止双击事件干扰
key_down_hold = 0;
} else {
comb_hold = 0; // 组合键松开归零
// ▲ (KEY_UP) 按键状态机
if (raw_up) {
key_up_hold++;
if (key_up_hold == 300) { // 达到 3 秒判定为长按
evt.key_event = KEY_EVENT_UP_LONG;
EventQueue_Push(evt);
}
} else {
// 如果松开时按下计数在消抖阈值20ms至长按阈值3s之间则判定为一次短按
if (key_up_hold >= 2 && key_up_hold < 300) {
evt.key_event = KEY_EVENT_UP_CLICK;
EventQueue_Push(evt);
}
key_up_hold = 0; // 释放归零
}
// ▼ (KEY_DOWN / 开关机) 按键状态机
if (raw_down) {
key_down_hold++;
if (key_down_hold == 100) { // 长按 1 秒 (100 * 10ms = 1000ms = 1s) 触发关机事件
evt.key_event = KEY_EVENT_POWER_OFF;
EventQueue_Push(evt);
}
} else {
// 如果松开时按下时长在 20ms 至 1s 之间,触发短按下翻/选择事件
if (key_down_hold >= 2 && key_down_hold < 100) {
evt.key_event = KEY_EVENT_DOWN_CLICK;
EventQueue_Push(evt);
}
key_down_hold = 0; // 释放归零
}
// ■ (KEY_CONFIRM) 确认按键状态机
if (raw_confirm) {
key_confirm_hold++;
if (key_confirm_hold == 300) { // 长按 3 秒打开主菜单
evt.key_event = KEY_EVENT_CONFIRM_LONG;
EventQueue_Push(evt);
}
} else {
if (key_confirm_hold >= 2 && key_confirm_hold < 300) {
evt.key_event = KEY_EVENT_CONFIRM_CLICK;
EventQueue_Push(evt);
}
key_confirm_hold = 0; // 释放归零
}
// SOS 物理求救按键状态机 (已解锁)
if (raw_sos) {
key_sos_hold++;
if (key_sos_hold == 300) { // 长按 3 秒,发出求救,插入队首
evt.key_event = KEY_EVENT_SOS_LONG;
evt.priority = EVENT_PRIORITY_URGENT;
EventQueue_InsertFront(evt);
Uart_SendString("[KEY-DIAG] SOS Long Press Dispatched!\r\n");
}
} else {
if (key_sos_hold >= 2 && key_sos_hold < 300) { // 短按即呼救,插入队首
evt.key_event = KEY_EVENT_SOS_CLICK;
evt.priority = EVENT_PRIORITY_URGENT;
EventQueue_InsertFront(evt);
Uart_SendString("[KEY-DIAG] SOS Click Dispatched!\r\n");
}
key_sos_hold = 0; // 释放归零
}
}
}