feat: 实现事件驱动应用框架,支持前台/后台分离、事件优先级和CPU占用控制

This commit is contained in:
2026-07-10 11:30:48 +08:00
parent 00c00fb60e
commit b4fabf85b5
11 changed files with 2487 additions and 541 deletions

262
App/appmgr.c Normal file
View File

@@ -0,0 +1,262 @@
#include "appmgr.h"
#include "../Drivers/lcd.h"
/* =========================================================================
* 应用管理器内部变量
* ========================================================================= */
// 应用注册表:存储所有已注册的应用
static WristbandApp app_registry[APP_ID_MAX];
// 当前活跃的前台应用指针
static WristbandApp *active_app = NULL;
// 默认应用(时钟)
#define DEFAULT_APP_ID APP_ID_CLOCK
/* =========================================================================
* 应用生命周期辅助函数
* ========================================================================= */
static void AppManager_ExecuteOnStart(WristbandApp *app)
{
if (app != NULL && app->OnStart != NULL)
{
app->OnStart();
app->is_active = 1;
app->is_running = 1;
}
}
static void AppManager_ExecuteOnClose(WristbandApp *app)
{
if (app != NULL && app->onClose != NULL)
{
app->onClose();
app->is_active = 0;
app->is_running = 0;
}
}
static void AppManager_ExecuteOnRun(WristbandApp *app)
{
if (app != NULL && app->onRun != NULL && app->is_running)
{
// CPU占用控制记录开始时间
u16 start_tick = ms_tick;
// 执行应用的onRun回调
app->onRun();
// 检测执行时间是否超限
u16 elapsed_ticks = ms_tick - start_tick;
if (elapsed_ticks > APP_ONRUN_MAX_TICKS)
{
// 应用执行时间过长,强制标记为非活跃
// 但不直接关闭,允许应用在下一次循环中恢复
app->is_running = 0;
}
}
}
static EventResult AppManager_ExecuteOnEvent(WristbandApp *app, SystemEvent evt)
{
if (app != NULL && app->onEvent != NULL)
{
return app->onEvent(evt);
}
return EVENT_IGNORED;
}
/* =========================================================================
* 应用管理器核心实现
* ========================================================================= */
void AppManager_Init(void)
{
u8 i;
// 初始化所有应用描述符
for (i = 0; i < APP_ID_MAX; i++)
{
app_registry[i].app_id = (AppID)i;
app_registry[i].app_type = APP_TYPE_FOREGROUND;
app_registry[i].app_name = "UNREGISTERED";
app_registry[i].OnStart = NULL;
app_registry[i].onRun = NULL;
app_registry[i].onClose = NULL;
app_registry[i].onEvent = NULL;
app_registry[i].is_active = 0;
app_registry[i].is_running = 0;
}
// 默认启动时钟应用
active_app = &app_registry[DEFAULT_APP_ID];
AppManager_ExecuteOnStart(active_app);
}
void AppManager_StartApp(AppID app_id)
{
// 参数有效性检查
if (app_id >= APP_ID_MAX)
{
return;
}
// 获取目标应用指针
WristbandApp *target_app = &app_registry[app_id];
// 目标应用必须是前台应用
if (target_app->app_type != APP_TYPE_FOREGROUND)
{
return;
}
// 目标应用必须已注册有OnStart回调
if (target_app->OnStart == NULL)
{
return;
}
// 如果目标应用已经是活跃应用,直接返回
if (active_app == target_app)
{
return;
}
// 关闭当前活跃应用
if (active_app != NULL)
{
AppManager_ExecuteOnClose(active_app);
}
// 切换到新应用
active_app = target_app;
// 启动新应用
AppManager_ExecuteOnStart(active_app);
}
void AppManager_CloseApp(void)
{
// 关闭当前活跃应用
if (active_app != NULL)
{
AppManager_ExecuteOnClose(active_app);
active_app = NULL;
}
// 启动默认应用(时钟)
active_app = &app_registry[DEFAULT_APP_ID];
AppManager_ExecuteOnStart(active_app);
}
AppID AppManager_GetActiveAppID(void)
{
if (active_app != NULL)
{
return active_app->app_id;
}
return DEFAULT_APP_ID;
}
void AppManager_DispatchEvent(SystemEvent evt)
{
EventResult result;
// 步骤1优先分发给当前活跃的前台应用
if (active_app != NULL && active_app->is_active)
{
result = AppManager_ExecuteOnEvent(active_app, evt);
// 前台应用已处理,无需继续分发
if (result == EVENT_HANDLED)
{
return;
}
}
// 步骤2前台未处理分发给所有后台应用
// 遍历应用注册表,找到所有后台应用
u8 i;
for (i = 0; i < APP_ID_MAX; i++)
{
WristbandApp *bg_app = &app_registry[i];
// 跳过前台应用和未注册应用
if (bg_app->app_type != APP_TYPE_BACKGROUND ||
bg_app->onEvent == NULL ||
!bg_app->is_running)
{
continue;
}
// 调用后台应用的事件处理函数
result = AppManager_ExecuteOnEvent(bg_app, evt);
// 如果后台应用处理了事件,停止继续分发
if (result == EVENT_HANDLED)
{
return;
}
}
// 步骤3所有应用都未处理事件被丢弃
// 可以在这里添加日志记录(如果需要)
}
void AppManager_RunActiveApp(void)
{
// 执行当前活跃应用的onRun回调带CPU占用控制
if (active_app != NULL && active_app->is_active)
{
AppManager_ExecuteOnRun(active_app);
}
}
void AppManager_BroadcastToBackground(SystemEvent evt)
{
// 广播事件给所有后台应用
u8 i;
for (i = 0; i < APP_ID_MAX; i++)
{
WristbandApp *bg_app = &app_registry[i];
// 仅发送给后台应用
if (bg_app->app_type == APP_TYPE_BACKGROUND &&
bg_app->onEvent != NULL &&
bg_app->is_running)
{
AppManager_ExecuteOnEvent(bg_app, evt);
}
}
}
/* =========================================================================
* 应用注册接口(供各应用模块调用)
* ========================================================================= */
void AppManager_RegisterApp(AppID app_id, AppType app_type, char *app_name,
AppStartFunc on_start, AppRunFunc on_run,
AppCloseFunc on_close, AppEventFunc on_event)
{
if (app_id >= APP_ID_MAX)
{
return;
}
WristbandApp *app = &app_registry[app_id];
app->app_type = app_type;
app->app_name = app_name;
app->OnStart = on_start;
app->onRun = on_run;
app->onClose = on_close;
app->onEvent = on_event;
// 后台应用默认处于运行状态
if (app_type == APP_TYPE_BACKGROUND)
{
app->is_running = 1;
}
}

