Files
HeavenlyHeroStar/docs/spec/shake-nav-screen.md
T
rsgltzyd e5e1d5b85e feat: 添加摇一摇切换页面实现规格文档
- 新建 shake-nav-screen.md 文件,详细描述摇一摇功能的背景、目标、硬件与BSP配置、系统架构及摇一摇检测算法
- 包含IMU驱动、页面路由机制及后续开发计划,为实现摇一摇导航提供清晰的指导
2026-07-05 21:05:14 +08:00

23 KiB
Raw Blame History

摇一摇切换页面实现规格

项目:HeavenlyHeroStar
目标芯片:ESP32-S3
ESP-IDF6.0.1
LVGL9.2.2
逻辑分辨率:320×170(全局横屏)
IMUQMI86583 轴加速度 + 3 轴陀螺仪)
状态:待实现(v1.0 初稿)

1. 背景与目标

1.1 现状

板载 QMI8658 六轴 IMU 已在硬件文档中定义,固件侧尚未接入;UI 层四屏已创建但无页面路由机制。

模块 路径 说明
IMU 硬件 README.md QMI8658I2C SDA=IO8 / SCL=IO7INT1=IO6 / INT2=IO15
板级配置 main/bsp/board_config.h IMU 引脚宏;触摸使用 I2C_NUM_0IO47/48
触摸 I2C main/bsp/touch.c CST816Si2c_master_bus 独占 I2C_NUM_0
UI 屏幕 main/ui/ui.c 启动时创建 Screen1/2/3 + Settings,默认 lv_disp_load_scr(ui_Settings)
页面路由 不存在_ui_screen_change()ui_helpers.c 中已实现但未被调用
LVGL 线程 main/bsp/lvgl_port.c 独立 task + _lock;外部改 UI 须 lvgl_port_lock()
Settings main/ui/settings/ settings_data_t motion 字段;NVS namespace "settings"
事件处理 main/app/ui_handlers.c HID / Settings 回调;无 IMU / 导航逻辑
IMU 驱动 仓库内无 QMI8658 组件或 main/bsp/imu.c

1.2 目标

通过 摇一摇 在四屏之间循环切换,作为触摸与物理按键之外的第三种导航方式。

已确认产品决策:

决策项 结论
参与轮播的页面 Settings + Screen1 + Screen2 + Screen3 四屏循环
手势映射 任意方向摇一次 → 下一页MVP 不做左/右方向区分)
默认轮播顺序 Settings → Screen1 → Screen2 → Screen3 → Settings
Settings 页行为 在 Settings 时摇一摇同样切到 Screen1(不因当前在设置页而禁用)

