#include "kit_time.h" #include "drv_rtc.h" #include "bms_bmu.h" #include "bms_ocv.h" #include "app_soc.h" #include "bms_fault.h" #include "bms_signal.h" #include "bms_eeprom.h" #include "bms_statistic.h" #include "bms_run_status.h" #include "bms_cur_hv.h" #define SOX_CHG_KEEP_SOC (9900u) #define SOX_DIS_KEEP_SOC (100u) #define SOX_MAX_SOC_VALUE (10000u) #define SOX_MIN_SOC_VALUE (0u) #define SOX_MAX_SOH_VALUE (10000u) #define SOX_MIN_SOH_VALUE (0u) #define SOX_CYCLE_UP_SOC_VALUE (9000u) #define SOX_CYCLE_DOWN_SOC_VALUE (3000u) #define SOX_MAX_SIGNAL_INTEGRAL (20000u) //单次积分最大值(2000A*10ms) #define SOX_SIGNAL_ACC_CAP_VALUE (360000u) //1Ams - 0.1Ah #define SOX_SIGNAL_ACC_ENERGY_VALUE (360000u) //1kWms - 0.1kWh typedef enum { kSocAdjustStep_Wait, kSocAdjustStep_Full, kSocAdjustStep_Empty, }SocAdjustStep; typedef struct { bool soc_keep_1; bool soc_keep_99; bool dis_adjust_flag; bool chg_adjust_flag; bool is_bsu_adjust; SocStatus soc_st; SocStatus soc_req_st; SocAdjustStep adjust_step; uint8_t cyc_flag; uint8_t dis_adjust_rate; uint8_t chg_adjust_rate; uint8_t stat_day; uint8_t soc_run_diff; uint8_t soc_run_min; uint16_t req_soc; uint16_t show_soc; uint32_t calculate_cap; //积分计算容量(单位Ams) uint32_t rated_cap; //额定容量(单位Ams) uint32_t soc_1_cap; //soc 1%对应容量(单位Ams) uint32_t soc_99_cap; //soc 99%对应容量(单位Ams) uint32_t last_tick; uint32_t tmp_dis_cap; //存储临时放电容量 (单位Ams) uint32_t tmp_chg_cap; //存储临时充电容量 (单位Ams) uint32_t tmp_dis_energy; uint32_t tmp_chg_energy; uint16_t soc; uint16_t soh; uint16_t cycle; uint32_t data[kCumulateData_End]; }SoxItem; SoxItem sox_item; uint16_t bms_get_show_soc(void) { return sox_item.show_soc; } uint16_t bms_get_soc(void) { return sox_item.soc; } void bms_set_soc(uint16_t value) { sox_item.req_soc = value; } uint16_t bms_get_soh(void) { return sox_item.soh; } uint16_t bms_get_cycle(void) { return sox_item.cycle; } uint16_t bms_get_cycle_flag(void) { return sox_item.cyc_flag; } uint8_t bms_get_stat_day(void) { return sox_item.stat_day; } void bms_set_cycle(uint16_t value) { sox_item.cycle = value; } SocStatus bms_get_soc_status(void) { return sox_item.soc_st; } void bms_set_soc_status(SocStatus st) { sox_item.soc_req_st = st; } uint32_t bms_get_cumulate_data(CumulateData idx) { uint32_t tmp = 0; if(idx < kCumulateData_End) { tmp = sox_item.data[idx]; } return tmp; } void bms_set_cumulate_data(CumulateData idx, uint32_t value) { if(idx < kCumulateData_End) { sox_item.data[idx] = value; } } uint16_t soh_init(uint16_t cycle, uint32_t dis_cap, uint32_t charge_cap) { uint16_t soh_loss1 = 0; //充放电次数计算出的SOH损耗百分比 uint16_t soh_loss2 = 0; //累计放电安时计算出的SOH损耗百分比 uint32_t rated_cap = bsp_eeprom_get_data(kEEData_RatedCapacity, kEepromDataType_Full); uint16_t cycle_70 = bsp_eeprom_get_data(kEEData_SOHSeventyCycle, kEepromDataType_Full); if(cycle <= 10) { return 10000; } //单位0.0001/bit //公式(0.3 / cycle_70) * cycle * 10000 soh_loss1 = (uint32_t)cycle * 3000 / cycle_70; if (soh_loss1 >= 3000) { return 7000; } soh_loss2 = (uint16_t)((3000.0 / cycle_70)*(1.0*(dis_cap+charge_cap) / (2*rated_cap))); if (soh_loss2 >= 3000) { return 7000; } if (soh_loss1 < soh_loss2) { soh_loss1 = SOX_MAX_SOH_VALUE - soh_loss1; } else { soh_loss1 = SOX_MAX_SOH_VALUE - soh_loss2; } return soh_loss1; } void cap_data_update(uint16_t base_time) { static uint16_t dly = 0; static uint8_t count = 0; static uint32_t chg_dly = 0, dis_dly = 0; uint16_t soc; RunStatus run_status; int32_t year, mon, day; int16_t cur = bms_get_current(); dly += base_time; if(dly >= KIT_SECOND_CONVERT(1)) { bms_set_signal(kSignalIdx_SocFull, (SignalStatus)((sox_item.soc_st & kSocStatus_Full) != 0)); bms_set_signal(kSignalIdx_SocEmpty, (SignalStatus)((sox_item.soc_st & kSocStatus_Empty) != 0)); dly -= KIT_SECOND_CONVERT(1); run_status = bms_get_run_status(); soc = bms_check_run_ocv(KIT_SECOND_CONVERT(1), sox_item.soc); if(soc != sox_item.soc) { bms_set_soc(soc); } //累计时间更新 if (run_status == kRunStatus_Chg) { chg_dly += KIT_SECOND_CONVERT(1); if(chg_dly >= KIT_MINUTE_CONVERT(6)) { chg_dly -= KIT_MINUTE_CONVERT(6); sox_item.data[kCumulateData_SigChgTime]++; sox_item.data[kCumulateData_DayChgTime]++; sox_item.data[kCumulateData_AccChgTime]++; } sox_item.data[kCumulateData_SigDisTime] = 0; } else if(run_status == kRunStatus_Dis) { dis_dly += KIT_SECOND_CONVERT(1); if(dis_dly >= KIT_MINUTE_CONVERT(6)) { dis_dly -= KIT_MINUTE_CONVERT(6); sox_item.data[kCumulateData_SigDisTime]++; sox_item.data[kCumulateData_DayDisTime]++; sox_item.data[kCumulateData_AccDisTime]++; } sox_item.data[kCumulateData_SigChgTime] = 0; } //充放电循环次数更新 <30% >90% if ((sox_item.soc <= SOX_CYCLE_DOWN_SOC_VALUE) && (sox_item.cyc_flag == 0)) { sox_item.cycle++; sox_item.cyc_flag = 1; } else if ((sox_item.soc >= SOX_CYCLE_UP_SOC_VALUE) && (sox_item.cyc_flag != 0)) { sox_item.cyc_flag = 0; } //累计容量更新 0.1Ah 更新一次 if (run_status == kRunStatus_Dis) { if (sox_item.tmp_dis_cap >= SOX_SIGNAL_ACC_CAP_VALUE) { sox_item.data[kCumulateData_SigDisCap]++; sox_item.data[kCumulateData_AccDisCap]++; sox_item.data[kCumulateData_DayDisCap]++; sox_item.tmp_dis_cap -= SOX_SIGNAL_ACC_CAP_VALUE; } sox_item.data[kCumulateData_SigChgCap] = 0; } else if(run_status == kRunStatus_Chg) { if (sox_item.tmp_chg_cap >= SOX_SIGNAL_ACC_CAP_VALUE) { sox_item.data[kCumulateData_SigChgCap]++; sox_item.data[kCumulateData_AccChgCap]++; sox_item.data[kCumulateData_DayChgCap]++; sox_item.tmp_chg_cap -= SOX_SIGNAL_ACC_CAP_VALUE; } sox_item.data[kCumulateData_SigDisCap] = 0; } drv_rtc_get_date(&year, &mon, &day); if(sox_item.stat_day != day) { if(count++ > 5) { count = 0; sox_item.stat_day = day; sox_item.data[kCumulateData_DayDisCap] = sox_item.data[kCumulateData_DayChgCap] = 0; sox_item.data[kCumulateData_DayDisTime] = sox_item.data[kCumulateData_DayChgTime] = 0; sox_item.data[kCumulateData_DayDisEnergy] = sox_item.data[kCumulateData_DayChgEnergy] = 0; } } else { count = 0; } if (run_status == kRunStatus_Dis) { //累计能量更新 0.1kWh 更新一次 while (sox_item.tmp_dis_energy >= SOX_SIGNAL_ACC_ENERGY_VALUE) { sox_item.data[kCumulateData_SigDisEnergy]++; sox_item.data[kCumulateData_AccDisEnergy]++; sox_item.data[kCumulateData_DayDisEnergy]++; sox_item.tmp_dis_energy -= SOX_SIGNAL_ACC_ENERGY_VALUE; } sox_item.data[kCumulateData_SigChgEnergy] = 0; } else if(run_status == kRunStatus_Chg) { while (sox_item.tmp_chg_energy >= SOX_SIGNAL_ACC_ENERGY_VALUE) { sox_item.data[kCumulateData_SigChgEnergy]++; sox_item.data[kCumulateData_AccChgEnergy]++; sox_item.data[kCumulateData_DayChgEnergy]++; sox_item.tmp_chg_energy -= SOX_SIGNAL_ACC_ENERGY_VALUE; } sox_item.data[kCumulateData_SigDisEnergy] = 0; } } } #define SOC_RUN_CALIBRATE_RATE_MAX 200 #define SOC_RUN_CALIBRATE_RATE_DEF 100 #define SOC_RUN_CALIBRATE_RATE_MIN 50 void soc_run_calibrate(int16_t current, uint16_t base_time) { static uint16_t dis_dly = 0; static uint16_t chg_dly = 0; uint8_t temp = 0; //放电 if((current < 0) && (sox_item.dis_adjust_flag == false) && (bms_get_statistic_data(kStatisticData_MinVolt) <= 3000) && (bms_get_ex_data(kExType_CellVolt) == kExStatus_None)) { chg_dly = 0; dis_dly += base_time; if(dis_dly >= KIT_SECOND_CONVERT(5)) { sox_item.dis_adjust_flag = true; temp = sox_item.soc * 100 / 2000; if(temp > SOC_RUN_CALIBRATE_RATE_MAX) temp = SOC_RUN_CALIBRATE_RATE_MAX; else if(temp < SOC_RUN_CALIBRATE_RATE_MIN) temp = SOC_RUN_CALIBRATE_RATE_MIN; sox_item.dis_adjust_rate = temp; } } //充电 else if((current > 0) && (sox_item.chg_adjust_flag == false) && (bms_get_statistic_data(kStatisticData_MaxVolt) >= 3450) && (bms_get_ex_data(kExType_CellVolt) == kExStatus_None)) { dis_dly = 0; chg_dly += base_time; if(chg_dly >= KIT_SECOND_CONVERT(5)) { sox_item.chg_adjust_flag = true; temp = 8000 * 100 / sox_item.soc; if(temp > SOC_RUN_CALIBRATE_RATE_MAX) temp = SOC_RUN_CALIBRATE_RATE_MAX; else if(temp < SOC_RUN_CALIBRATE_RATE_MIN) temp = SOC_RUN_CALIBRATE_RATE_MIN; sox_item.chg_adjust_rate = temp; } } else { dis_dly = chg_dly = 0; } } #define SOC_SMOOTH_ADJUST 0 void soc_exterme_calibrate(int16_t current, uint16_t base_time) { static uint16_t full_dly = 0; static uint16_t empty_dly = 0; uint16_t avg_volt, total_volt; uint32_t tmp_soc = sox_item.soc; if((bms_get_ex_data(kExType_CellVolt) != kExStatus_None) || (bms_get_bmu_fault_bit(kBmuFaultBit_Offline) != 0)) { empty_dly = 0; full_dly = 0; return; } switch(sox_item.adjust_step) { case kSocAdjustStep_Wait: avg_volt = bms_get_statistic_data(kStatisticData_AvgVolt); total_volt = bms_get_statistic_data(kStatisticData_AccVolt) / 100; if (((bms_get_statistic_data(kStatisticData_MinVolt) <= bsp_eeprom_get_data(kEEData_FullDisVolt, kEepromDataType_Full)) && (avg_volt <= bsp_eeprom_get_data(kEEData_FullDisAvgVolt, kEepromDataType_Full)) && (bms_get_ex_data(kExType_CellVolt) == kExStatus_None)) || (total_volt <= bsp_eeprom_get_data(kEEData_FullDisTotalVolt, kEepromDataType_Full)) || ((sox_item.soc_req_st == kSocStatus_Empty) && (sox_item.is_bsu_adjust == false))) { full_dly = 0; empty_dly += base_time; if (empty_dly >= KIT_SECOND_CONVERT(1)) { empty_dly = 0; sox_item.soc_st = kSocStatus_Empty; sox_item.calculate_cap = 0; sox_item.soc_keep_1 = false; sox_item.is_bsu_adjust = true; #if (SOC_SMOOTH_ADJUST == 1) sox_item.adjust_step = kSocAdjustStep_Empty; #else tmp_soc = SOX_MIN_SOC_VALUE; #endif } } else if (((bms_get_statistic_data(kStatisticData_MaxVolt) >= bsp_eeprom_get_data(kEEData_FullChgVolt, kEepromDataType_Full)) && (avg_volt >= bsp_eeprom_get_data(kEEData_FullChgAvgVolt, kEepromDataType_Full)) && (bms_get_ex_data(kExType_CellVolt) == kExStatus_None)) || (total_volt >= bsp_eeprom_get_data(kEEData_FullChgTotalVolt, kEepromDataType_Full)) || ((sox_item.soc_req_st == kSocStatus_Full) && (sox_item.is_bsu_adjust == false))) { empty_dly = 0; full_dly += base_time; if (full_dly >= KIT_SECOND_CONVERT(1)) { full_dly = 0; sox_item.soc_st = kSocStatus_Full; sox_item.calculate_cap = sox_item.rated_cap; sox_item.soc_keep_99 = false; sox_item.is_bsu_adjust = true; #if (SOC_SMOOTH_ADJUST == 1) sox_item.adjust_step = kSocAdjustStep_Full; #else tmp_soc = SOX_MAX_SOC_VALUE; #endif } } else { empty_dly = 0; full_dly = 0; } break; #if (SOC_SMOOTH_ADJUST == 1) case kSocAdjustStep_Full: full_dly += base_time; if (full_dly >= 1000) { full_dly = 0; tmp_soc++ ; if(tmp_soc >= SOX_MAX_SOC_VALUE) sox_item.adjust_step = kSocAdjustStep_Wait; } break; case kSocAdjustStep_Empty: empty_dly += base_time; if (empty_dly >= 1000) { empty_dly = 0; if(tmp_soc > SOX_MIN_SOC_VALUE) { tmp_soc-- ; } else sox_item.adjust_step = kSocAdjustStep_Wait; } break; #endif default: sox_item.adjust_step = kSocAdjustStep_Wait; break; } sox_item.soc = tmp_soc; } void bms_init_soc(void) { uint32_t soc; uint64_t tmp_64u; int32_t year, mon, day; soc = bsp_eeprom_get_data(kEEData_SOC, kEepromDataType_Full); soc = bms_check_pwr_on_ocv(soc); sox_item.soc_keep_99 = true; sox_item.soc_keep_1 = true; if (soc == 0) { sox_item.soc_keep_1 = false; } else if (soc == SOX_MAX_SOC_VALUE) { sox_item.soc_keep_99 = false; } sox_item.req_soc = 0xFFFF; sox_item.soc_st = kSocStatus_Normal; sox_item.adjust_step = kSocAdjustStep_Wait; sox_item.tmp_chg_cap = sox_item.tmp_dis_cap = 0; sox_item.tmp_chg_energy = sox_item.tmp_dis_energy = 0; sox_item.dis_adjust_flag = sox_item.chg_adjust_flag = false; sox_item.dis_adjust_rate = sox_item.chg_adjust_rate = SOC_RUN_CALIBRATE_RATE_DEF; sox_item.soc = soc; year = bsp_eeprom_get_data(kEE_SOCRunMax_Min, kEepromDataType_Low); mon = bsp_eeprom_get_data(kEE_SOCRunMax_Min, kEepromDataType_High); if((year < mon) && (year >= 0) && (mon <= 100)) { sox_item.soc_run_min = year; sox_item.soc_run_diff = mon - year; } else { sox_item.soc_run_min = 0; sox_item.soc_run_diff = 100; } tmp_64u = SOX_CONVERT_AH_TO_AMS(bsp_eeprom_get_data(kEEData_RatedCapacity, kEepromDataType_Full)) * sox_item.soc_run_diff / 100; sox_item.rated_cap = tmp_64u; sox_item.soc_1_cap = (SOX_DIS_KEEP_SOC * tmp_64u) / SOX_MAX_SOC_VALUE; sox_item.soc_99_cap = (SOX_CHG_KEEP_SOC * tmp_64u) / SOX_MAX_SOC_VALUE; sox_item.calculate_cap = (soc * tmp_64u) / SOX_MAX_SOC_VALUE; sox_item.cycle = bsp_eeprom_get_data(kEEData_CycleTimes, kEepromDataType_Full); sox_item.cyc_flag = bsp_eeprom_get_data(kEEData_StatDay_CycleTimesFlag, kEepromDataType_Low); sox_item.stat_day = bsp_eeprom_get_data(kEEData_StatDay_CycleTimesFlag, kEepromDataType_High); drv_rtc_get_date(&year, &mon, &day); if(sox_item.stat_day == day) { sox_item.data[kCumulateData_DayDisCap] = bsp_eeprom_get_data(kEEData_DayDisCapL, kEepromDataType_Double); sox_item.data[kCumulateData_DayChgCap] = bsp_eeprom_get_data(kEEData_DayChgCapL, kEepromDataType_Double); sox_item.data[kCumulateData_DayDisTime] = bsp_eeprom_get_data(kEEData_DayChg_DisTime, kEepromDataType_Low); sox_item.data[kCumulateData_DayChgTime] = bsp_eeprom_get_data(kEEData_DayChg_DisTime, kEepromDataType_High); sox_item.data[kCumulateData_DayChgEnergy] = bsp_eeprom_get_data(kEEData_DayChgEnergyL, kEepromDataType_Double); sox_item.data[kCumulateData_DayDisEnergy] = bsp_eeprom_get_data(kEEData_DayDisEnergyL, kEepromDataType_Double); } sox_item.data[kCumulateData_AccChgCap] = bsp_eeprom_get_data(kEEData_AccChgCapL, kEepromDataType_Double); sox_item.data[kCumulateData_AccDisCap] = bsp_eeprom_get_data(kEEData_AccDisCapL, kEepromDataType_Double); sox_item.data[kCumulateData_AccChgTime] = bsp_eeprom_get_data(kEEData_AccChgTimeL, kEepromDataType_Double); sox_item.data[kCumulateData_AccDisTime] = bsp_eeprom_get_data(kEEData_AccDisTimeL, kEepromDataType_Double); sox_item.data[kCumulateData_AccChgEnergy] = bsp_eeprom_get_data(kEEData_AccChgEnergyL, kEepromDataType_Double); sox_item.data[kCumulateData_AccDisEnergy] = bsp_eeprom_get_data(kEEData_AccDisEnergyL, kEepromDataType_Double); sox_item.soh = soh_init(sox_item.cycle, sox_item.data[kCumulateData_AccDisCap],sox_item.data[kCumulateData_AccChgCap]); sox_item.last_tick = kit_time_get_tick(); } void bms_poll_soh(uint32_t base_time) { static uint16_t dly; dly += base_time; if(dly >= 5000) { dly = 0; sox_item.soh = soh_init(sox_item.cycle, sox_item.data[kCumulateData_AccDisCap],sox_item.data[kCumulateData_AccChgCap]); } } /***************************************************************************** 积分电流单位 0.01A/bit ******************************************************************************/ void bms_integral_soc(int32_t current, uint16_t base_time) { uint16_t soc = sox_item.soc; uint16_t integral_time = 0, total_volt; uint32_t tmp_32u = 0; cap_data_update(base_time); soc_exterme_calibrate(current, base_time); if(sox_item.adjust_step != kSocAdjustStep_Wait) return; tmp_32u = kit_time_get_tick(); integral_time = kit_time_get_interval(sox_item.last_tick, tmp_32u); sox_item.last_tick = tmp_32u; total_volt = bms_get_high_volt(kHvType_Bat); if(sox_item.soc_req_st == kSocStatus_Normal) { sox_item.is_bsu_adjust = false; } if((sox_item.soc >= 1000) || (bms_get_statistic_data(kStatisticData_MinVolt) >= bsp_eeprom_get_data(kEEData_ForbidDisRelVolt, kEepromDataType_Full))) { sox_item.soc_keep_1 = true; sox_item.dis_adjust_flag = false; sox_item.dis_adjust_rate = SOC_RUN_CALIBRATE_RATE_DEF; KIT_CLR_BIT(sox_item.soc_st, 1); } //充电积分 if(current > 0) { //单位Ams //公式 (current / 100 * integral_time) * (sox_item.chg_adjust_rate / 100) if(current >= bsp_eeprom_get_data(kEE_SelfConsumpCurrent, kEepromDataType_Full)) { current = current - bsp_eeprom_get_data(kEE_SelfConsumpCurrent, kEepromDataType_Full); } tmp_32u = current * integral_time * sox_item.chg_adjust_rate / 10000; if (tmp_32u > SOX_MAX_SIGNAL_INTEGRAL) tmp_32u = SOX_MAX_SIGNAL_INTEGRAL; sox_item.tmp_chg_cap += tmp_32u; //单位 kWms sox_item.tmp_chg_energy += tmp_32u * total_volt / 10000; if(sox_item.calculate_cap < sox_item.rated_cap) { tmp_32u += sox_item.calculate_cap; if(tmp_32u > sox_item.soc_99_cap) { if (sox_item.soc_keep_99 == true) { sox_item.calculate_cap = sox_item.soc_99_cap; } else if(tmp_32u > sox_item.rated_cap) { sox_item.calculate_cap = sox_item.rated_cap; } else { sox_item.calculate_cap = tmp_32u; } } else { sox_item.calculate_cap = tmp_32u; } } } if((sox_item.soc <= 9500) || (bms_get_statistic_data(kStatisticData_MaxVolt) <= bsp_eeprom_get_data(kEEData_ForbidChgRelVolt, kEepromDataType_Full))) { sox_item.soc_keep_99 = true; sox_item.chg_adjust_flag = false; sox_item.chg_adjust_rate = SOC_RUN_CALIBRATE_RATE_DEF; KIT_CLR_BIT(sox_item.soc_st, 0); } //放电积分 if(current < 0) { current = KIT_ABS(current) + bsp_eeprom_get_data(kEE_SelfConsumpCurrent, kEepromDataType_Full); tmp_32u = (current + bsp_eeprom_get_data(kEE_SelfConsumpCurrent, kEepromDataType_Full)) * integral_time * sox_item.dis_adjust_rate / 10000; if (tmp_32u > SOX_MAX_SIGNAL_INTEGRAL) tmp_32u = SOX_MAX_SIGNAL_INTEGRAL; sox_item.tmp_dis_cap += tmp_32u; //单位 kWms sox_item.tmp_dis_energy += tmp_32u * total_volt / 10000; if(sox_item.calculate_cap > 0) { if (sox_item.calculate_cap < (sox_item.soc_1_cap + tmp_32u)) { if (sox_item.soc_keep_1 == true) { sox_item.calculate_cap = sox_item.soc_1_cap; } else if(tmp_32u >= sox_item.calculate_cap) { sox_item.calculate_cap = 0; } else { sox_item.calculate_cap -= tmp_32u; } } else { sox_item.calculate_cap -= tmp_32u; } } } if (sox_item.req_soc == 0xFFFF) { soc = (((uint64_t)sox_item.calculate_cap * SOX_MAX_SOC_VALUE * 10 / sox_item.rated_cap + 5)/10); //四舍五入 } else { if (soc != sox_item.req_soc) { soc = sox_item.req_soc; sox_item.calculate_cap = ((uint64_t)soc * sox_item.rated_cap) / SOX_MAX_SOC_VALUE; if(sox_item.req_soc == SOX_MAX_SOC_VALUE) sox_item.soc_keep_99 = false; else if(sox_item.req_soc == SOX_MIN_SOC_VALUE) sox_item.soc_keep_1 = false; } sox_item.req_soc = 0xFFFF; } if (soc > SOX_MAX_SOC_VALUE) { soc = SOX_MAX_SOC_VALUE; } sox_item.soc = soc; //计算显示soc sox_item.show_soc = (uint32_t)sox_item.soc_run_diff * soc / 100 + sox_item.soc_run_min * 100; }