33
App/appmgr.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef __APPMGR_H__
#define __APPMGR_H__
#include "config.h"
/* =========================================================================
* 应用管理器接口声明
* ========================================================================= */
// 初始化应用管理器,注册所有应用
void AppManager_Init(void);
// 启动指定应用(切换前台)
// 参数app_id - 目标应用ID
void AppManager_StartApp(AppID app_id);
// 关闭当前前台应用,回退到默认应用(时钟)
void AppManager_CloseApp(void);
// 获取当前活跃的前台应用ID
AppID AppManager_GetActiveAppID(void);
// 事件分发函数:将事件分发给前台/后台应用
// 分发逻辑:前台应用优先处理 -> 后台应用兜底处理 -> 未处理则丢弃
void AppManager_DispatchEvent(SystemEvent evt);
// 运行当前活跃应用的onRun回调带CPU占用控制
void AppManager_RunActiveApp(void);
// 广播事件给所有后台应用(用于系统级事件)
void AppManager_BroadcastToBackground(SystemEvent evt);
#endif

497
App/apps.c Normal file
View File

@@ -0,0 +1,497 @@
#include "config.h"
#include "appmgr.h"
#include "../Drivers/lcd.h"
#include "../Drivers/rf.h"
/* =========================================================================
* 全局变量声明(从 main.c 迁移)
* ========================================================================= */
extern volatile u16 ms_tick;
extern volatile u32 pair_timeout_ms;
extern volatile u16 alarm_timer_ms;
extern volatile bit alarm_flash_flag;
extern SystemState current_state;
extern u8 current_hour, current_min;
extern u8 menu_select;
extern u32 captured_addr;
extern u8 captured_type;
extern u8 alarm_sensor_slot;
extern Sensor_Slot xdata sensor_list[16];
extern sbit MOTOR;
extern bit LED_R, LED_G, LED_B;
extern bit rgb_test_mode;
extern char *code default_names[5];
extern u8 code default_zones[5];
extern char *code menu_items[5];
extern void Delay_ms(u16 ms);
extern void Format_Sensor_Name(u8 slot, char *buf);
extern void Uart_SendString(char *s);
extern void Uart_SendHex8(u8 val);
/* =========================================================================
* 应用私有状态变量
* ========================================================================= */
// 时钟应用状态
static bit clock_redraw_flag = 0;
// 配对应用状态
static u8 pair_menu_select = 0;
static u8 pair_state = 0; // 0: MENU, 1: WAIT, 2: CONFIRM, 3: SUCCESS, 4: FAIL
// 报警应用状态
static u8 alarm_last_frame = 99;
// SOS应用状态
static u8 sos_last_tx = 99;
/* =========================================================================
* 时钟应用实现
* ========================================================================= */
static void ClockApp_OnStart(void)
{
Uart_SendString("[APP] ClockApp Started\r\n");
current_state = STATE_NORMAL;
clock_redraw_flag = 1;
}
static void ClockApp_onRun(void)
{
// 分钟变化时重绘时钟
if (clock_redraw_flag)
{
clock_redraw_flag = 0;
UI_ShowClockPage(current_state, current_hour, current_min);
}
// 监听射频信号(后台监测)
{
u32 rx_addr;
u8 rx_data;
if (EV1527_Decode(&rx_addr, &rx_data))
{
u8 slot;
if (Check_Sensor_ID(rx_addr, &slot))
{
u32 slot_addr = ((u32)sensor_list[slot].addr[0] << 16) |
((u32)sensor_list[slot].addr[1] << 8) |
sensor_list[slot].addr[2];
u8 sensor_type = slot_addr & 0x0F;
if (sensor_type < 5)
{
u8 zone = default_zones[sensor_type];
if (zone == 0 || current_state == STATE_ARMED)
{
captured_addr = rx_addr;
alarm_sensor_slot = slot;
AppManager_StartApp(APP_ID_ALARM);
}
}
}
}
}
}
static void ClockApp_onClose(void)
{
Uart_SendString("[APP] ClockApp Closed\r\n");
}
static EventResult ClockApp_onEvent(SystemEvent evt)
{
// 处理定时器发送的时钟更新事件priority为LOWkey_event为NONE
if (evt.key_event == KEY_EVENT_NONE && evt.priority == EVENT_PRIORITY_LOW)
{
// 从extra_data中提取时间信息
u8 hour = (u8)(evt.extra_data >> 8);
u8 min = (u8)(evt.extra_data & 0xFF);
// 更新时钟显示
UI_ShowClockPage(current_state, hour, min);
return EVENT_HANDLED;
}
switch (evt.key_event)
{
case KEY_EVENT_UP_DOWN_COMB:
// 组合键进入菜单
AppManager_StartApp(APP_ID_MENU);
return EVENT_HANDLED;
case KEY_EVENT_SOS_CLICK:
// 短按SOS进入求救模式
AppManager_StartApp(APP_ID_SOS);
return EVENT_HANDLED;
case KEY_EVENT_SOS_LONG:
// 长按SOS绑定IPC
UI_ShowBindingPage();
RF_SetMode(2);
EV1527_Transmit(CLONED_ADDR, 0x01);
RF_SetMode(0);
Delay_ms(2000);
AppManager_StartApp(APP_ID_CLOCK);
return EVENT_HANDLED;
default:
return EVENT_IGNORED;
}
}
/* =========================================================================
* 菜单应用实现
* ========================================================================= */
static void MenuApp_OnStart(void)
{
Uart_SendString("[APP] MenuApp Started\r\n");
pair_menu_select = 0;
RF_SetMode(0);
UI_ShowPairMenuPage(pair_menu_select);
}
static void MenuApp_onRun(void)
{
// 菜单应用无持续运行逻辑
}
static void MenuApp_onClose(void)
{
Uart_SendString("[APP] MenuApp Closed\r\n");
RF_SetMode(1);
}
static EventResult MenuApp_onEvent(SystemEvent evt)
{
switch (evt.key_event)
{
case KEY_EVENT_UP_CLICK:
if (pair_menu_select > 0)
{
pair_menu_select--;
UI_ShowPairMenuPage(pair_menu_select);
}
return EVENT_HANDLED;
case KEY_EVENT_DOWN_CLICK:
if (pair_menu_select < 4)
{
pair_menu_select++;
UI_ShowPairMenuPage(pair_menu_select);
}
return EVENT_HANDLED;
case KEY_EVENT_SOS_CLICK:
// 确认进入配对等待
MOTOR = 1;
Delay_ms(100);
MOTOR = 0;
AppManager_StartApp(APP_ID_PAIR);
return EVENT_HANDLED;
case KEY_EVENT_DOWN_LONG:
// 长按返回时钟
AppManager_CloseApp();
return EVENT_HANDLED;
default:
return EVENT_IGNORED;
}
}
/* =========================================================================
* 配对应用实现
* ========================================================================= */
static void PairApp_OnStart(void)
{
Uart_SendString("[APP] PairApp Started\r\n");
pair_state = 1; // 进入等待状态
pair_timeout_ms = 0;
captured_type = pair_menu_select;
RF_SetMode(1);
LCD_Clear(COLOR_BLACK);
LCD_ShowStringCentered(135, "RECHERCHE...", COLOR_WHITE, COLOR_BLACK);
LCD_ShowStringCentered(160, "DECLENCH. CAPT", COLOR_CYAN, COLOR_BLACK);
UI_ShowPairWaitPage(0);
}
static void PairApp_onRun(void)
{
switch (pair_state)
{
case 1: // WAIT状态
{
// 雷达动画
u8 frame = (u8)((ms_tick / 250) % 3);
static u8 last_frame = 99;
if (frame != last_frame)
{
last_frame = frame;
UI_ShowPairWaitPage(frame);
}
// 监听射频信号
u32 rx_addr;
u8 rx_data;
if (EV1527_Decode(&rx_addr, &rx_data))
{
captured_addr = rx_addr;
pair_state = 2; // 进入确认状态
RF_SetMode(0);
UI_ShowPairConfirmPage(captured_addr);
}
// 超时检测
if (pair_timeout_ms >= 30000UL)
{
pair_timeout_ms = 0;
UI_ShowPairFailPage(1);
Delay_ms(2500);
AppManager_CloseApp();
}
break;
}
case 2: // CONFIRM状态
// 等待用户操作
break;
case 3: // SUCCESS状态
// 成功动画已在事件处理中完成
break;
default:
break;
}
}
static void PairApp_onClose(void)
{
Uart_SendString("[APP] PairApp Closed\r\n");
RF_SetMode(1);
pair_state = 0;
}
static EventResult PairApp_onEvent(SystemEvent evt)
{
switch (pair_state)
{
case 1: // WAIT状态
{
if (evt.key_event == KEY_EVENT_DOWN_LONG)
{
AppManager_CloseApp();
return EVENT_HANDLED;
}
break;
}
case 2: // CONFIRM状态
{
if (evt.key_event == KEY_EVENT_SOS_LONG)
{
// 保存对码
Add_Sensor(captured_addr, captured_type);
pair_state = 3; // 进入成功状态
UI_ShowPairSuccessPage(captured_addr);
// 物理反馈
MOTOR = 1;
LED_G = 0;
LED_R = 1;
LED_B = 1;
Delay_ms(400);
MOTOR = 0;
Delay_ms(1600);
LED_G = 1;
Delay_ms(500);
AppManager_CloseApp();
return EVENT_HANDLED;
}
else if (evt.key_event == KEY_EVENT_DOWN_LONG)
{
AppManager_CloseApp();
return EVENT_HANDLED;
}
break;
}
default:
break;
}
return EVENT_IGNORED;
}
/* =========================================================================
* 报警应用实现
* ========================================================================= */
static void AlarmApp_OnStart(void)
{
Uart_SendString("[APP] AlarmApp Started\r\n");
current_state = STATE_ALARM;
u8 slot = alarm_sensor_slot;
static char alarm_name[24];
char *name = "UNKNOWN SENSOR";
if (Check_Sensor_ID(captured_addr, &slot))
{
Format_Sensor_Name(slot, alarm_name);
name = alarm_name;
}
UI_ShowAlarmPage(name, 1);
// 重置报警定时器
alarm_timer_ms = 0;
}
static void AlarmApp_onRun(void)
{
// 警报边框闪烁效果
if (alarm_flash_flag)
{
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
}
else
{
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_BLACK);
}
}
static void AlarmApp_onClose(void)
{
Uart_SendString("[APP] AlarmApp Closed\r\n");
current_state = STATE_NORMAL;
// 停止报警效果
MOTOR = 0;
LED_R = 1;
LED_G = 1;
LED_B = 1;
}
static EventResult AlarmApp_onEvent(SystemEvent evt)
{
// 任何按键都可以清除报警
if (evt.key_event != KEY_EVENT_NONE)
{
AppManager_CloseApp();
return EVENT_HANDLED;
}
return EVENT_IGNORED;
}
/* =========================================================================
* SOS应用实现
* ========================================================================= */
static void SOSApp_OnStart(void)
{
Uart_SendString("[APP] SOSApp Started\r\n");
current_state = STATE_SOS_EMITTED;
UI_ShowSosPage();
}
static void SOSApp_onRun(void)
{
// 每500ms发射一次射频求救帧
u8 sec_phase = (u8)((ms_tick / 500) % 2);
if (sec_phase != sos_last_tx)
{
sos_last_tx = sec_phase;
RF_SetMode(2);
EV1527_Transmit(CLONED_ADDR, 0x08);
RF_SetMode(0);
}
// 红色警告边框闪烁
if (alarm_flash_flag)
{
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
LCD_DrawRectBorder(8, 8, 104, 224, COLOR_WHITE);
}
else
{
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_BLACK);
LCD_DrawRectBorder(8, 8, 104, 224, COLOR_BLACK);
}
}
static void SOSApp_onClose(void)
{
Uart_SendString("[APP] SOSApp Closed\r\n");
current_state = STATE_NORMAL;
// 停止求救效果
MOTOR = 0;
LED_R = 1;
LED_G = 1;
LED_B = 1;
}
static EventResult SOSApp_onEvent(SystemEvent evt)
{
// 长按任意键解除求救
if (evt.key_event == KEY_EVENT_SOS_LONG ||
evt.key_event == KEY_EVENT_UP_LONG ||
evt.key_event == KEY_EVENT_DOWN_LONG)
{
AppManager_CloseApp();
return EVENT_HANDLED;
}
return EVENT_IGNORED;
}
/* =========================================================================
* 应用注册函数
* ========================================================================= */
void Apps_RegisterAll(void)
{
// 注册时钟应用(前台)
AppManager_RegisterApp(APP_ID_CLOCK, APP_TYPE_FOREGROUND, "Clock",
ClockApp_OnStart, ClockApp_onRun,
ClockApp_onClose, ClockApp_onEvent);
// 注册菜单应用(前台)
AppManager_RegisterApp(APP_ID_MENU, APP_TYPE_FOREGROUND, "Menu",
MenuApp_OnStart, MenuApp_onRun,
MenuApp_onClose, MenuApp_onEvent);
// 注册配对应用(前台)
AppManager_RegisterApp(APP_ID_PAIR, APP_TYPE_FOREGROUND, "Pair",
PairApp_OnStart, PairApp_onRun,
PairApp_onClose, PairApp_onEvent);
// 注册报警应用(前台)
AppManager_RegisterApp(APP_ID_ALARM, APP_TYPE_FOREGROUND, "Alarm",
AlarmApp_OnStart, AlarmApp_onRun,
AlarmApp_onClose, AlarmApp_onEvent);
// 注册SOS应用前台
AppManager_RegisterApp(APP_ID_SOS, APP_TYPE_FOREGROUND, "SOS",
SOSApp_OnStart, SOSApp_onRun,
SOSApp_onClose, SOSApp_onEvent);
Uart_SendString("[APP] All applications registered\r\n");
}

