精简 usr_app 架构:去掉关机/休眠死代码,按键复用 key.c 状态机,ISR 重活统一延后到任务上下文

Type-C 长供电、上电即工作、无软关机,删除 app_boot/app_power 及 DP0 开关相关逻辑,
去掉空占位 app_fan_run 和无效的 usr_timer_pick_mode/rearm(数码管 4ms 扫描已 priority=1,
系统本来就不会进低功耗)。按键 5s 配对 / 12s OTA 改走 key.c 长按分级,去掉自计时与 1~5s 死区。
新增 app_defer,NVM/配对/OTA 只在 ISR 置位、在 500ms 任务上下文落地,避免再在 usr_timer 里写 flash。
规格书与注释同步精简。
This commit is contained in:
2026-08-31 17:57:52 +08:00
parent 5cc7e9dbd6
commit e324ebd99b
51 changed files with 542 additions and 886 deletions
+33 -129
View File
@@ -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_scanCT1642 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 无内部扫描 RAM4ms 快节拍轮询 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_tickusr_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 动作落地 */
}