Files
stc32g128k/App/event.c

381 lines
14 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"
// 定义事件环形队列的最大深度为 8
#define EVENT_QUEUE_DEPTH 8
// 环形队列存储数组 (存放在单片机 idata 快速RAM区中)
static SystemEvent idata 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_LOW;
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; // 捕获到有效事件,清零休眠计时器,重置闲置超时
// 串口打印调试信息
Uart_SendString("[EVENT] key=");
Uart_SendHex8((u8)evt.key_event);
Uart_SendString(", pri=");
Uart_SendHex8((u8)evt.priority);
Uart_SendString("\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;
}
// 传感器入侵报警过滤与跳转处理:
// 当接收到高优先级且带有 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);
}
/**
* @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; // 开启全局中断允许
}
/**
* @brief 定时器 1 中断服务程序 (每 1ms 触发一次)
* @details 承担了系统软时钟计算、报警与求救下的马达控制状态机以及 10ms 周期性按键消抖扫描的功能。
*/
void Timer1_Isr(void) interrupt 3
{
SystemEvent evt;
// 1ms 递增滴答
ms_tick++;
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 周期亮灭,进行颜色爆闪指示
alarm_flash_flag = (ms_tick / 100) % 2;
/* 物理按键扫描及消抖状态机逻辑 (每 10ms 轮询一次) */
key_scan_timer++;
if (key_scan_timer >= 10) {
// 读取按键的电平信号 (由于有上拉电阻,未按下为高 1按下为低 0所以取反做正逻辑)
u8 raw_up = !KEY_UP;
u8 raw_down = !KEY_DOWN;
u8 raw_confirm = !KEY_CONFIRM;
u8 raw_sos = !KEY_SOS;
key_scan_timer = 0; // 重置扫描分频计数器
// 检测到有任意按键处于下压状态,立即将无操作闲置休眠计时器归零
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;
// 双键组合判定:▲ 键和 ▼ 键同时按下的消抖与生成
if (raw_up && raw_down) {
comb_hold++;
if (comb_hold == 300) { // 持续按下达 3 秒 (300 * 10ms = 3s)
evt.key_event = KEY_EVENT_UP_DOWN_COMB;
evt.priority = EVENT_PRIORITY_NORMAL;
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;
evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(evt);
}
} else {
// 如果松开时按下计数在消抖阈值20ms至长按阈值3s之间则判定为一次短按
if (key_up_hold >= 2 && key_up_hold < 300) {
evt.key_event = KEY_EVENT_UP_CLICK;
evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(evt);
}
key_up_hold = 0; // 释放归零
}
// ▼ (KEY_DOWN) 按键状态机
if (raw_down) {
key_down_hold++;
if (key_down_hold == 300) { // 长按 3 秒
evt.key_event = KEY_EVENT_DOWN_LONG;
evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(evt);
}
} else {
if (key_down_hold >= 2 && key_down_hold < 300) {
evt.key_event = KEY_EVENT_DOWN_CLICK;
evt.priority = EVENT_PRIORITY_NORMAL;
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;
evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(evt);
}
} else {
if (key_confirm_hold >= 2 && key_confirm_hold < 300) {
evt.key_event = KEY_EVENT_CONFIRM_CLICK;
evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(evt);
}
key_confirm_hold = 0; // 释放归零
}
// SOS 物理求救按键状态机
if (raw_sos) {
key_sos_hold++;
if (key_sos_hold == 300) { // 长按 3 秒,发出 URGENT 优先级求救,插入队首
evt.key_event = KEY_EVENT_SOS_LONG;
evt.priority = EVENT_PRIORITY_URGENT;
EventQueue_InsertFront(evt);
}
} 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);
}
key_sos_hold = 0; // 释放归零
}
}
}
}