Files
stc32g128k/work-order/wo-05
edisondeng 79d604a387 fix(sleep): pin P3.6 to a fixed rising-edge trigger before Power Down
RF_HandleEdgeInterrupt()'s software ping-pong toggle can leave P3IM0
parked in either edge direction while awake; Enter_Low_Power_Sleep()
never reset it, so sleep entry could randomly land on a direction that
misses the wake edge. Per the datasheet truth table (WO-04 2.9), only
edge modes support power-down wake, not level modes, so this forces a
known rising-edge state (matching RF_Init()'s own boot default) instead
of switching to level-triggered as originally requested. wo-05 updated
to record the deviation and rationale.
2026-07-31 18:14:22 +08:00

83 lines
10 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 工单 WO-05细节问题修复清单
**优先级**:中
**状态**:待处理
---
## 问题列表
1. 开机启动瞬间背光应保持关闭,等所有初始化都完成、即将进入 `while(1)` 主循环前才点亮,避免开机闪烁。
2. 长按 KEY_CONFIRM 打开主菜单后10 秒内无任何按键操作,自动关闭菜单返回时钟界面。
3. 按 App 重新梳理菜单导航结构menu_app 只保留 4 个入口(语言开关内置于 menu_app其余 3 项各自跳转到独立的 App),每个子 App 统一用长按 CONFIRM 退出、回到自己的父 App而不是像现在这样有的退回上一级、有的直接跳回主菜单/首页,规则不统一。
4. 默认菜单语言改为英文(目前首次开机/Flash 擦除态默认是法语)。
5. 主菜单上下键切换菜单项允许循环切换(到顶/底后可以绕到另一端)。
6. P3.6 进入休眠前改成电平触发方式作为唤醒源,现在经常唤醒不了处理器。
---
## 实现思路
### 1. 背光延迟点亮
- `Drivers/lcd.c` 的 `LCD_Init()` 第 89 行 `SGM_CTRL = 0;`(点亮背光)在整个 ST7789V 寄存器初始化序列**最开头**就执行了,这就是闪烁的直接原因——去掉这一行(或改成 `SGM_CTRL = 1` 熄灭)。
- `App/system.c` 的 `GPIO_Init()` 只把 P1.7 配置成推挽输出,没有写初始电平,上电瞬间电平不确定;补一行 `SGM_CTRL = 1;`(熄灭),让背光从上电第一刻就确定是关的。
- `App/main.c` 的 `main()` 里,在 `AppManager_StartApp(APP_ID_HOME)`(画出第一帧时钟界面,若第 3 条改名重构先落地)之后、`while(1)` 之前,加一行 `SGM_CTRL = 0;` 点亮背光。
### 2. 菜单 10 秒无操作自动关闭
- 现有的 `inactivity_timer`(`App/main.c`)是全局 60 秒深度休眠计时,粒度和用途都不一样,不能直接复用。
- 在 `App/app_menu.c` 里新增一个 `static u16 menu_idle_enter_ms`,仿照 `Drivers/rf.c` 的 `RF_CheckDecodeWatchdog()`/`app_menu.c` 里已有的 `last_bind_tx_ms` 那种 `ms_tick` 环绕安全减法模式(`ms_tick` 是 0~999 循环的 1ms 计时)。
- `MenuApp_onEvent()` 每收到一次按键事件就刷新这个时间戳。
- 需要一个周期性检查点:目前 AppManager 框架没有 `OnTick`/`OnUpdate` 回调,需要确认要不要新增一个,或者简单地在 `main.c` 的 `while(1)` 里,当前台 App 是 `menu_app` 时调用一个新增的 `MenuApp_CheckIdleTimeout()`。超时后调用 `AppManager_SwitchToForeground(&home_app)`(若第 3 条改名重构先落地,否则仍是 `&clock_app`)。
### 3. 按 App 重新梳理菜单导航结构
**现状问题**`app_menu.c` 用一个 `menu_level`(0/1/2)在单个 App 内部模拟出三层菜单,其中 `menu_level=1` 短按 CONFIRM 会切换到独立的 `pair_app``pair_app` 退出时又统一调用 `AppManager_SwitchToForeground(&menu_app)`,而 `MenuApp_OnStart()`/`MenuApp_onClose()` 都会无条件把 `menu_level` 清零——导致不管从哪个子菜单退出,最后都被强制拉回顶层主菜单,退出规则不统一,也不符合"退出到上一级"的预期。时间设置(`Start_Time_Edit()`)则是另一套模式:混在 `clock_app` 内部用 `time_edit_active` 标志位实现,进入时还要先 `AppManager_SwitchToForeground(&clock_app)` 闪一下正常时钟画面再叠加编辑界面,也是一处潜在的画面闪烁点。
**改造方向 —— 完全去掉 `menu_level` 这个概念,统一按 App 组织,父子关系一致、退出手势一致**`clock_app` 已经确认是开机默认显示的 App(`App/main.c:280` 开机调用 `AppManager_StartApp(APP_ID_CLOCK)`),直接更名为 `home_app`;从中新建一个专门的 `set_clock_app` 承载时间设置。
| App | 现状 | 改造后 |
|---|---|---|
| `home_app`(时钟主页) | 即现有 `clock_app`(`App/app_clock.c`),兼管时间编辑(`time_edit_active`) | 整体更名:文件 `App/app_clock.c`→`App/app_home.c`,结构体实例 `clock_app`→`home_app``APP_ID_CLOCK`→`APP_ID_HOME`,函数前缀 `ClockApp_*`→`HomeApp_*`;剥离时间编辑逻辑,只保留待机时钟显示与布撤防 |
| `menu_app`(主菜单) | 内部 `menu_level` 0/1/2 三态,语言开关 + 传感器子菜单 + 摄像头绑定全挤在一个 App 里 | 不再有 `menu_level`,只保留顶层 4 项列表(`menu_select` 0~3语言开关内置处理不跳转);其余 3 项短按 CONFIRM 各自切到独立 App |
| `set_clock_app`(时间设置) | 不是独立 App是 `clock_app` 内的一个模式标志(`time_edit_active`) | 新建独立 App(`App/app_set_clock.c`),从 `app_clock.c` 抽出 `Start_Time_Edit`/`time_edit_*` 相关代码 |
| `pair_app`(传感器配对) | 已经是独立 App但传感器类型选择这一步(门磁/PIR/烟感/气感/水浸)现在留在 `menu_app` 的 `menu_level=1` 里,选完才切到 `pair_app` 开始搜索 | 把类型选择合并成 `pair_app` 的新初始状态(比如 `PAIR_STATE_SELECT_TYPE`,排在现有 `PAIR_STATE_WAIT` 之前)`menu_app` 上"传感器配对"这一项短按 CONFIRM 直接整体切到 `pair_app``pair_app` 不管在哪个状态退出,都统一回到 `menu_app` |
| `ipc_bind_app`(摄像头绑定) | 不是独立 App是 `menu_app` 内 `menu_level=2` 的一段代码(呼吸灯+定时发射绑定帧) | 新建独立 App(`App/app_ipc_bind.c`),仿照 `pair_app` 的结构抽出来 |
**统一导航规则**
- `home_app` 长按 CONFIRM → 进入 `menu_app`。
- `menu_app` 长按 CONFIRM → 回到 `home_app`(现有行为,不变,只是改了名字)。
- `menu_app` 顶层短按 CONFIRM 选中某一项 → 进入对应子 App(`set_clock_app`/`pair_app`/`ipc_bind_app`)。
- 三个子 App 无论是长按 CONFIRM 主动退出、还是完成/超时自动退出(`pair_app` 的成功/失败/取消),都统一 → 回到 `menu_app`(它们共同的父 App),不再有"直接跳回首页"或"退到某个具体层级"的特殊情况。
**需要新增/修改的文件**
- `App/app_clock.c` → 更名为 `App/app_home.c``clock_app`→`home_app`、`APP_ID_CLOCK`→`APP_ID_HOME`、`ClockApp_*`→`HomeApp_*` 函数全部改名;去掉时间编辑相关代码(`time_edit_active`/`time_edit_field`/`time_edit_blink_on`/`Start_Time_Edit`),长按 CONFIRM 改为切到 `menu_app`(不变)。
- 新建 `App/app_set_clock.c`:承接从 `app_clock.c` 抽出来的时间编辑逻辑,退出(短按 CONFIRM 保存 或 长按退出)统一改为 `AppManager_SwitchToForeground(&menu_app)`。
- 新建 `App/app_ipc_bind.c`:把 `app_menu.c` 里 `menu_level==2` 的呼吸灯效果、`EV1527_Transmit` 绑定广播、`last_bind_tx_ms` 计时都搬过去,退出统一改为 `AppManager_SwitchToForeground(&menu_app)`。
- 精简 `App/app_menu.c`:彻底去掉 `menu_level` 变量和三态分支,只保留 `menu_select` 这一个 0~3 的顶层索引;短按 CONFIRM 按 `menu_select` 分支跳转到三个子 App 或原地切语言。
- `App/app_manager.h`(或对应头文件)`APP_ID_CLOCK`→`APP_ID_HOME`,新增 `APP_ID_SET_CLOCK`/`APP_ID_IPC_BIND``extern WristbandApp clock_app` 等声明一并改名/新增。
- 全仓库搜索 `clock_app`/`APP_ID_CLOCK` 的引用点(至少 `App/main.c`、`App/app_menu.c`、`App/app_pair.c`、`App/event.c` 中出现过)统一改名,避免漏改。
- `wristband.uvproj`:文件改名(`app_clock.c`→`app_home.c`)与两个新文件(`app_set_clock.c`、`app_ipc_bind.c`)都要同步维护工程文件列表,不能像之前 `isr_jump.asm` 那样漏加。
**待确认的行为变化**:现在"时间设置"短按 CONFIRM 保存后,是直接回到时钟主页;改造后按统一规则会回到 `menu_app`(主菜单)而不是直接回时钟——这是一个和现状不一样的地方,需要确认是否符合预期,还是希望 `set_clock_app` 作为特例、保存后也直接回 `home_app`。
### 4. 默认语言改英文
- `App/language.c``static Language current_lang = LANG_FR;` 改成 `LANG_EN`。
- 同文件 `Lang_Init()` 里的越界回退分支 `(lang < LANG_COUNT) ? lang : LANG_FR` 改成 `: LANG_EN`。
- 两处都要改,否则开机静态初始值和 Flash 擦除态回退值会不一致。
### 5. 菜单上下循环切换
- 涉及两处 UP/DOWN 边界判断(若第 3 条的 App 化重构先落地,第二处会搬到 `pair_app` 里,但改法一样)
- `App/app_menu.c` 顶层菜单(现第 106-121 行4 项,索引 0~3)
- 传感器类型选择(现在是 `app_menu.c` 的 `menu_level==1`,第 158-172 行;重构后是 `pair_app` 的 `PAIR_STATE_SELECT_TYPE`5 项,索引 0~4)
- 现在是 `if (menu_select > 0) menu_select--;` / `if (menu_select < MAX) menu_select++;`,到边界就不动;改成到边界时绕到另一端,例如 UP 在 `menu_select==0` 时跳到 `MAX`DOWN 在 `menu_select==MAX` 时跳回 `0`。
### 6. P3.6 休眠前唤醒触发方式修复
- 原因:`RF_HandleEdgeInterrupt()` 用软件乒乓单边沿切换模拟双边沿触发,`P3IM0` 会在上升沿/下降沿之间动态翻转;`Enter_Low_Power_Sleep()` 进入休眠前完全没有重新设置 `P3IM0`/`P3IM1`,导致进入 Power Down 那一刻 `P3IM0` 具体停在哪个方向是不确定的,如果停在"等下降沿"而实际先来的是上升沿,就可能唤醒不了或唤醒延迟。
- **已实现方案与最初需求不同,供确认**:根据 WO-04 §2.9 查到的《STC32G 技术手册》15.1.3 节真值表,`PxIM1/PxIM0` 的"高电平中断"和"低电平中断"两种模式**都标注为不支持掉电唤醒**,只有下降沿/上升沿两种边沿模式支持。既然手册明确说电平模式不支持掉电唤醒,改成电平触发大概率解决不了"唤醒不了"的问题,所以最终没有采用"改成电平触发",而是实现了手册认可的方案:`App/system.c` 的 `Enter_Low_Power_Sleep()` 里,`P3WKUE |= 0x40;` 之前,把 `P3IM1`/`P3IM0` 强制固定为上升沿 (`P3IM1&=~0x40; P3IM0|=0x40;`,与 `RF_Init()` 开机初始配置一致),不再依赖乒乓切换留下的随机状态。`Wakeup_Restore()` 不需要改动,因为固定后的值就是开机时的初始值,唤醒后解码状态机自然从已知状态开始。
- 状态:代码已完成,待实机验证休眠后 P3.6 (RF 信号) 能否稳定唤醒;如果实测仍不能唤醒,再考虑真的去试电平触发(即便手册说不支持)。