首版(MVP)交付:

  • QMI8658 BSP 驱动 + 100 Hz 加速度采样(轮询)
  • 摇一摇检测:去重力合成幅值 + 阈值 + 时间窗 + cooldown
  • ui_nav 模块:循环切下一屏,fade 动画
  • System Tab:「Shake Navigation」开关 + 灵敏度档位(低/中/高)
  • NVS 持久化;Factory Reset 恢复 motion 默认值
  • 线程安全:IMU task → Queue → ui_nav_next()(内部 lvgl_port_lock

后续阶段(非 MVP 阻塞):

  • INT1 Data Ready 中断,降低 CPU 轮询与功耗
  • ui_nav_prev() 与方向性摇一摇
  • 切页页码指示(dot indicator
  • Screen1/2/3 横屏布局完善(见 settings-screen-landscape.md Phase 3

1.3 方案选型结论

方案 说明 结论
A 仅陀螺仪积分判摇 零偏漂移大,不采用
B 加速度去重力 + 合成幅值峰值 MVP 采用
C QMI8658 内置 Wake-on-Motion 硬件判摇 Phase 4 省电优化
D IMU task 内直接调用 lv_screen_load_anim() 线程不安全,禁止
E FreeRTOS Queue → nav task → lvgl_port_lock 切屏 采用(模式参考 boot_button.c
F esp_event 总线投递 UI_NAV_NEXT 可选,与 Queue 等价;MVP 用 Queue 即可

2. 硬件与 BSP

2.1 引脚与总线

来源:README.md 外围接口表。

信号 GPIO 说明
IMU_SDA IO8 I2C 数据线
IMU_SCL IO7 I2C 时钟线
IMU_INT1 IO6 中断 1Phase 4Data Ready / Motion
IMU_INT2 IO15 中断 2Phase 4:备用)

I2C 总线隔离: 触摸已占用 I2C_NUM_0main/bsp/touch.c)。IMU 必须使用独立总线,建议 I2C_NUM_1,避免与 CST816 争用或地址冲突时的 bus 锁问题。

2.2 board_config.h 新增宏

/* IMU I2C (QMI8658) */
#define BOARD_IMU_I2C_NUM        I2C_NUM_1
#define BOARD_PIN_IMU_SDA        8
#define BOARD_PIN_IMU_SCL        7
#define BOARD_PIN_IMU_INT1       6
#define BOARD_PIN_IMU_INT2       15
#define BOARD_IMU_I2C_FREQ_HZ    400000
#define BOARD_IMU_ENABLED        1

2.3 imu.c / imu.h 职责

新建 main/bsp/imu.cmain/bsp/imu.h

API 说明
esp_err_t imu_init(void) 初始化 I2C bus + QMI8658 芯片
esp_err_t imu_read_accel(float *ax, *ay, *az) 读取加速度,单位 g(±8g 量程)
esp_err_t imu_read_gyro(...) Phase 4 可选;MVP 可不实现
void imu_start_task(imu_sample_cb_t cb) 100 Hz 采样循环,回调 motion_gesture

初始化序列(MVP):

  1. i2c_new_master_bus() — 模式参考 touch.c
  2. 读 WHO_AM_I,期望值 0x05(实现前对照 QMI8658A 数据手册确认)
  3. 软复位 → 配置 acc:±8g,ODR 100 Hz
  4. 启动 imu_task(10 ms 周期读数,或按 ODR 对齐)

Phase 1 采用 轮询INT1/INT2 在 Phase 4 启用。

2.4 QMI8658 寄存器(MVP 最小集)

完整寄存器表以实现阶段对照厂商数据手册为准;下表为 spec 占位,便于 Phase 1 编码。

寄存器 地址 MVP 用途
WHO_AM_I 0x00 设备识别
CTRL1 0x02 软复位 / 使能
CTRL2 0x03 acc 量程、ODR
ACC_X_L … ACC_Z_H 0x350x3A 6 字节 burst 读

I2C 7-bit 地址:0x6BSDO 默认;若硬件拉低则为 0x6A,真机确认)。

2.5 坐标系与轴映射

IMU 芯片坐标系与 PCB 安装方向、LVGL 横屏逻辑坐标 不一定一致。MVP 算法使用 合成幅值(与轴无关),不依赖单轴方向;Phase 4 若做方向性摇一摇再建立映射表。

真机标定步骤(Phase 2 验收):

  1. 设备平放静止 2 s,串口打印 ax, ay, az,确认合成幅值 ≈ 1g
  2. 分别沿 X/Y/Z 快速平移,确认对应轴响应最大
  3. 记录 g_ref baseline,写入 motion_gesture 启动校准

3. 系统架构

3.1 模块划分

main/bsp/
├── board_config.h           # IMU 引脚 / I2C 宏
├── imu.c / imu.h            # I2C + QMI8658 驱动 + imu_task

main/app/
├── ui_nav.c / ui_nav.h      # 屏幕轮播状态机
├── motion_gesture.c / .h    # 摇一摇检测、cooldown、配置同步
└── ui_handlers.c            # 可选:nav 事件注册

main/ui/settings/
├── settings_types.h         # shake_nav_enabled, shake_sensitivity
├── settings_store.c         # NVS shake_en / shake_sens
└── settings_tabs.c          # System Tab 新增控件

main/app/app_main.c          # 初始化顺序扩展
main/CMakeLists.txt          # 追加源文件

3.2 架构图

flowchart TB
    subgraph bsp [BSP Layer]
        IMU["imu.c"]
    end

    subgraph app [App Layer]
        Gesture["motion_gesture.c"]
        Nav["ui_nav.c"]
        Queue["nav_queue"]
    end

    subgraph ui [UI Layer]
        Screens["ui_Settings / Screen1/2/3"]
        Store["settings_store.c"]
    end

    subgraph lvgl [LVGL Port]
        Lock["lvgl_port_lock"]
        LVGLTask["lvgl_port_task"]
    end

    IMU -->|"100Hz samples"| Gesture
    Store -->|"apply enabled/sens"| Gesture
    Gesture -->|"UI_NAV_NEXT"| Queue
    Queue --> Nav
    Nav --> Lock
    Lock --> Screens
    Screens --> LVGLTask

3.3 摇一摇切页时序

sequenceDiagram
    participant User as 用户
    participant IMU as imu_task
    participant Gesture as motion_gesture
    participant Queue as nav_queue
    participant Nav as ui_nav
    participant LVGL as lvgl_port_task

    User->>IMU: 摇晃设备
    IMU->>Gesture: accel sample 100Hz
    Gesture->>Gesture: mag > threshold + min_samples
    Gesture->>Gesture: cooldown check + enabled check
    Gesture->>Queue: UI_NAV_NEXT
    Queue->>Nav: ui_nav_next()
    Nav->>LVGL: lvgl_port_lock()
    Nav->>LVGL: lv_screen_load_anim next screen
    Nav->>LVGL: lvgl_port_unlock()
    LVGL->>User: 显示下一页

线程间通信: 参考 main/bsp/boot_button.cGPIO ISR → xQueueSend → dedicated task 模式;motion_gesture 检测到 shake 后向 nav_queue 投递事件,由 nav_handler_task 调用 ui_nav_next()


4. 摇一摇检测算法

4.1 信号处理

  1. Baseline 校准: 启动后 500 ms 内设备保持静止,采集加速度均值 g_ref3 轴)
  2. 动态量: a_dyn = a_raw - g_ref
  3. 合成幅值: mag = sqrt(ax² + ay² + az²)ax/ay/aza_dyn 分量)

