diff --git a/AC701N.cbp b/AC701N.cbp
index 2473078..28ed40b 100644
--- a/AC701N.cbp
+++ b/AC701N.cbp
@@ -796,10 +796,6 @@
-
-
-
-
@@ -832,14 +828,14 @@
+
+
+
+
-
-
-
-
diff --git a/Doc/温湿度风扇_固件需求规格书.md b/Doc/温湿度风扇_固件需求规格书.md
index 16b44d0..6cabb8a 100644
--- a/Doc/温湿度风扇_固件需求规格书.md
+++ b/Doc/温湿度风扇_固件需求规格书.md
@@ -7,12 +7,18 @@
| 工程 | `温湿度风扇_soc_JieLi-JL7016` |
| 产品码 | ProdtCode=`CBR3`,productCode=`PRCBR3` |
| 协议 | 见宝 SOC 55AA,V1.0.0 |
-| 文档版本 | V1.2.0 |
+| 文档版本 | V2.2.0 |
| 日期 | 2026.08.31 |
| 作者 | cyWu |
| 读者 | WorkBuddy / 固件开发 |
| 参考工程 | `D:\Jianbao\Project\产品\宠物\门夹弹力绳\门夹弹力绳_soc_JieLi-JL7016` |
+> **V2.2.0 变更(2026-08-31):** 顺着 V2.1.0 删 `app_power` 的思路,继续清掉同一类"保留但无调用者"的死代码:删除 `app_function_soft_off()` / `app_function_is_power_on()` / `app_function_power_on_silent()`,`gDevData.power` 恒为 1、写死在 `app_function_init()`(不再需要 `s_func.app_power` 状态字段);`usr_on_key_short()` 里对应的"软关态唤醒"死分支一并删除。
+>
+> **V2.1.0 变更(2026-08-31):** 关机功能确认彻底不存在(关机=拔电),**整模块删除 `app_power.c/.h`**(原 request/finalize 深睡钩子已无任何触发源,纯累赘代码);`usr_jb_main.c`/`app_function.c`/`app_disp.c` 里所有 `app_power_poweroff_pending()` 判断一并清理。同时把配对/OTA/NVM 落盘的"ISR 打标志"统一收进新模块 `app_defer.c/.h`(见 16 章)。
+>
+> **V2.0.0 变更(2026-08-31):** **上电即自动开机**,删除 `app_boot`(原阻塞式 3s/5s/12s 开机判定整模块移除)。按键语义改为运行态按住分级:**短按切档;按住 5s 松手清绑定进配对;按住 12s 到点即进 OTA;取消长按 3s 软关机**(拔电即关)。详见 5.1/5.2/5.3。
+>
> **V1.2.0 变更(2026-08-31):** 风扇档位占空比按用户拍板改为 **1档 50%(5000/10000)、2档 75%(7500/10000)**(原 35%/65%,第 5.3/7/17/18 章同步)。
>
> **V1.1.0 变更(2026-08-31):** IO 分离。CT1642 改走 **PG0(CLK)/PG1(DATA)**,AHT20 独占 **PC4(SCL)/PC5(SDA)**,两路物理隔离,**删除 `bsp_bus` 共线分时机制**(原 8.3 节整体替换为"IO 独立")。同时把 2.2 节显示协议修正为实测 **18bit 帧**。
@@ -25,9 +31,9 @@
1. **先改协议再写业务。** Excel 里 `cond_config1~7` 已是 **7 字节**,当前工程 `jb_protocol.*` / `dp_definition.json` 仍是 6 字节。必须按 Excel 重新生成协议,禁止继续按 6 字节解析。
2. **不要把业务算法写进 `jb_product.c`。** 只把 DP 写入 `gDevData` 并转调 `app_*`。组合根是 `usr_jb_main.c`(照门夹)。
-3. **不要抄门夹的电池 / USB 直通开机 / 低电拒绝开机。** 本机 USB 常电、无电池。插上电 **不会** 自动工作,必须长按 3s。
+3. **不要抄门夹的电池 / 低电拒绝开机。** 本机 USB 常电、无电池。**上电即开机**(V2.0.0 起,不再长按 3s 才开机);拔电即关,无软关机深睡。
4. **不要把调试 UART 放在 PC5。** 调试 UART TX = **PG6**(`IO_PORTG_06`)。PC5 是 AHT20 SDA(独立使用)。
-5. **DP0 `power` 预留,不当开关。** 真开关只有按键 3s 软开关机。运行中上报 `power=1`,写忽略。
+5. **DP0 `power` 预留,不当开关。** 上电恒为开机态(`power=1`),运行中上报 `power=1`,写忽略;停机靠短按切到 0 档或拔电。
6. **CT1642 与 AHT20 已 IO 分离(V1.1.0)。** CT1642 走 PG0(CLK)/PG1(DATA),AHT20 走 PC4(SCL)/PC5(SDA),两路独立、无总线竞争,**不存在分时/总线锁**(见 8.3)。
7. 每个模块必须能用 `board_pin.h` 的 `BOARD_xxx_ENABLE` **单独关掉** 仍能编过、能配网上报(关掉的模块返回空操作)。
8. 日志全部 **英文**。中文只出现在注释和本文。
@@ -95,45 +101,48 @@ apps/earphone/app_main.c
└─ usr_rtc_init()
└─ usr_jb_init() ← 唯一组合根
bsp_hw_init()
- app_led_init() ← 必须在开机等待之前
- app_boot_wait_key() ← 3s/5s/12s,失败则 soft poweroff 不返回
+ app_led_init() ← 灯效事件表最先装载
jbInit() + userInit()
- 若 usr_goto_updata → OTA
- app_nvm_load() ← 恢复 VM
- app_fan_init()
- app_disp_init()
- app_th_init()
- app_mode_init()
- app_cond_init()
- app_timer_init()
+ (上电即开机:无按键等待,不再有 app_boot)
+ app_fan_init() / app_disp_init()
+ usr_timer_add(app_disp_scan, 4ms, priority=1) ← CT1642 扫描,常驻
+ app_th_init() / app_mode_init() / app_arbiter_init()
+ app_cond_init() / app_timer_init()
+ app_nvm_init() + app_nvm_load() ← 恢复 VM
app_function_init()
user_key_init() + lock_if_held
- usr_timer_rearm(20ms)
+ usr_timer_add(usr_timer_tick, 20ms, priority=1) ← 常驻,永不重挂
```
-**20ms 业务节拍**(`usr_timer_tick`,与门夹相同,禁止再开 1ms ISR):
+**20ms 业务节拍**(`usr_timer_tick`,硬件定时器中断上下文,禁止再开 1ms ISR):
-1. `jbTimerIrq()` × dt
-2. `user_key_scan` + `user_key_handle`
-3. `app_th_poll`(内部 2s 才真正采;IO 分离后采集与显示互不干扰)
-4. `app_cond_run` / `app_timer_run` / `app_mode_run`(仲裁见第 12 章)
-5. `app_fan_run`
-6. `app_disp_run`(闪烁、刷新)
-7. `app_led_run`
-8. `app_function_poll`(同步 DP)
-9. 重装定时器(按键按住 / 风扇转 / 关机流程中 → 不可睡)
+1. `jbTimerIrq()` × dt
+2. `user_key_scan` + `user_key_handle`
+3. `app_th_poll`(内部 2s 才真正采;IO 分离后采集与显示互不干扰)
+4. `app_mode_run`(通风/强力状态机;条件/定时引擎在 500ms 任务上下文,见下)
+5. `app_disp_run`(闪烁、刷新)
+6. `app_function_poll`(同步 DP)
+7. `app_nvm_tick`(变化检测 + 500ms 防抖计时,不写 flash)
+8. `app_led_run`
-`usr_timer_loop()`(BLE 500ms)只做:`userHandle()` 填温湿度 DP + `jbProtocolHandle()`。
+**priority 固定 1、只注册一次,不动态重挂**:本产品 Type-C 长供电,不需要低功耗;
+且 `app_disp_scan` 本身就以 priority=1 常驻,系统本来就永远无法进入低功耗——
+按业务状态动态切换 `usr_timer_tick` 自己的 priority 没有任何实际效果,已删除
+(原 `usr_timer_pick_mode`/`usr_timer_rearm`,规格书 V2.3.0 起废弃)。
+
+`usr_timer_loop()`(BLE 500ms 任务上下文):`userHandle()` 填温湿度 DP、
+`jbProtocolHandle()`、`app_cond_run()`/`app_timer_run()`(条件/定时引擎,
+仲裁见第 12 章)、`app_function_loop()`(VM 落盘 + 配对/OTA 落地)。
可直接复用门夹源码(拷贝后改引脚/事件表,不要改框架语义):
| 复用 | 路径 | 改什么 |
|---|---|---|
-| 按键 FSM | `key.c` / `key.h` / `key_manager.c` | 短按切档、长按关机 |
+| 按键 FSM | `key.c` / `key.h` / `key_manager.c` | 短按切档;**按住分级 5s 配对 / 12s OTA**(V2.0.0,无长按关机) |
| LED 事件库 | `led.c` / `led.h` / `led_manager.c` | 单灯 PC2,删红绿双通道 |
-| 开机判定 | `app_boot.c` | **删除 USB 直通、低电拒绝** |
+| ~~开机判定~~ | ~~`app_boot.c`~~ | **V2.0.0 已整模块删除**(上电即开机,判定下沉到 key_manager) |
| 配对会话 | `app_pair.c` | 120s 超时,灯效改单灯 |
-| 软关机 | `app_power.c` 精简 | 无电池、无充电;只保留 request/finalize |
+| ~~软关机~~ | ~~`app_power.c`~~ | **V2.1.0 已整模块删除**:无电池、无充电,关机=拔电,无软件关机路径 |
| RTC | `usr_rtc.c` | 原样 |
| 协议骨架 | `usr_jb_proto` | 重生 7 字节条件 |
@@ -170,38 +179,57 @@ P0 建议先 `MODE/COND/TIMER/NVM=0`,台上只验按键、灯、PWM、数码
## 5. 电源与按键
-### 5.1 关机 / 开机
+### 5.1 上电 / 断电(V2.0.0 改:上电即开机)
-- 关机 = `power_set_soft_poweroff()`:风扇 PWM=0 并关 MCPWM,数码管锁存灭(全段灭),LED 灭,BLE 停。
-- **插 USB / 冷启动不会自动进入工作态。** 必须按住 3s。
-- DP0 不参与。不要用 APP 开关机。
-- 无电池逻辑。
+- **插 USB / 冷启动直接进入工作态**,无需按键,无 3s 等待。上电流程:
+ `bsp_hw_init()` → `app_led_init()` → `jbInit()/userInit()` → 各 `app_*_init()`
+ (`app_function_init()` 里写死 `gDevData.power=1`)→ `app_nvm_load()` →
+ `user_key_init()` + `user_key_lock_if_held()` → 起 20ms 业务节拍。
+- **关机 = 拔电。** 已取消按键长按 3s 软关机(原 3s 与新的 5s/12s 时间轴直接冲突)。
+ `app_power.c` 的软关机能力(request/finalize + 关机前落盘)**V2.1.0 已整模块删除**
+ (无任何触发源,纯累赘代码);**V2.2.0** 进一步删除 `app_function_soft_off` /
+ `app_function_is_power_on` / `app_function_power_on_silent`(同样无调用者的
+ 死代码,`power` 恒 1 不再需要状态字段)。
+- 上电若键仍按着:`user_key_lock_if_held()` 只锁**短按**(防一上电就切档),
+ 不锁长按分级 —— 所以"插电时就按住 5s"照样进配对。
+- DP0 不参与开关机,运行中恒上报 `power=1`(写死,非每 tick 同步),写忽略。
+- 无电池、无充电逻辑。
-### 5.2 关机态按住(`app_boot.c`,用 `timer_get_ms()` 轮询,与运行态 FSM 分开)
+### 5.2 按住分级(运行态,`key_manager.c` 直接复用 `key.c` 长按/连按/释放事件)
-| 松开时已按住 | 行为 |
+上电后**任意时刻**按住 PB1 都按同一套时序判定(不再有"关机态/开机态"两套):
+
+| 按住时长 | 行为 |
|---|---|
-| < 3000 ms | 继续软关机 |
-| ≥ 3000 且 < 5000 ms | 正常开机,加载 VM,恢复上次 mode/gear 并从周期起点开跑 |
-| ≥ 5000 且 < 12000 ms | 开机 + `usr_clear_pair_info` + 进配对(换手机用这条) |
-| ≥ 12000 ms | 进 OTA(门夹 `usr_app_ota_init` 路径) |
+| < 5000 ms 松开 | 短按:切档(见 5.3) |
+| 满 5000 ms(仍按住) | **只**切配对提示灯(100ms 快闪),**不清绑定** |
+| 满 5s 后、12s 前松开 | 提交配对:`usr_goto_pair_mode()`(断连+清绑定+**重发绑定广播**)+ `app_pair_start()` 起 120s 会话(换手机用这条) |
+| 满 12000 ms(仍按住,到点即触发) | 进 OTA(`usr_app_ota_init`),不等松手;同时撤销本次配对 |
+
+设计理由:配对刻意不在 5s 到点提交 —— 用户可能想继续按到 12s 进 OTA,
+提前清绑定会误伤已配对的手机。
+
+⚠ **上下文约束**:这几个回调跑在 20ms 硬件定时器中断上下文,只准置标志 +
+切灯效(纯 RAM)。清绑定(写 VM)与 `usr_app_ota_init`(BLE 广播切换)
+一律延后到 `app_function_loop()`(500ms 任务上下文)执行 —— 违反会栈溢出软复位
+(2026-08-31 事故定论)。因此配对/OTA 的实际生效有最多 500ms 延迟。
按住过程 LED 提示(单灯):
-- ≥3s:常亮(将开机)
-- ≥5s:100ms 闪(将配对)
-- ≥12s:200ms 闪(将 OTA)
-
-开机后若键仍按着:`user_key_lock_if_held()`,直到松开才接受短按/长按。
+- ≥5s:100ms 闪(将配网,松手生效)
+- ≥12s:200ms 闪(已进入 OTA)
### 5.3 开机态按键
| 事件 | 行为 |
|---|---|
-| 短按 | 风扇档位循环:1(50%) → 2(75%) → 3(100%) → 0(关风扇) → 1 … **立刻退出通风/强力**,改为手动档。设备仍开机,数码管继续刷 |
-| 长按 3s | 请求软关机。VM **不清** |
+| 短按(<1s 松开) | 风扇档位循环:1(50%) → 2(75%) → 3(100%) → 0(关风扇) → 1 … **立刻退出通风/强力**,改为手动档。设备仍开机,数码管继续刷 |
+| 按住 5s 后松开 | 清绑定 + 进配对(见 5.2) |
+| 按住 12s | 进 OTA(见 5.2) |
+| ~~长按 3s 软关机~~ | **V2.0.0 已取消**,拔电即关 |
-配对进行中:长按关机可忽略(与门夹一致,避免配到一半睡死)。短按仍可切档。
+OTA 进行中:忽略配对/短按之外的重复分级(`app_led_is_ota()` 为 1 时不再重入)。
+配对进行中:短按仍可切档。
---
@@ -211,6 +239,7 @@ P0 建议先 `MODE/COND/TIMER/NVM=0`,台上只验按键、灯、PWM、数码
|---|---|---|
| 软关机 / 未开机 | 灭 | — |
| 正常开机 | 常亮 | 低 |
+| 按住满 5s(待配网,未松手) | 100ms 亮 / 100ms 灭 | 中 |
| 配对中 | 100ms 亮 / 100ms 灭,直到成功或 120s 失败 | 中 |
| 配对成功 | 常亮(可保持 3s 再回到常亮,允许直接常亮) | 中 |
| 配对失败 | 200ms 闪 3s,然后常亮 | 中 |
@@ -232,9 +261,10 @@ LED **不** 表示风扇档位,**不** 表示温湿度告警(告警走数码
| 2 | 75% | 7500/10000(用户拍板 2026-08-31) |
| 3 | 100% | 10000/10000 |
-- 模块接口建议:`app_fan_init` / `app_fan_set_gear(uint8_t)` / `app_fan_get_gear` / `app_fan_run`。
+- 模块接口:`app_fan_init` / `app_fan_set_gear(uint8_t)` / `app_fan_get_gear`。
- DP2 必须等于 **当前真实档位**(通风停 5 分钟那段报 0)。
-- 换档无需软爬坡(P0 不做)。若 100% 上电冲击过大,允许后续加 200ms 爬升,不作为 P0 验收。
+- 换档无需软爬坡(P0 不做,也不做占位接口)。若 100% 上电冲击过大,后续要加
+ 200ms 爬升时再新增 `app_fan_run` 接口即可,不作为 P0 验收。
---
@@ -441,11 +471,11 @@ usr_rtc_get_time() 失败 → 全部定时不执行(等同 time_en=0)
- 条件作废标志、模式已运行秒数、AHT20 原始采样
- BLE 三元组(走现有 `CFG_USER_PAIR_INFO`,出厂重置 **不清**)
-开机 3s 成功后:加载上述字段,**按上次 mode/gear 从周期起点继续工作**。
+上电(V2.0.0 起无需按键)后:加载上述字段,**按上次 mode/gear 从周期起点继续工作**。
首次上电(VM 无效):mode=0,gear=0,条件/定时全 0,`cond_en=time_en=0`。
-写 VM 时机:APP 改配置后、单次定时自毁使能时、软关机前、档位/模式稳定变化时(可做 500ms 防抖,避免短按循环打爆 Flash)。
+写 VM 时机:APP 改配置后、单次定时自毁使能时、档位/模式稳定变化时(可做 500ms 防抖,避免短按循环打爆 Flash)。
---
@@ -454,7 +484,7 @@ usr_rtc_get_time() 失败 → 全部定时不执行(等同 time_en=0)
- **清全部业务 VM**(第 13 章列表)。
- **保留 BLE 配对,不断开、不软复位。**
- RAM 中 mode=0,gear=0,风扇停,条件/定时停止。
-- 换机:用户关机后 **长按 5s** 清配对进绑定广播。
+- 换机:运行中 **按住 5s 后松手** 清配对进绑定广播(见 5.2,无需关机)。
---
@@ -478,7 +508,7 @@ usr_rtc_get_time() 失败 → 全部定时不执行(等同 time_en=0)
| 25 factory_reset | 第 14 章 |
| 26~28 | 存 RAM/VM 可不实现动作 |
-OTA:12s 开机走门夹 `usr_app_ota_init`。`jbOnOtaNotify` 等协议 OTA **P2 可继续 stub**,注释写明未实现,禁止假装升级成功后擦用户区。
+OTA:运行态按住 12s 走门夹 `usr_app_ota_init`(在 `app_function_loop` 任务上下文调用,灯效 200ms 闪)。`jbOnOtaNotify` 等协议 OTA **P2 可继续 stub**,注释写明未实现,禁止假装升级成功后擦用户区。
配对超时 120s,失败保持开机。
@@ -495,11 +525,10 @@ apps/usr_periph/
usr_rtc.c/.h # 已有则保留
apps/usr_app/
- key.c/.h, key_manager.c/.h # 拷贝门夹
+ key.c/.h, key_manager.c/.h # 拷贝门夹;短按切档 + 按住 5s/12s 分级
led.c/.h, led_manager.c/.h # 改单灯
- app_boot.c/.h # 拷贝后删 USB/电池
- app_led.c/.h
- app_power.c/.h # 精简
+ app_led.c/.h # 含长按 5s "将配网"提示灯
+ app_defer.c/.h # ISR→任务上下文延后执行标志位(NVM/配对/OTA 统一收口)
app_pair.c/.h
app_fan.c/.h
app_disp.c/.h # 数值、闪烁、--
@@ -523,13 +552,17 @@ apps/usr_app/
**目标:** 两路 IO(CT1642 显示 / AHT20 采集)、PWM、按键、灯可独立开关验证。
- [ ] `board_pin.h` + UART TX=PG6,打印正常,且不影响 PC5
-- [ ] 关机态 3s 开机、<3s 仍睡;开机态 3s 关机
-- [ ] 短按四态循环,示波器/听感确认 50/75/100/0%
+- [ ] 插电即开机(不按键,直接进工作态、数码管亮、LED 常亮)
+- [ ] 长按 3s **不再**关机(已取消软关机按键入口)
+- [ ] 短按(<5s 松手)四态循环,示波器/听感确认 50/75/100/0%
+- [ ] 按住满 5s → 100ms 快闪;此时松手 → 清绑定进配对(APP 可搜到)
+- [ ] 按住满 5s 不松手继续到 12s → 不进配对,直接进 OTA(200ms 闪)
- [ ] LED 开机常亮、模拟配对闪、模拟 OTA 闪(可用宏强制)
- [ ] CT1642 两管能稳定显示写死的 `25` 和 `60`,无鬼影
- [ ] AHT20 串口打印 ℃/%,CRC 正确;采数时数码管不闪、不乱码(IO 独立,无需锁存)
- [ ] `BOARD_TH_ENABLE=0` 时显示仍可刷;`BOARD_DISP_ENABLE=0` 时传感器仍可打印
- [ ] 12s 能进 OTA 广播(可先只打日志 + LED)
+- [ ] 长按进配对/OTA 后不复位(验证动作确实走的任务上下文)
**P0 关闭:** `BOARD_MODE/COND/TIMER/NVM_ENABLE=0`
@@ -539,7 +572,7 @@ apps/usr_app/
- [ ] 通风 25/5、强力 15min 后停;短按立即退模式
- [ ] 超限闪(可用调试宏灌假温度)、失败 `--`
- [ ] DP0 写 0 风扇不变、设备不关
-- [ ] VM:改档/改模式/写下条件定时后断电,3s 开机从周期头恢复 mode
+- [ ] VM:改档/改模式/写下条件定时后断电,重新上电从周期头恢复 mode
- [ ] 定时引擎可只存不跑(`BOARD_TIMER_ENABLE=0` 仍能保存 DP)
### P2 — 自动化
diff --git a/Makefile b/Makefile
index 2919771..ece0b2a 100644
--- a/Makefile
+++ b/Makefile
@@ -394,9 +394,7 @@ c_SRC_FILES := \
apps/usr_app/key.c \
apps/usr_app/key_manager.c \
apps/usr_app/app_mode.c \
- apps/usr_app/app_power.c \
apps/usr_app/app_pair.c \
- apps/usr_app/app_boot.c \
apps/usr_app/app_function.c \
cpu/br28/adc_api.c \
cpu/br28/app_audio.c \
@@ -454,7 +452,8 @@ c_SRC_FILES := \
apps/usr_app/app_cond.c \
apps/usr_app/app_timer_task.c \
apps/usr_app/app_arbiter.c \
- apps/usr_app/app_nvm.c
+ apps/usr_app/app_nvm.c \
+ apps/usr_app/app_defer.c
S_SRC_FILES := \
apps/earphone/sdk_version.z.S \
diff --git a/apps/earphone/app_main.c b/apps/earphone/app_main.c
index cc77ba4..2839b89 100644
--- a/apps/earphone/app_main.c
+++ b/apps/earphone/app_main.c
@@ -326,7 +326,7 @@ void app_main()
usr_var.usr_power_charge_flag=2;
}
- /* 配对只由 app_boot 长按 5s 置 usr_goto_pair;禁止上电清三元组(规格书 5.2 / 14 章) */
+ /* 配对只由运行态长按 5s(key_manager 分级)置 usr_goto_pair;禁止上电清三元组(规格书 5.2 / 14 章) */
ui_manage_init();
ui_update_status(STATUS_POWERON);
diff --git a/apps/usr_app/app_arbiter.c b/apps/usr_app/app_arbiter.c
index a18f4a4..0a7fa70 100644
--- a/apps/usr_app/app_arbiter.c
+++ b/apps/usr_app/app_arbiter.c
@@ -3,9 +3,7 @@
* @brief CBR3 模式仲裁器实现(规格书第 12 章:手动 > 定时 > 条件)
* @author cyWu <1917507415@qq.com>
* @date 2026.08.31
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.31, cyWu, 首次发布(P2)
+ * @version V1.0.0,首次发布(P2)
*
* @note 本模块是唯一模式占用源,运行在 usr_timer_loop(任务上下文)。
* 规则落地(规格书 12 章):
diff --git a/apps/usr_app/app_arbiter.h b/apps/usr_app/app_arbiter.h
index fa248ca..0cff6b5 100644
--- a/apps/usr_app/app_arbiter.h
+++ b/apps/usr_app/app_arbiter.h
@@ -3,9 +3,7 @@
* @brief CBR3 模式仲裁器(规格书第 12 章):手动 > 定时 > 条件
* @author cyWu <1917507415@qq.com>
* @date 2026.08.31
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.31, cyWu, 首次发布(P2)
+ * @version V1.0.0,首次发布(P2)
*
* @note 把"谁在驱动模式"收成一处,避免 mode/cond/timer 互调成环。
* 唯一状态源是 owner(NONE/COND/TIMER)+ 条件快照;
diff --git a/apps/usr_app/app_boot.c b/apps/usr_app/app_boot.c
deleted file mode 100644
index 41826bc..0000000
--- a/apps/usr_app/app_boot.c
+++ /dev/null
@@ -1,132 +0,0 @@
-/******************************************************************************
- * @file app_boot.c
- * @brief CBR3 上电按键判定实现(3s 开机 / 5s 配对 / 12s OTA)
- * @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹:删 USB 直通、删低电拒绝)
- ******************************************************************************/
-
-#include "app_boot.h"
-#include "app_pair.h"
-#include "bsp_hw.h"
-#include "board_pin.h"
-#include "app_main.h"
-#include "usr_le_api.h"
-#include "led_manager.h"
-#include "asm/power/power_api.h"
-#include "asm/power/power_reset.h"
-
-#define LOG_TAG_CONST APP
-#define LOG_TAG "[APP_BOOT]"
-#define LOG_INFO_ENABLE
-#include "debug.h"
-
-extern void clr_wdt(void);
-extern u32 timer_get_ms(void);
-
-/**
- * @brief 忙等若干毫秒,并喂狗
- * @param ms 等待时长
- * @return 无
- */
-static void boot_delay_ms(uint32_t ms)
-{
- uint32_t t0 = timer_get_ms();
-
- while ((timer_get_ms() - t0) < ms) {
- clr_wdt();
- }
-}
-
-/**
- * @brief BLE 起来前进深睡(业务定时器尚未启动),原地喂狗不返回
- * @note power_set_soft_poweroff() 只是向 SDK 底层发起软关机请求(置个标志位),
- * 不是同步断电——真正切电源是底层电源管理在之后某个时机才执行的。
- * 此时 BLE 栈、业务定时器都还没起来,如果直接 return,代码会接着把
- * usr_jb_init 剩下的初始化跑完,等于白判定。所以必须用 while(1) 卡住:
- * 一边喂狗防止看门狗打断,一边等底层真正把电断掉。
- * @return 无(不会返回)
- */
-static void boot_sleep(void)
-{
- log_info("boot sleep\n");
- bsp_hw_enter_sleep_io();
- power_set_soft_poweroff();
- while (1) {
- clr_wdt();
- }
-}
-
-/**
- * @brief 等待并判定开机按键
- * @note 配对(5s)不在到点时立即提交:继续按住观察是否会按到 12s 触发 OTA,
- * 真正的配对提交放在松手那一刻做。按满 5s 灯先切到配对快闪提示。
- * 3s 常亮 / 5s 快闪直接复用 led_manager 的 LED_EVENT_STANDBY /
- * LED_EVENT_PAIRING 事件。LED 事件库由调用方(usr_jb_init)在本函数
- * 之前完成初始化,这里直接假定可用。
- * @return 无(深睡分支不返回)
- */
-void app_boot_wait_key(void)
-{
- uint32_t t0;
- uint32_t elapsed;
- uint8_t pair_armed = 0;
-
- usr_var.usr_goto_pair = 0;
-
- /* ⚠ 无 USB 直通:插电/冷启动一律走按键判定(规格书 5.1)。 */
-
- if (!bsp_key_is_pressed()) {
- log_info("boot no key, sleep\n");
- boot_sleep();
- }
-
- t0 = timer_get_ms();
- log_info("boot key hold, wait 3s on / 5s pair / 12s ota\n");
-
- while (bsp_key_is_pressed()) {
- elapsed = timer_get_ms() - t0;
- clr_wdt();
-
- /* 12s 优先于配对:按满 12s 直接进 OTA,不再等松手 */
- if (elapsed >= BOARD_KEY_OTA_MS) {
- log_info("boot hold %ums, power on + ota\n", (unsigned)elapsed);
- usr_app_ota_init();
- return;
- }
-
- /* 5s 只武装配对并切到快闪,真正提交放到松手那一刻 */
- if (!pair_armed && (elapsed >= BOARD_KEY_PAIR_MS)) {
- pair_armed = 1;
- LED_EventDelete(LED_EVENT_STANDBY);
- LED_EventAdd(LED_EVENT_PAIRING);
- log_info("boot hold %ums, pair hint blink (release now for pair, "
- "keep holding to %ums for ota)\n",
- (unsigned)elapsed, (unsigned)BOARD_KEY_OTA_MS);
- } else if (!pair_armed && !LED_IsEventExist(LED_EVENT_STANDBY)
- && (elapsed >= BOARD_KEY_POWER_MS)) {
- LED_EventAdd(LED_EVENT_STANDBY);
- log_info("boot hold 3s, will power on\n");
- }
-
- user_led_handle(); /* 按事件表当前状态刷新一次灯 */
- }
-
- elapsed = timer_get_ms() - t0;
- if (elapsed < BOARD_KEY_POWER_MS) {
- log_info("boot hold %ums < 3s, sleep\n", (unsigned)elapsed);
- boot_sleep();
- }
-
- /* 松手时已过 5s(且没按到 12s):提交配对;否则只是正常开机 */
- if (pair_armed) {
- usr_var.usr_goto_pair = 1;
- usr_clear_pair_info_noreset();
- app_pair_start();
- log_info("boot hold %ums, power on + pair\n", (unsigned)elapsed);
- } else {
- log_info("boot hold %ums, power on\n", (unsigned)elapsed);
- }
-}
diff --git a/apps/usr_app/app_boot.h b/apps/usr_app/app_boot.h
deleted file mode 100644
index 5dc180e..0000000
--- a/apps/usr_app/app_boot.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/******************************************************************************
- * @file app_boot.h
- * @brief CBR3 上电按键判定:3s 开机,5s 配对,12s OTA,不足 3s 松手再睡
- * @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹:删 USB 直通开机、删低电拒绝开机)
- *
- * @note 必须在 BLE 协议栈起来、业务定时器启动之前调用。杰理 jiffies 此时
- * 往往 10ms 才跳一次,所以内部一律用 timer_get_ms 直接量时间。
- * ⚠ 本产品 USB 常电、无电池:插电/冷启动【不会】自动进入工作态,
- * 必须按住 3s(规格书 5.1/5.2)。门夹的 USB 直通开机逻辑禁止移植。
- ******************************************************************************/
-
-#ifndef __APP_BOOT_H__
-#define __APP_BOOT_H__
-
-/**
- * @brief 等待并判定开机按键:不足 3s 松手、或全程未按键,直接原地深睡(不返回);
- * 满 3s 视为正常开机;按满 5s 绿灯改快闪提示"松手即配网",真正清绑定 /
- * 开绑定广播仍等松手(内部调用 app_pair_start);持续按满 12s 直接开机
- * 并进入 OTA(调用 usr_app_ota_init)。
- * @return 无(深睡分支通过 while(1) 喂狗不返回;其余情况正常返回)
- */
-void app_boot_wait_key(void);
-
-#endif /* __APP_BOOT_H__ */
diff --git a/apps/usr_app/app_cond.c b/apps/usr_app/app_cond.c
index 788e9d4..99d2fc1 100644
--- a/apps/usr_app/app_cond.c
+++ b/apps/usr_app/app_cond.c
@@ -2,14 +2,10 @@
* @file app_cond.c
* @brief CBR3 条件引擎(P2):7 组 7 字节条件,比较成立满时长触发模式
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V2.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(P0 stub)
- * - V1.1.0, 2026.08.31, cyWu, P1:set_en/set_config 存 VM(引擎仍关)
- * - V2.0.0, 2026.08.31, cyWu, P2 实现完整条件引擎(规格书第 10 章):
- * 7 字节解析/AND 比较/时长累计(采样成功才累计)/触发/保持/
- * 条件强力循环/后覆盖不回栈/手动作废(RAM)/快照恢复/闪烁喂真值
+ * @date 2026.08.31
+ * @version V2.0.0,完整条件引擎(规格书第 10 章):7 字节解析/AND 比较/时长
+ * 累计(采样成功才累计)/触发/保持/条件强力循环/后覆盖不回栈/手动
+ * 作废(RAM)/快照恢复/闪烁喂真值
*
* @note 运行上下文:app_cond_run 由 usr_timer_loop(500ms,任务上下文)调用
* (规格书 8.4 时长按采样周期 2s 计,500ms 粒度足够;且触发时可能
diff --git a/apps/usr_app/app_cond.h b/apps/usr_app/app_cond.h
index f2a0f69..10b5fea 100644
--- a/apps/usr_app/app_cond.h
+++ b/apps/usr_app/app_cond.h
@@ -3,9 +3,7 @@
* @brief CBR3 条件引擎(P2):7 组 7 字节条件,比较成立满时长触发模式
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(P0 仅接口与 stub)
+ * @version V1.0.0,首次发布(P0 仅接口与 stub)
*
* @note P2 实现(BOARD_COND_ENABLE=1)。字节布局见规格书 10.2:
* Byte0=使能 Byte1-2=温度阈值(int16 大端 ×10) Byte3=湿度阈值
diff --git a/apps/usr_app/app_defer.c b/apps/usr_app/app_defer.c
new file mode 100644
index 0000000..fd04b09
--- /dev/null
+++ b/apps/usr_app/app_defer.c
@@ -0,0 +1,57 @@
+/******************************************************************************
+ * @file app_defer.c
+ * @brief ISR→任务上下文的延后执行标志位实现
+ * @author cyWu <1917507415@qq.com>
+ * @date 2026.08.31
+ * @version V1.0.0,首次发布
+ ******************************************************************************/
+
+#include "system/includes.h"
+#include "app_defer.h"
+
+/** 位掩码,volatile:request() 在中断上下文写,take()/is_pending()/cancel()
+ * 在任务上下文读写,两侧都用 local_irq_disable/enable 包一下读改写,
+ * 避免"任务侧正在清 A 位时中断侧设 B 位"这种读改写竞态把 B 位冲丢。 */
+static volatile uint8_t s_flags;
+
+void app_defer_request(AppDeferBit_t bit)
+{
+ if (bit >= APP_DEFER_MAX) {
+ return;
+ }
+ local_irq_disable();
+ s_flags |= (uint8_t)(1u << bit);
+ local_irq_enable();
+}
+
+uint8_t app_defer_take(AppDeferBit_t bit)
+{
+ uint8_t was_set;
+
+ if (bit >= APP_DEFER_MAX) {
+ return 0;
+ }
+ local_irq_disable();
+ was_set = (s_flags & (uint8_t)(1u << bit)) ? 1 : 0;
+ s_flags &= (uint8_t)~(1u << bit);
+ local_irq_enable();
+ return was_set;
+}
+
+uint8_t app_defer_is_pending(AppDeferBit_t bit)
+{
+ if (bit >= APP_DEFER_MAX) {
+ return 0;
+ }
+ return (s_flags & (uint8_t)(1u << bit)) ? 1 : 0;
+}
+
+void app_defer_cancel(AppDeferBit_t bit)
+{
+ if (bit >= APP_DEFER_MAX) {
+ return;
+ }
+ local_irq_disable();
+ s_flags &= (uint8_t)~(1u << bit);
+ local_irq_enable();
+}
diff --git a/apps/usr_app/app_defer.h b/apps/usr_app/app_defer.h
new file mode 100644
index 0000000..8094035
--- /dev/null
+++ b/apps/usr_app/app_defer.h
@@ -0,0 +1,59 @@
+/******************************************************************************
+ * @file app_defer.h
+ * @brief ISR→任务上下文的延后执行标志位(统一收口 2026-08-31 复位事故教训)
+ * @author cyWu <1917507415@qq.com>
+ * @date 2026.08.31
+ * @version V1.0.0,首次发布
+ *
+ * @note ⚠ 硬约束:flash 擦写(syscfg_write)、BLE 广播切换、关机收口等"重活"
+ * 禁止在 usr_timer_add 的中断上下文回调(本产品 20ms usr_timer_tick /
+ * 按键长按回调)里直接执行,只能在 usr_timer_loop(BLE 线程,任务
+ * 上下文,栈充足)里执行。之前每个模块各自发明一个 pending 标志
+ * (off_pending/pair_pending/ota_pending/nvm 的 s_need_save),
+ * 容易被后来的功能遗漏这条红线——本模块把"打标志"这一步收成统一
+ * 接口:ISR 只准调 app_defer_request(),真正执行永远在
+ * app_function_loop() 里用 app_defer_take() 消费。
+ ******************************************************************************/
+
+#ifndef __APP_DEFER_H__
+#define __APP_DEFER_H__
+
+#include
+
+/** 延后动作位,新增一种"需要写flash/切广播/关机"的动作就加一项 */
+typedef enum {
+ APP_DEFER_NVM_SAVE = 0, /**< VM 防抖到点,需要落盘 */
+ APP_DEFER_PAIR, /**< 长按 5s 松手,需要清绑定进配对 */
+ APP_DEFER_OTA, /**< 长按 12s 到点,需要进 OTA */
+ APP_DEFER_MAX
+} AppDeferBit_t;
+
+/**
+ * @brief 中断上下文调用:只置位,不做任何实际动作
+ * @param bit 要延后的动作
+ * @return 无
+ */
+void app_defer_request(AppDeferBit_t bit);
+
+/**
+ * @brief 任务上下文调用:查询并清除标志(取出即消费,最多处理一次)
+ * @param bit 要消费的动作
+ * @return 1=之前挂起过(现在可以执行真正动作),0=没挂起
+ */
+uint8_t app_defer_take(AppDeferBit_t bit);
+
+/**
+ * @brief 任务上下文调用:只查询是否挂起,不清除(多个动作互斥判断用)
+ * @param bit 要查询的动作
+ * @return 1=挂起中,0=未挂起
+ */
+uint8_t app_defer_is_pending(AppDeferBit_t bit);
+
+/**
+ * @brief 任务上下文调用:撤销挂起(如 OTA 触发时要撤销配对)
+ * @param bit 要撤销的动作
+ * @return 无
+ */
+void app_defer_cancel(AppDeferBit_t bit);
+
+#endif /* __APP_DEFER_H__ */
diff --git a/apps/usr_app/app_disp.c b/apps/usr_app/app_disp.c
index aa12dcd..aff7d8c 100644
--- a/apps/usr_app/app_disp.c
+++ b/apps/usr_app/app_disp.c
@@ -2,14 +2,10 @@
* @file app_disp.c
* @brief CBR3 数码管显示实现
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.1.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布
- * - V1.0.1, 2026.08.29, cyWu, 改动态扫描:20ms 节拍只算数据写缓冲,
- * 另起 BOARD_DISP_SCAN_MS 快节拍定时器轮询点亮 1 位,解决闪烁
- * - V1.1.0, 2026.08.31, cyWu, P1 加告警闪烁:温度/湿度管独立 500ms 亮灭,
- * BOARD_DISP_ALARM_TEST 宏可强制模拟超限;fault 时 "--" 优先
+ * @date 2026.08.31
+ * @version V1.1.0,动态扫描(20ms 算段码写缓冲 + BOARD_DISP_SCAN_MS 快节拍
+ * 轮询点亮 1 位)+ 告警闪烁(温度/湿度管独立 500ms 亮灭,
+ * BOARD_DISP_ALARM_TEST 宏可强制模拟超限;fault 时 "--" 优先)
*
* @note ⚠ CT1642 内部只有单个 12 位锁存器(4 位选 + 8 段码),无按位显示 RAM、
* 不自动扫描,一次只点亮当前锁存的那 1 位。要让 4 位都亮且不闪,必须由 MCU
@@ -22,7 +18,6 @@
#include "app_disp.h"
#include "app_th.h"
-#include "app_power.h"
#include "bsp_ct1642.h"
#include "board_pin.h"
@@ -212,7 +207,6 @@ void app_disp_run(uint16_t dt)
* @note 必须短、不可阻塞。每次只发 1 帧,靠高频轮询让 4 位视觉同亮不闪。
* IO 分离后 CT1642 独占 PG0/PG1,与 AHT20 的 PC4/PC5 无总线竞争,
* 扫描回调无需锁、无跳过。
- * 软关机流程:4 位全灭(com=0x00 → 所有 Cx 高,位选全关)。
* @return 无
*/
void app_disp_scan(void)
@@ -224,12 +218,6 @@ void app_disp_scan(void)
return;
}
- /* 软关机流程:数码管锁存灭(规格书 5.1:风扇 0、数码管锁存灭、LED 灭) */
- if (app_power_poweroff_pending()) {
- bsp_ct1642_send_frame(0x00, 0x00); /* com=0x00 → 4 位全灭 */
- return;
- }
-
#if BOARD_DISP_SCAN_TEST == 1
/* 段扫描:4 位同显一段。⭐ 值变化才刷(无鬼影),com=0xF 四位全选同显。 */
static uint8_t seg = 0;
diff --git a/apps/usr_app/app_disp.h b/apps/usr_app/app_disp.h
index 283bab8..355ace0 100644
--- a/apps/usr_app/app_disp.h
+++ b/apps/usr_app/app_disp.h
@@ -2,13 +2,10 @@
* @file app_disp.h
* @brief CBR3 数码管显示:温度/湿度整数、负号、-- 故障、告警闪烁
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.1.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布
- * - V1.1.0, 2026.08.31, cyWu, P1 加告警闪烁:温度/湿度管独立 500ms 亮灭,
- * 由 app_disp_set_temp/humi_alarm 驱动(P2 条件引擎喂真值,
- * P1 可用 BOARD_DISP_ALARM_TEST 宏强制验证)
+ * @date 2026.08.31
+ * @version V1.1.0,加告警闪烁:温度/湿度管独立 500ms 亮灭,由
+ * app_disp_set_temp/humi_alarm 驱动(P2 条件引擎喂真值,也可用
+ * BOARD_DISP_ALARM_TEST 宏强制验证)
*
* @note 两块 2 位 7 段数码管:温度管整数 ℃、湿度管整数 %。DP 恒灭。
* 0~99 显示绝对值;<0 显示 -9~-1(G 段负号);<-9 钳位 -9;>99 钳位 99。
diff --git a/apps/usr_app/app_fan.c b/apps/usr_app/app_fan.c
index be73b1f..5d9d4c1 100644
--- a/apps/usr_app/app_fan.c
+++ b/apps/usr_app/app_fan.c
@@ -2,10 +2,8 @@
* @file app_fan.c
* @brief CBR3 风扇档位实现
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布
+ * @date 2026.08.31
+ * @version V1.1.0,删除 app_fan_run(P0 无动作的空占位,从未真正做过软爬坡)
******************************************************************************/
#include "app_fan.h"
@@ -70,13 +68,3 @@ uint8_t app_fan_get_gear(void)
{
return s_gear;
}
-
-/**
- * @brief 业务节拍(P0 无动作,保留接口供 P1 爬坡)
- * @param dt 距上次调用的毫秒数
- * @return 无
- */
-void app_fan_run(uint16_t dt)
-{
- (void)dt;
-}
diff --git a/apps/usr_app/app_fan.h b/apps/usr_app/app_fan.h
index 2f4f112..7412943 100644
--- a/apps/usr_app/app_fan.h
+++ b/apps/usr_app/app_fan.h
@@ -2,10 +2,8 @@
* @file app_fan.h
* @brief CBR3 风扇档位:0/1/2/3 → 0%/50%/75%/100%
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布
+ * @date 2026.08.31
+ * @version V1.1.0,删除 app_fan_run(从未真正做过软爬坡的空占位)
******************************************************************************/
#ifndef __APP_FAN_H__
@@ -32,11 +30,4 @@ void app_fan_set_gear(uint8_t gear);
*/
uint8_t app_fan_get_gear(void);
-/**
- * @brief 业务节拍(P0 无动作,保留接口供 P1 爬坡)
- * @param dt 距上次调用的毫秒数
- * @return 无
- */
-void app_fan_run(uint16_t dt);
-
#endif /* __APP_FAN_H__ */
diff --git a/apps/usr_app/app_function.c b/apps/usr_app/app_function.c
index 783ec5e..abd2062 100644
--- a/apps/usr_app/app_function.c
+++ b/apps/usr_app/app_function.c
@@ -2,31 +2,30 @@
* @file app_function.c
* @brief CBR3 功能总装实现
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.1.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布
- * - V1.1.0, 2026.08.31, cyWu, 长按关机改为延后到任务上下文执行:按键在
- * usr_timer 20ms(硬件定时器中断上下文)里处理,直接调
- * app_power_request_poweroff 会在中断里跑 VM 落盘(栈溢出复位);
- * 现改为置 off_pending,由 app_function_loop(usr_timer_loop,
- * BLE 线程)先落盘再关机
+ * @date 2026.08.31
+ * @version V2.2.0,上电即开机:短按切档;长按分级满 5s 提示→松手提交配对、
+ * 满 12s 到点进 OTA;配对/OTA 的实质动作(清绑定写 VM、
+ * usr_app_ota_init)统一放 app_function_loop(任务上下文)执行,
+ * 中断层只置标志;DP0 power 恒为开机态(规格书 0 章第 5 条:预留
+ * 不当开关),已删除 soft_off/is_power_on 等围绕"可关机"的死代码
******************************************************************************/
+#include "system/includes.h" /* 必须先于 app_main.h:u8/u16/u32 等类型由 SDK 定义 */
#include "app_function.h"
-#include "app_power.h"
#include "app_pair.h"
#include "app_mode.h"
#include "app_fan.h"
#include "app_nvm.h"
+#include "app_defer.h"
#include "app_arbiter.h"
#include "app_cond.h"
#include "app_timer_task.h"
#include "app_th.h"
#include "app_disp.h"
#include "app_led.h"
+#include "app_main.h"
+#include "usr_le_api.h"
#include "jb_product.h"
-#include "system/includes.h"
#include
#define LOG_TAG_CONST APP
@@ -34,49 +33,54 @@
#define LOG_INFO_ENABLE
#include "debug.h"
-/** 功能模块状态 */
-typedef struct {
- uint8_t app_power; /**< DP0:1=开机态(本产品无 APP 软关) */
- uint8_t off_pending; /**< 1=长按已触发关机,等任务上下文落盘后再关机 */
-} AppFunctionCtx_t;
-
-static AppFunctionCtx_t s_func;
-
/**
* @brief 初始化功能模块状态
+ * @note DP0 power 恒为开机态:规格书 0 章第 5 条"预留,不当开关",
+ * 协议层 DP0 写入本就忽略(jb_product.c),本产品无软关机,
+ * 这里写死一次即可,不需要额外状态字段
* @return 无
*/
void app_function_init(void)
{
- s_func.app_power = 0;
- s_func.off_pending = 0;
+ gDevData.power = 1;
}
/**
* @brief 任务上下文周期处理(由 usr_timer_loop 500ms 调用)
- * @note ⚠ 本函数是唯一允许做 VM 落盘与关机收口的地方:usr_timer_loop 跑在
- * BLE 线程(sys_timer 同步接口,任务上下文,栈充足)。20ms 的
- * usr_timer_tick 是硬件定时器中断上下文,禁止做这两件事。
+ * @note ⚠ 本函数是唯一允许做 VM 落盘、清绑定、OTA 启动的地方:
+ * usr_timer_loop 跑在 BLE 线程(sys_timer 同步接口,任务上下文,栈充足)。
+ * 20ms 的 usr_timer_tick 是硬件定时器中断上下文,做这些会栈溢出复位。
* @return 无
*/
void app_function_loop(void)
{
app_nvm_flush(); /* 防抖满后真正写 VM(syscfg_write 必须在任务上下文) */
- if (s_func.off_pending) {
- s_func.off_pending = 0;
- app_power_request_poweroff(); /* 落盘后再走关机流程 */
+ /* OTA 优先于配对:按满 12s 说明用户不是想配网,放弃本次清绑定 */
+ if (app_defer_take(APP_DEFER_OTA)) {
+ app_defer_cancel(APP_DEFER_PAIR);
+ usr_app_ota_init(); /* BLE 侧:置 usr_goto_updata + 1s 定时器切 OTA 广播 */
+ app_led_request_ota(); /* 200ms 闪,优先级最高 */
+ log_info("key hold 12s -> ota\n");
+ } else if (app_defer_take(APP_DEFER_PAIR)) {
+ /* ⚠ 真正发起配对广播:usr_goto_pair_mode 内部会断连+清绑定(写VM)+重发绑定广播
+ * 必须在任务上下文调用(写 VM)。仅置 usr_var.usr_goto_pair 标志无效——
+ * SDK adv 模块只消费 usr_goto_updata,不消费该标志,不会开广播。 */
+ usr_goto_pair_mode();
+ app_pair_start(); /* 起 120s 配对会话,灯交由 app_led_run 接管 */
+ app_led_clear_pair_hint();
+ log_info("key hold 5s -> pair\n");
}
}
/**
- * @brief 同步 DP(power 恒 1、fan_gear、mode、fault_code),每个业务 tick 调用
- * @note DP3/DP4 温湿度在 userHandle(500ms)填;DP0 写忽略,运行中恒上报 1
+ * @brief 同步 DP(fan_gear、mode、fault_code),每个业务 tick 调用
+ * @note DP3/DP4 温湿度在 userHandle(500ms)填;DP0 power 不在这里同步,
+ * 恒为开机态,写死在 app_function_init
* @return 无
*/
void app_function_poll(void)
{
- gDevData.power = (s_func.app_power) ? 1 : 0;
gDevData.fan_gear = app_fan_get_gear(); /* DP2 必须等于当前真实档位 */
gDevData.mode = app_mode_get_active();
/* 规格书 8.2/15:传感器故障优先 2;否则温湿度比较成立报 1 */
@@ -89,47 +93,6 @@ void app_function_poll(void)
}
}
-/**
- * @brief 停手动 + 模式,风扇关(关机请求 / 软关时用)
- * @return 无
- */
-void app_function_stop_all(void)
-{
- app_mode_stop();
- app_fan_set_gear(0);
-}
-
-/**
- * @brief 软关:风扇停、DP0=0(本产品 DP0 写忽略,软关仅按键 3s 触发)
- * @return 无
- */
-void app_function_soft_off(void)
-{
- s_func.app_power = 0;
- gDevData.power = 0;
- app_function_stop_all();
- log_info("soft off\n");
-}
-
-/**
- * @brief 上电默认开启功能(DP0=1),不带开机灯效提示
- * @return 无
- */
-void app_function_power_on_silent(void)
-{
- s_func.app_power = 1;
- gDevData.power = 1;
-}
-
-/**
- * @brief 查询功能开关状态(DP0)
- * @return 1=开,0=关
- */
-uint8_t app_function_is_power_on(void)
-{
- return s_func.app_power;
-}
-
/**
* @brief 短按:风扇档位循环 1→2→3→0→1…(规格书 5.3)
* @note 立刻退出通风/强力改为手动档:用 app_mode_stop() 只清模式、
@@ -141,17 +104,6 @@ void usr_on_key_short(void)
{
uint8_t g;
- if (app_power_poweroff_pending()) {
- return;
- }
- if (!s_func.app_power) {
- /* 软关态短按只唤醒,不切档(P0 无 APP 软关,保留分支) */
- s_func.app_power = 1;
- gDevData.power = 1;
- app_led_hint_power_on();
- return;
- }
-
app_arbiter_manual_intervene(); /* 手动动作:释放条件/定时占用(12 章) */
app_mode_stop(); /* 退模式(不动档位,下面立刻切循环下一档) */
g = app_fan_get_gear(); /* 当前实际档:手动=手动档;模式中=模式强制档(1/3) */
@@ -161,23 +113,48 @@ void usr_on_key_short(void)
}
/**
- * @brief 长按 3s:请求软关机(配对中忽略,避免配到一半睡死,规格书 5.3)
- * @note ⚠ 本回调在 usr_timer 20ms(硬件定时器中断上下文)执行,禁止直接
- * 调 app_power_request_poweroff(其钩子会做 VM 落盘 → 栈溢出)。
- * 只置 off_pending,由 app_function_loop(任务上下文)落盘后关机。
+ * @brief 按住满 5s:切配对提示灯,提示"松手即配网,继续按到 12s 进 OTA"
+ * @note 本回调在 usr_timer 20ms(硬件定时器中断上下文)执行,只准动灯效事件
+ * (纯 RAM)。清绑定写 VM 在 app_function_loop 做。
* @return 无
*/
-void usr_on_key_long(void)
+void usr_on_key_pair_hint(void)
{
- if (app_power_poweroff_pending()) {
+ if (app_led_is_ota()) {
+ return; /* OTA 灯效最高优先级,不打断 */
+ }
+ app_led_hint_pair();
+ log_info("key hold 5s, pair hint (release to pair, keep to 12s for ota)\n");
+}
+
+/**
+ * @brief 按满 5s 后松手:提交配对(只置标志)
+ * @note ⚠ 本回调在 usr_timer 20ms(硬件定时器中断上下文)执行,只置标志。
+ * 清绑定(usr_clear_pair_info_noreset 会写 VM)放到任务上下文。
+ * @return 无
+ */
+void usr_on_key_pair_start(void)
+{
+ if (app_led_is_ota()) {
return;
}
- if (app_pair_is_active()) {
- log_info("key hold 3s ignored, pairing\n");
- return;
+ app_defer_request(APP_DEFER_PAIR);
+ log_info("key hold 5s released, pair pending\n");
+}
+
+/**
+ * @brief 按住满 12s:进 OTA(只置标志,到点即触发不等松手)
+ * @note ⚠ 本回调在 usr_timer 20ms(硬件定时器中断上下文)执行,只置标志。
+ * usr_app_ota_init 涉及 BLE 广播切换,放到任务上下文。
+ * @return 无
+ */
+void usr_on_key_ota_start(void)
+{
+ if (app_led_is_ota()) {
+ return; /* 已在 OTA,不重复发起 */
}
- log_info("key hold 3s, poweroff pending (wait vm flush)\n");
- s_func.off_pending = 1;
+ app_defer_request(APP_DEFER_OTA);
+ log_info("key hold 12s, ota pending\n");
}
/**
diff --git a/apps/usr_app/app_function.h b/apps/usr_app/app_function.h
index 6b50799..d62f58d 100644
--- a/apps/usr_app/app_function.h
+++ b/apps/usr_app/app_function.h
@@ -1,11 +1,11 @@
/******************************************************************************
* @file app_function.h
- * @brief CBR3 功能总装:按键动作(短按切档/长按关机)+ DP 同步
+ * @brief CBR3 功能总装:按键动作(短按切档 / 长按 5s 配对 / 长按 12s OTA)+ DP 同步
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹语义重写)
+ * @date 2026.08.31
+ * @version V2.2.0,上电即开机:长按分级回调 usr_on_key_pair_hint/pair_start/
+ * ota_start;DP0 power 恒为开机态(无软关机),已删除
+ * app_function_soft_off/is_power_on 等死代码
*
* @note 本模块只做"按键 → 动作"和"状态 → DP",不写业务算法。
* 档位/模式/条件的执行在各自 app_* 模块(规格书第 0 章第 2 条)。
@@ -23,44 +23,21 @@
void app_function_init(void);
/**
- * @brief 同步 DP(power 恒 1、fan_gear、mode、fault_code),每个业务 tick 调用
+ * @brief 同步 DP(fan_gear、mode、fault_code),每个业务 tick 调用
+ * @note DP0 power 不在这里同步:恒为开机态,写死在 app_function_init
* @return 无
*/
void app_function_poll(void);
/**
* @brief 任务上下文周期处理(由 usr_timer_loop 500ms 调用)
- * @note ⚠ VM 落盘(syscfg_write)与关机收口只允许在这里做:usr_timer_loop
+ * @note ⚠ VM 落盘(syscfg_write)与配对/OTA 落地只允许在这里做:usr_timer_loop
* 跑在 BLE 线程(任务上下文,栈充足);usr_timer_tick 是硬件定时器
- * 中断上下文,做这两件事会栈溢出复位。
+ * 中断上下文,做这些会栈溢出复位。
* @return 无
*/
void app_function_loop(void);
-/**
- * @brief 停手动 + 模式,风扇关(关机请求 / 软关时用)
- * @return 无
- */
-void app_function_stop_all(void);
-
-/**
- * @brief 软关:风扇停、DP0=0(本产品 DP0 写忽略,软关仅按键 3s 触发)
- * @return 无
- */
-void app_function_soft_off(void);
-
-/**
- * @brief 上电默认开启功能(DP0=1),不带开机灯效提示
- * @return 无
- */
-void app_function_power_on_silent(void);
-
-/**
- * @brief 查询功能开关状态(DP0)
- * @return 1=开,0=关
- */
-uint8_t app_function_is_power_on(void);
-
/**
* @brief 业务层短按回调(key_manager 触发):档位 1→2→3→0→1…
* @return 无
@@ -68,10 +45,25 @@ uint8_t app_function_is_power_on(void);
void usr_on_key_short(void);
/**
- * @brief 业务层长按 3s 回调(key_manager 触发):请求软关机
+ * @brief 业务层"按住满 5s"回调(key_manager 触发,20ms 中断上下文)
+ * @note 只切提示灯,不清绑定
* @return 无
*/
-void usr_on_key_long(void);
+void usr_on_key_pair_hint(void);
+
+/**
+ * @brief 业务层"按满 5s 后松手"回调(key_manager 触发,20ms 中断上下文)
+ * @note 只置标志:清绑定要写 VM,由 app_function_loop 在任务上下文执行
+ * @return 无
+ */
+void usr_on_key_pair_start(void);
+
+/**
+ * @brief 业务层"按住满 12s"回调(key_manager 触发,20ms 中断上下文)
+ * @note 只置标志:usr_app_ota_init 由 app_function_loop 在任务上下文执行
+ * @return 无
+ */
+void usr_on_key_ota_start(void);
/**
* @brief 出厂重置:清业务 VM 与 RAM,保留 BLE 配对(规格书 14 章)
diff --git a/apps/usr_app/app_led.c b/apps/usr_app/app_led.c
index de88492..0c358f3 100644
--- a/apps/usr_app/app_led.c
+++ b/apps/usr_app/app_led.c
@@ -2,10 +2,9 @@
* @file app_led.c
* @brief CBR3 灯效适配:单灯 PC2,OTA/配对/常亮
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹,删红绿双通道/充电/低电/空电)
+ * @date 2026.08.31
+ * @version V1.1.0,新增 s_pair_hint:长按满 5s 到松手提交配对这段窗口内维持
+ * 配对快闪,避免被 app_led_run 叠上开机常亮
******************************************************************************/
#include "app_led.h"
@@ -15,7 +14,8 @@
#include "app_pair.h"
static uint8_t s_initialized;
-static uint8_t s_ota; /* 1=OTA 灯效中,app_led_run 不再叠其它灯 */
+static uint8_t s_ota; /* 1=OTA 灯效中,app_led_run 不再叠其它灯 */
+static uint8_t s_pair_hint; /* 1=长按已满 5s 但还没松手提交配对,维持配对快闪 */
/**
* @brief 重新播放一次「一次性/限时」灯效,让它从第 0 帧重新开始
@@ -64,6 +64,30 @@ void app_led_hint_power_on(void)
led_restart(LED_EVENT_STANDBY);
}
+/**
+ * @brief 长按满 5s:挂配对快闪,提示"松手即配网,继续按到 12s 进 OTA"
+ * @return 无
+ */
+void app_led_hint_pair(void)
+{
+ if (!s_initialized || s_ota) {
+ return;
+ }
+ s_pair_hint = 1;
+ LED_EventAllDelete();
+ bsp_led_set(0);
+ LED_EventAdd(LED_EVENT_PAIRING);
+}
+
+/**
+ * @brief 结束"将配网"提示,交回 app_led_run 按 app_pair 状态接管
+ * @return 无
+ */
+void app_led_clear_pair_hint(void)
+{
+ s_pair_hint = 0;
+}
+
/**
* @brief 进入 OTA:清掉其它灯,挂 200ms 闪一直闪
* @return 无
@@ -74,6 +98,7 @@ void app_led_request_ota(void)
return;
}
s_ota = 1;
+ s_pair_hint = 0; /* 已改判 OTA,撤销配网提示 */
LED_EventAllDelete();
bsp_led_set(0);
LED_EventAdd(LED_EVENT_OTA);
@@ -122,7 +147,7 @@ void app_led_run(uint16_t dt, uint8_t power_on)
if (!power_on) {
LED_EventDelete(LED_EVENT_STANDBY);
LED_EventDelete(LED_EVENT_PAIRING);
- } else if (app_pair_is_active()) {
+ } else if (s_pair_hint || app_pair_is_active()) {
LED_EventAdd(LED_EVENT_PAIRING);
LED_EventDelete(LED_EVENT_STANDBY);
} else if (!LED_IsEventExist(LED_EVENT_PAIR_OK) && !LED_IsEventExist(LED_EVENT_PAIR_FAIL)) {
diff --git a/apps/usr_app/app_led.h b/apps/usr_app/app_led.h
index 9b3dc14..218f93f 100644
--- a/apps/usr_app/app_led.h
+++ b/apps/usr_app/app_led.h
@@ -2,10 +2,9 @@
* @file app_led.h
* @brief CBR3 灯效:单灯 PC2,OTA/配对/常亮按优先级
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹,删红绿双通道/充电/低电)
+ * @date 2026.08.31
+ * @version V1.1.0,新增长按 5s 的"将配网"提示灯(配对改由运行态长按触发,
+ * 需要在按住期间给反馈)
******************************************************************************/
#ifndef __APP_LED_H__
@@ -38,6 +37,19 @@ void app_led_run(uint16_t dt, uint8_t power_on);
*/
void app_led_hint_power_on(void);
+/**
+ * @brief 长按满 5s 的"松手即配网"提示:清掉其它灯,挂配对快闪
+ * @note 此时还没清绑定(要等松手),只是给按住的用户一个反馈
+ * @return 无
+ */
+void app_led_hint_pair(void);
+
+/**
+ * @brief 结束"将配网"提示(配对会话已由 app_pair 接管,或改判为 OTA)
+ * @return 无
+ */
+void app_led_clear_pair_hint(void);
+
/**
* @brief 进入 OTA:清掉其它灯,挂 200ms 闪一直闪
* @return 无
diff --git a/apps/usr_app/app_mode.c b/apps/usr_app/app_mode.c
index bed390e..d0f0a10 100644
--- a/apps/usr_app/app_mode.c
+++ b/apps/usr_app/app_mode.c
@@ -2,12 +2,9 @@
* @file app_mode.c
* @brief CBR3 工作模式实现(P1):通风 25/5 循环、强力 15min 一轮
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.1.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(P0 stub)
- * - V1.1.0, 2026.08.31, cyWu, P1 实现状态机(规格书第 9 章):
- * 通风 25/5、强力 15min 后停、手动档快照、退出语义、开机从周期头
+ * @date 2026.08.31
+ * @version V1.1.0,实现状态机(规格书第 9 章):通风 25/5、强力 15min 后停、
+ * 手动档快照、退出语义、开机从周期头
*
* @note 模式激活时强制覆盖风扇档位(通风=1 档、强力=3 档、通风停段=0),
* DP2 由 app_fan_get_gear 上报真实档位(app_function_poll 同步)。
diff --git a/apps/usr_app/app_mode.h b/apps/usr_app/app_mode.h
index 88ea2d2..8703757 100644
--- a/apps/usr_app/app_mode.h
+++ b/apps/usr_app/app_mode.h
@@ -3,9 +3,7 @@
* @brief CBR3 工作模式:通风(25/5 循环)/ 强力(15min 一轮)
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(P0 仅接口与 stub,P1 实现状态机)
+ * @version V1.0.0,首次发布(P0 仅接口与 stub,P1 实现状态机)
*
* @note P1 语义(规格书第 9 章):mode=1 强制 1 档开 25min 停 5min 循环;
* mode=2 强制 3 档跑 15min 后风扇 0、mode 回 0。短按切档走
diff --git a/apps/usr_app/app_nvm.c b/apps/usr_app/app_nvm.c
index cccf49e..f7ab4dc 100644
--- a/apps/usr_app/app_nvm.c
+++ b/apps/usr_app/app_nvm.c
@@ -2,11 +2,8 @@
* @file app_nvm.c
* @brief CBR3 VM 持久化实现(P1):mode/fan_gear/条件/定时/使能
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.1.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(P0 stub)
- * - V1.1.0, 2026.08.31, cyWu, P1 实现:3 个 VM item + 防抖 + 立即写
+ * @date 2026.08.31
+ * @version V1.1.0,实现:3 个 VM item + 防抖 + 立即写
*
* @note VM item 分配在 syscfg 用户自定义区 [1~49](syscfg_id.h 已占用
* CFG_USER_PAIR_INFO=2 / CFG_USER_TIM_FLAG=3 / VM_ALARM*=4~24 /
@@ -19,7 +16,7 @@
* 减少 flash 磨损;首次上电(magic 不符)保持 gDevData 全 0 默认。
*
* 写入时机(规格书第 13 章):APP 改配置后(mark_dirty)、档位/模式
- * 稳定变化(500ms 防抖)、软关机前(save 立即写)。
+ * 稳定变化(500ms 防抖)。
*
* ⚠⚠ 上下文硬约束(2026-08-31 复位事故定论,勿再踩):
* usr_timer_add() 是**异步接口**,回调在**硬件定时器中断上下文**执行
@@ -29,14 +26,14 @@
* 绝对不能调 syscfg_write**(VM 擦写 flash 栈用量大且中断里做
* flash 操作 → 栈溢出软复位;实测 APP 下发数据后 500ms 必崩)。
* - 真正写入由 app_nvm_flush() 在 usr_timer_loop()(BLE 线程、
- * 任务上下文、栈充足)执行;usr_poweroff_request_hook 里的
- * app_nvm_save() 也必须保证在任务上下文被调用(见 app_function_loop)。
+ * 任务上下文、栈充足)执行。
* 另:nvm_write_item 的比较缓冲改 static,再省 128B 栈。
******************************************************************************/
#include "app_nvm.h"
#include "app_mode.h"
#include "app_fan.h"
+#include "app_defer.h"
#include "jb_product.h"
#include "board_pin.h"
#include "system/includes.h"
@@ -81,8 +78,9 @@ typedef struct {
#if BOARD_NVM_ENABLE
static uint8_t s_dirty; /**< 1=防抖计时中 */
-static uint8_t s_need_save; /**< 1=防抖已满,待任务上下文落盘 */
static uint16_t s_deb_ms; /**< 防抖计时 */
+/* 防抖已满时用 app_defer_request(APP_DEFER_NVM_SAVE) 打标志,真正落盘
+ * 由 app_nvm_flush() 在任务上下文里 app_defer_take() 消费,见文件头说明 */
/* 上次已同步到 VM 的状态快照:变化检测基准(覆盖按键/APP/模式引擎所有入口) */
static uint8_t s_last_gear;
@@ -222,7 +220,7 @@ void app_nvm_init(void)
}
s_dirty = 0;
s_deb_ms = 0;
- s_need_save = 0;
+ app_defer_cancel(APP_DEFER_NVM_SAVE);
#endif
}
@@ -254,7 +252,7 @@ void app_nvm_save(void)
nvm_write_time();
s_dirty = 0;
s_deb_ms = 0;
- s_need_save = 0;
+ app_defer_cancel(APP_DEFER_NVM_SAVE);
nvm_sync_snapshot();
#endif
}
@@ -358,7 +356,7 @@ void app_nvm_tick(uint16_t dt)
if (s_deb_ms >= CBR3_VM_DEBOUNCE_MS) {
s_deb_ms = 0;
s_dirty = 0;
- s_need_save = 1; /* 只置标志:等任务上下文 app_nvm_flush 真正落盘 */
+ app_defer_request(APP_DEFER_NVM_SAVE); /* 只置标志:真正落盘在 app_nvm_flush */
}
#endif
}
@@ -374,10 +372,9 @@ void app_nvm_flush(void)
#if !BOARD_NVM_ENABLE
return;
#else
- if (!s_need_save) {
+ if (!app_defer_take(APP_DEFER_NVM_SAVE)) {
return;
}
- s_need_save = 0;
app_nvm_save();
#endif
}
@@ -417,7 +414,7 @@ void app_nvm_reset(void)
syscfg_write(CFG_USER_CBR3_TIME, &tf, sizeof(tf));
s_dirty = 0;
s_deb_ms = 0;
- s_need_save = 0;
+ app_defer_cancel(APP_DEFER_NVM_SAVE);
log_info("nvm factory reset, business vm cleared (pairing kept)\n");
#endif
}
diff --git a/apps/usr_app/app_nvm.h b/apps/usr_app/app_nvm.h
index cfce212..d2c2a21 100644
--- a/apps/usr_app/app_nvm.h
+++ b/apps/usr_app/app_nvm.h
@@ -2,17 +2,13 @@
* @file app_nvm.h
* @brief CBR3 VM 持久化(P1 起):mode/fan_gear/条件/定时/使能
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.1.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(P0 仅接口与 stub)
- * - V1.1.0, 2026.08.31, cyWu, P1 实现:3 个 VM item(状态/条件/定时)+ 500ms
- * 防抖写入 + 立即写接口
+ * @date 2026.08.31
+ * @version V1.1.0,实现:3 个 VM item(状态/条件/定时)+ 500ms 防抖写入 +
+ * 立即写接口
*
* @note 存:mode、fan_gear、cond_en、cond_config1~7、time_en、time_config1~7、
* fault_en、alarm_en。不存:DP0、条件作废标志、模式已运行秒数、原始采样。
- * 写 VM 时机:APP 改配置后、单次定时自毁、软关机前、档位/模式稳定变化
- * (500ms 防抖)。
+ * 写 VM 时机:APP 改配置后、单次定时自毁、档位/模式稳定变化(500ms 防抖)。
******************************************************************************/
#ifndef __APP_NVM_H__
@@ -27,9 +23,8 @@
void app_nvm_init(void);
/**
- * @brief 立即保存全部业务字段到 VM(软关机前等关键时机调用)
- * @note syscfg 写入不能在裸中断里调用;本函数由业务时基回调触发,
- * 与 SDK norflash 缓存同步(sys_timeout 回调)同级,可安全执行
+ * @brief 立即保存全部业务字段到 VM
+ * @note syscfg 写入不能在裸中断里调用;只由 app_nvm_flush()(任务上下文)调用
* @return 无
*/
void app_nvm_save(void);
diff --git a/apps/usr_app/app_pair.c b/apps/usr_app/app_pair.c
index 8730f6f..65d3c26 100644
--- a/apps/usr_app/app_pair.c
+++ b/apps/usr_app/app_pair.c
@@ -3,9 +3,7 @@
* @brief 配对会话计时实现
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.25, cyWu, 首次发布,实现配对会话计时与成功/超时判定
+ * @version V1.0.0,首次发布,实现配对会话计时与成功/超时判定
******************************************************************************/
#include "app_pair.h"
diff --git a/apps/usr_app/app_pair.h b/apps/usr_app/app_pair.h
index 2ab1baa..521d5da 100644
--- a/apps/usr_app/app_pair.h
+++ b/apps/usr_app/app_pair.h
@@ -3,9 +3,7 @@
* @brief 配对会话计时:开机配对窗口是否成功/超时,与 BLE 配对本身解耦
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.25, cyWu, 首次发布,实现配对会话计时与成功/超时判定
+ * @version V1.0.0,首次发布,实现配对会话计时与成功/超时判定
*
* @note 真正的 BLE 广播/绑定逻辑在 usr_le_code;本模块只负责“进入配对后
* 120s 内 usr_get_pair_flag() 是否变 1”这件事,纯状态,不碰灯/按键。
diff --git a/apps/usr_app/app_power.c b/apps/usr_app/app_power.c
deleted file mode 100644
index 64973b8..0000000
--- a/apps/usr_app/app_power.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/******************************************************************************
- * @file app_power.c
- * @brief CBR3 软关机生命周期实现:无电池、无充电
- * @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹精简:删 USB/充电/电量/低电)
- ******************************************************************************/
-
-#include "app_power.h"
-#include "system/includes.h"
-
-#define LOG_TAG_CONST APP
-#define LOG_TAG "[APP_PWR]"
-#define LOG_INFO_ENABLE
-#include "debug.h"
-
-/** 电源模块总状态 */
-typedef struct {
- uint8_t req_poweroff;
- AppPowerHook_t on_request;
- AppPowerHook_t on_finalize;
-} AppPowerCtx_t;
-
-static AppPowerCtx_t s_power;
-
-static void power_do_poweroff(void *priv);
-
-/**
- * @brief 初始化电源模块状态
- * @return 无
- */
-void app_power_init(void)
-{
- s_power.req_poweroff = 0;
- s_power.on_request = NULL;
- s_power.on_finalize = NULL;
-}
-
-/**
- * @brief 注册深睡请求的两段式回调
- * @param on_request 刚请求深睡时调用(停风扇;灯由调用方按关机类型决定)
- * @param on_finalize 延时到点后调用(真正进深睡,不会返回)
- * @return 无
- */
-void app_power_set_hooks(AppPowerHook_t on_request, AppPowerHook_t on_finalize)
-{
- s_power.on_request = on_request;
- s_power.on_finalize = on_finalize;
-}
-
-/**
- * @brief 请求深睡(按键 3s 等):立即触发 on_request 钩子,50ms 后触发 on_finalize
- * @return 无
- */
-void app_power_request_poweroff(void)
-{
- if (s_power.req_poweroff) {
- return; /* 已经在关机流程里,不要重复挂定时器 */
- }
- s_power.req_poweroff = 1;
- log_info("request deep off\n");
- if (s_power.on_request) {
- s_power.on_request(); /* 立刻停风扇、灯全灭 */
- }
- sys_timeout_add(NULL, power_do_poweroff, 50); /* 给 GPIO 落稳一点再真正掉电 */
-}
-
-/**
- * @brief 查询深睡请求是否已发起
- * @return 1=已发起,0=未发起
- */
-uint8_t app_power_poweroff_pending(void)
-{
- return s_power.req_poweroff;
-}
-
-/**
- * @brief 当前这次关机是否为空电路径(本产品无电池,恒 0)
- * @return 0
- */
-uint8_t app_power_empty_off_pending(void)
-{
- return 0;
-}
-
-/**
- * @brief 50ms 延时到点:真正进入深睡
- * @param priv 未使用
- * @return 无
- */
-static void power_do_poweroff(void *priv)
-{
- (void)priv;
- log_info("enter deep off\n");
- if (s_power.on_finalize) {
- s_power.on_finalize();
- }
-}
diff --git a/apps/usr_app/app_power.h b/apps/usr_app/app_power.h
deleted file mode 100644
index 39ad2cd..0000000
--- a/apps/usr_app/app_power.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/******************************************************************************
- * @file app_power.h
- * @brief CBR3 软关机生命周期:无电池、无充电,只保留 request/finalize
- * @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹精简:删 USB/充电/电量/低电)
- *
- * @note 本产品 USB 常电、无电池,插电不会自动开机,必须长按 3s。
- * SDK 层 TCFG_CHARGE 相关配置保留为板级模板默认,但业务层完全不
- * 参与充电/电量逻辑(规格书第 0 章第 3 条)。
- ******************************************************************************/
-
-#ifndef __APP_POWER_H__
-#define __APP_POWER_H__
-
-#include
-
-/** 深睡请求的钩子:on_request 立刻停执行体并按关机类型处理灯;on_finalize 真正掉电 */
-typedef void (*AppPowerHook_t)(void);
-
-/**
- * @brief 初始化电源模块状态
- * @return 无
- */
-void app_power_init(void);
-
-/**
- * @brief 注册深睡请求的两段式回调
- * @param on_request 刚请求深睡时调用(停风扇;灯由调用方按关机类型决定)
- * @param on_finalize 延时到点后调用(真正进深睡,不会返回)
- * @return 无
- */
-void app_power_set_hooks(AppPowerHook_t on_request, AppPowerHook_t on_finalize);
-
-/**
- * @brief 请求深睡(按键 3s 等):立即触发 on_request 钩子,50ms 后触发 on_finalize
- * @return 无
- */
-void app_power_request_poweroff(void);
-
-/**
- * @brief 查询深睡请求是否已发起(发起后其它模块应停止响应新操作)
- * @return 1=已发起,0=未发起
- */
-uint8_t app_power_poweroff_pending(void);
-
-/**
- * @brief 当前这次关机是否为空电路径(本产品无电池,恒 0,保留接口)
- * @return 0
- */
-uint8_t app_power_empty_off_pending(void);
-
-#endif /* __APP_POWER_H__ */
diff --git a/apps/usr_app/app_th.c b/apps/usr_app/app_th.c
index af05ead..878740b 100644
--- a/apps/usr_app/app_th.c
+++ b/apps/usr_app/app_th.c
@@ -3,9 +3,7 @@
* @brief CBR3 温湿度采集实现
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布
+ * @version V1.0.0,首次发布
******************************************************************************/
#include "app_th.h"
diff --git a/apps/usr_app/app_th.h b/apps/usr_app/app_th.h
index 768f2d4..1f55a8c 100644
--- a/apps/usr_app/app_th.h
+++ b/apps/usr_app/app_th.h
@@ -3,9 +3,7 @@
* @brief CBR3 温湿度采集:2s 周期、失败计数、最近有效值
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布
+ * @version V1.0.0,首次发布
*
* @note 规格书 8.4:条件"持续时长"只在本次采样成功且比较成立时累加;
* 采样失败不累加、不清零;连续 3 次失败显示 "--" 且 fault_code=2。
diff --git a/apps/usr_app/app_timer_task.c b/apps/usr_app/app_timer_task.c
index ee74db8..847645f 100644
--- a/apps/usr_app/app_timer_task.c
+++ b/apps/usr_app/app_timer_task.c
@@ -2,14 +2,10 @@
* @file app_timer_task.c
* @brief CBR3 定时引擎(P2):7 组 8 字节定时窗,RTC 门闩
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V2.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(P0 stub)
- * - V1.1.0, 2026.08.31, cyWu, P1:set_en/set_config 存 VM(引擎仍关)
- * - V2.0.0, 2026.08.31, cyWu, P2 实现完整定时引擎(规格书第 11 章):
- * RTC 门闩(未对时一律不跑)/对时补窗/7 组窗判断(跨午夜、星期位图)/
- * 窗内切模式/结束收口(风扇 0 mode 0)/单次自毁清使能写 VM
+ * @date 2026.08.31
+ * @version V2.0.0,完整定时引擎(规格书第 11 章):RTC 门闩(未对时一律不
+ * 跑)/对时补窗/7 组窗判断(跨午夜、星期位图)/窗内切模式/结束收口
+ * (风扇 0 mode 0)/单次自毁清使能写 VM
*
* @note 运行上下文:app_timer_run 由 usr_timer_loop(500ms,任务上下文)
* 调用——窗判断分钟级精度足够,且单次自毁要写 VM(禁止中断上下文)。
diff --git a/apps/usr_app/app_timer_task.h b/apps/usr_app/app_timer_task.h
index ead9b41..6d7a564 100644
--- a/apps/usr_app/app_timer_task.h
+++ b/apps/usr_app/app_timer_task.h
@@ -3,9 +3,7 @@
* @brief CBR3 定时引擎(P2):7 组 8 字节定时窗,RTC 门闩
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(P0 仅接口与 stub)
+ * @version V1.0.0,首次发布(P0 仅接口与 stub)
*
* @note 文件名避开 usr_rtc。字节布局见规格书 11.1:Byte0=使能 Byte1=模式
* Byte2=类型(0单次/1每天/2每周) Byte3=星期位图 Byte4-5=开始时/分
diff --git a/apps/usr_app/key.c b/apps/usr_app/key.c
index 411d0a0..701b744 100644
--- a/apps/usr_app/key.c
+++ b/apps/usr_app/key.c
@@ -3,9 +3,7 @@
* @brief 按键状态机实现
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.25, cyWu, 首次发布,实现按键短按/长按/释放状态机
+ * @version V1.0.0,首次发布,实现按键短按/长按/释放状态机
******************************************************************************/
#include "key.h"
diff --git a/apps/usr_app/key.h b/apps/usr_app/key.h
index 9634cb1..693ddec 100644
--- a/apps/usr_app/key.h
+++ b/apps/usr_app/key.h
@@ -3,9 +3,7 @@
* @brief 按键状态机:短按 / 长按 / 释放
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.25, cyWu, 首次发布,实现按键短按/长按/释放状态机
+ * @version V1.0.0,首次发布,实现按键短按/长按/释放状态机
******************************************************************************/
#ifndef __KEY_H__
diff --git a/apps/usr_app/key_manager.c b/apps/usr_app/key_manager.c
index 9514159..beb2b1e 100644
--- a/apps/usr_app/key_manager.c
+++ b/apps/usr_app/key_manager.c
@@ -1,11 +1,9 @@
/******************************************************************************
* @file key_manager.c
- * @brief CBR3 按键:读 PB1,短按切档、长按 3s 关机交给 usr_jb_main
+ * @brief CBR3 按键:读 PB1,短按切档;按住分级 5s 配对 / 12s OTA
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹,短按语义改切档)
+ * @date 2026.08.31
+ * @version V3.0.0,直接复用 key.c 原生长按/连按/释放事件做按住分级
******************************************************************************/
#include "system/includes.h"
@@ -18,10 +16,11 @@
#define LOG_INFO_ENABLE
#include "debug.h"
-/** 模块状态,替代原来 2 个散落的 static */
+/** 模块状态 */
typedef struct {
uint8_t initialized;
- uint8_t boot_key_lock; /**< 1=开机判定时键还按着,等松开才认短按/长按 */
+ uint8_t boot_key_lock; /**< 1=上电时键还按着,等松开才认短按(不挡长按分级) */
+ uint8_t ota_fired; /**< 1=已到点触发 OTA,本次按住松手不再提交配对 */
} KeyManagerCtx_t;
static KeyManagerCtx_t s_km;
@@ -36,25 +35,13 @@ static uint8_t key_user_read(void)
}
/**
- * @brief 松开:清掉开机按着键的锁
- * @return 无
- */
-static void key_user_release(void)
-{
- if (s_km.boot_key_lock) {
- s_km.boot_key_lock = 0;
- log_info("boot key released\n");
- }
-}
-
-/**
- * @brief 短按:开机未松手前忽略
+ * @brief 短按(SHORT_Event,松手时刻 <5s):上电未松手前忽略,否则切档
* @return 无
*/
static void key_user_short(void)
{
if (s_km.boot_key_lock) {
- s_km.boot_key_lock = 0; /* 开机松手当成释放,不当短按切档 */
+ s_km.boot_key_lock = 0; /* 上电松手当成释放,不当短按切档 */
log_info("boot key released\n");
return;
}
@@ -62,15 +49,44 @@ static void key_user_short(void)
}
/**
- * @brief 长按 3s:开机未松手前忽略,避免开机手还按着就关机
+ * @brief 长按到点(LONG_Event,满 5s 仍按住):挂配对提示灯
+ * @note ⚠ 跑在 usr_timer 20ms(硬件定时器中断上下文):只准打标志 + 切灯效
+ * 事件(纯 RAM)。清绑定(写 VM)留给 app_function_loop(任务上下文)。
* @return 无
*/
-static void key_user_long(void)
+static void key_user_pair_hint(void)
{
- if (s_km.boot_key_lock) {
- return; /* 开机手还按着,忽略这次 3s 长按,避免一开机就关机 */
+ usr_on_key_pair_hint();
+}
+
+/**
+ * @brief 连按到点(LAST_Event,满 12s 仍按住,到点即触发不等松手):进 OTA
+ * @note ⚠ 同上,只置标志;usr_app_ota_init 由 app_function_loop 执行。
+ * key.c 的 keyHandle 对 LAST_Event 不清 eventType,松手前每 20ms 都会
+ * 再派发一次,这里用 ota_fired 挡住重复调用。
+ * @return 无
+ */
+static void key_user_ota_start(void)
+{
+ if (s_km.ota_fired) {
+ return;
}
- usr_on_key_long();
+ s_km.ota_fired = 1;
+ usr_on_key_ota_start();
+}
+
+/**
+ * @brief 释放(RELEASE_Event,只有按满 ≥5s 才会触发):未进 OTA 则提交配对
+ * @note 按满 12s 已触发 OTA 的这次按住,松手不再提交配对(ota_fired 拦住)。
+ * @return 无
+ */
+static void key_user_release(void)
+{
+ s_km.boot_key_lock = 0;
+ if (!s_km.ota_fired) {
+ usr_on_key_pair_start();
+ }
+ s_km.ota_fired = 0;
}
static keyCategory_t s_keys[USR_KEY_COUNT] = {
@@ -78,12 +94,13 @@ static keyCategory_t s_keys[USR_KEY_COUNT] = {
.fsm.keyShield = KEY_ENABLE,
.fsm.keyDownLevel = Bit_SET,
.fsm.eventType = NULL_Event,
- .fsm.keyLongTime = BOARD_KEY_POWER_MS,
- .fsm.keyLastTime = (BOARD_KEY_POWER_MS + 7000), /* 大于长按即可,不使用持续按 */
+ .fsm.keyLongTime = BOARD_KEY_PAIR_MS, /* 满 5s 仍按住 -> longPressCb */
+ .fsm.keyLastTime = BOARD_KEY_OTA_MS, /* 满 12s 仍按住 -> lastPressCb */
.fsm.keyReadValue = key_user_read,
.func.ShortPressCb = key_user_short,
- .func.longPressCb = key_user_long,
.func.releasePressCb = key_user_release,
+ .func.longPressCb = key_user_pair_hint,
+ .func.lastPressCb = key_user_ota_start,
},
};
@@ -98,18 +115,20 @@ void user_key_init(void)
}
keyParaInit(s_keys);
s_km.boot_key_lock = 0;
+ s_km.ota_fired = 0;
s_km.initialized = 1;
}
/**
- * @brief 开机判定结束后:键还按着则锁住,松开才认短按/长按
+ * @brief 上电后:键还按着则锁住短按,松开才认短按
+ * @note 只锁短按,**不锁长按分级**:上电就按住 5s 进配对是受支持的用法。
* @return 无
*/
void user_key_lock_if_held(void)
{
reset_key_Status(USR_KEY_USER);
if (bsp_key_is_pressed()) {
- s_km.boot_key_lock = 1; /* 开机手还按着,等松开才认短按/长按,避免立刻关机 */
+ s_km.boot_key_lock = 1; /* 上电手还按着,等松开才认短按,避免一上电就切档 */
log_info("boot key held, wait release\n");
}
}
diff --git a/apps/usr_app/key_manager.h b/apps/usr_app/key_manager.h
index eb6dd6a..784be84 100644
--- a/apps/usr_app/key_manager.h
+++ b/apps/usr_app/key_manager.h
@@ -1,11 +1,9 @@
/******************************************************************************
* @file key_manager.h
- * @brief CBR3 单按键:短按切档,长按 3s 关机;开机 3s/5s/12s 不走本库
+ * @brief CBR3 单按键:短按切档,按住分级 5s 配对 / 12s OTA(无长按关机)
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹,语义改切档/关机)
+ * @date 2026.08.31
+ * @version V3.0.0,直接复用 key.c 原生长按/连按/释放事件做按住分级
******************************************************************************/
#ifndef __KEY_MANAGER_H__
@@ -20,10 +18,24 @@
void usr_on_key_short(void);
/**
- * @brief 业务层长按 3s 回调(app_function 实现)
+ * @brief 业务层"按住满 5s"回调(app_function 实现):切配对提示灯,尚未清绑定
* @return 无
*/
-void usr_on_key_long(void);
+void usr_on_key_pair_hint(void);
+
+/**
+ * @brief 业务层"按满 5s 后松手"回调(app_function 实现):提交配对
+ * @note 只置标志;清绑定(写 VM)在 app_function_loop(任务上下文)执行
+ * @return 无
+ */
+void usr_on_key_pair_start(void);
+
+/**
+ * @brief 业务层"按住满 12s"回调(app_function 实现):到点即进 OTA
+ * @note 只置标志;usr_app_ota_init 在 app_function_loop(任务上下文)执行
+ * @return 无
+ */
+void usr_on_key_ota_start(void);
/**
* @brief 装载按键表
@@ -32,7 +44,7 @@ void usr_on_key_long(void);
void user_key_init(void);
/**
- * @brief 开机判定结束后:若键还按着则锁住,松开才认短按/长按
+ * @brief 上电后:若键还按着则锁住短按,松开才认短按(不挡长按分级)
* @return 无
*/
void user_key_lock_if_held(void);
diff --git a/apps/usr_app/led.c b/apps/usr_app/led.c
index 8fc5823..5cbdcbe 100644
--- a/apps/usr_app/led.c
+++ b/apps/usr_app/led.c
@@ -3,9 +3,7 @@
* @brief LED 事件框架实现:只改 ledMask 对应通道,红绿互不抢
* @author cyWu <1917507415@qq.com>
* @date 2026.08.25
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.25, cyWu, 首次发布,实现按通道掩码独立开关的 LED 事件框架
+ * @version V1.0.0,首次发布,实现按通道掩码独立开关的 LED 事件框架
******************************************************************************/
#include "led.h"
diff --git a/apps/usr_app/led.h b/apps/usr_app/led.h
index 4393f96..c7cdd4e 100644
--- a/apps/usr_app/led.h
+++ b/apps/usr_app/led.h
@@ -3,11 +3,7 @@
* @brief LED 事件框架:按通道掩码独立开关
* @author cyWu <1917507415@qq.com>
* @date 2026.08.31
- * @version V1.2.0
- * @history
- * - V1.0.0, 2026.08.25, cyWu, 首次发布,实现按通道掩码独立开关的 LED 事件框架
- * - V1.1.0, 2026.08.27, cyWu, 新增 LED_EVENT_BOOT_REJECT
- * - V1.2.0, 2026.08.31, cyWu, CBR3 单灯:裁掉充电/低电/空电等门夹事件
+ * @version V1.2.0,CBR3 单灯:裁掉充电/低电/空电等门夹事件
******************************************************************************/
#ifndef __LED_H__
diff --git a/apps/usr_app/led_manager.c b/apps/usr_app/led_manager.c
index bad623e..66f6050 100644
--- a/apps/usr_app/led_manager.c
+++ b/apps/usr_app/led_manager.c
@@ -3,9 +3,7 @@
* @brief CBR3 灯事件:单灯 PC2,高优先级覆盖低优先级
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹,删红绿双通道改单灯)
+ * @version V1.0.0,首次发布(移植门夹,删红绿双通道改单灯)
*
* @note 灯效优先级(见规格书第 6 章,高者覆盖):
* OTA(200ms 闪) > 配对中(100ms 快闪) / 配对结果 > 正常开机常亮。
diff --git a/apps/usr_app/led_manager.h b/apps/usr_app/led_manager.h
index 790109d..2e91a35 100644
--- a/apps/usr_app/led_manager.h
+++ b/apps/usr_app/led_manager.h
@@ -3,9 +3,7 @@
* @brief CBR3 单 LED(PC2)通道与事件表
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(单灯版,删红绿双通道)
+ * @version V1.0.0,首次发布(单灯版,删红绿双通道)
******************************************************************************/
#ifndef __LED_MANAGER_H__
diff --git a/apps/usr_jb_proto/usr_jb_main.c b/apps/usr_jb_proto/usr_jb_main.c
index d3830d9..ccac278 100644
--- a/apps/usr_jb_proto/usr_jb_main.c
+++ b/apps/usr_jb_proto/usr_jb_main.c
@@ -2,21 +2,23 @@
* @file usr_jb_main.c
* @brief CBR3 业务组合根:只做"先后调用谁、谁通知谁",不含具体业务算法
* @author cyWu <1917507415@qq.com>
- * @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹:删电池/USB,加风扇/显示/传感)
+ * @date 2026.08.31
+ * @version V2.3.0,上电即开机(无 app_boot);5s 配对 / 12s OTA 由 key_manager
+ * 长按分级触发;关机=拔电,已删除全部 app_power 软关机基础设施及
+ * DP0 开关相关死代码(power 恒 1,写死在 app_function_init);
+ * 删除 usr_timer_pick_mode/rearm 动态休眠调度(Type-C 长供电不
+ * 需要低功耗,且 app_disp_scan 常驻 priority=1 已让系统永远无法
+ * 休眠,该机制本来就没有实际效果),业务定时器改为一次性固定注册
*
* @note 协议生成文件 jb_product.c 不要填业务。APP 下行写 gDevData。
* usr_timer_loop 由 BLE 回调,勿再注册第二份。
* 具体业务逻辑去对应模块找:
- * app_boot —— 开机 3s/5s/12s 按键判定
- * app_power —— 软关机请求(无电池/充电)
+ * key_manager —— 短按切档 + 按住 5s 配对 / 12s OTA 分级
* app_pair —— 配对会话计时
* app_fan —— 风扇档位
* app_th —— AHT20 采样
* app_disp —— 数码管显示
- * app_function —— 按键动作 + DP 同步
+ * app_function —— 按键动作 + DP 同步 + 配对/OTA 动作落地
******************************************************************************/
#include "system/includes.h"
@@ -32,9 +34,7 @@
#include "usr_le_product.h"
#include "usr_le_api.h"
#include "bsp_hw.h"
-#include "app_power.h"
#include "app_pair.h"
-#include "app_boot.h"
#include "app_function.h"
#include "app_fan.h"
#include "app_th.h"
@@ -46,7 +46,6 @@
#include "app_nvm.h"
#include "app_led.h"
#include "key_manager.h"
-#include "asm/power/power_api.h"
#define LOG_TAG_CONST APP
#define LOG_TAG "[USR_JB]"
@@ -65,128 +64,39 @@ const uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN] = {
#define USR_TIMER_IDLE_MS 20 /**< 全局统一节拍(规格书第 3 章,禁止再开 1ms ISR) */
-/**
- * @brief 业务节拍档位
- * @note 全系统只用一种节拍周期(USR_TIMER_IDLE_MS),区别只在"能不能休眠"
- */
-typedef enum {
- USR_TICK_IDLE = 0, /**< 可休眠 */
- USR_TICK_BLOCK_SLEEP /**< 禁止休眠(按键按住/风扇转/关机流程/配对/OTA) */
-} UsrTickMode_t;
-
-/** 调度器状态 */
-typedef struct {
- int timer_id;
- UsrTickMode_t mode;
-} UsrSchedulerCtx_t;
-
-static UsrSchedulerCtx_t s_sched;
-
static void usr_timer_tick(void *priv);
-static void usr_timer_rearm(UsrTickMode_t mode);
static void usr_log_boot_version(void);
/**
- * @brief 软关机请求钩子:先落盘当前 mode/gear/配置,再停风扇、灯全灭
- * @note ⚠ app_nvm_save() 必须在 app_function_stop_all() 之前调用:
- * stop_all 会把 mode/gear 清 0,先存才能保住用户最后的运行状态
- * (规格书 13 章:软关机前写 VM,开机恢复上次 mode/gear)
- * @return 无
- */
-static void usr_poweroff_request_hook(void)
-{
- app_nvm_save(); /* 软关机前立即写 VM(不等待 500ms 防抖) */
- app_function_stop_all();
- app_led_clear();
-}
-
-/**
- * @brief 软关机最终钩子:50ms 到点,真正进入深睡
- * @note 不走 sys_enter_soft_poweroff():那是耳机 SDK 的优雅关机流程,实测慢。
- * 本产品无 DAC/音频,直接进软关机(规格书 5.1:风扇 0、数码管锁存灭、
- * LED 灭、BLE 停)。
- * @return 无
- */
-static void usr_poweroff_finalize_hook(void)
-{
- bsp_hw_enter_sleep_io();
- power_set_soft_poweroff();
-}
-
-/**
- * @brief 按当前业务状态判断能不能休眠
- * @note 按键按住、风扇转、关机流程、配对中、OTA 灯效中 → 不能睡
- * @return 本次应使用的档位
- */
-static UsrTickMode_t usr_timer_pick_mode(void)
-{
- if (bsp_key_is_pressed() || app_power_poweroff_pending()
- || (app_fan_get_gear() != 0) || app_mode_get_active()
- || app_pair_is_active() || app_led_is_ota()) {
- return USR_TICK_BLOCK_SLEEP;
- }
- return USR_TICK_IDLE;
-}
-
-/**
- * @brief 切换业务定时器。priority=1 时系统无法 sleep;节拍周期恒为 USR_TIMER_IDLE_MS
- * @param mode 目标档位
- * @return 无
- */
-static void usr_timer_rearm(UsrTickMode_t mode)
-{
- if (s_sched.timer_id) {
- usr_timer_del((u16)s_sched.timer_id);
- s_sched.timer_id = 0;
- }
- s_sched.mode = mode;
- s_sched.timer_id = (int)usr_timer_add(NULL, usr_timer_tick, USR_TIMER_IDLE_MS,
- (mode == USR_TICK_BLOCK_SLEEP) ? 1 : 0);
-}
-
-/**
- * @brief 业务定时器:固定 USR_TIMER_IDLE_MS 一拍;忙时禁睡,空闲可睡
+ * @brief 业务定时器:固定 USR_TIMER_IDLE_MS 一拍,priority=1 常驻注册
* @param priv 未使用
* @note ⚠ 运行在硬件定时器时基上下文:禁止阻塞等待、禁止耗时操作。
- * 关机请走 app_power 的钩子,不要在这里直接 power_set_soft_poweroff。
+ * priority 固定 1、只在 usr_jb_init 注册一次、永不重挂:本产品
+ * Type-C 长供电,不需要低功耗;且 app_disp_scan(CT1642 4ms 扫描)
+ * 本就以 priority=1 常驻,系统本来就永远无法进入低功耗,动态切换
+ * 这里的 priority 没有任何实际效果,纯属多余的复杂度。
* @return 无
*/
static void usr_timer_tick(void *priv)
{
uint16_t dt = USR_TIMER_IDLE_MS;
uint16_t i;
- UsrTickMode_t want;
(void)priv;
for (i = 0; i < dt; i++) {
jbTimerIrq();
}
- if (!app_power_poweroff_pending()) {
- user_key_scan(dt);
- user_key_handle(); /* 长按 3s 会在这里同步触发 app_power_request_poweroff() */
- }
-
- app_th_poll(dt); /* 内部 2s 才真正采;IO 分离后采集与显示互不干扰 */
+ user_key_scan(dt); /* 内部完成 5s/12s 长按分级,只置标志 */
+ user_key_handle(); /* 派发短按:切档 */
+ app_th_poll(dt); /* 每2s采集一次温湿度 */
app_mode_run(dt); /* 通风/强力状态机(20ms 计时精度;条件/定时在 500ms 任务上下文) */
- app_fan_run(dt); /* P0 无动作 */
app_disp_run(dt); /* 刷新显示(告警闪烁) */
app_function_poll(); /* 同步 DP */
app_nvm_tick(dt); /* 档位/模式/配置变化检测 + 500ms 防抖计时(不写 flash!) */
- /* OTA 灯效期间最高优先级;关机流程中灯灭 */
- if (app_led_is_ota()) {
- app_led_run(dt, app_function_is_power_on());
- } else if (app_power_poweroff_pending()) {
- app_led_clear();
- } else {
- app_led_run(dt, app_function_is_power_on());
- }
-
- want = usr_timer_pick_mode(); /* 判断能不能休眠 */
- if (want != s_sched.mode) {
- usr_timer_rearm(want);
- }
+ /* OTA 灯效期间最高优先级;power_on 恒 1(DP0 预留不当开关,规格书 0 章第 5 条) */
+ app_led_run(dt, 1);
}
/**
@@ -203,31 +113,25 @@ static void usr_log_boot_version(void)
/**
* @brief 业务初始化(在 BLE 栈起来之前由 app_main 调用)
- * @note 内部会等按键 3s/5s/12s;不足 3s 直接深睡,不会启动业务定时器。
- * app_led_init() 排在 app_boot_wait_key() 前面:3s/5s 提示灯要用
- * LED 事件表(规格书 5.2)。boot 深睡分支不返回,后面初始化不会浪费。
+ * @note ⚠ 上电即开机(规格书 5.1,2026-08-31 改):不再等待按键,插电/冷启动
+ * 直接进入工作态。5s 配对 / 12s OTA 改由运行态 key_manager 长按分级
+ * 触发,动作在 app_function_loop(任务上下文)落地,故此处无需判定。
+ * app_led_init() 仍排在最前:后面模块的日志/灯效都依赖事件表已装载。
* @return 无
*/
void usr_jb_init(void)
{
- usr_log_boot_version(); /* UART 已在 app_main 起来,按键判定前就能看到版本 */
+ usr_log_boot_version(); /* UART 已在 app_main 起来,最先能看到版本 */
bsp_hw_init();
- app_led_init(); /* 提前到按键判定之前:3s 常亮/5s 配对闪都要用灯效事件表 */
-
- app_boot_wait_key(); /* 深睡分支不会返回;返回即代表确定要开机 */
+ app_led_init(); /* 先于一切业务:灯效事件表要最先装载 */
jbInit();
userInit();
- if (usr_var.usr_goto_updata) {
- /* 开机 12s 已进 OTA:立刻挂 200ms 闪 */
- app_led_request_ota();
- }
app_fan_init();
app_disp_init();
- /* CT1642 无内部扫描 RAM,4ms 快节拍轮询 4 位。priority=1 会挡住系统 sleep,
- * USB 常电产品故意如此:管子必须持续扫描,否则肉眼闪。勿改成 priority=0。 */
+
usr_timer_add(NULL, app_disp_scan, BOARD_DISP_SCAN_MS, 1);
app_th_init();
app_mode_init();
@@ -237,16 +141,16 @@ void usr_jb_init(void)
app_nvm_init();
app_nvm_load();
- app_power_init();
- app_power_set_hooks(usr_poweroff_request_hook, usr_poweroff_finalize_hook);
app_function_init();
user_key_init();
+ /* 上电时键若还按着:只锁短按(避免一上电就切档),长按 5s/12s 分级照常生效,
+ * 所以"插电时就按住 5s 进配对"这一用法仍然成立 */
user_key_lock_if_held();
- app_function_power_on_silent();
- usr_timer_rearm(USR_TICK_IDLE);
- log_info("timer idle %ums, sleep ok\n", (unsigned)USR_TIMER_IDLE_MS);
+ /* priority=1 常驻注册,永不重挂(Type-C 长供电不需要低功耗,见 usr_timer_tick) */
+ usr_timer_add(NULL, usr_timer_tick, USR_TIMER_IDLE_MS, 1);
+ log_info("timer started, period=%ums\n", (unsigned)USR_TIMER_IDLE_MS);
}
/**
@@ -267,7 +171,7 @@ void usr_jb_le_recieve_data(u8 *data, u16 len)
* (BLE 线程,栈充足),与 20ms 的 usr_timer_tick(usr_timer_add 异步
* 接口 = 硬件定时器中断上下文,栈极小)不同。
* **所有重量级/自动化引擎必须放这里**:条件/定时引擎(500ms 粒度对
- * 2s 采样与分钟级时长足够,且单次自毁要写 VM)、VM 落盘、关机收口。
+ * 2s 采样与分钟级时长足够,且单次自毁要写 VM)、VM 落盘、配对/OTA 落地。
* 禁止放进 usr_timer_tick——曾因在 20ms 中断里调 syscfg_write 栈溢出软复位。
* @return 无
*/
@@ -277,5 +181,5 @@ void usr_timer_loop(void)
jbProtocolHandle(&gDevData);
app_cond_run(500); /* 条件引擎(任务上下文;条件/定时仲裁见 app_arbiter) */
app_timer_run(500); /* 定时引擎(任务上下文;RTC 门闩 + 窗判断) */
- app_function_loop(); /* 任务上下文:VM 防抖落盘 + 延后关机收口 */
+ app_function_loop(); /* 任务上下文:VM 防抖落盘 + 配对/OTA 动作落地 */
}
diff --git a/apps/usr_periph/board_pin.h b/apps/usr_periph/board_pin.h
index 27d835b..f8811df 100644
--- a/apps/usr_periph/board_pin.h
+++ b/apps/usr_periph/board_pin.h
@@ -3,9 +3,7 @@
* @brief CBR3 温湿度风扇板级引脚、电平与时序(换板只改本文件)
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(P0 Bring-up)
+ * @version V1.0.0,首次发布(P0 Bring-up)
*
* @note 产品 GPIO 全部集中到本文件;板级 board_jl701n_demo_cfg.h 只保留
* UART0_TX=PG6 一项改动。CT1642 用 PG0(CLK)/PG1(DATA),AHT20 用
@@ -54,10 +52,15 @@
/* 按键 / 开机 */
/*============================================================================*/
-#define BOARD_KEY_CLICK_MS 1000 /**< 开机后:按下 <1s 松开为短按切档 */
-#define BOARD_KEY_POWER_MS 3000 /**< 关机态:按住 3s 开机;开机态:按住 3s 关机 */
-#define BOARD_KEY_PAIR_MS 5000 /**< 关机态:按住 5s 开机并进配对 */
-#define BOARD_KEY_OTA_MS 12000 /**< 关机态:按住 12s 开机并进 OTA */
+/**
+ * 直接作为 key.c 状态机的 keyLongTime/keyLastTime 使用:
+ * 松手时刻
* @date 2026.08.29
- * @version V1.0.3
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布
- * - V1.0.1, 2026.08.29, cyWu, 修复 SCL 高电平期间推挽输出 SDA 导致的假 STOP
- * (读数据必 CRC fail 的根因);补 AHT20 0x71 校准检查 + 0xBE 初始化
- * - V1.0.2, 2026.08.29, cyWu, 排障结束:回退诊断代码(电平自检/地址扫描/IN 直读),
- * 保留时序修复、内部上拉依赖、总线恢复、0x71/0xBE 初始化流程
- * - V1.0.3, 2026.08.29, cyWu, IO 分离:AHT20 独占 PC4(SCL)/PC5(SDA),删除
- * bsp_bus 共线锁与 measure_start/finish 拆分锁,采集恢复一次完成;
- * PC4/PC5 上拉配置从 bsp_bus_init 移入本文件 init
+ * @version V1.0.3,IO 分离:AHT20 独占 PC4(SCL)/PC5(SDA),无总线共享锁,
+ * 采集一次完成;修复 SCL 高电平期间推挽输出 SDA 导致的假 STOP,
+ * 补 0x71 校准检查 + 0xBE 初始化
******************************************************************************/
#include "bsp_aht20.h"
diff --git a/apps/usr_periph/bsp_aht20.h b/apps/usr_periph/bsp_aht20.h
index 56c9d41..045764b 100644
--- a/apps/usr_periph/bsp_aht20.h
+++ b/apps/usr_periph/bsp_aht20.h
@@ -3,13 +3,8 @@
* @brief CBR3 AHT20 温湿度传感器:软件 I2C + 测量 + CRC8
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.3
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布
- * - V1.0.1, 2026.08.29, cyWu, 修复 SCL 高电平期间推挽输出 SDA 导致的假 STOP
- * - V1.0.2, 2026.08.29, cyWu, 回退诊断代码,保留时序修复/上拉/总线恢复/0x71 校准
- * - V1.0.3, 2026.08.29, cyWu, IO 分离:AHT20 独占 PC4(SCL)/PC5(SDA),删除
- * bsp_bus 共线锁与 measure_start/finish 拆分锁,恢复一次完成采集
+ * @version V1.0.3,IO 分离:AHT20 独占 PC4(SCL)/PC5(SDA),无总线共享锁,
+ * 恢复一次完成采集;含 SCL 高电平假 STOP 修复与 0x71 校准检查
*
* @note I2C 7 位地址 0x38(写 0x70 / 读 0x71)。测量命令 0xAC 0x33 0x00,
* 等 ≥80ms 后读 7 字节:Status + 20bit 湿度 + 20bit 温度 + CRC8。
diff --git a/apps/usr_periph/bsp_ct1642.c b/apps/usr_periph/bsp_ct1642.c
index 300cc21..1387360 100644
--- a/apps/usr_periph/bsp_ct1642.c
+++ b/apps/usr_periph/bsp_ct1642.c
@@ -3,12 +3,7 @@
* @brief CBR3 CT1642 类数码管驱动实现(软件移位)
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.1
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布
- * - V1.0.1, 2026.08.29, cyWu, IO 分离:CT1642 独立 PG0=CLK/PG1=DATA,删除
- * bsp_bus 共线机制;删调试遗留 latch/show4/scan_seg/scan_com,
- * 新增 bsp_ct1642_init()
+ * @version V1.0.1,IO 分离:CT1642 独立 PG0=CLK/PG1=DATA,无总线共享机制
*
* @note 驱动协议严格参照 CT1642 手册(与 CSDN yitx 一致):
* 一帧 = 18bit,CLK 上升沿采样 DATA(DATA 只在 CLK=低时改变):
diff --git a/apps/usr_periph/bsp_ct1642.h b/apps/usr_periph/bsp_ct1642.h
index 6c1933d..ea65995 100644
--- a/apps/usr_periph/bsp_ct1642.h
+++ b/apps/usr_periph/bsp_ct1642.h
@@ -3,12 +3,8 @@
* @brief CBR3 CT1642 类数码管驱动:18bit 移位、锁存、送出
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.1
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布
- * - V1.0.1, 2026.08.29, cyWu, IO 分离:CT1642 独立 PG0=CLK/PG1=DATA(飞线),
- * AHT20 独占 PC4/PC5,删除 bsp_bus 共线机制;删调试遗留
- * latch/show4/scan_seg/scan_com,新增 bsp_ct1642_init()
+ * @version V1.0.1,IO 分离:CT1642 独立 PG0=CLK/PG1=DATA(飞线),AHT20
+ * 独占 PC4/PC5,无总线共享机制
*
* @note PG0=CLK,PG1=DATA,独立引脚、无总线锁,可随时调用。
* 协议:一帧 18bit(C1-C4 位选 + 6 位保留 + Q2-Q9 段码),CLK 上升沿采样
diff --git a/apps/usr_periph/bsp_hw.c b/apps/usr_periph/bsp_hw.c
index 7a75983..4e4136a 100644
--- a/apps/usr_periph/bsp_hw.c
+++ b/apps/usr_periph/bsp_hw.c
@@ -3,9 +3,7 @@
* @brief CBR3 板级 GPIO / MCPWM:LED、按键、风扇
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹:删电机/充电/电量,改单 LED + 风扇 PWM)
+ * @version V1.0.0,首次发布(移植门夹:删电机/充电/电量,改单 LED + 风扇 PWM)
*
* @note CT1642 在 PG0/PG1、AHT20 在 PC4/PC5,各自独立驱动,
* bsp_hw 只管 LED/KEY/FAN,不碰这两组脚。
diff --git a/apps/usr_periph/bsp_hw.h b/apps/usr_periph/bsp_hw.h
index f8207aa..9e7bd9b 100644
--- a/apps/usr_periph/bsp_hw.h
+++ b/apps/usr_periph/bsp_hw.h
@@ -3,9 +3,7 @@
* @brief CBR3 板级硬件:LED / 按键 / 风扇 PWM
* @author cyWu <1917507415@qq.com>
* @date 2026.08.29
- * @version V1.0.0
- * @history
- * - V1.0.0, 2026.08.29, cyWu, 首次发布(移植门夹:删电机/充电/电量,改单 LED + 风扇 PWM)
+ * @version V1.0.0,首次发布(移植门夹:删电机/充电/电量,改单 LED + 风扇 PWM)
******************************************************************************/
#ifndef __BSP_HW_H__
diff --git a/apps/usr_periph/usr_rtc.c b/apps/usr_periph/usr_rtc.c
index d44dbe4..ca67ecb 100644
--- a/apps/usr_periph/usr_rtc.c
+++ b/apps/usr_periph/usr_rtc.c
@@ -3,10 +3,7 @@
* @brief 板级 RTC 外设实现(BR28 硬件 rtc_dev_ops)
* @author cyWu <1917507415@qq.com>
* @date 2026.08.19
- * @version V1.1.0
- * @history
- * - V1.0.0, 2026.08.19, cyWu, 从模组固件移植至 SOC SDK
- * - V1.1.0, 2026.08.19, cyWu, 精简公共接口与缓存逻辑
+ * @version V1.1.0,从模组固件移植至 SOC SDK,精简公共接口与缓存逻辑
******************************************************************************/
#include "system/includes.h"
diff --git a/apps/usr_periph/usr_rtc.h b/apps/usr_periph/usr_rtc.h
index 9c5de7e..f7e56d0 100644
--- a/apps/usr_periph/usr_rtc.h
+++ b/apps/usr_periph/usr_rtc.h
@@ -3,10 +3,8 @@
* @brief 板级 RTC:硬件对时 + 客户读本地日历
* @author cyWu <1917507415@qq.com>
* @date 2026.08.19
- * @version V1.1.0
- * @history
- * - V1.0.0, 2026.08.19, cyWu, 从模组固件移植至 SOC SDK
- * - V1.1.0, 2026.08.19, cyWu, 精简接口;usr_rtc_get_time 返回本地日历
+ * @version V1.1.0,从模组固件移植至 SOC SDK;精简接口,usr_rtc_get_time
+ * 返回本地日历
******************************************************************************/
#ifndef __USR_RTC_H__
diff --git a/cpu/br28/tools/download/earphone/download_app_ota.bat b/cpu/br28/tools/download/earphone/download_app_ota.bat
index 59fc4df..81617ed 100644
--- a/cpu/br28/tools/download/earphone/download_app_ota.bat
+++ b/cpu/br28/tools/download/earphone/download_app_ota.bat
@@ -10,7 +10,7 @@ copy ..\..\ota.bin .
copy ..\..\anc_coeff.bin .
copy ..\..\anc_gains.bin .
-..\..\isd_download.exe ..\..\isd_config.ini -tonorflash -dev br28 -boot 0x120000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin -res ..\..\cfg_tool.bin tone.cfg p11_code.bin -uboot_compress
+..\..\isd_download.exe ..\..\isd_config.ini -tonorflash -dev br28 -boot 0x120000 -div8 -wait 300 -uboot ..\..\uboot.boot -app ..\..\app.bin -res ..\..\cfg_tool.bin tone.cfg p11_code.bin -uboot_compress -key RS-5B88.key
:: -format all -key RS-5B88.key
::-reboot 2500