127 lines
4.8 KiB
Markdown
127 lines
4.8 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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|