KT25-1015_AC695x_SDK310/apps/kaotings/kt_battery.c

323 lines
12 KiB
C
Raw Permalink Normal View History

2026-05-07 14:11:18 +00:00
/*
* kt_battery.c
*
* SDK get_vbat_percent() , KT_CFG_VBAT_DET_PIN(PA12)
* 100K/100K ADC 线 4.2V/3.2V
*
* :
* 1) , IC CV 4.2V, SOC
* ~4.2V,** SOC**;
* 2) ,"极化电压"( OCV
* 0.1~0.3V),~退 OCV;
* 3) ,( PA ) SOC ,
* , +
*
* :
* 1) (vbat_charging=1): SOC ,"时间法"
* ( KT_BAT_CHARGE_BUMP_SEC +1%, 100%),;
* 2) : KT_BAT_RECOVERY_MS 退, %,
* OCV;
* 3) 退: %,,;
* 4) :16 + "只许降" + 1% ,
*
* ,便
*/
#include "kt_battery.h"
#include "asm/adc_api.h"
#include "system/timer.h"
2026-05-08 14:12:19 +00:00
#include "user_cfg_id.h"
2026-05-07 14:11:18 +00:00
/* 采样周期 ms;周期 × 缓冲长度 = 平滑窗口 */
#define KT_BAT_SAMPLE_MS 200u
/* 滑动平均缓冲长度,值越大越稳越慢 */
#define KT_BAT_FILTER_N 16u
/* 滞回门槛(百分点),新值与旧值差值需 ≥ 该门槛才采纳 */
#define KT_BAT_HYSTERESIS 1u
/* 拔下充电器后的极化消退期(ms):锂电池端电压从充电"极化高电压"回落到真实 OCV
* ,~ 8s 4217 3924, 12s */
#define KT_BAT_RECOVERY_MS 12000u
#define KT_BAT_RECOVERY_TICKS ((u8)(KT_BAT_RECOVERY_MS / KT_BAT_SAMPLE_MS))
/* 充电模拟爬升:分两段,贴合锂电池 CC/CV 真实充电速率。
* CC ( < 4.2V,SOC < 80%): IC ,;
* CV ( = 4.2V,SOC 80~100%): SOC ,
*
* 4000mAh / IP5306 0.8A:
* CC: (40 mAh/1%) / 800 mA × 3600 180 /1% 0~80% 4
* CV: ~0.4A,(40 mAh/1%) / 400 mA × 3600 360 /1% 80~100% 2
* 6
*
* 80%( CCCV )/ */
#define KT_BAT_CC_BUMP_SEC 180u
#define KT_BAT_CV_BUMP_SEC 360u
#define KT_BAT_CC_CV_THRESHOLD 80u
#define KT_BAT_CC_BUMP_TICKS ((u16)(KT_BAT_CC_BUMP_SEC * 1000u / KT_BAT_SAMPLE_MS))
#define KT_BAT_CV_BUMP_TICKS ((u16)(KT_BAT_CV_BUMP_SEC * 1000u / KT_BAT_SAMPLE_MS))
2026-05-08 14:12:19 +00:00
/* VM 写入策略: 百分比变化至少 2%,且间隔至少 10 分钟 */
#define KT_BAT_VM_SAVE_DELTA_PERCENT 2u
#define KT_BAT_VM_SAVE_MIN_SEC 600u
#define KT_BAT_VM_SAVE_MIN_TICKS ((u16)(KT_BAT_VM_SAVE_MIN_SEC * 1000u / KT_BAT_SAMPLE_MS))
/* 首次无历史记录时,插电开机随机起点 65~75% */
#define KT_BAT_BOOT_FALLBACK_MIN 65u
#define KT_BAT_BOOT_FALLBACK_MAX 75u
/* VM 数据校验 */
#define KT_BAT_VM_TAG 0xA5u
2026-05-07 14:11:18 +00:00
static u16 vbat_buf[KT_BAT_FILTER_N];
static u8 vbat_buf_idx;
static u8 vbat_buf_filled;
static u16 vbat_avg_mv;
static u8 vbat_percent_cached = 100u;
static u8 vbat_charging;
static u8 vbat_recovery_ticks; /* > 0 表示处于"极化消退期",期间不更新 % */
static u16 vbat_charge_bump_cnt; /* 充电中累计的 200ms tick 数,达到阈值就 +1% */
2026-05-08 14:12:19 +00:00
static u16 vbat_vm_save_ticks;
static u8 vbat_vm_last_saved_percent = 0xFFu;
2026-05-07 14:11:18 +00:00
static u16 vbat_timer_id;
2026-05-08 14:12:19 +00:00
struct kt_bat_vm_record {
u8 tag;
u8 percent;
u8 checksum;
};
static u8 kt_battery_vm_checksum(u8 tag, u8 percent)
{
return (u8)(tag ^ percent ^ 0x5Au);
}
static u8 kt_battery_random_percent_fallback(void)
{
u8 rnd = 0;
get_random_number(&rnd, 1);
return (u8)(KT_BAT_BOOT_FALLBACK_MIN
+ (rnd % (KT_BAT_BOOT_FALLBACK_MAX - KT_BAT_BOOT_FALLBACK_MIN + 1u)));
}
static u8 kt_battery_vm_load_percent(u8 *percent)
{
struct kt_bat_vm_record rec;
if (!percent) {
return 0u;
}
if (syscfg_read(VM_KT_BAT_LAST_PERCENT, &rec, sizeof(rec)) != sizeof(rec)) {
return 0u;
}
if (rec.tag != KT_BAT_VM_TAG) {
return 0u;
}
if (rec.checksum != kt_battery_vm_checksum(rec.tag, rec.percent)) {
return 0u;
}
if ((rec.percent == 0u) || (rec.percent >= 100u)) {
return 0u;
}
*percent = rec.percent;
return 1u;
}
static void kt_battery_vm_save_percent(u8 percent)
{
struct kt_bat_vm_record rec;
if ((percent == 0u) || (percent >= 100u)) {
return;
}
rec.tag = KT_BAT_VM_TAG;
rec.percent = percent;
rec.checksum = kt_battery_vm_checksum(rec.tag, rec.percent);
if (syscfg_write(VM_KT_BAT_LAST_PERCENT, &rec, sizeof(rec)) == sizeof(rec)) {
vbat_vm_last_saved_percent = percent;
vbat_vm_save_ticks = 0;
printf("kt_battery: vm save percent=%d\n", percent);
}
}
2026-05-07 14:11:18 +00:00
static u16 kt_battery_read_raw_mv(void)
{
/* SDK adc_get_voltage 返回引脚电压 (mV),× 分压系数 = 电池电压 (mV) */
u32 pin_mv = adc_get_voltage(AD_CH_PA12);
u32 bat_mv = pin_mv * KT_BAT_DIVIDER_NUM;
if (bat_mv > 0xFFFFu) {
bat_mv = 0xFFFFu;
}
return (u16)bat_mv;
}
static u8 kt_battery_mv_to_percent(u16 mv)
{
if (mv >= KT_BAT_FULL_MV) {
return 100u;
}
if (mv <= KT_BAT_EMPTY_MV) {
return 0u;
}
return (u8)(((u32)(mv - KT_BAT_EMPTY_MV) * 100u)
/ (KT_BAT_FULL_MV - KT_BAT_EMPTY_MV));
}
/* 用即时电压重新种子化整个滑窗缓冲,后续采样从这个值开始平滑 */
static void kt_battery_reseed(u16 mv)
{
for (u8 i = 0; i < KT_BAT_FILTER_N; i++) {
vbat_buf[i] = mv;
}
vbat_buf_idx = 0;
vbat_buf_filled = 1;
vbat_avg_mv = mv;
}
static void kt_battery_sample_cb(void *priv)
{
(void)priv;
u16 raw = kt_battery_read_raw_mv();
/* 1) 充电中:充电 IC CV 阶段把端电压维持在 4.2V,与真实 SOC 无关。
* ,"时间法"
* CC/CV ,线 */
if (vbat_charging) {
vbat_avg_mv = raw;
if (vbat_percent_cached < 100u) {
u16 bump_ticks = (vbat_percent_cached < KT_BAT_CC_CV_THRESHOLD)
? KT_BAT_CC_BUMP_TICKS /* CC 阶段:快 */
: KT_BAT_CV_BUMP_TICKS; /* CV 阶段:慢 */
vbat_charge_bump_cnt++;
if (vbat_charge_bump_cnt >= bump_ticks) {
vbat_charge_bump_cnt = 0;
vbat_percent_cached++;
printf("kt_battery: charge bump -> %d%% (%s)\n",
vbat_percent_cached,
(vbat_percent_cached <= KT_BAT_CC_CV_THRESHOLD) ? "CC" : "CV");
}
}
return;
}
/* 2) 刚拔下充电器:端电压还在"极化电压"阶段(高于真实 OCV),继续锁住 %,
* 退 OCV SOC */
if (vbat_recovery_ticks > 0) {
vbat_recovery_ticks--;
vbat_avg_mv = raw;
if (vbat_recovery_ticks == 0) {
kt_battery_reseed(raw);
vbat_percent_cached = kt_battery_mv_to_percent(raw);
2026-05-08 14:12:19 +00:00
/* 恢复期结束后从 0 重新计时,避免立刻写 VM */
vbat_vm_save_ticks = 0;
2026-05-07 14:11:18 +00:00
}
return;
}
/* 3) 正常放电:滑窗平均 + 单向下降滞回 */
vbat_buf[vbat_buf_idx++] = raw;
if (vbat_buf_idx >= KT_BAT_FILTER_N) {
vbat_buf_idx = 0;
vbat_buf_filled = 1;
}
u8 n = vbat_buf_filled ? (u8)KT_BAT_FILTER_N : vbat_buf_idx;
if (n == 0) {
return;
}
u32 sum = 0;
for (u8 i = 0; i < n; i++) {
sum += vbat_buf[i];
}
vbat_avg_mv = (u16)(sum / n);
u8 new_p = kt_battery_mv_to_percent(vbat_avg_mv);
/* 放电只允许 % 下降:负载突变(如 PA 工作)瞬时电压回弹不会让 % 反弹,
* 0% , 1% */
if (new_p == 0u) {
vbat_percent_cached = 0u;
} else if ((u16)new_p + KT_BAT_HYSTERESIS <= vbat_percent_cached) {
vbat_percent_cached = new_p;
}
2026-05-08 14:12:19 +00:00
if (vbat_vm_save_ticks < 0xFFFFu) {
vbat_vm_save_ticks++;
}
if ((vbat_percent_cached > 0u)
&& (vbat_percent_cached < 100u)
&& (vbat_vm_last_saved_percent != 0xFFu)
&& ((u8)((vbat_vm_last_saved_percent > vbat_percent_cached)
? (vbat_vm_last_saved_percent - vbat_percent_cached)
: (vbat_percent_cached - vbat_vm_last_saved_percent)) >= KT_BAT_VM_SAVE_DELTA_PERCENT)
&& (vbat_vm_save_ticks >= KT_BAT_VM_SAVE_MIN_TICKS)) {
kt_battery_vm_save_percent(vbat_percent_cached);
}
2026-05-07 14:11:18 +00:00
}
u8 kt_get_vbat_percent(void)
{
return vbat_percent_cached;
}
u16 kt_get_vbat_mv(void)
{
return vbat_avg_mv;
}
void kt_set_charging(u8 charging)
{
u8 was_charging = vbat_charging;
vbat_charging = charging ? 1u : 0u;
/* 充电 → 不充电 的下降沿:启动极化消退期 */
if (was_charging && !vbat_charging) {
vbat_recovery_ticks = KT_BAT_RECOVERY_TICKS;
/* 消退期内不参与平均,先把缓冲清掉,消退期结束时重新种子化 */
vbat_buf_idx = 0;
vbat_buf_filled = 0;
printf("kt_battery: charging->discharging, polarization recovery %dms\n",
KT_BAT_RECOVERY_MS);
} else if (!was_charging && vbat_charging) {
/* 从插上充电那一刻重新计时,而非累计上一次充电的剩余 tick */
vbat_charge_bump_cnt = 0;
printf("kt_battery: discharging->charging, freeze at %d%%, bump CC=%ds CV=%ds\n",
vbat_percent_cached, KT_BAT_CC_BUMP_SEC, KT_BAT_CV_BUMP_SEC);
}
}
void kt_battery_init(void)
{
2026-05-08 14:12:19 +00:00
u8 vm_percent = 0;
u8 have_vm_percent = kt_battery_vm_load_percent(&vm_percent);
2026-05-07 14:11:18 +00:00
/* 用一次即时采样把环形缓冲全部种子化,避免开机瞬间百分比从 100% 跳到真实值 */
u16 seed = kt_battery_read_raw_mv();
kt_battery_reseed(seed);
vbat_recovery_ticks = 0;
vbat_charge_bump_cnt = 0;
2026-05-08 14:12:19 +00:00
vbat_vm_save_ticks = 0;
2026-05-07 14:11:18 +00:00
if (gpio_read(KT_CFG_USB_PLUG_DET_PIN)) {
vbat_charging = 1u;
2026-05-08 14:12:19 +00:00
if (have_vm_percent) {
vbat_percent_cached = vm_percent;
printf("kt_battery_init: USB inserted, use vm percent=%d%%, seed_mv=%d\n",
vbat_percent_cached, vbat_avg_mv);
} else {
vbat_percent_cached = kt_battery_random_percent_fallback();
printf("kt_battery_init: USB inserted, vm empty, random percent=%d%%, seed_mv=%d\n",
vbat_percent_cached, vbat_avg_mv);
}
vbat_vm_last_saved_percent = vbat_percent_cached;
printf("kt_battery_init: charging from %d%%\n", vbat_percent_cached);
2026-05-07 14:11:18 +00:00
} else {
vbat_charging = 0u;
2026-05-08 14:12:19 +00:00
vbat_percent_cached = kt_battery_mv_to_percent(seed);
vbat_vm_last_saved_percent = vbat_percent_cached;
2026-05-07 14:11:18 +00:00
printf("kt_battery_init: seed_mv=%d init_percent=%d\n",
vbat_avg_mv, vbat_percent_cached);
}
if (!vbat_timer_id) {
vbat_timer_id = sys_timer_add(NULL, kt_battery_sample_cb, KT_BAT_SAMPLE_MS);
}
}