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

4.9 KiB

编码实现 - 应用执行与页面渲染 (Docs/60_coding/mod-app.md)

本文件对应手环前台 App 调度管理、活跃实例生命周期交互与各子应用页面像素级渲染的具体编码实现。

1. 对应代码源文件

  • App/main.c (定义 App 数组、StartApp 生命周期函数、各子应用的 OnStart / onRun / onClose / onEvent 钩子回调)
  • App/config.h (声明 WristbandApp 结构体原型及应用 ID 枚举)

2. 关键代码片段与逻辑实现

2.1 应用生命周期对象定义

每个功能页面被抽象为结构体 WristbandApp 的实体实例,定义了完整的输入与输出操作回调:

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() 执行非阻塞的前台应用切换:

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 窗口中:

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 数据库:

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);
    }
}