# 编码实现 - 应用执行与页面渲染 (Docs/60_coding/mod-app.md) 本文件对应手环前台 App 调度管理、活跃实例生命周期交互与各子应用页面像素级渲染的具体编码实现。 ## 1. 对应代码源文件 * [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (定义 App 数组、`StartApp` 生命周期函数、各子应用的 `OnStart` / `onRun` / `onClose` / `onEvent` 钩子回调) * [App/config.h](file:///c:/workfile/105/stc32g12k128/App/config.h) (声明 `WristbandApp` 结构体原型及应用 ID 枚举) ## 2. 关键代码片段与逻辑实现 ### 2.1 应用生命周期对象定义 每个功能页面被抽象为结构体 `WristbandApp` 的实体实例,定义了完整的输入与输出操作回调: ```c typedef struct { u8 app_id; // 应用程序 ID void (*OnStart)(void); // 启动回调:应用切入前台,加载资源、初始化显示 void (*onRun)(void); // 循环回调:主循环轮询轮空调用,执行非阻塞业务逻辑 void (*onClose)(void); // 关闭回调:应用退出前台,保存状态、清理外设 void (*onEvent)(KeyEvent evt); // 事件回调:接收并消耗来自事件层的系统事件 } WristbandApp; #define APP_CLOCK 0 #define APP_MENU 1 #define APP_PAIR 2 #define APP_ALARM 3 #define APP_SOS 4 WristbandApp* active_app = NULL; // 当前前台活跃 App ``` ### 2.2 AppManager 调度执行引擎 在主循环框架中,通过调用 `StartApp()` 执行非阻塞的前台应用切换: ```c void StartApp(u8 app_id) { // 1. 切出当前活跃应用,释放资源并清理引脚状态 if (active_app && active_app->onClose) { active_app->onClose(); } // 2. 指针重定向 switch (app_id) { case APP_CLOCK: active_app = &App_Clock_Instance; break; case APP_MENU: active_app = &App_Menu_Instance; break; case APP_PAIR: active_app = &App_Pair_Instance; break; case APP_ALARM: active_app = &App_Alarm_Instance; break; case APP_SOS: active_app = &App_SOS_Instance; break; default: active_app = &App_Clock_Instance; break; } // 3. 切入新应用前台,并强制标记 Redraw 触发 GRAM 刷新 if (active_app && active_app->OnStart) { active_app->OnStart(); } } ``` ### 2.3 待机大字时钟页面像素刷写 (ClockApp) 在待机状态时,应用通过 LCD 驱动将 `32x64px` 居中字模刷写在屏幕的 Column [0, 119], Row [0, 239] 的 Column+4 窗口中: ```c void UI_ShowClockPage(SystemState state, u8 hour, u8 min) { char time_str[6]; char bat_str[10]; u8 bat_val = Read_Battery_Percent(); // 读取 P0.0 ADC 数据进行百分比计算 bit charging = Is_Charging(); // 读取 P2.7 数字 IO 输入 LCD_Clear(COLOR_BLACK); // 1. 绘制顶部电量状态栏 (16x32px 标准无障碍字体) if (charging) { sprintf(bat_str, "%d%%+", (int)bat_val); LCD_ShowString(40, 20, bat_str, COLOR_GREEN, COLOR_BLACK); } else { sprintf(bat_str, "%d%%", (int)bat_val); LCD_ShowString(50, 20, bat_str, COLOR_GRAY, COLOR_BLACK); } // 2. 绘制中央超大数字时钟 (32x64px) sprintf(time_str, "%02d:%02d", (int)hour, (int)min); LCD_ShowString32x64Centered(80, time_str, COLOR_WHITE, COLOR_BLACK); // 3. 绘制底部系统布撤防锁状态 (32x32px 大图标) if (state == STATE_ARMED) { LCD_ShowImage(44, 180, 32, 32, bmp_lock_32x32, COLOR_RED, COLOR_BLACK); } else { LCD_ShowImage(44, 180, 32, 32, bmp_unlock_32x32, COLOR_GREEN, COLOR_BLACK); } } ``` ### 2.4 配对微调确认与保存页面交互 (PairApp) 当捕获到新的 EV1527 对码信号时,`PairApp` 负责在 `onEvent` 钩子中接收微调的按键输入并保存至 Flash 数据库: ```c void PairApp_onEvent(KeyEvent evt) { if (evt == KEY_EVENT_UP_CLICK) { // 短按 ▲ 键增加后缀序号 if (selected_suffix < 99) { selected_suffix++; Redraw = 1; } } else if (evt == KEY_EVENT_DOWN_CLICK) { // 短按 ▼ 键减少后缀序号 if (selected_suffix > 1) { selected_suffix--; Redraw = 1; } } else if (evt == KEY_EVENT_SOS_CLICK) { // 短按 ■ 确认键保存对码,数据格式 flags_suffix 位域重新分配 // Bit 7: 有效位=1; Bit 0~6: 后缀序号 u8 flags = 0x80 | (selected_suffix & 0x7F); // 保存至持久化扇区 Add_Sensor_To_Flash(captured_rf_addr, flags); // 开启 300ms 成功振动反馈并退回时钟 Motor_Start_Pattern(0); // 300ms 短振 StartApp(APP_CLOCK); } else if (evt == KEY_EVENT_SOS_LONG) { // 长按 ■ 确认键 3 秒放弃保存并回退 StartApp(APP_CLOCK); } } ```