View File

@@ -37,6 +37,76 @@ typedef struct {
unsigned char addr[3]; // 24位对码地址 (EV1527 20位物理地址 + 4位状态数据码)
} Sensor_Slot;
/* =========================================================================
* 应用框架核心定义
* ========================================================================= */
// 应用ID定义
typedef enum {
APP_ID_CLOCK, // 0: 待机时钟应用
APP_ID_MENU, // 1: 主菜单应用
APP_ID_PAIR, // 2: 配对与微调应用
APP_ID_ALARM, // 3: 传感器报警应用
APP_ID_SOS, // 4: 主动求救应用
APP_ID_MAX // 应用数量上限
} AppID;
// 应用类型枚举
typedef enum {
APP_TYPE_FOREGROUND, // 前台应用:独占屏幕和用户交互
APP_TYPE_BACKGROUND // 后台应用:无屏幕显示,仅监听事件
} AppType;
// 事件处理返回值
typedef enum {
EVENT_HANDLED, // 事件已处理
EVENT_IGNORED // 事件未处理,继续传递给下一个应用
} EventResult;
// 事件优先级枚举
typedef enum {
EVENT_PRIORITY_LOW, // 低优先级:普通按键等
EVENT_PRIORITY_NORMAL, // 正常优先级:大部分事件
EVENT_PRIORITY_HIGH, // 高优先级:报警相关
EVENT_PRIORITY_URGENT // 紧急优先级SOS等直接越过队列
} EventPriority;
// 扩展事件结构体
typedef struct {
KeyEvent key_event; // 原始按键事件
EventPriority priority; // 事件优先级
u32 extra_data; // 额外数据(如射频地址等)
u8 extra_size; // 额外数据大小
} SystemEvent;
// 应用生命周期回调函数类型
typedef void (*AppStartFunc)(void);
typedef void (*AppRunFunc)(void);
typedef void (*AppCloseFunc)(void);
typedef EventResult (*AppEventFunc)(SystemEvent evt);
// 应用描述符结构体
typedef struct {
AppID app_id; // 应用唯一标识
AppType app_type; // 应用类型(前台/后台)
char *app_name; // 应用名称(用于调试)
AppStartFunc OnStart; // 启动回调:应用切入前台,加载资源、初始化显示
AppRunFunc onRun; // 循环回调:主循环轮询调用,执行非阻塞业务逻辑
AppCloseFunc onClose; // 关闭回调:应用退出前台,保存状态、清理外设
AppEventFunc onEvent; // 事件回调:接收并处理来自事件层的系统事件
bit is_active; // 当前是否活跃(仅对前台应用有效)
bit is_running; // 应用是否处于运行状态
} WristbandApp;
/* =========================================================================
* 事件队列配置
* ========================================================================= */
#define EVENT_QUEUE_DEPTH 8 // 事件队列深度(环形缓冲区大小)
#define APP_ONRUN_MAX_TICKS 5 // onRun最大执行时间限制单位1ms tick
#define MAIN_Fosc 24000000UL
#define CLONED_ADDR 0x37A86UL

