317 lines
11 KiB
C
317 lines
11 KiB
C
#include "event.h"
|
||
#include "app_manager.h"
|
||
#include "database.h"
|
||
#include "system.h"
|
||
#include "clock.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.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;
|
||
}
|
||
|
||
inactivity_timer = 0; // 捕获到有效事件,清零休眠计时器,重置闲置超时
|
||
|
||
// 串口打印调试信息
|
||
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 定时器 1 初始化,设定 1ms 中断周期
|
||
*/
|
||
void Timer1_Init(void)
|
||
{
|
||
AUXR |= 0x40; // 设定定时器 1 为 1T 模式 (无分频,主频直接输入)
|
||
TMOD &= 0x0F; // 设置定时器 1 模式寄存器,保留低四位 (定时器0配置)
|
||
{
|
||
// 按照系统主频 (MAIN_Fosc) 计算 1ms 对应的重装载值
|
||
u16 reload = (u16)(65536UL - (MAIN_Fosc / 1000UL));
|
||
TL1 = (u8)reload; // 载入低 8 位
|
||
TH1 = (u8)(reload >> 8);// 载入高 8 位
|
||
}
|
||
ET1 = 1; // 开启定时器 1 中断允许
|
||
TR1 = 1; // 启动定时器 1 计数
|
||
EA = 1; // 开启全局中断允许
|
||
}
|
||
|
||
// 全局按键扫描触发标志
|
||
volatile bit key_scan_flag = 0;
|
||
|
||
/**
|
||
* @brief 定时器 1 中断服务程序 (每 1ms 触发一次)
|
||
* @details 承担系统软时钟计算,并每 10ms 置位主循环按键扫描标志,不直接在中断中进行 ADC 多次采样。
|
||
*/
|
||
void Timer1_Isr(void) interrupt 3
|
||
{
|
||
// 1ms 定时更新软时钟
|
||
Clock_IncMS();
|
||
|
||
// 报警屏幕红边框以 100ms 周期亮灭,进行颜色爆闪指示
|
||
alarm_flash_flag = (ms_tick / 100) % 2;
|
||
|
||
/* 物理按键定时计数 (每 10ms 置位一次标志位) */
|
||
key_scan_timer++;
|
||
if (key_scan_timer >= 10) {
|
||
key_scan_timer = 0; // 重置扫描分频计数器
|
||
key_scan_flag = 1; // 仅在中断里置起标志位,留待主循环中执行实际读取与消抖
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @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");
|
||
}
|
||
}
|
||
|
||
// 检测到有任意按键处于下压状态,立即将无操作闲置休眠计时器归零
|
||
if (raw_up || raw_down || raw_confirm || raw_sos) {
|
||
inactivity_timer = 0;
|
||
} else {
|
||
// 如果在常态下(非休眠、非报警、非紧急求救),累加闲置时间
|
||
if (current_state != STATE_SLEEP && current_state != STATE_ALARM && current_state != STATE_SOS_EMITTED) {
|
||
inactivity_timer++;
|
||
}
|
||
}
|
||
|
||
// 默认将派发事件重置为空
|
||
evt.key_event = KEY_EVENT_NONE;
|
||
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;
|
||
EventQueue_InsertFront(evt);
|
||
}
|
||
} else {
|
||
if (key_sos_hold >= 2 && key_sos_hold < 300) { // 短按即呼救,插入队首
|
||
evt.key_event = KEY_EVENT_SOS_CLICK;
|
||
EventQueue_InsertFront(evt);
|
||
}
|
||
key_sos_hold = 0; // 释放归零
|
||
}
|
||
*/
|
||
}
|
||
}
|