- 新建 shake-nav-screen.md 文件,详细描述摇一摇功能的背景、目标、硬件与BSP配置、系统架构及摇一摇检测算法 - 包含IMU驱动、页面路由机制及后续开发计划,为实现摇一摇导航提供清晰的指导
23 KiB
摇一摇切换页面实现规格
项目:HeavenlyHeroStar
目标芯片:ESP32-S3
ESP-IDF:6.0.1
LVGL:9.2.2
逻辑分辨率:320×170(全局横屏)
IMU:QMI8658(3 轴加速度 + 3 轴陀螺仪)
状态:待实现(v1.0 初稿)
1. 背景与目标
1.1 现状
板载 QMI8658 六轴 IMU 已在硬件文档中定义,固件侧尚未接入;UI 层四屏已创建但无页面路由机制。
| 模块 | 路径 | 说明 |
|---|---|---|
| IMU 硬件 | README.md |
QMI8658,I2C SDA=IO8 / SCL=IO7,INT1=IO6 / INT2=IO15 |
| 板级配置 | main/bsp/board_config.h |
无 IMU 引脚宏;触摸使用 I2C_NUM_0(IO47/48) |
| 触摸 I2C | main/bsp/touch.c |
CST816S,i2c_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.mdPhase 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 | 中断 1(Phase 4:Data Ready / Motion) |
| IMU_INT2 | IO15 | 中断 2(Phase 4:备用) |
I2C 总线隔离: 触摸已占用 I2C_NUM_0(main/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.c、main/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):
i2c_new_master_bus()— 模式参考touch.c- 读 WHO_AM_I,期望值
0x05(实现前对照 QMI8658A 数据手册确认) - 软复位 → 配置 acc:±8g,ODR 100 Hz
- 启动
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 | 0x35–0x3A | 6 字节 burst 读 |
I2C 7-bit 地址:0x6B(SDO 默认;若硬件拉低则为 0x6A,真机确认)。
2.5 坐标系与轴映射
IMU 芯片坐标系与 PCB 安装方向、LVGL 横屏逻辑坐标 不一定一致。MVP 算法使用 合成幅值(与轴无关),不依赖单轴方向;Phase 4 若做方向性摇一摇再建立映射表。
真机标定步骤(Phase 2 验收):
- 设备平放静止 2 s,串口打印
ax, ay, az,确认合成幅值 ≈ 1g - 分别沿 X/Y/Z 快速平移,确认对应轴响应最大
- 记录
g_refbaseline,写入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.c 的 GPIO ISR → xQueueSend → dedicated task 模式;motion_gesture 检测到 shake 后向 nav_queue 投递事件,由 nav_handler_task 调用 ui_nav_next()。
4. 摇一摇检测算法
4.1 信号处理
- Baseline 校准: 启动后 500 ms 内设备保持静止,采集加速度均值
g_ref(3 轴) - 动态量:
a_dyn = a_raw - g_ref - 合成幅值:
mag = sqrt(ax² + ay² + az²)(ax/ay/az为a_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=Settings,1=Screen1,2=Screen2,3=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 ms,delay 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_change的target_init传NULL即可。
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_IO(GPIO4) 短按映射为 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.h 的 settings_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 |
1(Med) |
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 深度建议 4;motion_gesture 在 cooldown 内不应重复投递。
8.3 CMakeLists.txt 变更
在 main/CMakeLists.txt 的 SRCS 追加:
"bsp/imu.c"
"app/ui_nav.c"
"app/motion_gesture.c"
INCLUDE_DIRS 已含 app、bsp,无需新增。
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 | 确认四屏轮播顺序与「任意摇→下一页」 | 已确认 |
阶段 1:ui_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.c:I2C + WHO_AM_I + acc 读数 |
| 1.5 | 串口打印 100 Hz acc;确认 WHO_AM_I 与 1g 静止读数 |
Phase 1 验收: AC-2 用手动按键代替摇一摇通过;AC-1 通过。
阶段 2:motion_gesture + Queue 切页
| 步骤 | 内容 |
|---|---|
| 2.1 | 新建 motion_gesture.c:baseline、状态机、cooldown |
| 2.2 | nav_queue + nav_handler_task → ui_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。
阶段 3:Settings + NVS
| 步骤 | 内容 |
|---|---|
| 3.1 | 扩展 settings_types.h、settings_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 | 屏幕底部页码 dot(4 屏指示) |
| 4.4 | 静止武装窗口,减少走路误触 |
| 4.5 | VALUE_CHANGED 即时 apply shake 开关(免 Save 预览) |
10. 验收标准
| 编号 | 验收项 | 标准 |
|---|---|---|
| AC-1 | IMU 通信 | WHO_AM_I = 0x05;静止时合成幅值 ≈ 0.05–0.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. 风险与约束
- I2C 冲突: IMU 与触摸必须分 bus;禁止将 QMI8658 挂到
I2C_NUM_0与 CST816 共用。 - WHO_AM_I / 地址: 硬件 SDO 接法可能导致
0x6A;init 失败时优先排查。 - 轴映射: MVP 用合成幅值,方向错误仅影响 Phase 4 方向性手势,不影响 MVP。
- Settings 未 Save 切屏: 摇离 Settings 不 collect Tab;用户未 Save 的改动仍在内存,重启丢失——与现有 Settings 行为一致,需在 UI 文案或后续 Save 提示中考虑(非 MVP)。
- CPU / 功耗: 100 Hz 轮询 + I2C 读增加功耗;电池场景 Phase 4 改 INT 驱动。
- Screen 布局: Screen1/2/3 可能仍为 SquareLine 竖屏坐标,切页后布局可能错位;属
settings-screen-landscape.mdPhase 3,不阻塞本 spec MVP。 - 误触: 敲桌、走路仍可能触发;MVP 靠 threshold + cooldown,不做机器学习。
- Factory Reset 范围: 与 Settings spec 一致,Phase 1 仅擦除
"settings"namespace;BLE bonding 等保留。 - 陀螺仪: MVP 不读 gyro;减少驱动复杂度。
12. 参考
- 硬件引脚:
README.mdIMU 表 - 板级配置:
main/bsp/board_config.h - 触摸 I2C 范例:
main/bsp/touch.c - LVGL 线程锁:
main/bsp/lvgl_port.c(lvgl_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.md(Phase 3 多屏导航由本 specui_nav承接) - 姊妹规格:
docs/spec/ble-hid-keyboard.md(HID 与切页独立) - LVGL 9.2:
lv_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-1~AC-8 |