1、运行中空电关机改看显示百分比,到 5% 立刻停电机、关绿灯,红灯闪 5 下后再深睡,避免电压先掉、APP 还没到 5% 就关机
2、充电/放电电量缓冲加快,放电突变拦截先关掉,电量曲线满电点收到 4.12V 3、上电串口打印软硬件版本和编译时间
This commit is contained in:
+54
-33
@@ -2,13 +2,15 @@
|
||||
* @file app_power.c
|
||||
* @brief USB / 充电检测、电量采样与低电策略、深睡请求生命周期实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.26
|
||||
* @version V1.1.0
|
||||
* @date 2026.08.27
|
||||
* @version V1.2.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现 USB/充电检测、电量采样与深睡请求
|
||||
* - V1.1.0, 2026.08.26, cyWu, 取消低电闪烁 1 分钟后自动关机;自动关机门限
|
||||
* 改为约 5%(BOARD_VBAT_SHUTDOWN_MV),与开机
|
||||
* 低电拦截(app_boot)共用同一门限
|
||||
* - V1.2.0, 2026.08.27, cyWu, 运行中空电关机改用显示百分比,到 5% 立即停电机
|
||||
* 并请求空电灯效后再掉电,不再做连续次数窗口
|
||||
******************************************************************************/
|
||||
|
||||
#include "app_power.h"
|
||||
@@ -34,7 +36,6 @@
|
||||
#define BAT_BOOT_MS 3000 /* 上电 3s 内不因低电深睡 */
|
||||
#define BAT_LOG_MS 10000
|
||||
#define BAT_LOW_WIN 3 /* 连续约 2.4s 才进低电告警(仅闪灯,不关机) */
|
||||
#define BAT_EMPTY_WIN 5 /* 连续约 4s 才判定 <=5%,自动关机 */
|
||||
|
||||
/** 充电检测:CHG/VPWR 消抖后的结果 */
|
||||
typedef struct {
|
||||
@@ -57,11 +58,10 @@ typedef struct {
|
||||
|
||||
/** 低电告警:进入/退出都要连续窗口消抖,避免电机拉压降误报
|
||||
* @note 低电(BOARD_VBAT_LOW_MV)只负责红灯闪烁提示,不再有超时自动关机;
|
||||
* 真正的自动关机改由 power_battery_check_empty 在电量约 5%
|
||||
* (BOARD_VBAT_SHUTDOWN_MV)时触发 */
|
||||
* 真正的自动关机改由 power_battery_check_empty 在显示电量
|
||||
* <= BOARD_BATTERY_SHUTDOWN_PCT(5%)时立刻触发 */
|
||||
typedef struct {
|
||||
uint8_t low_bat;
|
||||
uint8_t empty_win;
|
||||
uint8_t enter_win;
|
||||
uint8_t exit_win;
|
||||
} AppBatteryAlarm_t;
|
||||
@@ -72,6 +72,7 @@ typedef struct {
|
||||
AppBatterySample_t sample;
|
||||
AppBatteryAlarm_t alarm;
|
||||
uint8_t req_poweroff;
|
||||
uint8_t empty_off; /**< 1=空电路径:闪完红灯再掉电 */
|
||||
AppPowerHook_t on_request;
|
||||
AppPowerHook_t on_finalize;
|
||||
} AppPowerCtx_t;
|
||||
@@ -83,7 +84,7 @@ static AppPowerEvt_t power_charge_tick(uint16_t dt);
|
||||
static void power_battery_tick(uint16_t dt);
|
||||
static uint8_t power_battery_sample_ready(uint16_t dt, uint16_t *out_mv);
|
||||
static void power_battery_update_percent(uint16_t mv);
|
||||
static void power_battery_check_empty(uint16_t mv);
|
||||
static void power_battery_check_empty(void);
|
||||
static void power_battery_check_low(uint16_t mv);
|
||||
static void power_battery_periodic_log(uint16_t dt);
|
||||
|
||||
@@ -100,8 +101,8 @@ void app_power_init(void)
|
||||
|
||||
/**
|
||||
* @brief 注册深睡请求的两段式回调
|
||||
* @param on_request 刚请求深睡时调用(停电机、灯全灭)
|
||||
* @param on_finalize 50ms 后调用(真正进深睡,不会返回)
|
||||
* @param on_request 刚请求深睡时调用(停电机;灯效由调用方按关机类型决定)
|
||||
* @param on_finalize 延时到点后调用(真正进深睡,不会返回)
|
||||
* @return 无
|
||||
*/
|
||||
void app_power_set_hooks(AppPowerHook_t on_request, AppPowerHook_t on_finalize)
|
||||
@@ -151,15 +152,16 @@ uint8_t app_power_low_bat(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 请求深睡:立即触发 on_request 钩子,50ms 后触发 on_finalize
|
||||
* @brief 请求深睡(按键 3s 等):立即触发 on_request 钩子,50ms 后触发 on_finalize
|
||||
* @return 无
|
||||
*/
|
||||
void app_power_request_poweroff(void)
|
||||
{
|
||||
if (s_power.req_poweroff) {
|
||||
return; /* 已经在关机流程里,不要重复挂 50ms 定时器 */
|
||||
return; /* 已经在关机流程里,不要重复挂定时器 */
|
||||
}
|
||||
s_power.req_poweroff = 1;
|
||||
s_power.empty_off = 0;
|
||||
log_info("request deep off\n");
|
||||
if (s_power.on_request) {
|
||||
s_power.on_request(); /* 立刻停电机、灯全灭 */
|
||||
@@ -167,6 +169,24 @@ void app_power_request_poweroff(void)
|
||||
sys_timeout_add(NULL, power_do_poweroff, 50); /* 给 GPIO 落稳一点再真正掉电 */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 空电关机:立刻停电机,等红灯闪完 5 下后再真正掉电
|
||||
* @return 无
|
||||
*/
|
||||
void app_power_request_empty_poweroff(void)
|
||||
{
|
||||
if (s_power.req_poweroff) {
|
||||
return;
|
||||
}
|
||||
s_power.req_poweroff = 1;
|
||||
s_power.empty_off = 1;
|
||||
log_info("request empty off, wait %ums\n", (unsigned)BOARD_LED_EMPTY_OFF_WAIT_MS);
|
||||
if (s_power.on_request) {
|
||||
s_power.on_request(); /* 立刻停电机,调用方挂空电红闪 */
|
||||
}
|
||||
sys_timeout_add(NULL, power_do_poweroff, BOARD_LED_EMPTY_OFF_WAIT_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查询深睡请求是否已发起
|
||||
* @return 1=已发起,0=未发起
|
||||
@@ -176,6 +196,15 @@ uint8_t app_power_poweroff_pending(void)
|
||||
return s_power.req_poweroff;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 当前这次关机是否为空电路径
|
||||
* @return 1=空电关机,0=普通关机
|
||||
*/
|
||||
uint8_t app_power_empty_off_pending(void)
|
||||
{
|
||||
return s_power.empty_off;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 50ms 延时到点:真正进入深睡
|
||||
* @param priv 未使用
|
||||
@@ -304,31 +333,24 @@ static void power_battery_update_percent(uint16_t mv)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 低电自动关机判定:连续 BAT_EMPTY_WIN 次采样都 <= SHUTDOWN_MV(约 5%)才认定,直接请求深睡
|
||||
* @note 充电中不判定;用连续窗口而非单次采样,避免电机拉压降瞬间跌破误报。
|
||||
* 对应“电量低于 5% 自动关机”,与开机时的低电拦截(app_boot)用同一门限。
|
||||
* @param mv 本次均值电压,单位 mV
|
||||
* @brief 空电关机判定:显示电量一到 <= 5% 立刻停电机并请求空电关机
|
||||
* @note 充电中不判定。百分比已经在 app_battery 做过滤波/缓冲,这里不再做连续次数窗口。
|
||||
* 开机拦截(app_boot)仍用 BOARD_VBAT_SHUTDOWN_MV,因为那时电量算法还没跑。
|
||||
* @return 无
|
||||
*/
|
||||
static void power_battery_check_empty(uint16_t mv)
|
||||
static void power_battery_check_empty(void)
|
||||
{
|
||||
AppBatteryAlarm_t *a = &s_power.alarm;
|
||||
uint8_t pct = s_power.sample.pct;
|
||||
|
||||
if (s_power.charge.usb_online || mv > BOARD_VBAT_SHUTDOWN_MV) {
|
||||
a->empty_win = 0; /* 充电中或电压还够,窗口清零 */
|
||||
if (s_power.charge.usb_online || pct > BOARD_BATTERY_SHUTDOWN_PCT) {
|
||||
return;
|
||||
}
|
||||
if (a->empty_win < 0xFF) {
|
||||
a->empty_win++;
|
||||
}
|
||||
if (a->empty_win < BAT_EMPTY_WIN) {
|
||||
return; /* 还没连续够次数,可能是电机拉压降的瞬间 */
|
||||
}
|
||||
a->low_bat = 1;
|
||||
s_power.alarm.low_bat = 1;
|
||||
gDevData.alm_low_battery = 1;
|
||||
if (!s_power.req_poweroff) {
|
||||
log_info("vbat %dmV <=5%%, deep off\n", mv);
|
||||
app_power_request_poweroff();
|
||||
log_info("battery %d%% <=%d%%, stop motor + blink red then off\n",
|
||||
pct, BOARD_BATTERY_SHUTDOWN_PCT);
|
||||
app_power_request_empty_poweroff();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,10 +413,9 @@ static void power_battery_periodic_log(uint16_t dt)
|
||||
/**
|
||||
* @brief 电量采样与低电策略,每个业务 tick 调用一次
|
||||
* @note 显示用的百分比(滤波/充放电基准/突变防抖/缓冲平滑)交给 app_battery
|
||||
* 模块处理;这里的 check_empty/check_low 是另一套基于原始电压的
|
||||
* 低电告警迟滞,直接判 mv 阈值,跟 app_battery 各管一段、互不干扰。
|
||||
* check_low(3.7V)只负责红灯闪烁提示,不再有超时自动关机;
|
||||
* check_empty(约 5%/3.25V)才是真正触发自动关机的判定。
|
||||
* 模块处理。check_low 仍按原始电压做 3.7V 红灯提示;check_empty
|
||||
* 按显示电量 <= BOARD_BATTERY_SHUTDOWN_PCT(5%)关机,跟 APP 看到
|
||||
* 的数字对齐,避免电压先掉、百分比还没到 5% 就关机。
|
||||
* @param dt 距上次调用的毫秒数
|
||||
* @return 无
|
||||
*/
|
||||
@@ -411,7 +432,7 @@ static void power_battery_tick(uint16_t dt)
|
||||
power_battery_update_percent(mv);
|
||||
/* 开机保护期内只更新百分比,不判定任何告警 */
|
||||
if (s->boot_ms >= BAT_BOOT_MS) {
|
||||
power_battery_check_empty(mv);
|
||||
power_battery_check_empty();
|
||||
power_battery_check_low(mv);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user