/******************************************************************************** Copyright (C), Sinowealth Electronic. Ltd. Author: Sino Version: V0.0 Date: 2014/05/30 History: V0.0 2014/05/30 Preliminary ********************************************************************************/ #include "stm32f10x.h" #include "global.h" #include "string.h" #include "rtc.h" uint8_t oldsoc; uint8_t newsoc; uint8_t fullToZeroStudyFlag; //满充开始学习标记 uint8_t zeroToFullStudyFlag; //满放开始学习标记 uint32_t packcheck_OV; //单体过压校准SOC的总压判断值 uint32_t packcheck_UV; //单体欠压校准SOC的总压判断值 uint16_t ncc_Ah; //额定容量,用于计算传给逆变器的并机总容量 单位1Ah uint16_t fcc_Ah; //满充容量,用于在上位机修改容量和SOC后,计算此时的剩余容量 单位1Ah uint16_t rcc_Ah; //剩余容量,用于计入累计容量,然后计算循环次数 uint16_t oldrcc_Ah; //旧现有容量,用于比较计入累计容量 uint16_t cumuliCapacity; //累积容量,单位0.1Ah uint8_t FullCharge_count; //满充减容量的延迟计数 uint8_t FullCharge_count2; //满充减容量的延迟计数2 uint8_t Cali_Soc_Flag; //15%校准执行标志 uint16_t CaliSocMoniCount; //15%校准等待时间 uint16_t oldcyc; uint8_t ClearEE[4] = {0xff,0xff,0xff,0xff}; uint8_t tmpRdFCC[8]; //用于读出总容量值 uint8_t tmpWrFCC[8]; //用于写入总容量值 uint32_t RdFCC; uint32_t fcc; //满充容量,单位mAS uint8_t fcc_CaliStartFlag; //当开始充电时,如果此时SOC=0/1%,开始计时 uint8_t fcc_fullFlag; //达到任一满充条件的标志,是最终执行满充容量校准的条件之一 /******************************************************************************* Function:InitGasGauge() Description: Calculate the remaining capacity according to pack voltage Input: NULL Output: NULL Others: *******************************************************************************/ void InitGasGauge(void) { uint8_t tmpRd[2],tmpWr[2]; uint16_t capacity,cyctime; //上电读EEPROM 额定容量值 EEPROM_RdMulByte(EE_NCC,tmpRd); capacity = tmpRd[0]<<8 | tmpRd[1]; if(capacity>0 && capacity<=1000) //如果之前写过容量值,就按照之前的值显示,范围1~1000,否则显示100Ah { bmsMem.ncc = 3600 * 1000 * capacity; ncc_Ah = capacity; } else { bmsMem.ncc = 3600 * 1000 * 100; //系统额定容量默认100AH = 100,000mAH = 360,000,000mAS ncc_Ah = 100; } //上电读EEPROM 满充容量值 //前4位是正值,后四位是反值,用来校验数值正确 EEPROM_RdMulByte(EE_FCC,tmpRdFCC); if( ((tmpRdFCC[0]^0xff) == tmpRdFCC[4]) && ((tmpRdFCC[1]^0xff) == tmpRdFCC[5]) && ((tmpRdFCC[2]^0xff) == tmpRdFCC[6]) && ((tmpRdFCC[3]^0xff) == tmpRdFCC[7]) ) { RdFCC = tmpRdFCC[0]<<24 | tmpRdFCC[1]<<16 | tmpRdFCC[2]<<8 | tmpRdFCC[3]; if((RdFCC>0) && (RdFCC <= 0xFFE00000)) //不出错的正数范围为1~4,292,870,144,大约1192Ah { fcc = RdFCC; fcc_Ah = RdFCC / 3600000; } else { fcc = bmsMem.ncc; fcc_Ah = bmsMem.ncc / 3600000; } } else { fcc = bmsMem.ncc; fcc_Ah = bmsMem.ncc / 3600000; } //上电读EEPROM 循环次数 EEPROM_RdMulByte(EE_CYCLE,tmpRd); cyctime = tmpRd[0]<<8 | tmpRd[1]; if(cyctime > paraMem.sohcali_stopTime+10) //初次读取 { oldcyc = 0; bmsMem.cycleCount = 0; } else { oldcyc = cyctime; bmsMem.cycleCount = cyctime; } //上电读EEPROM 累积容量值 //计算时,使用额定容量而不是满充容量 EEPROM_RdMulByte(EE_CUMULI,tmpRd); capacity = tmpRd[0]<<8 | tmpRd[1]; if((int)capacity>=0 && capacity<=10*ncc_Ah) //剩余容量范围0~10000*0.1Ah,超出则显示0 { cumuliCapacity = capacity; //获得进行循环次数计算的单位容量(此处不可为0) if(paraMem.sohcali_transCent != 0) { capacity = 10*ncc_Ah * paraMem.sohcali_transCent/100; } else { capacity = 10*ncc_Ah * 90/100; //默认90% } //计算循环次数 if(cumuliCapacity >= capacity) //当累积容量超出总容量的90%,增加循环次数 { bmsMem.cycleCount += cumuliCapacity / capacity; cumuliCapacity = cumuliCapacity % capacity; //保存参与过循环次数计算后的累积容量 tmpWr[0] = (cumuliCapacity >> 8) & 0xff; tmpWr[1] = (cumuliCapacity >> 0) & 0xff; EEPROM_WrMulByte(EE_CUMULI,tmpWr); delay_ms(5); } } else { cumuliCapacity = 0; } //上电读EEPROM soc值 EEPROM_RdMulByte(EE_SOC,&tmpRd[0]); //正常 if(((int)tmpRd[0]>=0) && (tmpRd[0]<=100)) //在比较操作中使用类型转换不会改变变量本身的类型 { oldsoc = tmpRd[0]; bmsMem.soc = tmpRd[0]; bmsMem.rcc = fcc/100 * bmsMem.soc; rcc_Ah = fcc_Ah * bmsMem.soc /100; oldrcc_Ah = rcc_Ah; } //首次上电根据电压校准soc else { OCV_CaliSOC_DataWr(); bmsMem.soc = OCV_CaliSoc_dp(); bmsMem.rcc = fcc/100 * bmsMem.soc; rcc_Ah = fcc_Ah * bmsMem.soc /100; oldrcc_Ah = rcc_Ah; } } //Manage the capacity of the pack //interval 1s void GaugeManage(void) { uint8_t tempWr[2]; uint16_t capacity; /*参与判断的总压限值*/ //模块过压值 packcheck_OV = cell_OV * bmsMem.ucCellNum -8000; //模块欠压值 packcheck_UV = cell_UV * bmsMem.ucCellNum +5000; /*计算剩余容量*/ //电流积分法:微小电流认为是干扰,不参与计算 if( (bmsMem.packCurrent <= (-100)) || (bmsMem.packCurrent >= 100) ) { bmsMem.rcc += bmsMem.packCurrent; } if(bmsMem.rcc > 0xFFE00000)//4.21添加,防止过放数据出错 //因改为无符号,需清零的负数范围设-1~-2,097,151,电流值在2000A范围内皆可接受,不出错的正数范围为0~4,292,870,144,大约1192Ah { bmsMem.rcc = 0; } if((paraMem.cali_min_disable & 0x8000) != 0) //不允许做满充容量校准 { //在校准容量判断前,保证参数正确 if(fcc_CaliStartFlag != 0) { fcc_CaliStartFlag = 0; EEPROM_WrMulByte(EE_FCC_TIME,ClearEE); delay_ms(5); } if(fcc != bmsMem.ncc) //校准总容量=额定容量 { //赋值满充容量=额定容量 fcc = bmsMem.ncc; fcc_Ah = ncc_Ah; tmpWrFCC[0] = (fcc>>24) & 0xff; tmpWrFCC[1] = (fcc>>16) & 0xff; tmpWrFCC[2] = (fcc>> 8) & 0xff; tmpWrFCC[3] = (fcc>> 0) & 0xff; tmpWrFCC[4] = tmpWrFCC[0] ^ 0xff; tmpWrFCC[5] = tmpWrFCC[1] ^ 0xff; tmpWrFCC[6] = tmpWrFCC[2] ^ 0xff; tmpWrFCC[7] = tmpWrFCC[3] ^ 0xff; EEPROM_WrMulByte(EE_FCC,tmpWrFCC); delay_ms(20); //赋值剩余容量=新的满充容量*SOC bmsMem.rcc = fcc/100 * bmsMem.soc; rcc_Ah = fcc_Ah * bmsMem.soc / 100; oldrcc_Ah = rcc_Ah; } } /*计算实时SOC=剩余容量/满充容量*/ if(bmsMem.rcc > fcc/100 * 99) //>99% { //若SOC已经是100%,不会下调 if(bmsMem.soc >= 100) { bmsMem.soc = 100; } //若SOC此前<=99,锁定99% else { bmsMem.soc = 99; } } else //0%~99% { //如果剩余容量的小数部分至少有0.1Ah,soc+1 if( (bmsMem.rcc%(fcc/100)) /360000 != 0) //取精度0.1%作为判断标准 360000mAS = 0.1*1000*3600 = 0.1Ah { bmsMem.soc = bmsMem.rcc/(fcc/100)+1; } else { bmsMem.soc = bmsMem.rcc/(fcc/100); } } /*若满足特殊条件,直接改动SOC值,注意剩余容量保持不变*/ //read bSTATUS1 ov bit 满充校准 //满充条件1:单体过压 if( ((paraMem.soc100_methods & 0x01) != 0) && ((bmsMem.bStatus1 & 0x0001) != 0) && (bmsMem.packVoltage > packcheck_OV)) //发生过压保护(会关MOS)//带总压判断(根据串数变化) { bmsMem.soc = 100; fcc_fullFlag = 1; //如果不在等待校准满充容量,同时更新rcc=fcc if(fcc_CaliStartFlag == 0) { bmsMem.rcc = fcc; } } //满充条件2:总体过压 else if( ((paraMem.soc100_methods & 0x02) != 0) && ((bmsMem.bStatus1 & 0x0100) != 0)) //发生总体过压保护(会关MOS) { bmsMem.soc = 100; fcc_fullFlag = 1; //如果不在等待校准满充容量,同时更新rcc=fcc if(fcc_CaliStartFlag == 0) { bmsMem.rcc = fcc; //不计入循环次数 rcc_Ah = bmsMem.rcc/3600/1000; oldrcc_Ah = rcc_Ah; } } //满充条件3:逆变器限压57.6V+2A小电流 else if( ((paraMem.soc100_methods & 0x04) != 0) && (bmsMem.packVoltage >= bmsMem.inverter_chgVolLimit*100) && (bmsMem.packCurrent >= 100) && (bmsMem.packCurrent <= 2000)) //电压大于逆变器充电限压值,电流小于2A(逆变器会逐渐停止充电,但BMS不关闭MOS) { bmsMem.soc = 100; fcc_fullFlag = 1; //如果不在等待校准满充容量,同时更新rcc=fcc if(fcc_CaliStartFlag == 0) { bmsMem.rcc = fcc; //不计入循环次数 rcc_Ah = bmsMem.rcc/3600/1000; oldrcc_Ah = rcc_Ah; } } //满充条件4:满充电压56V+5A截止电流 else if( ((paraMem.soc100_methods & 0x08) != 0) && ((bmsMem.bStatus1 & 0x0800) != 0)) //发生满充停止充电(会关MOS) { bmsMem.soc = 100; fcc_fullFlag = 1; //如果不在等待校准满充容量,同时更新rcc=fcc if(fcc_CaliStartFlag == 0) { bmsMem.rcc = fcc; //不计入循环次数 rcc_Ah = bmsMem.rcc/3600/1000; oldrcc_Ah = rcc_Ah; } } else { fcc_fullFlag = 0; } //read bFLAG1 ov bit 满放校准 if((bmsMem.bStatus1 & 0x0002) !=0) //发生单体欠压保护 { if(bmsMem.packVoltage < packcheck_UV) //总压判断(根据串数变化) { bmsMem.soc = 0; bmsMem.rcc = 0; } } else if((bmsMem.bStatus1 & 0x0200) !=0) //发生总体欠压保护 { bmsMem.soc = 0; bmsMem.rcc = 0; } else { if(bDSGING) //还正在放电但rcc已经为0,会上调一点容量,直到满足满放条件 { if(bmsMem.soc == 0) //若SOC原是0%,放电不破坏该值 { bmsMem.rcc = 0; } else if((bmsMem.rcc <= 360000) || (bmsMem.rcc > 0xFFE00000)) //因改为无符号,需清零的负数范围设-1~-2,097,151,电流值在2000A范围内皆可接受,不出错的正数范围为0~4,292,870,144,大约1192Ah { bmsMem.soc = 1; bmsMem.rcc = 360000 * 5; //增加0.5Ah用于继续下降 //在0.1Ah~0.5Ah之间维持(soc=1%) } } } //15%校准:当总电压小于等于50V时,若SOC大于15%则校准到15% //4.28增加电流条件放电15A以下 if((bmsMem.packVoltage <= (50000/16*bmsMem.ucCellNum)) && (bmsMem.soc > 15) && (bmsMem.packCurrent > (-15000)) && (bmsMem.packCurrent < 100)) { Cali_Soc_Flag = 1; } else { Cali_Soc_Flag = 0; } if((paraMem.cali_min_disable & 0x8000) == 0) //允许做满充容量校准 { Cali_FCC_Moni(); //电流和SOC满足条件后执行 } else //不允许 { //在以上所有执行完后,有问题再纠正下 if(bmsMem.soc == 100) //SOC已经到了100%,此时rcc最高不超过fcc { if(bmsMem.rcc > fcc) { bmsMem.rcc = fcc; } } else //SOC低于100%,此时rcc最高不超过fcc*99% { if(bmsMem.rcc > fcc/100 * 99) { if(bCHGING) //正在充电 { bmsMem.rcc = fcc - (fcc/1000*15); } else { bmsMem.rcc = fcc/100 * 99; } } } } bmsMem.can_soc = bmsMem.soc; //sum of all packs bmsMem.can_soh = bmsMem.soh; //sum of all packs /*数据存入EEPROM*/ //SOC write to eeprom if(bmsMem.soc != oldsoc) { oldsoc = bmsMem.soc; tempWr[0] = bmsMem.soc; EEPROM_WrMulByte(EE_SOC,&tempWr[0]); delay_ms(5); } //累积容量 write to eeprom rcc_Ah = bmsMem.rcc/3600/1000; if(rcc_Ah > oldrcc_Ah) //当容量上涨了1Ah { cumuliCapacity += 10*(rcc_Ah - oldrcc_Ah); //将增加部分放入累积容量中 oldrcc_Ah = rcc_Ah; //获得进行循环次数计算的单位容量(此处不可为0) if(paraMem.sohcali_transCent != 0) { capacity = 10*ncc_Ah * paraMem.sohcali_transCent/100; } else { capacity = 10*ncc_Ah * 90/100; //默认90% } //计算循环次数 if(cumuliCapacity >= capacity) //当累积容量超出总容量的90%,增加循环次数 { bmsMem.cycleCount += cumuliCapacity / capacity; cumuliCapacity = cumuliCapacity % capacity; } //保存当前(或参与过循环次数计算后的)累积容量 tempWr[0] = (cumuliCapacity >> 8) & 0xff; tempWr[1] = (cumuliCapacity >> 0) & 0xff; EEPROM_WrMulByte(EE_CUMULI,tempWr); delay_ms(5); } else { oldrcc_Ah = rcc_Ah; //要同步增减 } //循环次数 write to eeprom if(bmsMem.cycleCount != oldcyc) { oldcyc = bmsMem.cycleCount; tempWr[0] = (bmsMem.cycleCount >> 8) & 0xff; tempWr[1] = (bmsMem.cycleCount >> 0) & 0xff; EEPROM_WrMulByte(EE_CYCLE,tempWr); delay_ms(5); if(cumuliCapClear_flag == 1) { cumuliCapClear_flag = 0; //保存累积容量为0 cumuliCapacity = 0; tempWr[0] = (cumuliCapacity >> 8) & 0xff; tempWr[1] = (cumuliCapacity >> 0) & 0xff; EEPROM_WrMulByte(EE_CUMULI,tempWr); delay_ms(5); } } /*通过循环次数计算SOH*/ if(bmsMem.cycleCount <= paraMem.sohcali_stopTime) { if(bmsMem.cycleCount <= paraMem.sohcali_startTime) { bmsMem.soh = 100; //维持100% } else { uint16_t cent = (paraMem.sohcali_stopTime-1 - paraMem.sohcali_startTime) / (99-paraMem.sohcali_minSOH); //每1%对应的大概次数 bmsMem.soh = 99 - (bmsMem.cycleCount - paraMem.sohcali_startTime) / cent; //从99%开始下降 } } else //超过就维持最大次数 { bmsMem.cycleCount = paraMem.sohcali_stopTime; bmsMem.soh = paraMem.sohcali_minSOH; } //Wh版屏幕需要 bmsMem.can_cumuliCap = cumuliCapacity/10; //sum of all packs bmsMem.can_cycleCnt = bmsMem.cycleCount; //sum of all packs //过压报警的显示判断:在接近满电时,发生[过压保护],则不会因此亮灯屏幕也不显示过压 #if Key_PressLong if((ON_confirm_flg != 0) && (RST_confirm_flg != 1) && (OFF_confirm_flg != 1)) #endif { if(((bmsMem.bStatus1 & 0x0001) != 0) || ((bmsMem.bStatus1 & 0x0100) != 0)) //单体过压+总体过压 { if(bmsMem.soc<99) //在正常工作时,发生[过压保护],正常显示 { bAlarmFlag = 1; if(sleep_flag == 0) LED_ALARM_On(); else LED_ALARM_Off(); } else { //若出现其他报警,就不恢复原状(-单体过压-总体过压+急停) if( ((bmsMem.bStatus1 & 0x067e) == 0) && ((bmsMem.bStatus2 & 0x00ff) == 0) && ((bmsMem.bStatus3 & 0x0008) == 0) && ((bmsMem.temperaStatus & 0x0f7f) == 0) ) { bAlarmFlag = 0; bAlarmFlagOld = 0; LED_ALARM_Off(); } } } } //led指示 #if Key_PressLong if((ON_confirm_flg != 0) && (RST_confirm_flg != 1) && (OFF_confirm_flg != 1)) #endif { if(sleep_flag == 0) { if(bmsMem.soc<5) { LED1_Off(); LED2_Off(); LED3_Off(); LED4_Off(); } else if(bmsMem.soc>=5 && bmsMem.soc<30) { LED1_On(); LED2_Off(); LED3_Off(); LED4_Off(); } else if(bmsMem.soc>=30 && bmsMem.soc<60) { LED1_On(); LED2_On(); LED3_Off(); LED4_Off(); } else if(bmsMem.soc>=60 && bmsMem.soc<90) { LED1_On(); LED2_On(); LED3_On(); LED4_Off(); } else { LED1_On(); LED2_On(); LED3_On(); LED4_On(); } } else //休眠模式下灯全灭 { LED1_Off(); LED2_Off(); LED3_Off(); LED4_Off(); } } } #define CALI_SOC_CNT 12000 //2*60*100个10ms=2分钟 void Cali_SOC_Moni(void) { if(Cali_Soc_Flag == 1) { CaliSocMoniCount--; if(CaliSocMoniCount == 0) { CaliSocMoniCount = CALI_SOC_CNT; if(bmsMem.soc>15) { bmsMem.soc = 15; bmsMem.rcc = fcc/100 * bmsMem.soc; } } } else { CaliSocMoniCount = CALI_SOC_CNT; } } //校准满充容量的执行过程 //若刚开始充电时,SOC=0/1%,则允许校准满充容量,同步计时12h超过则不再校准 //若12h内,满足了满充判定的任一条件而使SOC=100%,在基本停止充电(电流<2A)后执行容量校准:赋值[满充容量]=此时的剩余容量并保存。 //若12h外,满足了满充判定的任一条件而使SOC=100%,不会变动满充容量。 //剩余容量可以一直增长,若超过了原来的满充容量,SOC保持100%不再增长。 void Cali_FCC_Moni(void) { //没有在计时 if(fcc_CaliStartFlag == 0) { if((bmsMem.packCurrent >= 2000) && (bmsMem.soc <= 1)) //充电(大于2A)时,若此时SOC处于低点,进行校准总容量的倒计时 { fcc_CaliStartFlag = 1; //更新起始点,如果晶振正常就把标志和起始点都写入EEPROM if(LSEErrFlag!=1) { uint8_t time[4]; fcc_Calitimecount=RTC_GetCounter(); //写入起始时间 time[0] = fcc_Calitimecount>>24 & 0xff; time[1] = fcc_Calitimecount>>16 & 0xff; time[2] = fcc_Calitimecount>>8 & 0xff; time[3] = fcc_Calitimecount>>0 & 0xff; EEPROM_WrMulByte(EE_FCC_TIME,time); delay_ms(5); } else { fcc_Cali_Moni_Count = FCCCALI_MON_CNT; } } } //此前已经启动计时 else { if((bmsMem.packCurrent < 2000) && (fcc_fullFlag == 1)) //不在充电时,若已满足满充条件,说明此时的剩余容量是当前的满充容量值 { //赋值满充容量=剩余容量 fcc = bmsMem.rcc; fcc_Ah = bmsMem.rcc / 3600000; tmpWrFCC[0] = (fcc>>24) & 0xff; tmpWrFCC[1] = (fcc>>16) & 0xff; tmpWrFCC[2] = (fcc>> 8) & 0xff; tmpWrFCC[3] = (fcc>> 0) & 0xff; tmpWrFCC[4] = tmpWrFCC[0] ^ 0xff; tmpWrFCC[5] = tmpWrFCC[1] ^ 0xff; tmpWrFCC[6] = tmpWrFCC[2] ^ 0xff; tmpWrFCC[7] = tmpWrFCC[3] ^ 0xff; EEPROM_WrMulByte(EE_FCC,tmpWrFCC); delay_ms(20); fcc_CaliStartFlag = 0; //清零 if(LSEErrFlag == 0) { EEPROM_WrMulByte(EE_FCC_TIME,ClearEE); delay_ms(5); } } } }