121
App/event.c Normal file
View File

@@ -0,0 +1,121 @@
#include "event.h"
/* =========================================================================
* 事件队列内部变量
* ========================================================================= */
// 环形缓冲区存储数组
static SystemEvent event_queue[EVENT_QUEUE_DEPTH];
// 队列读写指针
static u8 queue_head = 0; // 读指针:指向即将读取的位置
static u8 queue_tail = 0; // 写指针:指向即将写入的位置
static u8 queue_count = 0; // 当前队列中的事件数量
// 队列锁标志(用于中断保护)
static bit queue_locked = 0;
/* =========================================================================
* 事件队列核心实现
* ========================================================================= */
void EventQueue_Init(void)
{
queue_head = 0;
queue_tail = 0;
queue_count = 0;
queue_locked = 0;
}
bit EventQueue_IsEmpty(void)
{
return (queue_count == 0);
}
bit EventQueue_IsFull(void)
{
return (queue_count >= EVENT_QUEUE_DEPTH);
}
u8 EventQueue_GetCount(void)
{
return queue_count;
}
bit EventQueue_Push(SystemEvent evt)
{
// 队列已满,丢弃事件
if (EventQueue_IsFull())
{
return 0;
}
// 禁用中断保护队列操作
EA = 0;
// 将事件写入环形缓冲区
event_queue[queue_tail] = evt;
// 更新写指针(循环递增)
queue_tail++;
if (queue_tail >= EVENT_QUEUE_DEPTH)
{
queue_tail = 0;
}
// 更新事件计数
queue_count++;
// 恢复中断
EA = 1;
return 1;
}
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 (EventQueue_IsEmpty())
{
return evt;
}
// 禁用中断保护队列操作
EA = 0;
// 从队列头部读取事件
evt = event_queue[queue_head];
// 更新读指针(循环递增)
queue_head++;
if (queue_head >= EVENT_QUEUE_DEPTH)
{
queue_head = 0;
}
// 更新事件计数
queue_count--;
// 恢复中断
EA = 1;
return evt;
}
// 外部声明:应用管理器的事件分发函数
extern void AppManager_DispatchEvent(SystemEvent evt);
void EventQueue_EmergencyDispatch(SystemEvent evt)
{
// 紧急事件直接调用应用管理器分发,跳过队列
// 这种方式用于SOS等最高优先级事件确保立即响应
AppManager_DispatchEvent(evt);
}

