找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1195|回复: 2
收起左侧

锂电池常用的SOC算法分享

[复制链接]
ID:1064931 发表于 2023-3-2 18:09 | 显示全部楼层 |阅读模式
SOC.h
  1. #ifndef _SOC_H
  2. #define _SOC_H   
  3. #define MAX_SOC 100    //1%/位        0~100%

  4. #define MIN_SOC 0    //最小SOC     

  5. #define  ADJUSTCAP_OCV      1
  6. #define  ADJUSTCAP_CHARGE   2
  7. //范围0~999AH   3596400AS
  8. typedef struct      
  9. {
  10.    unsigned long ulAs;     //1AH = 1*3600As
  11.    
  12.    unsigned long ulWh;    //电量 0.1Wh/bit
  13.    
  14.    unsigned char ucSOC;    //剩余电量 1%/位            
  15. }TSOCPARA;

  16. extern  TSOCPARA g_SOCPara;

  17. typedef struct      
  18. {
  19.    signed long g_slChaAms; //充电累计容量
  20.    
  21.    unsigned char ucOriSOC; //充电起始SOC            
  22. }TCHAAHPARA;

  23. extern TCHAAHPARA g_tChaAhPara;

  24. extern volatile unsigned char CalSOCTimesOut;
  25. extern volatile signed long g_slAms; //JACK

  26. void WriteAs(void);
  27.        
  28. void ReadSoc( void );    //从EEPROM读取SOC

  29. void WriteDefaultSoc( void );    //写默认SOCEEPROM

  30. void WriteSoc( unsigned short usSoc );    //写SOC到EEPROM

  31. unsigned char CalAdjustCap( unsigned char ucSelect);

  32. #endif
复制代码


