标题:
锂电池常用的SOC算法分享
[打印本页]
作者:
archer168
时间:
2023-3-2 18:09
标题:
锂电池常用的SOC算法分享
SOC.h
#ifndef _SOC_H
#define _SOC_H
#define MAX_SOC 100 //1%/位 0~100%
#define MIN_SOC 0 //最小SOC
#define ADJUSTCAP_OCV 1
#define ADJUSTCAP_CHARGE 2
//范围0~999AH 3596400AS
typedef struct
{
unsigned long ulAs; //1AH = 1*3600As
unsigned long ulWh; //电量 0.1Wh/bit
unsigned char ucSOC; //剩余电量 1%/位
}TSOCPARA;
extern TSOCPARA g_SOCPara;
typedef struct
{
signed long g_slChaAms; //充电累计容量
unsigned char ucOriSOC; //充电起始SOC
}TCHAAHPARA;
extern TCHAAHPARA g_tChaAhPara;
extern volatile unsigned char CalSOCTimesOut;
extern volatile signed long g_slAms; //JACK
void WriteAs(void);
void ReadSoc( void ); //从EEPROM读取SOC
void WriteDefaultSoc( void ); //写默认SOCEEPROM
void WriteSoc( unsigned short usSoc ); //写SOC到EEPROM
unsigned char CalAdjustCap( unsigned char ucSelect);
#endif
复制代码
SOC.c
#include "TypeDefine.h"
#include "SOC.h"
#include "FM31256.H"
//安时*毫秒
//long型 最大596ah
//限制 500ah*3600*1000 = 1800000000Ams //1A*1ms
#define MAX_LIMIT_AMS 0x6B49D200 //1800000000Ams
TSOCPARA g_SOCPara;
//进行容量保存判断标准 相差值大小
unsigned char g_ucSOC_Back = 0;//
volatile unsigned char CalSOCTimesOut;
volatile signed long g_slAS = 0;//当前容量
volatile signed long g_slAms =0; //JACK
//SOC累计标准 初始状态上累计 对应g_slAms 变量
signed long g_slInitAS = 0;//初始容量
static unsigned char g_ucCalAdjustFlag = 0;
TCHAAHPARA g_tChaAhPara;
unsigned long diff(unsigned long w1,unsigned long w2)
{
if(w1>w2)
return(w1-w2);
else
return(w2-w1);
}
const unsigned int g_cuiOCVTable[ 5 ]={0,0,0,0,0};
const unsigned int g_cuiOCVSOCTable[ 5 ]={0,0,0,0,0};
/* lookUp1d
* ========
* Performs simple linear interpolation of a 2D map.
* Outputs are bounded by the upper and lower values
*
* Both axis are expected to be increasing in value
* as the index increases
*
* Return = -1 for error and 1 for success
* Params = int *ans > an address to put the result
* int x > the x value to lookup
* const int *xAxis > array of value for x axis
* const int lenX > length of x axis array
* int y > the y value to lookup
* const int *yAxis > array of value for y axis
* const int lenY > length of y axis array
*
*/
int lookUp1d(int *ans,
int x, const int *xAxis, const int lenX,
const int *tab)
{
int ret = -1; /* -1 is the error return value */
int ix=0;
/* make sure the lengths are good */
if ( (lenX <=0 ) ||
(xAxis == NULL) ||
(tab == NULL) ||
(ans == NULL) )
{
/* this has bad input data */
}
else
{
/* make sure it is in the boundaries before we do any looping*/
if (x<=xAxis[0])
{
x = xAxis[0];
}
else
{
if (x>=xAxis[lenX-1])
{
x = xAxis[lenX-1];
}
}
for (ix=0;ix<(lenX-1);ix++)
{
if (x>=xAxis[ix] && x<=xAxis[ix+1])
{
int x1,x2;
int z1,z2;
/* copy to a lot of temp variables to make code easier to read*/
x1=xAxis[ix];
x2=xAxis[ix+1];
z1=tab[ix];
z2=tab[(ix+1)];
/* then interpolate in the y direction for the answer */
*ans = z1+((z2-z1)*(x-x1))/(x2-x1);
/* indicate a valid result */
ret = 1;
break; /* exit as fast as possible */
}
}
}
return ret;
}
//返回: -1 for error and 1 for success
//参数:填充结果, OCV值
int OCV_SOC( int *resSOC, unsigned int uiVolt)
{
int res = 0;
res = lookUp1d(resSOC,uiVolt,g_cuiOCVTable,5,g_cuiOCVSOCTable);
return res;
}
//两种方法修正实际可用容量( 不考虑老化 )
unsigned char CalAdjustCap( unsigned char ucSelect)
{
int res1 = 0, res2 = 0;
unsigned char ucMaxSOC = 0, ucMinSOC = 0 ;
unsigned long ulTemp1 = 0,ulTemp2 = 0;
res1 = OCV_SOC( &ucMaxSOC, ECU_Rt.maxv );
res2 = OCV_SOC( &ucMinSOC, ECU_Rt.minv );
if( ( 1 == res1) && ( 1 == res2))
{
if( ADJUSTCAP_OCV == ucSelect)
{
//OCV 法 EV HEV
//OCVmax => SOCmax OCVmin => SOCmin
//Cpack=SOCmin*Crate + ( 1 - SOCmax) * Crate = 60 + 30 = 90Ah
ulTemp1 = Sys_Rt.Type_Cap;
ulTemp1 *= ucMinSOC;
ulTemp2 = Sys_Rt.Type_Cap;
ulTemp2 *= 100 - ucMaxSOC;
ulTemp1 += ulTemp2;
ulTemp1 /= 100;
Sys_Rt.ADJType_Cap = (UINT16)ulTemp1;
g_ucCalAdjustFlag = 1;
}
else if( ADJUSTCAP_CHARGE == ucSelect)
{
//充电法 (EV)
//OCVmin => SOCmin
//Cpack=SOCmin*Crate + Ccharged
ulTemp1 = g_tChaAhPara.ucOriSOC;
ulTemp1 *= Sys_Rt.Type_Cap;
ulTemp1 /= 100;
ulTemp2 = g_tChaAhPara.g_slChaAms;
ulTemp2 /= 1000;
ulTemp2 /= 3600;
ulTemp1 += ulTemp2;
Sys_Rt.ADJType_Cap = (UINT16)ulTemp1;
g_ucCalAdjustFlag = 1;
}
else
{
return 0;
}
return 1;
}
else
return 0;
}
void CalSOC( unsigned long ulAs ) //WriteAs(void)调用
{
unsigned long ulTemp = 0;
unsigned long ulTemp1 = 0;
unsigned int uiCapType = 0;
g_SOCPara.ulAs = ulAs;
ulTemp = g_SOCPara.ulAs;
ulTemp = ulTemp / 3600; //As => Ah
if( 1 == g_ucCalAdjustFlag)
uiCapType = Sys_Rt.ADJType_Cap; //使用校准后的额定容量
else
uiCapType = Sys_Rt.Type_Cap; //使用给定的额定容量
if( ulTemp < uiCapType) //当前容量小于额定容量(额定容量可修正)
{
//g_SOCPara.ulWh = ulTemp * g_tVoltDataPackage.ulSumData;//0.1Wh W=U*I*T
//1Ah => 0.01Ah 分辨率扩大100倍
ulTemp = g_SOCPara.ulAs;
ulTemp *= 100;
ulTemp = ulTemp / 3600;
ulTemp1 = uiCapType;//额定容量扩大100倍
ulTemp1 *= 100;
//XX% SOC
ulTemp *= 100;//剩余容量÷额定容量 × 100%100
ulTemp = ulTemp / ulTemp1; //计算SOC,剩余电量比例
g_SOCPara.ucSOC = (unsigned char) ulTemp;
}
else
{
g_SOCPara.ucSOC = 100;
if( 1 == g_ucCalAdjustFlag)
ulTemp = Sys_Rt.ADJType_Cap;//校准后容量
else
ulTemp = Sys_Rt.Type_Cap;//使用给定的额定容量
//g_SOCPara.ulWh = ulTemp * g_tVoltDataPackage.ulSumData;//0.1Wh
}
if( diff(g_ucSOC_Back,g_SOCPara.ucSOC) >= 1 )//SOC 百分比相差 1 SOC有改变才存储
{
g_ucSOC_Back = g_SOCPara.ucSOC;//更新最后记录的SOC
//写SOC到FM31256
(void)WriteMulti31256RAM( FLASH_SOC_ADDR, ( unsigned char *)&g_SOCPara, sizeof( g_SOCPara ) );
}
}
void WriteAs(void)
{
signed char scIncSOC = 0;
signed long slIncCap = 0;
unsigned long ulCapTemp = 0;
unsigned long ulTemp = 0;
unsigned char ucForceSetFlag = 0;
unsigned int uiCapType = 0;
if(CalSOCTimesOut != 0) return; //1s 定时处理SOC计算
CalSOCTimesOut = 200; //设定记数值
//累加当前充放电容量 正负相加减
if( g_slAms < 0 )
{
ulTemp = (-1) * g_slAms;
ulTemp /= 1000;//Ams => As
g_slAS = g_slInitAS;
g_slAS -= ulTemp;//累计上电后 放电 容量
}
else
{
ulTemp = g_slAms;
ulTemp /= 1000;//Ams => As
g_slAS = g_slInitAS;
g_slAS += ulTemp;//累计上电 充电 容量
}
//计算 g_slAS 为当前容量///////////////////////////////////
//当前容量 < 0
if( g_slAS < 0 )
{
g_slAS = 0;//容量归零
//放电 累计容量 最大限制 = 上电初始容量
g_slAms = g_slInitAS;
g_slAms *= 1000;//As => Ams
g_slAms =(-1) * g_slAms;
}
//当前容量 > 额定容量
if( 1 == g_ucCalAdjustFlag)
uiCapType = Sys_Rt.ADJType_Cap;//判断是否使用校准后容量
else
uiCapType = Sys_Rt.Type_Cap;//
ulTemp = g_slAS;
ulTemp /= 3600; //容量转化为Ah
if( ulTemp >= uiCapType) //当前容量大于额定容量(可修正)
{
//充电 累计容量 最大限制 = 额定容量-上电初始容量
g_slAms = uiCapType;//额定容量
g_slAms *= 3600;//Ah => As
g_slAS = g_slAms;//当前容量 = 额定容量 中间结果赋值
g_slAms -= g_slInitAS;//额定容量-上电初始容量
g_slAms *= 1000;//As => Ams
}
/*
if( abs( g_slAms ) > MAX_LIMIT_AMS)
{
g_slAms = MAX_LIMIT_AMS;//超界后清零
}
*/
/*0 < g_slAS < uiCapType*/
CalSOC( g_slAS ); //计算SOC 百分比
}
/******************************************************************************
*函数名: WriteDefaultSoc 设置SOC百分比 100% *
*功能描述: 写默认SOC *
*输入参数: 无 *
*输出参数: 无 *
*返回值: 无 *
******************************************************************************/
/**/
void WriteDefaultSoc( void )
{
unsigned char ucDefaultSoc = MAX_SOC; //SOC放大1倍
unsigned long ulTemp = 0;
//更新SOC
g_SOCPara.ucSOC = ucDefaultSoc;
g_ucSOC_Back = ucDefaultSoc;
if( 1 == g_ucCalAdjustFlag)
ulTemp = Sys_Rt.ADJType_Cap;//校准后容量
else
ulTemp = Sys_Rt.Type_Cap;//额定容量
ulTemp *= g_SOCPara.ucSOC;
ulTemp /= 100;//剩余AH
g_SOCPara.ulAs = ulTemp * 3600;//As
//g_SOCPara.ulWh = ulTemp * g_tVoltDataPackage.ulSumData;//0.1Wh
//写默认SOC到FM31256
(void)WriteMulti31256RAM( FLASH_SOC_ADDR, ( unsigned char *)&g_SOCPara, sizeof( g_SOCPara ) );
}
/******************************************************************************
*函数名: WriteSOC 设置SOC百分比 *
*功能描述: 写SOC *
*输入参数: usSoc:SOC *
*输出参数: 无 *
*返回值: 无 *
******************************************************************************/
void WriteSoc( unsigned char usSoc )
{
unsigned long ulTemp = 0;
if( usSoc > MAX_SOC ) //SOC越界
{
usSoc = MAX_SOC;
}//
//更新SOC
g_SOCPara.ucSOC = usSoc;
g_ucSOC_Back = usSoc;
if( 1 == g_ucCalAdjustFlag)
ulTemp = Sys_Rt.ADJType_Cap;//校准后容量
else
ulTemp = Sys_Rt.Type_Cap;//额定容量
ulTemp *= 100;//1Ah => 0.01Ah
ulTemp *= g_SOCPara.ucSOC;
ulTemp /= 100;//剩余AH
g_SOCPara.ulAs = ulTemp * 3600;//0.001As
g_SOCPara.ulAs /= 100;//0.001As => 1As
//g_SOCPara.ulWh = ulTemp * g_tVoltDataPackage.ulSumData;//0.001Wh
//g_SOCPara.ulWh /= 100;//0.001Wh =>0.1Wh
//写SOC到FM31256
(void)WriteMulti31256RAM( FLASH_SOC_ADDR, ( unsigned char *)&g_SOCPara, sizeof( g_SOCPara ) );
g_slAS = g_SOCPara.ulAs;//
g_slInitAS = g_SOCPara.ulAs;
g_slAms = 0;
}
/******************************************************************************
*函数名: ReadSoc *
*功能描述: 从EEPROM中读SOC 设置标称容量 *
*输入参数: 无 *
*输出参数: 无 *
*返回值: 无 *
******************************************************************************/
/**/
void ReadSoc( void )
{
unsigned long ulTemp = 0;
Sys_Rt.Type_Cap = 400;
//从FM31256读SOC
(void)ReadMulti31256RAM( FLASH_SOC_ADDR, ( unsigned char *)&g_SOCPara, sizeof( g_SOCPara ) );
if( g_SOCPara.ucSOC > MAX_SOC )
{
g_SOCPara.ucSOC = MAX_SOC;
} //
if( 1 == g_ucCalAdjustFlag)
ulTemp = Sys_Rt.ADJType_Cap;//校准后容量
else
ulTemp = Sys_Rt.Type_Cap;//额定容量
ulTemp *= 100;//1Ah => 0.01Ah
ulTemp *= g_SOCPara.ucSOC;
ulTemp /= 100;//剩余AH
g_SOCPara.ulAs = ulTemp * 3600;//0.001As
g_SOCPara.ulAs /= 100;//0.001As => 1As
//g_SOCPara.ulWh = ulTemp * g_tVoltDataPackage.ulSumData;//0.1Wh
//g_SOCPara.ulWh /= 100;//0.001Wh =>0.1Wh
g_slAS = g_SOCPara.ulAs;//
g_slInitAS = g_SOCPara.ulAs;
g_ucSOC_Back = g_SOCPara.ucSOC;
}
复制代码
作者:
youlinys
时间:
2023-3-3 11:35
相当有用。。
作者:
wis98
时间:
2023-4-20 11:44
学习一下了,我改行搞电池
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1