/** * @file test_charge_sm.c * @brief ChargeSM 状态机单元测试 * * 覆盖: 所有状态转移 / 故障触发 / 超时 / 电池断连 / 自定义配置 * 编译: gcc -std=gnu11 -D_GNU_SOURCE -I. -I../user test_charge_sm.c ../user/charge_sm.c -lm */ #include "test_common.h" #include "charge_sm.h" static ChargeSM sm; static void init_and_tick(int ticks) { ChargeSM_InitDefault(&sm); for (int i = 0; i < ticks; i++) { ChargeSM_Decision d = ChargeSM_Run(&sm, 0, 0, 25); (void)d; } } static ChargeSM_Decision tick(int32_t vbat, int32_t ibat, int8_t temp) { return ChargeSM_Run(&sm, vbat, ibat, temp); } /* ================================================================= * 测试用例 * ================================================================= */ /** 初始化后状态应为 IDLE, elapsed_ms = 0 */ TEST(init_is_idle) { ChargeSM_InitDefault(&sm); ASSERT_EQ(sm.state, CHARGE_STATE_IDLE); ASSERT_EQ(sm.elapsed_ms, 0); } /** VBAT=0 时应保持 IDLE 不跳转 */ TEST(idle_vbat_zero_stays_idle) { ChargeSM_InitDefault(&sm); ChargeSM_Decision d = tick(0, 0, 25); ASSERT_EQ(sm.state, CHARGE_STATE_IDLE); ASSERT_EQ(d.action, SM_ACTION_NONE); } /** VBAT 低于预充阈值(16170mV) 应跳转到 PRECHARGE, 电流 200mA */ TEST(idle_to_precharge) { ChargeSM_InitDefault(&sm); ChargeSM_Decision d = tick(12000, 50, 25); ASSERT_EQ(sm.state, CHARGE_STATE_PRECHARGE); ASSERT_EQ(d.action, SM_ACTION_SET_PRECHG); ASSERT_EQ(d.target_current_ma, 200); } /** VBAT 高于预充阈值 应直接进入 CC, 电流 2000mA */ TEST(idle_to_cc) { ChargeSM_InitDefault(&sm); ChargeSM_Decision d = tick(20000, 100, 25); ASSERT_EQ(sm.state, CHARGE_STATE_CC); ASSERT_EQ(d.action, SM_ACTION_SET_CC); ASSERT_EQ(d.target_current_ma, 2000); } /** PRECHARGE 中电压升到阈值 → CC */ TEST(precharge_to_cc_on_voltage_rise) { ChargeSM_InitDefault(&sm); tick(12000, 50, 25); ASSERT_EQ(sm.state, CHARGE_STATE_PRECHARGE); ChargeSM_Decision d = tick(17000, 50, 25); ASSERT_EQ(sm.state, CHARGE_STATE_CC); ASSERT_EQ(d.action, SM_ACTION_SET_CC); } /** 预充超时(30min) → FAULT */ TEST(precharge_timeout) { ChargeSM_InitDefault(&sm); tick(12000, 50, 25); /* 9000 ticks x 200ms = 1800s = 30min */ for (int i = 0; i < 9000; i++) { ChargeSM_Decision d = tick(12000, 50, 25); if (sm.state == CHARGE_STATE_FAULT) break; } ASSERT_EQ(sm.state, CHARGE_STATE_FAULT); } /** CC 中电压达满充(29400mV) → CV */ TEST(cc_to_cv_on_full_voltage) { ChargeSM_InitDefault(&sm); tick(20000, 2000, 25); ASSERT_EQ(sm.state, CHARGE_STATE_CC); tick(29400, 1800, 25); ASSERT_EQ(sm.state, CHARGE_STATE_CV); } /** CV 中电流降到终止值以下并稳定 30s → DONE */ TEST(cv_to_done_on_low_current) { ChargeSM_InitDefault(&sm); tick(20000, 2000, 25); tick(29400, 1500, 25); for (int i = 0; i < 200; i++) { ChargeSM_Decision d = tick(29400, 100, 25); if (sm.state == CHARGE_STATE_DONE) break; } ASSERT_EQ(sm.state, CHARGE_STATE_DONE); } /** DONE 中电压降到再充阈值以下 → CC (RESTART) */ TEST(done_to_recharge) { ChargeSM_InitDefault(&sm); tick(20000, 2000, 25); tick(29400, 1500, 25); for (int i = 0; i < 200; i++) tick(29400, 100, 25); ASSERT_EQ(sm.state, CHARGE_STATE_DONE); ChargeSM_Decision d = tick(26000, 0, 25); ASSERT_EQ(sm.state, CHARGE_STATE_CC); ASSERT_EQ(d.action, SM_ACTION_RESTART); } /** VBAT 超 31V → FAULT + STOP */ TEST(fault_on_vbat_overvoltage) { ChargeSM_InitDefault(&sm); tick(20000, 2000, 25); ChargeSM_Decision d = tick(32000, 2000, 25); ASSERT_EQ(sm.state, CHARGE_STATE_FAULT); ASSERT_EQ(d.action, SM_ACTION_STOP); } /** IBAT 超 5A → FAULT */ TEST(fault_on_ibat_overcurrent) { ChargeSM_InitDefault(&sm); tick(20000, 2000, 25); ChargeSM_Decision d = tick(25000, 6000, 25); ASSERT_EQ(sm.state, CHARGE_STATE_FAULT); } /** 温度超 50C → FAULT */ TEST(fault_on_overtemp) { ChargeSM_InitDefault(&sm); tick(20000, 2000, 25); ChargeSM_Decision d = tick(25000, 2000, 55); ASSERT_EQ(sm.state, CHARGE_STATE_FAULT); } /** 温度低于 0C → FAULT */ TEST(fault_on_undertemp) { ChargeSM_InitDefault(&sm); tick(20000, 2000, 25); ChargeSM_Decision d = tick(25000, 2000, -5); ASSERT_EQ(sm.state, CHARGE_STATE_FAULT); } /** VBAT=0 持续 >1s → NO_BATTERY */ TEST(no_battery_detection) { ChargeSM_InitDefault(&sm); for (int i = 0; i < 6; i++) { tick(0, 0, 25); } ASSERT_EQ(sm.state, CHARGE_STATE_NO_BATTERY); } /** NO_BATTERY 中 VBAT 恢复 → IDLE */ TEST(no_battery_reconnect) { ChargeSM_InitDefault(&sm); for (int i = 0; i < 6; i++) tick(0, 0, 25); ASSERT_EQ(sm.state, CHARGE_STATE_NO_BATTERY); tick(12000, 50, 25); ASSERT_EQ(sm.state, CHARGE_STATE_IDLE); } /** CV 状态转移不应产生 PWM/PFM 动作 (由上层任务处理) */ TEST(cv_does_not_set_pfm) { ChargeSM_InitDefault(&sm); tick(20000, 2000, 25); ChargeSM_Decision d = tick(29400, 1500, 25); ASSERT_EQ(sm.state, CHARGE_STATE_CV); ASSERT_EQ(d.action, SM_ACTION_NONE); } /** CC+CV 总超时(8hr) → FAULT */ TEST(cc_cv_total_timeout) { ChargeSM_InitDefault(&sm); tick(20000, 2000, 25); /* 28800s / 0.2s = 144000 ticks (需要 > 才触发) */ for (int i = 0; i < 144005; i++) { ChargeSM_Decision d = tick(20001, 2000, 25); if (sm.state == CHARGE_STATE_FAULT) break; } ASSERT_EQ(sm.state, CHARGE_STATE_FAULT); } /** 所有状态枚举有对应字符串 */ TEST(state_string_all) { ASSERT_STR_EQ(ChargeSM_StateStr(CHARGE_STATE_IDLE), "IDLE"); ASSERT_STR_EQ(ChargeSM_StateStr(CHARGE_STATE_PRECHARGE), "PRECHARGE"); ASSERT_STR_EQ(ChargeSM_StateStr(CHARGE_STATE_CC), "CC"); ASSERT_STR_EQ(ChargeSM_StateStr(CHARGE_STATE_CV), "CV"); ASSERT_STR_EQ(ChargeSM_StateStr(CHARGE_STATE_DONE), "DONE"); ASSERT_STR_EQ(ChargeSM_StateStr(CHARGE_STATE_FAULT), "FAULT"); ASSERT_STR_EQ(ChargeSM_StateStr(CHARGE_STATE_NO_BATTERY), "NO_BAT"); } /** 自定义配置 (2S/1A/不同阈值) 应正确载入 */ TEST(custom_config_init) { ChargeSM_Config custom = { .full_voltage_mv = 8400, .precharge_thresh_mv = 4620, .recharge_hyst_mv = 840, .cc_current_ma = 1000, .precharge_current_ma = 100, .term_current_ma = 100, .precharge_timeout_s = 600, .cc_cv_timeout_s = 7200, .cv_stable_time_s = 15, .vbat_overvoltage_mv = 9000, .ibat_overcurrent_ma = 3000, .temp_max_c = 45, .temp_min_c = 5, .tick_ms = 100, .no_battery_thresh_mv = 500, .no_battery_hold_ms = 500, }; ChargeSM_Init(&sm, &custom); ASSERT_EQ(sm.state, CHARGE_STATE_IDLE); ASSERT_EQ(sm.cfg.full_voltage_mv, 8400); ASSERT_EQ(sm.cfg.tick_ms, 100); } #define TEST_LIST \ RUN_TEST(init_is_idle); \ RUN_TEST(idle_vbat_zero_stays_idle); \ RUN_TEST(idle_to_precharge); \ RUN_TEST(idle_to_cc); \ RUN_TEST(precharge_to_cc_on_voltage_rise); \ RUN_TEST(precharge_timeout); \ RUN_TEST(cc_to_cv_on_full_voltage); \ RUN_TEST(cv_to_done_on_low_current); \ RUN_TEST(done_to_recharge); \ RUN_TEST(fault_on_vbat_overvoltage); \ RUN_TEST(fault_on_ibat_overcurrent); \ RUN_TEST(fault_on_overtemp); \ RUN_TEST(fault_on_undertemp); \ RUN_TEST(no_battery_detection); \ RUN_TEST(no_battery_reconnect); \ RUN_TEST(cv_does_not_set_pfm); \ RUN_TEST(cc_cv_total_timeout); \ RUN_TEST(state_string_all); \ RUN_TEST(custom_config_init); int main(void) { RUN_ALL; return __test_fail; }