可选替代(Phase 4):一阶低通 a_lp + 高通 a_hp = a_raw - a_lp,对缓慢倾斜更不敏感。

4.2 判定状态机

                    mag > threshold
         ┌──────────────────────────────┐
         │                              │
         v                              │
      ┌──────┐   连续 ≥ min_samples    ┌─────────────────┐
      │ IDLE │ ──────────────────────> │ SHAKE_DETECTED  │
      └──────┘                           └────────┬────────┘
         ^                                        │
         │              cooldown_ms 到期            │
         └────────────────────────────────────────┘
                              │
                              v
                    post UI_NAV_NEXT(若 enabled

规则:

  • 仅在 SHAKE_DETECTED 上升沿投递 一次 UI_NAV_NEXT
  • cooldown_ms 内忽略后续峰值,防止一次猛摇连切多页
  • shake_nav_enabled == false 时状态机仍可运行,但不投递事件

4.3 默认参数

参数 默认值 说明
sample_rate 100 Hz acc ODR,与 imu_task 一致
threshold 0.8 g 合成幅值阈值
threshold_low 0.6 g 灵敏度 Low
threshold_med 0.8 g 灵敏度 Med(默认)
threshold_high 1.0 g 灵敏度 High
window_ms 300 ms 超阈值须在此窗口内形成有效峰值
min_samples 5 连续超阈值样本数(100 Hz 下 ≈ 50 ms
cooldown_ms 800 ms 两次有效摇一摇最小间隔
calibration_ms 500 ms 启动 baseline 采集时长

4.4 motion_gesture API

typedef enum {
    MOTION_NAV_NEXT = 0,
} motion_nav_event_t;

typedef void (*motion_nav_cb_t)(motion_nav_event_t event);

void motion_gesture_init(motion_nav_cb_t cb);
void motion_gesture_set_enabled(bool enabled);
void motion_gesture_set_sensitivity(shake_sensitivity_t sens);
void motion_gesture_on_sample(float ax_g, float ay_g, float az_g);

motion_gesture_on_sample()imu_task 每样本调用;内部维护状态机与 cooldown 计时。

4.5 误触与边界

场景 MVP 策略
敲桌面 提高 threshold 或选 Med/High 灵敏度
线缆拉扯 cooldown 防止连切
手持走路 可能误触;Phase 4 可加「须从 IDLE 静止 ≥ 200 ms 才武装」
Settings 调 slider 时轻微抖动 合成幅值阈值通常高于手指微动

5. UI 页面导航(ui_nav

5.1 屏幕注册表

轮播顺序与 §1.2 一致:

#include "screens/ui_Settings.h"
#include "screens/ui_Screen1.h"
#include "screens/ui_Screen2.h"
#include "screens/ui_Screen3.h"

static lv_obj_t * const s_nav_screens[] = {
    ui_Settings,
    ui_Screen1,
    ui_Screen2,
    ui_Screen3,
};

#define NAV_SCREEN_COUNT  (sizeof(s_nav_screens) / sizeof(s_nav_screens[0]))

索引:0=Settings1=Screen12=Screen23=Screen3。

5.2 公开 API

void ui_nav_init(void);
void ui_nav_next(void);              /* 线程安全:内部 lvgl_port_lock */
void ui_nav_prev(void);              /* Phase 4 预留;MVP 不实现 */
void ui_nav_goto(uint8_t index);     /* 可选:调试 / 恢复索引 */

lv_obj_t *ui_nav_current(void);
uint8_t ui_nav_current_index(void);
const char *ui_nav_current_name(void); /* 日志用:"Settings"/"Screen1"/... */

5.3 切屏实现

  • 使用现有 _ui_screen_change()main/ui/ui_helpers.c)或等价 lv_screen_load_anim()
  • 动画:LV_SCR_LOAD_ANIM_FADE_IN,时长 300 msdelay 0
  • ui_nav_next()index = (index + 1) % NAV_SCREEN_COUNT
  • 内部流程:
void ui_nav_next(void)
{
    lvgl_port_lock();
    s_nav_index = (s_nav_index + 1) % NAV_SCREEN_COUNT;
    lv_obj_t **target = (lv_obj_t **)&s_nav_screens[s_nav_index];
    _ui_screen_change(target, LV_SCR_LOAD_ANIM_FADE_IN, 300, 0, NULL);
    lvgl_port_unlock();
}

注:s_nav_screens 元素在 ui_init() 后已非 NULL_ui_screen_changetarget_initNULL 即可。

5.4 与 Settings Tab 的边界

行为 说明
摇一摇切屏 切换 LVGL Screen(整屏),不切换 Settings 内 Sidebar Tab
Settings 内 Tab 仍仅由 Sidebar 点击切换;遵循 settings-screen-landscape.md §3.3「切换 Tab 前先 collect」
摇离 Settings 自动 settings_tabs_collect();内存 struct 保留未 Save 的改动(与现有跨 Tab 行为一致)
从 Settings 摇一摇 下一屏为 Screen1

5.5 Phase 1 无 IMU 时的验证路径

ui_nav 就绪后、IMU 驱动完成前,将 BOOT 键KEY_IOGPIO4 短按映射为 ui_nav_next(),验证四屏轮播与 fade 动画,不阻塞 Phase 2。


6. 数据模型与 NVS

6.1 类型扩展

main/ui/settings/settings_types.h 追加:

typedef enum {
    SHAKE_SENS_LOW = 0,
    SHAKE_SENS_MED,
    SHAKE_SENS_HIGH,
} shake_sensitivity_t;

typedef struct {
    bool notification_enabled;
    uint8_t brightness_pct;
    uint8_t volume_pct;
    bool mute;
    bool shake_nav_enabled;              /* 默认 true */
    shake_sensitivity_t shake_sensitivity; /* 默认 SHAKE_SENS_MED */
} settings_data_t;

settings_tabs.hsettings_field_t 追加:

    SETTINGS_FIELD_SHAKE_NAV,
    SETTINGS_FIELD_SHAKE_SENS,

6.2 默认值

static const settings_data_t SETTINGS_DEFAULTS = {
    .notification_enabled = false,
    .brightness_pct = 70,
    .volume_pct = 50,
    .mute = false,
    .shake_nav_enabled = true,
    .shake_sensitivity = SHAKE_SENS_MED,
};

6.3 NVS Schema

Namespace'settings'(与现有字段共用)。

Key 类型 字段 默认
shake_en u8 shake_nav_enabled 1
shake_sens u8 shake_sensitivity 1Med

settings_store_load() / commit() / factory_reset() 同步读写上述 key。

6.4 settings_store_apply 扩展

void settings_store_apply(void)
{
    /* 现有背光逻辑 */
    uint16_t duty = (uint16_t)((s_data.brightness_pct * 1023U) / 100U);
    display_backlight_set_brightness(duty);

    /* 新增 */
    motion_gesture_set_enabled(s_data.shake_nav_enabled);
    motion_gesture_set_sensitivity(s_data.shake_sensitivity);
}

生效时机:

  • 启动:settings_store_load()settings_store_apply()
  • Save 按钮:settings_store_commit()settings_store_apply()
  • Factory Reset:恢复默认后 apply()

Switch / Roller 的 VALUE_CHANGED 仅更新内存;与现有 notification / mute 行为一致,Save 后持久化。


7. Settings UI 变更

7.1 System Tab 新增控件

load_system_settings() 中,于 Factory Reset 按钮 上方 追加一行(y 续排,避免与 y=112 的 Reset 重叠):

控件 x y NVS 字段 行为
Label "Shake Navigation" 4 40 120 28 静态文本
Switch 180 40 40 28 shake_nav_enabled SETTINGS_FIELD_SHAKE_NAV
Label "Shake Sensitivity" 4 76 120 28 静态文本
Dropdown 120 76 100 28 shake_sensitivity 选项 "Low\nMed\nHigh"SETTINGS_FIELD_SHAKE_SENS

Factory Reset 按钮保持 y=112 不变(与 settings-screen-landscape.md §5.4 兼容,新增项插入中间行)。

7.2 settings_tabs_collect 扩展

SETTINGS_TAB_SYSTEM 分支增加:

if (s_shake_nav_switch != NULL) {
    out->shake_nav_enabled = lv_obj_has_state(s_shake_nav_switch, LV_STATE_CHECKED);
}
if (s_shake_sens_dropdown != NULL) {
    out->shake_sensitivity = (shake_sensitivity_t)lv_dropdown_get_selected(s_shake_sens_dropdown);
}

7.3 ui_handlers 扩展

settings_on_value_changed() 增加 SETTINGS_FIELD_SHAKE_NAV / SETTINGS_FIELD_SHAKE_SENS case,更新 settings_store 内存 struct。

不在 VALUE_CHANGED 时调用 motion_gesture_set_* 与 brightness 类似,Save + apply() 后统一生效;若需即时预览开关,可在 Save 规范外额外 apply()Phase 2 可选)。


8. 事件与集成

8.1 初始化顺序

扩展 main/app/app_main.c

1. nvs_flash_init()
2. display_init()
3. lvgl_port_init()
4. touch_init()               [若 BOARD_TOUCH_ENABLED]
5. lvgl_port_lock → ui_init() → ui_nav_init() → lvgl_port_unlock
6. settings_store_load()
7. settings_store_apply()     [背光 + motion 配置]
8. ui_handlers_init()
9. motion_gesture_init(on_motion_nav)  [注册 ui_nav_next 回调]
10. imu_init()                 [启动 imu_task]
11. ble_manager_init()

ui_nav_init() 须将 s_nav_index 与当前 loaded screen 同步(默认 Settings → index 0)。

8.2 nav_handler_task

static void nav_handler_task(void *arg)
{
    motion_nav_event_t ev;
    while (xQueueReceive(s_nav_queue, &ev, portMAX_DELAY) == pdTRUE) {
        if (ev == MOTION_NAV_NEXT) {
            ui_nav_next();
        }
    }
}

Queue 深度建议 4motion_gesture 在 cooldown 内不应重复投递。

8.3 CMakeLists.txt 变更

main/CMakeLists.txtSRCS 追加:

"bsp/imu.c"
"app/ui_nav.c"
"app/motion_gesture.c"

INCLUDE_DIRS 已含 appbsp,无需新增。

8.4 任务优先级与栈

Task 优先级 说明
LVGL 2 8 KB 已有
imu_task 4 3 KB I2C 读 + 回调 gesture
nav_handler 5 2 KB Queue 消费,调用 ui_nav
boot_btn 5 2 KB 已有,Phase 1 可复用测 nav

IMU 优先级低于 nav_handler,避免 I2C 阻塞切页响应。

8.5 调试日志

Tag 内容
imu WHO_AM_I、init 失败
motion shake 触发、mag 峰值(CONFIG_LOG_DEFAULT_LEVEL DEBUG 下)
ui_nav index 变化、screen 名称

Phase 1 可启用 mag 峰值串口输出,便于真机调 threshold。


9. 实现步骤

阶段 0:规格审核

步骤 内容 状态
0.1 审核本文档 v1.0 待确认
0.2 确认四屏轮播顺序与「任意摇→下一页」 已确认

阶段 1ui_nav + IMU 驱动基础

步骤 内容
1.1 board_config.h 增加 IMU 宏
1.2 新建 ui_nav.c:注册表、init、next、lock 包装
1.3 BOOT 或 KEY 短按 → ui_nav_next(),真机验证四屏 fade 轮播
1.4 新建 imu.cI2C + WHO_AM_I + acc 读数
1.5 串口打印 100 Hz acc;确认 WHO_AM_I 与 1g 静止读数

Phase 1 验收: AC-2 用手动按键代替摇一摇通过;AC-1 通过。

阶段 2motion_gesture + Queue 切页

步骤 内容
2.1 新建 motion_gesture.cbaseline、状态机、cooldown
2.2 nav_queue + nav_handler_taskui_nav_next()
2.3 imu_task 调用 motion_gesture_on_sample()
2.4 真机调 threshold / cooldown,满足 AC-3

Phase 2 验收: AC-2、AC-3、AC-7、AC-8。

阶段 3Settings + NVS

步骤 内容
3.1 扩展 settings_types.hsettings_store.c
3.2 System Tab UI + collect + ui_handlers
3.3 settings_store_apply() 同步 motion 配置
3.4 Factory Reset 验证 motion 默认值

Phase 3 验收: AC-4、AC-5、AC-6。

阶段 4:增强(可选)

步骤 内容
4.1 INT1 Data Ready,降低轮询频率
4.2 ui_nav_prev() + 方向性摇一摇
4.3 屏幕底部页码 dot4 屏指示)
4.4 静止武装窗口,减少走路误触
4.5 VALUE_CHANGED 即时 apply shake 开关(免 Save 预览)