SOC.c
  1. #include "TypeDefine.h"
  2. #include "SOC.h"   
  3. #include "FM31256.H"

  4. //安时*毫秒
  5. //long型 最大596ah
  6. //限制  500ah*3600*1000 = 1800000000Ams //1A*1ms
  7. #define MAX_LIMIT_AMS 0x6B49D200 //1800000000Ams

  8. TSOCPARA g_SOCPara;

  9. //进行容量保存判断标准 相差值大小
  10. unsigned char  g_ucSOC_Back = 0;//

  11. volatile unsigned char CalSOCTimesOut;

  12. volatile signed long g_slAS = 0;//当前容量
  13. volatile signed long g_slAms =0; //JACK

  14. //SOC累计标准 初始状态上累计 对应g_slAms 变量
  15. signed long g_slInitAS = 0;//初始容量

  16. static unsigned char g_ucCalAdjustFlag = 0;

  17. TCHAAHPARA g_tChaAhPara;

  18. unsigned long diff(unsigned long w1,unsigned long w2)
  19. {
  20.         if(w1>w2)
  21.                 return(w1-w2);
  22.         else
  23.            return(w2-w1);
  24. }

  25. const unsigned int g_cuiOCVTable[ 5 ]={0,0,0,0,0};

  26. const unsigned int g_cuiOCVSOCTable[ 5 ]={0,0,0,0,0};

  27. /* lookUp1d
  28. * ========
  29. * Performs simple linear interpolation of a 2D map.
  30. * Outputs are bounded by the upper and lower values
  31. *
  32. * Both axis are expected to be increasing in value
  33. * as the index increases
  34. *
  35. * Return = -1 for error and 1 for success
  36. * Params = int *ans         > an address to put the result
  37. *          int x            > the x value to lookup
  38. *          const int *xAxis > array of value for x axis
  39. *          const int lenX   > length of x axis array
  40. *          int y            > the y value to lookup
  41. *          const int *yAxis > array of value for y axis
  42. *          const int lenY   > length of y axis array
  43. *
  44. */

  45. int lookUp1d(int *ans,
  46.                    int x, const int *xAxis, const int lenX,
  47.                    const int *tab)
  48. {
  49.         int ret = -1; /* -1 is the error return value */
  50.         int ix=0;

  51.         /* make sure the lengths are good */
  52.         if ( (lenX <=0 ) ||
  53.                  (xAxis == NULL) ||
  54.                  (tab == NULL)   ||
  55.                  (ans == NULL) )
  56.         {
  57.                 /* this has bad input data */
  58.         }
  59.         else
  60.         {

  61.                 /* make sure it is in the boundaries before we do any looping*/
  62.                 if (x<=xAxis[0])
  63.                 {
  64.                         x = xAxis[0];
  65.                 }
  66.                 else
  67.                 {
  68.                         if (x>=xAxis[lenX-1])
  69.                         {
  70.                                 x = xAxis[lenX-1];
  71.                         }
  72.                 }

  73.                 for (ix=0;ix<(lenX-1);ix++)
  74.                 {
  75.                         if (x>=xAxis[ix] && x<=xAxis[ix+1])
  76.                         {
  77.                                 int x1,x2;
  78.                                 int z1,z2;

  79.                                 /* copy to a lot of temp variables to make code easier to read*/
  80.                                 x1=xAxis[ix];
  81.                                 x2=xAxis[ix+1];                                                
  82.                                 z1=tab[ix];
  83.                                 z2=tab[(ix+1)];

  84.                                 /* then interpolate in the y direction for the answer */
  85.                                 *ans = z1+((z2-z1)*(x-x1))/(x2-x1);

  86.                                 /* indicate a valid result */
  87.                                 ret  = 1;
  88.                                 break; /* exit as fast as possible */
  89.                         }
  90.                 }
  91.         }

  92.         return ret;
  93. }

  94. //返回: -1 for error and 1 for success
  95. //参数:填充结果, OCV值
  96. int OCV_SOC( int *resSOC, unsigned int uiVolt)
  97. {
  98.         int res = 0;
  99.         
  100.         res = lookUp1d(resSOC,uiVolt,g_cuiOCVTable,5,g_cuiOCVSOCTable);

  101.         return res;        
  102. }

  103. //两种方法修正实际可用容量( 不考虑老化 )
  104. unsigned char CalAdjustCap( unsigned char ucSelect)
  105. {
  106.         int res1 = 0, res2 = 0;

  107.         unsigned char ucMaxSOC = 0, ucMinSOC = 0 ;

  108.         unsigned long ulTemp1 = 0,ulTemp2 = 0;

  109.         res1 = OCV_SOC( &ucMaxSOC, ECU_Rt.maxv );

  110.         res2 = OCV_SOC( &ucMinSOC, ECU_Rt.minv );
  111.         
  112.         if( ( 1 == res1) && ( 1 == res2))
  113.         {

  114.                 if( ADJUSTCAP_OCV == ucSelect)
  115.                 {
  116.                         //OCV 法 EV HEV
  117.                         //OCVmax => SOCmax   OCVmin => SOCmin
  118.                         //Cpack=SOCmin*Crate + ( 1 - SOCmax) * Crate = 60 + 30  = 90Ah
  119.                         ulTemp1 = Sys_Rt.Type_Cap;
  120.                         ulTemp1 *= ucMinSOC;

  121.                         ulTemp2 = Sys_Rt.Type_Cap;
  122.                         ulTemp2 *= 100 - ucMaxSOC;

  123.                         ulTemp1 += ulTemp2;

  124.                         ulTemp1 /= 100;

  125.                         Sys_Rt.ADJType_Cap = (UINT16)ulTemp1;

  126.                         g_ucCalAdjustFlag = 1;                        
  127.                 }
  128.                 else if( ADJUSTCAP_CHARGE == ucSelect)
  129.                 {
  130.                     //充电法 (EV)
  131.                     //OCVmin => SOCmin
  132.                     //Cpack=SOCmin*Crate + Ccharged
  133.                         ulTemp1 = g_tChaAhPara.ucOriSOC;
  134.                         ulTemp1 *= Sys_Rt.Type_Cap;
  135.                         ulTemp1 /= 100;

  136.                         ulTemp2 = g_tChaAhPara.g_slChaAms;
  137.                         ulTemp2 /= 1000;
  138.                         ulTemp2 /= 3600;
  139.                         
  140.                         ulTemp1 += ulTemp2;
  141.                         
  142.                         Sys_Rt.ADJType_Cap = (UINT16)ulTemp1;
  143.                         
  144.                         g_ucCalAdjustFlag = 1;                        
  145.                 }
  146.                 else
  147.                 {
  148.                         return 0;                        
  149.                 }
  150.                 return 1;               
  151.         }
  152.         else
  153.                 return 0;        
  154. }

  155. void CalSOC( unsigned long ulAs )  //WriteAs(void)调用
  156. {
  157.         unsigned long ulTemp = 0;

  158.         unsigned long ulTemp1 = 0;

  159.         unsigned int uiCapType = 0;
  160.         
  161.         g_SOCPara.ulAs = ulAs;

  162.         ulTemp = g_SOCPara.ulAs;

  163.         ulTemp = ulTemp / 3600;    //As => Ah
  164.    
  165.         if( 1 == g_ucCalAdjustFlag)
  166.                 uiCapType = Sys_Rt.ADJType_Cap;  //使用校准后的额定容量
  167.         else
  168.                 uiCapType = Sys_Rt.Type_Cap;   //使用给定的额定容量


  169.         if( ulTemp < uiCapType)  //当前容量小于额定容量(额定容量可修正)
  170.         {
  171.                 //g_SOCPara.ulWh = ulTemp * g_tVoltDataPackage.ulSumData;//0.1Wh W=U*I*T

  172.                  //1Ah => 0.01Ah  分辨率扩大100倍
  173.                 ulTemp = g_SOCPara.ulAs;
  174.                 ulTemp *= 100;
  175.                 ulTemp = ulTemp / 3600;
  176.                
  177.                 ulTemp1 = uiCapType;//额定容量扩大100倍
  178.                 ulTemp1 *= 100;

  179.                 //XX% SOC
  180.                 ulTemp *= 100;//剩余容量÷额定容量 × 100%100

  181.                 ulTemp = ulTemp /  ulTemp1;  //计算SOC,剩余电量比例

  182.                 g_SOCPara.ucSOC  = (unsigned char) ulTemp;
  183.                
  184.         }
  185.         else
  186.         {
  187.                 g_SOCPara.ucSOC = 100;

  188.                 if( 1 == g_ucCalAdjustFlag)               
  189.                         ulTemp = Sys_Rt.ADJType_Cap;//校准后容量               
  190.                 else               
  191.                         ulTemp = Sys_Rt.Type_Cap;//使用给定的额定容量
  192.                
  193.                 //g_SOCPara.ulWh = ulTemp * g_tVoltDataPackage.ulSumData;//0.1Wh               
  194.         }        
  195.         if( diff(g_ucSOC_Back,g_SOCPara.ucSOC) >= 1 )//SOC 百分比相差 1 SOC有改变才存储
  196.         {        
  197.                 g_ucSOC_Back = g_SOCPara.ucSOC;//更新最后记录的SOC
  198.                
  199.                 //写SOC到FM31256
  200.                 (void)WriteMulti31256RAM( FLASH_SOC_ADDR, ( unsigned char *)&g_SOCPara, sizeof( g_SOCPara ) );  
  201.         }        
  202. }

  203. void WriteAs(void)
  204. {

  205.         signed char scIncSOC  = 0;

  206.         signed long slIncCap = 0;

  207.         unsigned long ulCapTemp = 0;

  208.         unsigned long ulTemp = 0;

  209.         unsigned char ucForceSetFlag = 0;

  210.         unsigned int uiCapType = 0;

  211.         if(CalSOCTimesOut != 0)        return;  //1s 定时处理SOC计算
  212.         
  213.         CalSOCTimesOut = 200;    //设定记数值
  214.    
  215.         //累加当前充放电容量  正负相加减
  216.         if( g_slAms < 0 )
  217.         {
  218.                 ulTemp = (-1) * g_slAms;
  219.                
  220.                 ulTemp /= 1000;//Ams => As

  221.                 g_slAS = g_slInitAS;

  222.                 g_slAS -= ulTemp;//累计上电后 放电 容量
  223.         }
  224.         else
  225.         {
  226.                 ulTemp = g_slAms;
  227.                
  228.                 ulTemp /= 1000;//Ams => As

  229.                 g_slAS = g_slInitAS;

  230.                 g_slAS += ulTemp;//累计上电 充电 容量
  231.         }

  232.         //计算 g_slAS 为当前容量///////////////////////////////////

  233.         //当前容量  < 0
  234.         if( g_slAS < 0 )  
  235.         {
  236.                 g_slAS = 0;//容量归零

  237.                 //放电 累计容量 最大限制 = 上电初始容量
  238.                 g_slAms = g_slInitAS;
  239.                 g_slAms *= 1000;//As => Ams
  240.                 g_slAms =(-1) * g_slAms;
  241.         }

  242.         //当前容量  > 额定容量
  243.         if( 1 == g_ucCalAdjustFlag)
  244.                 uiCapType = Sys_Rt.ADJType_Cap;//判断是否使用校准后容量
  245.         else
  246.                 uiCapType = Sys_Rt.Type_Cap;//

  247.         ulTemp = g_slAS;
  248.         ulTemp /= 3600;  //容量转化为Ah

  249.         if( ulTemp >= uiCapType)   //当前容量大于额定容量(可修正)
  250.         {
  251.                 //充电 累计容量 最大限制 =  额定容量-上电初始容量

  252.                 g_slAms = uiCapType;//额定容量
  253.                 g_slAms *= 3600;//Ah => As

  254.                 g_slAS = g_slAms;//当前容量 = 额定容量  中间结果赋值

  255.                 g_slAms -= g_slInitAS;//额定容量-上电初始容量
  256.                 g_slAms *= 1000;//As => Ams
  257.         }

  258.         /*
  259.         if( abs( g_slAms ) > MAX_LIMIT_AMS)
  260.         {
  261.                 g_slAms = MAX_LIMIT_AMS;//超界后清零
  262.         }
  263.         */
  264.         /*0 < g_slAS < uiCapType*/
  265.         CalSOC( g_slAS );        //计算SOC 百分比
  266. }

  267. /******************************************************************************
  268. *函数名:        WriteDefaultSoc   设置SOC百分比 100%                                                                                        *
  269. *功能描述:  写默认SOC                                                                         *
  270. *输入参数:        无                                                                *
  271. *输出参数:        无                                                                                                                              *
  272. *返回值:        无                                                                                                                                *
  273. ******************************************************************************/
  274. /**/
  275. void WriteDefaultSoc( void )
  276. {        
  277.         unsigned char ucDefaultSoc = MAX_SOC;    //SOC放大1倍

  278.         unsigned long ulTemp = 0;

  279.         //更新SOC
  280.         g_SOCPara.ucSOC = ucDefaultSoc;

  281.         g_ucSOC_Back = ucDefaultSoc;

  282.         if( 1 == g_ucCalAdjustFlag)
  283.                 ulTemp = Sys_Rt.ADJType_Cap;//校准后容量
  284.         else
  285.                 ulTemp = Sys_Rt.Type_Cap;//额定容量

  286.         ulTemp *= g_SOCPara.ucSOC;

  287.         ulTemp /= 100;//剩余AH
  288.         
  289.         g_SOCPara.ulAs = ulTemp * 3600;//As
  290.                
  291.         //g_SOCPara.ulWh = ulTemp * g_tVoltDataPackage.ulSumData;//0.1Wh
  292.                
  293.          //写默认SOC到FM31256
  294.         (void)WriteMulti31256RAM( FLASH_SOC_ADDR, ( unsigned char *)&g_SOCPara, sizeof( g_SOCPara ) );                                
  295. }

  296. /******************************************************************************
  297. *函数名:        WriteSOC        设置SOC百分比                                                                                             *
  298. *功能描述:  写SOC                                                                              *
  299. *输入参数:        usSoc:SOC                                                         *
  300. *输出参数:        无                                                                                                                              *
  301. *返回值:        无                                                                                                                                *
  302. ******************************************************************************/

  303. void WriteSoc( unsigned char usSoc )
  304. {
  305.         unsigned long ulTemp = 0;
  306.         
  307.         if( usSoc > MAX_SOC )    //SOC越界
  308.         {               
  309.                 usSoc = MAX_SOC;            
  310.         }//
  311.                
  312.         //更新SOC
  313.         g_SOCPara.ucSOC = usSoc;

  314.         g_ucSOC_Back = usSoc;

  315.         if( 1 == g_ucCalAdjustFlag)
  316.                 ulTemp = Sys_Rt.ADJType_Cap;//校准后容量
  317.         else
  318.                 ulTemp = Sys_Rt.Type_Cap;//额定容量

  319.         ulTemp *= 100;//1Ah => 0.01Ah

  320.         ulTemp *= g_SOCPara.ucSOC;

  321.         ulTemp /= 100;//剩余AH
  322.         
  323.         g_SOCPara.ulAs = ulTemp * 3600;//0.001As

  324.         g_SOCPara.ulAs /= 100;//0.001As => 1As
  325.                
  326.         //g_SOCPara.ulWh = ulTemp * g_tVoltDataPackage.ulSumData;//0.001Wh

  327.         //g_SOCPara.ulWh /= 100;//0.001Wh =>0.1Wh
  328.         
  329.        //写SOC到FM31256
  330.         (void)WriteMulti31256RAM( FLASH_SOC_ADDR, ( unsigned char *)&g_SOCPara, sizeof( g_SOCPara ) );   

  331.         g_slAS = g_SOCPara.ulAs;//

  332.         g_slInitAS  =  g_SOCPara.ulAs;

  333.         g_slAms = 0;
  334.         
  335. }

  336. /******************************************************************************
  337. *函数名:        ReadSoc                                                                                                     *
  338. *功能描述:  从EEPROM中读SOC  设置标称容量                                                                    *
  339. *输入参数:        无                                                                *
  340. *输出参数:        无                                                                                                                              *
  341. *返回值:        无                                                                                                                                *
  342. ******************************************************************************/
  343. /**/
  344. void ReadSoc( void )
  345. {
  346.         unsigned long ulTemp = 0;
  347.         
  348.         Sys_Rt.Type_Cap = 400;
  349.         //从FM31256读SOC
  350.         (void)ReadMulti31256RAM( FLASH_SOC_ADDR, ( unsigned char *)&g_SOCPara, sizeof( g_SOCPara ) );
  351.             
  352.         if( g_SOCPara.ucSOC > MAX_SOC )
  353.         {
  354.             g_SOCPara.ucSOC = MAX_SOC;                  
  355.         } //

  356.         if( 1 == g_ucCalAdjustFlag)
  357.                 ulTemp = Sys_Rt.ADJType_Cap;//校准后容量
  358.         else
  359.                 ulTemp = Sys_Rt.Type_Cap;//额定容量
  360.         
  361.         ulTemp *= 100;//1Ah => 0.01Ah

  362.         ulTemp *= g_SOCPara.ucSOC;

  363.         ulTemp /= 100;//剩余AH
  364.         
  365.         g_SOCPara.ulAs = ulTemp * 3600;//0.001As

  366.         g_SOCPara.ulAs /= 100;//0.001As => 1As
  367.                
  368.         //g_SOCPara.ulWh = ulTemp * g_tVoltDataPackage.ulSumData;//0.1Wh

  369.         //g_SOCPara.ulWh /= 100;//0.001Wh =>0.1Wh

  370.         g_slAS = g_SOCPara.ulAs;//

  371.         g_slInitAS  =  g_SOCPara.ulAs;

  372.         g_ucSOC_Back = g_SOCPara.ucSOC;  
  373. }

复制代码


回复

使用道具 举报

ID:983641 发表于 2023-3-3 11:35 | 显示全部楼层
相当有用。。
回复

使用道具 举报

ID:33544 发表于 2023-4-20 11:44 | 显示全部楼层
学习一下了,我改行搞电池
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表