Files
stc32g128k/Docs/60_coding/mod-app.md
2026-07-15 15:57:37 +08:00

129 lines
4.9 KiB
Markdown

# 编码实现 - 应用执行与页面渲染 (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);
}
}
```
<!-- Touched to refresh dirty check baseline -->