/** * @file charge_sm.c * @brief 充电状态机 - AI-4 版本 (7S Li-ion, 2A CC, NTC) * * 6 状态: IDLE -> PRECHARGE -> CC -> CV -> DONE + FAULT + NO_BATTERY * 200ms 周期驱动, 所有阈值可配置。 */ #include "charge_sm.h" #include #include /* ================================================================= * AI-4 默认配置宏 * ================================================================= */ #define AI4_CELLS 7 #define AI4_VPER_CELL_MV 4200 #define AI4_FULL_VOLTAGE_MV (AI4_CELLS * AI4_VPER_CELL_MV) #define AI4_CC_CURRENT_MA 2000 #define AI4_PRECHG_CURRENT_MA 200 #define AI4_TERM_CURRENT_MA 200 #define AI4_TEMP_MAX_C 50 #define AI4_TEMP_MIN_C 0 #define AI4_PRECHG_TIMEOUT_S 1800 #define AI4_CC_CV_TIMEOUT_S 28800 #define AI4_CV_STABLE_S 30 #define AI4_VBAT_OV_MV 31000 #define AI4_IBAT_OC_MA 5000 #define AI4_RECHARGE_HYST_MV 2900 #define AI4_PRECHG_THRESH_MV ((AI4_FULL_VOLTAGE_MV * 55) / 100) /* ================================================================= * AI-4 默认配置实例 * ================================================================= */ static const ChargeSM_Config AI4_DefaultCfg = { .full_voltage_mv = AI4_FULL_VOLTAGE_MV, .precharge_thresh_mv = AI4_PRECHG_THRESH_MV, .recharge_hyst_mv = AI4_RECHARGE_HYST_MV, .cc_current_ma = AI4_CC_CURRENT_MA, .precharge_current_ma = AI4_PRECHG_CURRENT_MA, .term_current_ma = AI4_TERM_CURRENT_MA, .precharge_timeout_s = AI4_PRECHG_TIMEOUT_S, .cc_cv_timeout_s = AI4_CC_CV_TIMEOUT_S, .cv_stable_time_s = AI4_CV_STABLE_S, .vbat_overvoltage_mv = AI4_VBAT_OV_MV, .ibat_overcurrent_ma = AI4_IBAT_OC_MA, .temp_max_c = AI4_TEMP_MAX_C, .temp_min_c = AI4_TEMP_MIN_C, .tick_ms = 200, .no_battery_thresh_mv = 1000, .no_battery_hold_ms = 1000, }; /* ================================================================= * 初始化 * ================================================================= */ void ChargeSM_InitDefault(ChargeSM *sm) { ChargeSM_Init(sm, &AI4_DefaultCfg); } void ChargeSM_Init(ChargeSM *sm, const ChargeSM_Config *cfg) { memset(sm, 0, sizeof(*sm)); sm->cfg = *cfg; sm->state = CHARGE_STATE_IDLE; sm->no_bat_hold_ms = 0; } /* ================================================================= * 调试 * ================================================================= */ const char* ChargeSM_StateStr(ChargeState s) { switch (s) { case CHARGE_STATE_IDLE: return "IDLE"; case CHARGE_STATE_PRECHARGE: return "PRECHARGE"; case CHARGE_STATE_CC: return "CC"; case CHARGE_STATE_CV: return "CV"; case CHARGE_STATE_DONE: return "DONE"; case CHARGE_STATE_FAULT: return "FAULT"; case CHARGE_STATE_NO_BATTERY: return "NO_BAT"; default: return "?"; } } /* ================================================================= * 内部辅助函数 * ================================================================= */ /** * @brief 故障检查 * @param sm 状态机实例 * @param vbat_mv 电池电压 (mV) * @param ibat_ma 充电电流 (mA) * @param temp_c NTC 温度 (C) * @param reason 输出故障原因 (至少 32 字节) * @return 1=有故障, 0=正常 */ static int CheckFault(const ChargeSM *sm, int32_t vbat_mv, int32_t ibat_ma, int8_t temp_c, char *reason) { if (vbat_mv > 0 && (uint32_t)vbat_mv > sm->cfg.vbat_overvoltage_mv) { snprintf(reason, 32, "VBAT_OV %ldmV", (long)vbat_mv); return 1; } if (ibat_ma > 0 && (uint32_t)ibat_ma > sm->cfg.ibat_overcurrent_ma) { snprintf(reason, 32, "IBAT_OC %ldmA", (long)ibat_ma); return 1; } if (sm->cfg.temp_max_c > 0 && temp_c > sm->cfg.temp_max_c) { snprintf(reason, 32, "TEMP_HI %dC", temp_c); return 1; } if (sm->cfg.temp_min_c >= 0 && temp_c < sm->cfg.temp_min_c) { snprintf(reason, 32, "TEMP_LO %dC", temp_c); return 1; } return 0; } /** * @brief 切换状态并重置计时器 */ static void set_state(ChargeSM *sm, ChargeState ns) { sm->state = ns; sm->state_enter_ms = sm->elapsed_ms; sm->term_hold_ms = 0; sm->last_action = SM_ACTION_NONE; } /* ================================================================= * ChargeSM_Run - 主状态机 * ================================================================= */ ChargeSM_Decision ChargeSM_Run(ChargeSM *sm, int32_t vbat_mv, int32_t ibat_ma, int8_t temp_c) { ChargeSM_Decision d = { .action = SM_ACTION_NONE, .target_current_ma = 0 }; d.reason[0] = '\0'; sm->elapsed_ms += sm->cfg.tick_ms; uint32_t state_time_ms = sm->elapsed_ms - sm->state_enter_ms; /* 故障检查 (最高优先级) */ char fr[32]; if (CheckFault(sm, vbat_mv, ibat_ma, temp_c, fr)) { if (sm->state != CHARGE_STATE_FAULT) { set_state(sm, CHARGE_STATE_FAULT); d.action = SM_ACTION_STOP; snprintf(d.reason, 32, "FAULT:%s", fr); } return d; } switch (sm->state) { case CHARGE_STATE_NO_BATTERY: if (vbat_mv > 0 && (uint32_t)vbat_mv > sm->cfg.no_battery_thresh_mv) { sm->no_bat_hold_ms = 0; set_state(sm, CHARGE_STATE_IDLE); snprintf(d.reason, 32, "BAT_RECON %ldmV", (long)vbat_mv); } break; case CHARGE_STATE_IDLE: if (vbat_mv <= 0) break; if ((uint32_t)vbat_mv < sm->cfg.precharge_thresh_mv) { set_state(sm, CHARGE_STATE_PRECHARGE); d.action = SM_ACTION_SET_PRECHG; d.target_current_ma = sm->cfg.precharge_current_ma; snprintf(d.reason, 32, "->PRECHG %ldmV", (long)vbat_mv); } else { set_state(sm, CHARGE_STATE_CC); d.action = SM_ACTION_SET_CC; d.target_current_ma = sm->cfg.cc_current_ma; snprintf(d.reason, 32, "->CC %ldmV", (long)vbat_mv); } break; case CHARGE_STATE_PRECHARGE: if (sm->cfg.precharge_timeout_s && state_time_ms > sm->cfg.precharge_timeout_s * 1000) { set_state(sm, CHARGE_STATE_FAULT); d.action = SM_ACTION_STOP; snprintf(d.reason, 32, "PRECHG_TO"); break; } if (vbat_mv > 0 && (uint32_t)vbat_mv >= sm->cfg.precharge_thresh_mv) { set_state(sm, CHARGE_STATE_CC); d.action = SM_ACTION_SET_CC; d.target_current_ma = sm->cfg.cc_current_ma; snprintf(d.reason, 32, "->CC %ldmV", (long)vbat_mv); } break; case CHARGE_STATE_CC: if (sm->cfg.cc_cv_timeout_s && state_time_ms > sm->cfg.cc_cv_timeout_s * 1000) { set_state(sm, CHARGE_STATE_FAULT); d.action = SM_ACTION_STOP; snprintf(d.reason, 32, "CC_CV_TO"); break; } if (vbat_mv > 0 && (uint32_t)vbat_mv >= sm->cfg.full_voltage_mv) { set_state(sm, CHARGE_STATE_CV); } break; case CHARGE_STATE_CV: if (sm->cfg.cc_cv_timeout_s && state_time_ms > sm->cfg.cc_cv_timeout_s * 1000) { set_state(sm, CHARGE_STATE_FAULT); d.action = SM_ACTION_STOP; snprintf(d.reason, 32, "CC_CV_TO"); break; } if (ibat_ma > 0 && (uint32_t)ibat_ma <= sm->cfg.term_current_ma) { sm->term_hold_ms += sm->cfg.tick_ms; } else { sm->term_hold_ms = 0; } if (sm->term_hold_ms >= sm->cfg.cv_stable_time_s * 1000) { set_state(sm, CHARGE_STATE_DONE); d.action = SM_ACTION_STOP; snprintf(d.reason, 32, "DONE %ldmA", (long)ibat_ma); } break; case CHARGE_STATE_DONE: if (vbat_mv > 0 && (uint32_t)vbat_mv <= sm->cfg.full_voltage_mv - sm->cfg.recharge_hyst_mv) { set_state(sm, CHARGE_STATE_CC); d.action = SM_ACTION_RESTART; d.target_current_ma = sm->cfg.cc_current_ma; snprintf(d.reason, 32, "RECHG %ldmV", (long)vbat_mv); } break; case CHARGE_STATE_FAULT: break; } /* 电池断连检测 */ if (sm->state != CHARGE_STATE_NO_BATTERY && sm->state != CHARGE_STATE_FAULT) { if (vbat_mv >= 0 && (uint32_t)vbat_mv <= sm->cfg.no_battery_thresh_mv) { sm->no_bat_hold_ms += sm->cfg.tick_ms; } else { sm->no_bat_hold_ms = 0; } if (sm->no_bat_hold_ms >= sm->cfg.no_battery_hold_ms) { set_state(sm, CHARGE_STATE_NO_BATTERY); d.action = SM_ACTION_STOP; snprintf(d.reason, 32, "NO_BAT %ldmV", (long)vbat_mv); } } return d; } void ChargeSM_ForceFault(ChargeSM *sm, const char *reason) { (void)reason; set_state(sm, CHARGE_STATE_FAULT); }