33
App/event.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef __EVENT_H__
#define __EVENT_H__
#include "config.h"
/* =========================================================================
* 事件队列模块接口声明
* ========================================================================= */
// 初始化事件队列
void EventQueue_Init(void);
// 将事件推入队列(非阻塞)
// 返回值1-成功0-队列已满
bit EventQueue_Push(SystemEvent evt);
// 从队列头部弹出一个事件
// 返回值:事件结构体,若队列为空则 key_event 为 KEY_EVENT_NONE
SystemEvent EventQueue_Pop(void);
// 判断队列是否为空
bit EventQueue_IsEmpty(void);
// 判断队列是否已满
bit EventQueue_IsFull(void);
// 获取队列中事件数量
u8 EventQueue_GetCount(void);
// 紧急事件直通函数:跳过队列直接发送给应用层
void EventQueue_EmergencyDispatch(SystemEvent evt);
#endif

View File

@@ -1,6 +1,8 @@
#include "stc32g.h"
#include "intrins.h"
#include "config.h"
#include "event.h"
#include "appmgr.h"
#include "../Drivers/lcd.h"
#include "../Drivers/rf.h"
@@ -739,7 +741,15 @@ void Timer1_Isr(void) interrupt 3
current_hour = 0;
}
}
clock_updated = 1; // 标记时钟更新,在主循环中更新屏幕
// 通过事件队列通知时钟应用分钟变化
{
SystemEvent evt;
evt.key_event = KEY_EVENT_NONE;
evt.priority = EVENT_PRIORITY_LOW;
evt.extra_data = ((u32)current_hour << 8) | current_min;
evt.extra_size = 2;
EventQueue_Push(evt);
}
}
}
@@ -966,19 +976,38 @@ void Timer1_Isr(void) interrupt 3
}
/* =========================================================================
* 系统主循环逻辑 (交互状态机与射频匹配核心)
* 外部声明:应用注册函数
* ========================================================================= */
extern void Apps_RegisterAll(void);
/* =========================================================================
* 时钟更新回调:在定时器中断中触发,通知时钟应用重绘
* ========================================================================= */
void ClockApp_NotifyUpdate(void)
{
// 通过事件队列通知时钟应用分钟变化
SystemEvent evt;
evt.key_event = KEY_EVENT_NONE;
evt.priority = EVENT_PRIORITY_LOW;
evt.extra_data = ((u32)current_hour << 8) | current_min;
evt.extra_size = 2;
EventQueue_Push(evt);
}
/* =========================================================================
* 系统主循环逻辑 (事件驱动框架核心)
* ========================================================================= */
void main(void)
{
KeyEvent ev;
u8 state_changed = 0; // 用 u8 代替 bit 变量
u8 last_radar_sec = 99;
SystemEvent evt;
char cmd;
u8 last_led_r = 1;
u8 last_led_g = 1;
u8 last_led_b = 1;
u8 last_motor = 0;
char cmd;
// 单片机时钟与看门狗基础初始化
WTST = 0;
@@ -1007,10 +1036,12 @@ void main(void)
Load_Database();
Timer1_Init();
// 默认进入 NORMAL 待机模式,显示时钟,并将射频调制芯片置为被动接收模式
current_state = STATE_NORMAL;
UI_ShowClockPage(current_state, current_hour, current_min);
RF_SetMode(1); // 开接收天线
// 初始化事件队列和应用管理器
EventQueue_Init();
Apps_RegisterAll();
AppManager_Init();
Uart_SendString("[FRAMEWORK] Event-driven framework started\r\n");
while(1)
{
@@ -1018,76 +1049,96 @@ void main(void)
cmd = Uart_RxChar();
if (cmd != '\0')
{
SystemEvent sys_evt;
sys_evt.priority = EVENT_PRIORITY_NORMAL;
sys_evt.extra_data = 0;
sys_evt.extra_size = 0;
if (cmd == 'u')
{
key_event_buf = KEY_EVENT_UP_CLICK;
sys_evt.key_event = KEY_EVENT_UP_CLICK;
EventQueue_Push(sys_evt);
Uart_SendString("[UART SIM] KEY_UP Clicked\r\n");
}
else if (cmd == 'U')
{
key_event_buf = KEY_EVENT_UP_LONG;
sys_evt.key_event = KEY_EVENT_UP_LONG;
EventQueue_Push(sys_evt);
Uart_SendString("[UART SIM] KEY_UP Long Pressed\r\n");
}
else if (cmd == 'd')
{
key_event_buf = KEY_EVENT_DOWN_CLICK;
sys_evt.key_event = KEY_EVENT_DOWN_CLICK;
EventQueue_Push(sys_evt);
Uart_SendString("[UART SIM] KEY_DOWN Clicked\r\n");
}
else if (cmd == 'D')
{
key_event_buf = KEY_EVENT_DOWN_LONG;
sys_evt.key_event = KEY_EVENT_DOWN_LONG;
EventQueue_Push(sys_evt);
Uart_SendString("[UART SIM] KEY_DOWN Long Pressed\r\n");
}
else if (cmd == 's')
{
key_event_buf = KEY_EVENT_SOS_CLICK;
Uart_SendString("[UART SIM] KEY_SOS Clicked\r\n");
sys_evt.key_event = KEY_EVENT_SOS_CLICK;
sys_evt.priority = EVENT_PRIORITY_URGENT;
EventQueue_EmergencyDispatch(sys_evt);
Uart_SendString("[UART SIM] KEY_SOS Clicked (Emergency)\r\n");
}
else if (cmd == 'S')
{
key_event_buf = KEY_EVENT_SOS_LONG;
Uart_SendString("[UART SIM] KEY_SOS Long Pressed\r\n");
sys_evt.key_event = KEY_EVENT_SOS_LONG;
sys_evt.priority = EVENT_PRIORITY_URGENT;
EventQueue_EmergencyDispatch(sys_evt);
Uart_SendString("[UART SIM] KEY_SOS Long Pressed (Emergency)\r\n");
}
else if (cmd == 'c' || cmd == 'C')
{
key_event_buf = KEY_EVENT_UP_DOWN_COMB;
sys_evt.key_event = KEY_EVENT_UP_DOWN_COMB;
EventQueue_Push(sys_evt);
Uart_SendString("[UART SIM] KEY_UP + KEY_DOWN Combined\r\n");
}
else if (cmd == 'a' || cmd == 'A')
{
// 模拟报警事件
u32 mock_addr = 0x123456;
u8 mock_slot;
if (!Check_Sensor_ID(mock_addr, &mock_slot))
{
Add_Sensor(mock_addr, 0); // 0: 门磁
Add_Sensor(mock_addr, 0);
}
captured_addr = mock_addr;
current_state = STATE_ALARM;
state_changed = 1;
alarm_sensor_slot = mock_slot;
sys_evt.key_event = KEY_EVENT_NONE;
sys_evt.priority = EVENT_PRIORITY_HIGH;
sys_evt.extra_data = mock_addr;
sys_evt.extra_size = 4;
EventQueue_Push(sys_evt);
AppManager_StartApp(APP_ID_ALARM);
Uart_SendString("[UART SIM] Mock RF Alarm: ADDR 0x123456\r\n");
}
else if (cmd == 'p' || cmd == 'P')
{
if (current_state == STATE_PAIR_WAIT)
{
captured_addr = 0x789ABC;
captured_type = menu_select;
current_state = STATE_PAIR_CONFIRM;
state_changed = 1;
Uart_SendString("[UART SIM] Mock RF Pair Signal: ADDR 0x789ABC\r\n");
}
else
{
Uart_SendString("[UART SIM] Ignore: Not in PAIR WAIT state\r\n");
}
// 模拟配对信号
captured_addr = 0x789ABC;
captured_type = 0;
sys_evt.key_event = KEY_EVENT_NONE;
sys_evt.priority = EVENT_PRIORITY_NORMAL;
sys_evt.extra_data = 0x789ABC;
sys_evt.extra_size = 4;
EventQueue_Push(sys_evt);
Uart_SendString("[UART SIM] Mock RF Pair Signal: ADDR 0x789ABC\r\n");
}
else if (cmd == 't' || cmd == 'T')
{
Uart_SendString("[UART SIM] SPI 3MHz Clock Test on P2.5 (SCLK) and P2.3 (MOSI) active. Press reset to exit.\r\n");
// Configure SCLK (P2.5) and MOSI (P2.3) as push-pull output
P2M1 &= ~((1 << 3) | (1 << 5));
P2M0 |= ((1 << 3) | (1 << 5));
SPCTL = 0xD0; // SPI Master, 6.0 MHz (SYSCLK / 4)
SPCTL = 0xD0;
while(1)
{
SPSTAT = 0xC0;
@@ -1097,366 +1148,116 @@ void main(void)
}
else if (cmd == '1')
{
current_state = STATE_NORMAL;
state_changed = 1;
Uart_SendString("[UART SIM] Set State to: NORMAL (Standby)\r\n");
AppManager_StartApp(APP_ID_CLOCK);
Uart_SendString("[UART SIM] Set Active App to: Clock\r\n");
}
else if (cmd == '2')
{
current_state = STATE_ARMED;
state_changed = 1;
Uart_SendString("[UART SIM] Set State to: ARMED (Armed)\r\n");
Uart_SendString("[UART SIM] Set State to: ARMED\r\n");
}
else if (cmd == '3')
{
current_state = STATE_DISARMED;
state_changed = 1;
Uart_SendString("[UART SIM] Set State to: DISARMED (Disarmed)\r\n");
Uart_SendString("[UART SIM] Set State to: DISARMED\r\n");
}
else if (cmd == '4')
{
rgb_test_mode = 1;
LED_R = 0; LED_G = 1; LED_B = 1; // 红色亮 (0代表亮)
LED_R = 0; LED_G = 1; LED_B = 1;
Uart_SendString("[UART SIM] RGB Test -> RED\r\n");
}
else if (cmd == '5')
{
rgb_test_mode = 1;
LED_R = 1; LED_G = 0; LED_B = 1; // 绿色亮
LED_R = 1; LED_G = 0; LED_B = 1;
Uart_SendString("[UART SIM] RGB Test -> GREEN\r\n");
}
else if (cmd == '6')
{
rgb_test_mode = 1;
LED_R = 1; LED_G = 1; LED_B = 0; // 蓝色亮
LED_R = 1; LED_G = 1; LED_B = 0;
Uart_SendString("[UART SIM] RGB Test -> BLUE\r\n");
}
else if (cmd == '7')
{
rgb_test_mode = 1;
LED_R = 0; LED_G = 0; LED_B = 1; // 黄色亮 (红+绿)
LED_R = 0; LED_G = 0; LED_B = 1;
Uart_SendString("[UART SIM] RGB Test -> YELLOW\r\n");
}
else if (cmd == '8')
{
rgb_test_mode = 1;
LED_R = 0; LED_G = 1; LED_B = 0; // 紫色亮 (红+蓝)
LED_R = 0; LED_G = 1; LED_B = 0;
Uart_SendString("[UART SIM] RGB Test -> PURPLE\r\n");
}
else if (cmd == '9')
{
rgb_test_mode = 1;
LED_R = 1; LED_G = 0; LED_B = 0; // 青色亮 (绿+蓝)
LED_R = 1; LED_G = 0; LED_B = 0;
Uart_SendString("[UART SIM] RGB Test -> CYAN\r\n");
}
else if (cmd == '0')
{
rgb_test_mode = 1;
LED_R = 0; LED_G = 0; LED_B = 0; // 白色亮 (红+绿+蓝)
LED_R = 0; LED_G = 0; LED_B = 0;
Uart_SendString("[UART SIM] RGB Test -> WHITE\r\n");
}
else if (cmd == 'f' || cmd == 'F')
{
rgb_test_mode = 0;
LED_R = 1; LED_G = 1; LED_B = 1; // 熄灭
Uart_SendString("[UART SIM] RGB Test -> OFF (Normal Mode)\r\n");
LED_R = 1; LED_G = 1; LED_B = 1;
Uart_SendString("[UART SIM] RGB Test -> OFF\r\n");
}
}
// 1. 读取按键事件并自动清空
ev = key_event_buf;
key_event_buf = KEY_EVENT_NONE;
if (ev != KEY_EVENT_NONE)
// 1. 处理按键事件缓冲区(来自定时器中断)
if (key_event_buf != KEY_EVENT_NONE)
{
rgb_test_mode = 0; // 任何物理/按键事件触发时退出 RGB 测试模式
// 通过串口打印获取到的按键事件代号进行测试联调
Uart_SendString("Key Event Captured: ");
Uart_SendHex8((u8)ev);
Uart_SendString("\r\n");
// 根据不同的系统当前状态,执行状态迁移逻辑
if (current_state == STATE_NORMAL || current_state == STATE_ARMED || current_state == STATE_DISARMED)
{
if (ev == KEY_EVENT_UP_DOWN_COMB) // 同时长按 UP + DOWN 3 秒进入配对菜单
{
current_state = STATE_PAIR_MENU;
menu_select = 0;
state_changed = 1;
}
else if (ev == KEY_EVENT_SOS_CLICK) // 短按 SOS 发射 SOS 报警
{
current_state = STATE_SOS_EMITTED;
state_changed = 1;
}
else if (ev == KEY_EVENT_SOS_LONG) // 长按 SOS 5 秒与 IPC 绑定
{
UI_ShowBindingPage();
RF_SetMode(2); // TX mode
EV1527_Transmit(CLONED_ADDR, 0x01); // 对码绑定电报数据为 0x01
RF_SetMode(0); // Idle
Delay_ms(2000); // 维持 2 秒
current_state = STATE_NORMAL;
state_changed = 1;
}
}
else if (current_state == STATE_PAIR_MENU)
{
if (ev == KEY_EVENT_UP_CLICK) // 菜单选择上移
{
if (menu_select > 0)
{
menu_select--;
state_changed = 1;
}
}
else if (ev == KEY_EVENT_DOWN_CLICK) // 菜单选择下移
{
if (menu_select < 4)
{
menu_select++;
state_changed = 1;
}
}
else if (ev == KEY_EVENT_SOS_CLICK) // 单击 SOS 确认选项,进入对码搜索并触发 100ms 短振与白灯快闪
{
MOTOR = 1;
Delay_ms(100);
MOTOR = 0;
current_state = STATE_PAIR_WAIT;
pair_timeout_ms = 0; // 重置对码超时计时器
state_changed = 1;
}
else if (ev == KEY_EVENT_DOWN_LONG) // 长按 DOWN 键 3 秒强行返回待机
{
current_state = STATE_NORMAL;
state_changed = 1;
}
}
else if (current_state == STATE_PAIR_WAIT)
{
if (ev == KEY_EVENT_DOWN_LONG) // 在雷达页面,长按 DOWN 3秒强行退出
{
current_state = STATE_NORMAL;
state_changed = 1;
}
}
else if (current_state == STATE_PAIR_CONFIRM)
{
if (ev == KEY_EVENT_SOS_LONG) // 在确认页面,长按 SOS 3 秒保存对码结果
{
Add_Sensor(captured_addr, captured_type);
current_state = STATE_PAIR_SUCCESS;
state_changed = 1;
}
else if (ev == KEY_EVENT_DOWN_LONG) // 在确认页面,长按 DOWN 3 秒放弃保存返回待机
{
current_state = STATE_NORMAL;
state_changed = 1;
}
}
else if (current_state == STATE_ALARM)
{
// 在防区报警闪烁状态下,短按任意物理按键均可清除报警声光
current_state = STATE_NORMAL;
state_changed = 1;
}
else if (current_state == STATE_SOS_EMITTED)
{
// 再次长按任意物理按键即可主动解除求救模式
if (ev == KEY_EVENT_SOS_LONG || ev == KEY_EVENT_UP_LONG || ev == KEY_EVENT_DOWN_LONG)
{
current_state = STATE_NORMAL;
state_changed = 1;
}
}
}
// 2. 对码雷达扫描 30 秒超时自动跳回时钟主界面检测
if (current_state == STATE_PAIR_WAIT && pair_timeout_ms >= 30000UL)
{
pair_timeout_ms = 0;
UI_ShowPairFailPage(1); // 调用 Fail 绘制接口并传入超时标志 1
Delay_ms(2500); // 维持 2.5 秒超时警告
current_state = STATE_NORMAL;
state_changed = 1;
}
// 3. 状态切换的屏幕重写渲染 (只有发生状态改变才清屏,杜绝频闪)
if (state_changed)
{
state_changed = 0; // 仅在此处清除状态改变标记,确保异步状态转移时屏幕能被正确重绘
rgb_test_mode = 0;
// 通过串口反馈状态机重绘过程
Uart_SendString("State Transition to: ");
Uart_SendHex8((u8)current_state);
Uart_SendString("\r\n");
if (current_state == STATE_NORMAL || current_state == STATE_ARMED || current_state == STATE_DISARMED)
SystemEvent sys_evt;
sys_evt.key_event = key_event_buf;
sys_evt.extra_data = 0;
sys_evt.extra_size = 0;
// 设置事件优先级
if (key_event_buf == KEY_EVENT_SOS_CLICK ||
key_event_buf == KEY_EVENT_SOS_LONG)
{
RF_SetMode(1); // 切回接收模式监听防区
UI_ShowClockPage(current_state, current_hour, current_min);
}
else if (current_state == STATE_PAIR_MENU)
{
RF_SetMode(0); // 关闭射频节省能量
UI_ShowPairMenuPage(menu_select);
}
else if (current_state == STATE_PAIR_WAIT)
{
RF_SetMode(1); // 开启射频解调芯片,开始抓包
LCD_Clear(COLOR_BLACK);
LCD_ShowStringCentered(135, "RECHERCHE...", COLOR_WHITE, COLOR_BLACK);
LCD_ShowStringCentered(160, "DECLENCH. CAPT", COLOR_CYAN, COLOR_BLACK);
UI_ShowPairWaitPage(0);
last_radar_sec = 99; // 强行重置雷达波纹帧计数器
}
else if (current_state == STATE_PAIR_CONFIRM)
{
RF_SetMode(0); // 捕获成功,关闭射频
UI_ShowPairConfirmPage(captured_addr);
}
else if (current_state == STATE_PAIR_SUCCESS)
{
RF_SetMode(0);
UI_ShowPairSuccessPage(captured_addr);
// 触发对码成功物理反馈:强振 1 次 (400ms)LED 绿色常亮 2 秒
MOTOR = 1;
LED_G = 0; // 开启绿灯
LED_R = 1;
LED_B = 1;
Delay_ms(400); // 振动 400ms
MOTOR = 0; // 停止振动
Delay_ms(1600); // 维持绿灯常亮累计满 2 秒 (400 + 1600 = 2000ms)
LED_G = 1; // 熄灭绿灯
Delay_ms(500); // 补齐页面显示总时间 2.5 秒 (2000 + 500 = 2500ms)
current_state = STATE_NORMAL;
UI_ShowClockPage(current_state, current_hour, current_min);
RF_SetMode(1);
}
else if (current_state == STATE_ALARM)
{
u8 slot;
static char alarm_name[24];
char *name = "UNKNOWN SENSOR";
if (Check_Sensor_ID(captured_addr, &slot))
{
Format_Sensor_Name(slot, alarm_name);
name = alarm_name;
alarm_sensor_slot = slot;
}
UI_ShowAlarmPage(name, 1);
}
else if (current_state == STATE_SOS_EMITTED)
{
UI_ShowSosPage();
}
}
// 4. 定期局部刷新、动画更新与射频解调轮询
if (current_state == STATE_NORMAL || current_state == STATE_ARMED || current_state == STATE_DISARMED)
{
// 时钟分钟数字局部刷新 (防闪烁)
if (clock_updated)
{
clock_updated = 0;
UI_ShowClockPage(current_state, current_hour, current_min);
}
// 监听并解析防区发射的信号码
{
u32 rx_addr;
u8 rx_data;
if (EV1527_Decode(&rx_addr, &rx_data))
{
u8 slot;
if (Check_Sensor_ID(rx_addr, &slot)) // 检查接收 of 地址是否在配对列表中
{
u32 slot_addr = ((u32)sensor_list[slot].addr[0] << 16) |
((u32)sensor_list[slot].addr[1] << 8) |
sensor_list[slot].addr[2];
u8 sensor_type = slot_addr & 0x0F;
if (sensor_type < 5) // 仅触发普通防区传感器彻底过滤并排除SOS信号接收
{
u8 zone = default_zones[sensor_type];
// 24小时防区 (zone==0) 任何时候都触发,普通防区 (zone==1) 仅在 ARMED 状态下触发
if (zone == 0 || current_state == STATE_ARMED)
{
captured_addr = rx_addr;
current_state = STATE_ALARM;
state_changed = 1;
}
}
}
}
}
}
else if (current_state == STATE_PAIR_WAIT)
{
// 雷达搜索页面每 250ms 局部刷新一帧雷达天线波纹
u8 frame = (u8)((ms_tick / 250) % 3);
if (frame != last_radar_sec)
{
last_radar_sec = frame;
UI_ShowPairWaitPage(frame);
}
// 在对码搜索状态下监听任意 EV1527 无线电码
{
u32 rx_addr;
u8 rx_data;
if (EV1527_Decode(&rx_addr, &rx_data))
{
captured_addr = rx_addr;
captured_type = menu_select;
current_state = STATE_PAIR_CONFIRM; // 捕获到信号,移至确认页
state_changed = 1;
}
}
}
else if (current_state == STATE_SOS_EMITTED)
{
// 主动 SOS 求救状态下,每 500ms 通过 LT4455 发射一次射频求救帧
u8 sec_phase = (u8)((ms_tick / 500) % 2);
if (sec_phase != last_radar_sec)
{
last_radar_sec = sec_phase;
RF_SetMode(2); // 射频置为发射端模式
EV1527_Transmit(CLONED_ADDR, 0x08); // 发射手环自带地址 CLONED_ADDR 且数据为 0x08
RF_SetMode(0); // 发射完成复位,关闭发射机
}
// 双实线红色警告边框的闪烁效果
if (alarm_flash_flag)
{
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
LCD_DrawRectBorder(8, 8, 104, 224, COLOR_WHITE);
sys_evt.priority = EVENT_PRIORITY_URGENT;
// 紧急事件直接分发,跳过队列
EventQueue_EmergencyDispatch(sys_evt);
}
else
{
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_BLACK);
LCD_DrawRectBorder(8, 8, 104, 224, COLOR_BLACK);
sys_evt.priority = EVENT_PRIORITY_NORMAL;
EventQueue_Push(sys_evt);
}
key_event_buf = KEY_EVENT_NONE;
}
else if (current_state == STATE_ALARM)
// 2. 处理事件队列中的事件
while (!EventQueue_IsEmpty())
{
// 警报闪烁状态下,交替绘制红色和黑色边框
if (alarm_flash_flag)
evt = EventQueue_Pop();
if (evt.key_event != KEY_EVENT_NONE || evt.priority != EVENT_PRIORITY_LOW)
{
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
}
else
{
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_BLACK);
Uart_SendString("[EVENT] Processing event: ");
Uart_SendHex8((u8)evt.key_event);
Uart_SendString(" Priority: ");
Uart_SendHex8((u8)evt.priority);
Uart_SendString("\r\n");
AppManager_DispatchEvent(evt);
}
}
// 仿真 LED/马达状态串口输出
// 3. 运行当前活跃应用的 onRun 回调带CPU占用控制
AppManager_RunActiveApp();
// 4. 仿真 LED/马达状态串口输出
if (LED_R != last_led_r || LED_G != last_led_g || LED_B != last_led_b)
{
last_led_r = LED_R;