/****************************************************************************** * @file app_boot.c * @brief 深睡唤醒 / 上电按键判定实现 * @author cyWu <1917507415@qq.com> * @date 2026.08.26 * @version V1.1.0 * @history * - V1.0.0, 2026.08.25, cyWu, 首次发布,实现深睡唤醒按键判定(3s 开机 / 5s 配对) * - V1.1.0, 2026.08.26, cyWu, 新增 12s 长按进 OTA、开机前低电量拦截 ******************************************************************************/ #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 "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 起来前进深睡(1ms 定时器尚未启动),原地喂狗不返回 * @note power_set_soft_poweroff() 只是向 SDK 底层发起软关机请求(置个标志位), * 不是同步断电——仓库里其它调用点(如 app_power_manage.c)调用完都是 * 正常往下走/return,真正切电源是底层电源管理在之后某个时机才执行的。 * 此时 BLE 栈、1ms 业务定时器都还没起来,如果这里直接 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 拒绝开机前的低电提示:红灯闪烁 BOARD_BOOT_LOWBAT_BLINK_N 下 * @return 无 */ static void boot_blink_low_battery(void) { uint8_t i; for (i = 0; i < BOARD_BOOT_LOWBAT_BLINK_N; i++) { bsp_led_red_set(1); boot_delay_ms(BOARD_BOOT_LOWBAT_ON_MS); bsp_led_red_set(0); boot_delay_ms(BOARD_BOOT_LOWBAT_OFF_MS); } } /** * @brief 开机前电量判定:连续采样取均值,避免单次毛刺误判 * @note 此时 app_battery/app_power 均未初始化,只能摸裸电压,不走那套 * 滤波/缓冲算法;和运行中的自动关机(app_power)共用同一门限 * BOARD_VBAT_SHUTDOWN_MV(约 5%),口径一致 * @return 1=电量过低(约 <=5%),0=正常 */ static uint8_t boot_battery_too_low(void) { uint32_t acc = 0; uint8_t i; const uint8_t n = 4; for (i = 0; i < n; i++) { acc += bsp_vbat_mv(); boot_delay_ms(5); } return (uint8_t)((acc / n) <= BOARD_VBAT_SHUTDOWN_MV); } /** * @brief 上电是否跳过 3s/5s 按键判定 * @note 只有“真的断电后重新插电池 / 用户主动按键”才需要走 3s/5s 判定; * USB 在位、或者芯片自己意外重启的情况,都应该直接保持开机, * 否则用户会遇到“设备莫名关机、还要求重新按 3 秒才能开机”的怪现象。 * @return 1=USB / 软复位 / LVD / 看门狗,保持开机且不进配对;0=需要走按键判定 */ static uint8_t boot_skip_key_wait(void) { uint8_t i; /* USB 在位(充电口有电):说明是插着电源,不是用户凭空按键开机 */ for (i = 0; i < 10; i++) { if (bsp_chg_is_low() || bsp_vpwr_is_online()) { log_info("boot usb, skip key wait\n"); return 1; } boot_delay_ms(5); } // /* 以下都是“芯片自己意外重启了一下”,不是用户主动按开机键, // * 不能因此逼用户重新按键,否则相当于设备无故“关机”了一次 */ // if (cpu_reset_by_soft() /* SDK 综合判断:是否软件复位 */ // || is_reset_source(MSYS_SOFT_RST) /* 主系统软件复位(断言/异常/OTA 后重启等) */ // || is_reset_source(P33_SOFT_RST) /* P33 软件复位,最常见的软复位入口 */ // || is_reset_source(P33_VDDIO_LVD_RST) /* VDDIO 低压复位(电机启动拉低电压误触发) */ // || is_reset_source(P11_WDT_RST)) { /* 看门狗复位(程序卡死被强制重启) */ // log_info("boot reset-keep, skip key wait\n"); // return 1; // } return 0; } /** * @brief 等待并判定开机按键 * @note 配对(5s)不再一到点就立即提交:需要继续按住观察是否会按到 12s * 触发 OTA,真正的配对提交放在松手那一刻做,行为上仍是“按满 5s * 再松手即进配对”,只是多给了继续按到 12s 改道 OTA 的机会。 * @return 无(深睡分支不返回) */ void app_boot_wait_key(void) { uint32_t t0; uint32_t elapsed; uint8_t green_on = 0; uint8_t pair_armed = 0; usr_var.usr_goto_pair = 0; /* USB 在位直接开机,不走 3s/5s/12s 按键判定 */ if (boot_skip_key_wait()) { return; } if (!bsp_key_is_pressed()) { log_info("boot no key, sleep\n"); boot_sleep(); } /* 未充电时电量过低:闪灯提示后继续深睡,不给开机 */ if (boot_battery_too_low()) { log_info("boot battery <=5%%, blink %d and stay off\n", BOARD_BOOT_LOWBAT_BLINK_N); boot_blink_low_battery(); boot_sleep(); } t0 = timer_get_ms(); bsp_led_all_off(); 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 只武装配对,真正提交放到松手那一刻,给继续按到 12s 改道 OTA 的机会 */ if (!pair_armed && (elapsed >= BOARD_KEY_PAIR_MS)) { pair_armed = 1; log_info("boot hold %ums, pair armed (release now for pair, " "keep holding to %ums for ota)\n", (unsigned)elapsed, (unsigned)BOARD_KEY_OTA_MS); } if (!green_on && (elapsed >= BOARD_KEY_POWER_MS)) { green_on = 1; bsp_led_green_set(1); log_info("boot hold 3s, will power on\n"); } } 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); } }