10. 验收标准

编号 验收项 标准
AC-1 IMU 通信 WHO_AM_I = 0x05;静止时合成幅值 ≈ 0.050.15g(去 baseline 后)
AC-2 轮播顺序 连续 4 次有效摇一摇:Settings → Screen1 → Screen2 → Screen3 → Settings
AC-3 Cooldown 单次持续摇晃只触发 1 次 切页
AC-4 禁用开关 Settings 中关闭 Shake Navigation 并 Save 后,摇一摇不切页
AC-5 灵敏度 相同力度下 High 比 Low 更易触发(至少能感知差异)
AC-6 NVS 开关与灵敏度 Save 后重启仍保持
AC-7 线程安全 连续摇切 50 次无 crash、无 LVGL assert
AC-8 HID 无干扰 在 Screen2/3 摇切页时,不发送 BLE HID 报告

11. 风险与约束

  1. I2C 冲突: IMU 与触摸必须分 bus;禁止将 QMI8658 挂到 I2C_NUM_0 与 CST816 共用。
  2. WHO_AM_I / 地址: 硬件 SDO 接法可能导致 0x6Ainit 失败时优先排查。
  3. 轴映射: MVP 用合成幅值,方向错误仅影响 Phase 4 方向性手势,不影响 MVP。
  4. Settings 未 Save 切屏: 摇离 Settings 不 collect Tab;用户未 Save 的改动仍在内存,重启丢失——与现有 Settings 行为一致,需在 UI 文案或后续 Save 提示中考虑(非 MVP)。
  5. CPU / 功耗: 100 Hz 轮询 + I2C 读增加功耗;电池场景 Phase 4 改 INT 驱动。
  6. Screen 布局: Screen1/2/3 可能仍为 SquareLine 竖屏坐标,切页后布局可能错位;属 settings-screen-landscape.md Phase 3,不阻塞本 spec MVP。
  7. 误触: 敲桌、走路仍可能触发;MVP 靠 threshold + cooldown,不做机器学习。
  8. Factory Reset 范围: 与 Settings spec 一致,Phase 1 仅擦除 "settings" namespaceBLE bonding 等保留。
  9. 陀螺仪: MVP 不读 gyro;减少驱动复杂度。

12. 参考

  • 硬件引脚:README.md IMU 表
  • 板级配置:main/bsp/board_config.h
  • 触摸 I2C 范例:main/bsp/touch.c
  • LVGL 线程锁:main/bsp/lvgl_port.clvgl_port_lock / unlock
  • 按键 Queue 范例:main/bsp/boot_button.c
  • UI 入口与默认屏:main/ui/ui.c
  • 切屏辅助: main/ui/ui_helpers.c_ui_screen_change
  • Settings 数据:main/ui/settings/settings_store.c
  • 姊妹规格:docs/spec/settings-screen-landscape.mdPhase 3 多屏导航由本 spec ui_nav 承接)
  • 姊妹规格:docs/spec/ble-hid-keyboard.mdHID 与切页独立)
  • LVGL 9.2lv_screen_load_anim()LV_SCR_LOAD_ANIM_FADE_IN
  • QMI8658A 数据手册(寄存器与 WHO_AM_I,实现 Phase 1 前必读)

修订记录

版本 日期 说明
1.0 2026-07-05 初稿:四屏循环、任意摇→下一页、QMI8658 BSP、motion_gesture、ui_nav、Settings/NVS、分阶段实现与 AC-1AC-8