Files
2026-08-25 17:30:40 +08:00

657 lines
17 KiB
C

/********************************************************************************
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);
}
}
}
}