/** ****************************************************************************** * @file OCV.c * @author * @version * @date * @brief ****************************************************************************** * @attention * * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "stm32f10x.h" #include "global.h" uint8_t OCV_soc; //通过OCV曲线获得的soc值 uint8_t OCV_status; //定时校准的当前状态,0:刚开机要获取存储时间 1:符合条件正在计时 2:不在静置 3:不允许OCV校准 uint8_t OCV_Wait_flag; //允许OCV校准走倒计时的标志 uint8_t OCV_CaliSOC_flag; //允许执行OCV校准获取当前电压对应soc的标志 uint16_t OCV_WrTime_count; //在非待机状态下,每过30min记录一次,防止关机时未回到待机状态导致不可用 OCV_Data ocv_data[15]; //OCV曲线数组 uint16_t ocv_Media_dp[15]; //根据写入数据获得中间值 //根据paraMem值,填充ocv_data[]和ocv_Media_dp[] void OCV_CaliSOC_DataWr(void) { uint8_t i; //填充SOC,共15个点 ocv_data[0].soc = 0; ocv_data[1].soc = 5; ocv_data[2].soc = 10; ocv_data[3].soc = 15; ocv_data[4].soc = 20; ocv_data[5].soc = 30; ocv_data[6].soc = 40; ocv_data[7].soc = 50; ocv_data[8].soc = 60; ocv_data[9].soc = 70; ocv_data[10].soc = 80; ocv_data[11].soc = 85; ocv_data[12].soc = 90; ocv_data[13].soc = 95; ocv_data[14].soc = 100; //填充电压,共15个点 for(i=0;i<15;i++) { //ocv_data[i].ocv_cp = paraMem.ocv_cpBuf[i] * bmsMem.ucCellNum; ocv_data[i].ocv_dp = paraMem.ocv_dpBuf[i] * bmsMem.ucCellNum; } //赋值中间量,共14个点 for(i=0;i<=13;i++) { ocv_Media_dp[i] = (ocv_data[i].ocv_dp + ocv_data[i+1].ocv_dp) / 2; } //50%-90%之间不考虑 ocv_Media_dp[7] = ocv_data[7].ocv_dp; //低于50%对应基准值,校准50% ocv_Media_dp[11] = ocv_data[12].ocv_dp; //高于90%对应基准值,校准90% } //计算OCV表格的dp里,当前总电压对应的SOC uint8_t OCV_CaliSoc_dp(void) { uint16_t caliSoc = 0; //对总电压求出对应SOC if(bmsMem.packVoltage >= ocv_Media_dp[13]) //超过最大值认为是满电 { caliSoc = 100; } else if((bmsMem.packVoltage < ocv_Media_dp[13]) && (bmsMem.packVoltage >= ocv_Media_dp[12])) { caliSoc = 95; } else if((bmsMem.packVoltage < ocv_Media_dp[12]) && (bmsMem.packVoltage >= ocv_Media_dp[11])) { caliSoc = 90; } else if((bmsMem.packVoltage < ocv_Media_dp[11]) && (bmsMem.packVoltage > ocv_Media_dp[7])) { //当电压在>50%、<90%的电压值内时,若实时SOC不在范围内,则校准到50%/90%,否则不校准 if(bmsMem.soc > 90) { caliSoc = 90; } else if(bmsMem.soc < 50) { caliSoc = 50; } else { caliSoc = bmsMem.soc; //等于当前值,使不会校准 } } else if((bmsMem.packVoltage < ocv_Media_dp[7]) && (bmsMem.packVoltage >= ocv_Media_dp[6])) { caliSoc = 50; } else if((bmsMem.packVoltage < ocv_Media_dp[6]) && (bmsMem.packVoltage >= ocv_Media_dp[5])) { caliSoc = 40; } else if((bmsMem.packVoltage < ocv_Media_dp[5]) && (bmsMem.packVoltage >= ocv_Media_dp[4])) { caliSoc = 30; } else if((bmsMem.packVoltage < ocv_Media_dp[4]) && (bmsMem.packVoltage >= ocv_Media_dp[3])) { caliSoc = 20; } else if((bmsMem.packVoltage < ocv_Media_dp[3]) && (bmsMem.packVoltage >= ocv_Media_dp[2])) { caliSoc = 15; } else if((bmsMem.packVoltage < ocv_Media_dp[2]) && (bmsMem.packVoltage >= ocv_Media_dp[1])) { caliSoc = 10; } else if((bmsMem.packVoltage < ocv_Media_dp[1]) && (bmsMem.packVoltage >= ocv_Media_dp[0])) { caliSoc = 5; } else if(bmsMem.packVoltage <= ocv_Media_dp[0]) //小于最小值认为是空电 { caliSoc = 0; } return caliSoc; } //在RTC有效+允许执行校准倒计时+满足静置时间时,获取开路电压法对应soc //在电芯温度合适+原SOC不靠谱时,将该soc写入 //执行校准后,刷新计时起点,重新等待30min后再次执行校准 void OCV_CaliSOC(void) { uint8_t tempEE[4]; //用于保存计时起点 if((LSEErrFlag == 0) && ((paraMem.ocv_min_disable & 0x8000) == 0)) //RTC有效,且允许开路电压校准SOC { //静置状态,对应OCV_status为1 if((bmsMem.packCurrent > (-500)) && (bmsMem.packCurrent < 500)) { //刚开机,读取存在EEPROM的时间 if(OCV_status == 0) { OCV_status = 1; OCV_Wait_flag = 1; //允许在rtc函数中进行时间判断 EEPROM_RdMulByte(EE_TIME_OCV, tempEE); ocvtimecount = tempEE[0]<<24 | tempEE[1]<<16 | tempEE[2]<<8 | tempEE[3]; if(ocvtimecount > timecount) //存的数据异常 { //更新计时起点 ocvtimecount = RTC_GetCounter(); //保存到EEPROM tempEE[0] = (ocvtimecount >> 24) & 0xff; tempEE[1] = (ocvtimecount >> 16) & 0xff; tempEE[2] = (ocvtimecount >> 8) & 0xff; tempEE[3] = (ocvtimecount >> 0) & 0xff; EEPROM_WrMulByte(EE_TIME_OCV, tempEE); delay_ms(10); } } //从其他状态回来/从不允许改为允许,更新当前时间为计时起点 else if(OCV_status != 1) { OCV_status = 1; OCV_Wait_flag = 1; //允许在rtc函数中进行时间判断 //更新计时起点 ocvtimecount = RTC_GetCounter(); //保存到EEPROM tempEE[0] = (ocvtimecount >> 24) & 0xff; tempEE[1] = (ocvtimecount >> 16) & 0xff; tempEE[2] = (ocvtimecount >> 8) & 0xff; tempEE[3] = (ocvtimecount >> 0) & 0xff; EEPROM_WrMulByte(EE_TIME_OCV, tempEE); delay_ms(10); } //当倒计时结束,校准SOC并更新计时起点 if((OCV_Wait_flag == 1) && (OCV_CaliSOC_flag == 1)) { //准备下一次计时 OCV_CaliSOC_flag = 0; ocvtimecount = RTC_GetCounter(); //保存到EEPROM tempEE[0] = (ocvtimecount >> 24) & 0xff; tempEE[1] = (ocvtimecount >> 16) & 0xff; tempEE[2] = (ocvtimecount >> 8) & 0xff; tempEE[3] = (ocvtimecount >> 0) & 0xff; EEPROM_WrMulByte(EE_TIME_OCV, tempEE); delay_ms(10); //更新OCV中值表 OCV_CaliSOC_DataWr(); //获取校准SOC OCV_soc = OCV_CaliSoc_dp(); if((OCV_soc == 0) && ((bmsMem.packVoltage == 0) || (bmsMem.packVoltage > ocv_data[0].ocv_dp))) //电压异常改0% or 电压正常但计算得0% = 不执行 { return; } else if(OCV_soc < bmsMem.soc-paraMem.ocv_soc_Range) //只减不增 { //若温度和原值差距过大则不替换 if((TemperatureAverage > 250+2731-paraMem.ocv_T_Range*10) && (TemperatureAverage < 250+2731+paraMem.ocv_T_Range*10)) { bmsMem.soc = OCV_soc; bmsMem.rcc = fcc/100 * bmsMem.soc; rcc_Ah = fcc_Ah * bmsMem.soc /100; oldrcc_Ah = rcc_Ah; } } } OCV_WrTime_count = 0; } //不在静置状态,对应OCV_status为2 else { OCV_status = 2; //清除标志位 OCV_Wait_flag = 0; OCV_CaliSOC_flag = 0; //非静置状态,每隔30min记录一次计时起点,防止突然断电,导致倒计时偏差过大 //此值在带电流重启后立刻静置的条件下启用 OCV_WrTime_count++; if(OCV_WrTime_count > 60*30) //等30min { OCV_WrTime_count = 0; ocvtimecount = RTC_GetCounter(); //保存到EEPROM tempEE[0] = (ocvtimecount >> 24) & 0xff; tempEE[1] = (ocvtimecount >> 16) & 0xff; tempEE[2] = (ocvtimecount >> 8) & 0xff; tempEE[3] = (ocvtimecount >> 0) & 0xff; EEPROM_WrMulByte(EE_TIME_OCV, tempEE); delay_ms(10); } } } //关闭开路电压法,对应OCV_status为3 else { OCV_status = 3; //清除倒计时和标志位 ocvtimecount = 0; OCV_Wait_flag = 0; OCV_CaliSOC_flag = 0; } }