找回密码
 请使用中文注册
查看: 50|回复: 0

锂电池充放电管理:电量百分比计算与电路实现

[复制链接]
阅读字号:

1600

主题

58

回帖

3万

积分

超级版主

积分
32460
发表于 昨天 17:29 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?请使用中文注册

×
如何在系统中通过硬件电路和软件算法计算锂电池的电量百分比,涉及电池电压、电流检测、ADC数据处理以及充电状态监控等关键步骤。      
一、说明

本文讲述锂电池在系统中进行充放电过程中进行电量百分比计算,仅供参考,电池电量百分比计算相对于来说是略微复杂,不仅需要分压电路并且还需要根据电池电压的大小来进行分级,因此除去最基础的ADC驱动(不同平台配置不同),整个充放电系统流程都有涉及到。

二、电路1.电池电压检测电路

在电量百分比计算中,硬件电路尤为重要,简单来说就是对电池进行分压,运用串联电阻分压的特性,防止电压超过最高电压烧坏MCU的情况出现,检测电池电压。


02999a588db9f87e7dc0b6ca5066d5fe.png 2.充电电压检测电路

检测充电电压。


fa0fd84af0043ead388d637257f62eb8.png 3.充电电流检测电路

检测充电电流电压。


ce9f1d5830e79535a734bff23f79a28f.png 三、软件计算1ADC采集

建议使用DMA进行读取,这里从获取ADC的数据开始,再划出ADC缓存区,根据不同的采集通道来存放各自的数据。


  • //放置在.h文件

  • [color=#576B95 !important][url=]#define[/url] ADC_BUFFER_LEN       10//ADC单个通道缓存区大小

  • [color=#576B95 !important][url=]#define[/url] ADC_CHANNEL_NUM      4//ADC通道总数

  • [color=#576B95 !important][url=]#define[/url] ADC_BATTERY_VOLTAGE  0

  • [color=#576B95 !important][url=]#define[/url] ADC_BATTERY_NTC      1

  • [color=#576B95 !important][url=]#define[/url] ADC_CHARGE_VOLTAGE   2

  • [color=#576B95 !important][url=]#define[/url] ADC_CHARGE_CURRENT   3

  • /*定义ADC数据的结构体*/

  • struct ADC_Sample{

  • uint16_t buffer[ADC_BUFFER_LEN];

  • uint8_t size;

  • uint8_t index;

  • };

  • /*定义ADC通道枚举*/

  • enum{

  • BATTERY_VOLTAGE=0,

  • BATTERY_NTC,

  • CHARGE_VOLTAGE,

  • CHARGE_CURRENT,

  • };

  • uint16_t ADC_GetValue(uint8_t channel);

  • void Buffer_fill(struct ADC_Sample *channel,uint16_t data);

  • void Range_Updata(void);

  • //放置在.c文件

  • struct ADC_Sample ADC_buffer_fill[ADC_CHANNEL_NUM];

  • /*获取ADC相对应的通道数据*/

  • uint16_t ADC_GetValue(uint8_t channel){

  • if(channel >= ADC_CHANNEL_NUM){

  • return0;

  • }

  • return ADC_Buffer[channel];

  • }

  • /*ADC缓存区循环填充*/

  • void Buffer_fill(struct ADC_Sample *channel,uint16_t data){

  • channel->buffer[channel->index] =data;

  • channel->index++;

  • if(channel->index>= CURRENT_BUFFER_LEN){

  • channel->index=0;

  • }

  • }

  • /*更新ADC采样值*/

  • void Range_Updata(void)//需要在定时器进行中断更新

  • {

  • Buffer_fill(&ADC_buffer_fill[BATTERY_VOLTAGE],ADC_GetValue(ADC_BATTERY_VOLTAGE));

  • Buffer_fill(&ADC_buffer_fill[BATTERY_NTC],ADC_GetValue(ADC_BATTERY_NTC));

  • Buffer_fill(&ADC_buffer_fill[CHARGE_VOLTAGE],ADC_GetValue(ADC_CHARGE_VOLTAGE));

  •         Buffer_fill(&ADC_buffer_fill[CHARGE_CURRENT],ADC_GetValue(ADC_CHARGE_CURRENT));

  • }


2.采集电池电压

电池采集的数据为电压类,因此采用电压类的ADC转换


  • /*获取电压类ADC*/

  • uint16_t Get_Voltage(uint8_t channle){

  • struct ADC_Sample *adc_dev =&vol_buffer[channle];

  • uint32_t temp =0;

  •         int i =0;

  • for (; i < CURRENT_BUFFER_LEN; i++) {

  • temp += adc_dev->buffer;

  • }

  • return temp/CURRENT_BUFFER_LEN;

  • }

  • /*获取电池电压*/

  • float Get_BatteryVol(void)

  • {

  • return (float)Get_Voltage(BATTERY_VOLTAGE) /4096.0f *3.3f * xxx;//xxx为分压转换系数

  • }

3采集充电电压

同上


  • /*获取充电电压 */

  • float Get_ChargeVol(void)

  • {

  • return (float)Get_Vol(VOL_CHARGE) /4096.0f *3.3f * xxx;//xxx为分压转换系数

  • }

4.采集充电电流

电流类相较于电压类是需要进行转换的,因此充电电流采取电流类转换


  • /*充电电流电流类*/

  • uint16_t Get_Current(uint8_t channle){

  • struct ADC_Sample *adc_dev =¤t_buffer[channle];

  • uint32_t temp =0;

  •         int i =0;

  •         uint16_t current;

  • for (; i < CURRENT_BUFFER_LEN; i++) {

  • temp += adc_dev->buffer;

  • }

  • temp/=CURRENT_BUFFER_LEN;

  • current = temp *3300/4096*10;

  • return current;

  • }

  • /*当前充电电流*/

  • uint16_t Get_ChargeCurrent(void)

  • {

  •         uint16_t current_I;

  • current_I  = (uint16_t)(Get_Current(CHARGE_CURRENT)/10);

  • if(current_I >reference_current){

  • return (current_I-reference_current);

  •                 }else{

  • reference_current = current_I;

  • return0;

  •                 }

  • }


5.判断是否接入适配器

通过判断充电电压是否在正常电压范围内进行判断有无适配器接入。


  • /*判断是否有外接电源适配器*/

  • uint8_t ChargeAccess(void){

  • charge_vol = ADC_GetValue(ADC_CHARGE_DET) /4096.0f *3.3f * xxx;//xxx为分压转换系数

  • if(charge_vol >3.5f && charge_vol <5.5f){

  • return1;

  • }

  • return0;

  • }

6.当前充电基准电流

基准电流在充电其实设置即可。


  • /*设置当前充电基准电流*/

  • void Set_ChargeRefCurrent(void)//在开启充电时进行设置

  • {

  • reference_current = (uint16_t)(Get_Current(CURRENT_CHARGE)/10);

  • }

7.充电过温保护

过温保护即为电池过温保护,使用NTC的查表法进行温度检测,达到设置温度停止充电,防止出现危险情况。


  • 放置.h文件

  • [color=#576B95 !important][url=]#define[/url] NTC_HIGH_PRECISION         1//1=0.1℃ 0=1℃

  • [color=#576B95 !important][url=]#define[/url] NUM 141

  • //-20℃~120℃

  • static int16_t NTC3435_10K[NUM]=

  • {

  • 3581,3559,3535,3511,3486,3461,3435,3408,3381,3353,

  • 3324,3295,3266,3234,3203,3171,3138,3105,3072,3037,

  • 3003,                                             //0

  • 2968,2932,2896,2859,2823,2785,2748,2710,2672,2633,

  • 2595,2556,2517,2478,2438,2399,2360,2321,2281,2242,

  • 2203,2164,2125,2086,2048,2009,1971,1933,1896,1858,

  • 1822,1785,1749,1713,1678,1643,1896,1896,1540,1507,

  • 1475,1436,1411,1380,1349,1319,1289,1260,1231,1203,

  • 1176,1149,1122,1096,1070,1045,1021,997,974,951,

  • 928,906,885,864,843,823,803,784,766,747,

  • 729,712,695,678,662,646,631,616,601,587,

  • 573,559,546,533,520,508,496,484,473,462,

  • 451,440,430,420,410,400,391,382,373,365,

  • 356,348,340,332,325,317,310,303,296,290,

  • 283,277,271,265,259,253,247,242,237,231,

  • };

  • int16_t Read_NTC_Temperature(int16_t *list,u16 rADC,int16_t BaseValue);

  • //输入参数:ADC表  采集的ADC值   ADC表的起始温度值(-20℃=-200)

  • //返回值:温度值 单位0.1℃ 例如返回值是100,对应的就是100*0.1℃=10℃。

  • //该函数得到的结果还需要除以10得到的结果才是正确的结果

  • int16_t Read_NTC_Temperature(int16_t *list,uint16_t rADC,int16_t BaseValue)

  • {

  •         uint16_t index=0;

  •         uint16_t deta=0;

  •         uint16_t t=0;

  •         int16_t result=0;

  • if(rADC>=list[0])

  • return BaseValue;

  • if(rADC<=*(list+NUM-1))

  •         {

  •                 result=((NUM-1)*10+BaseValue);

  • return result;

  •         }

  • index=NTC_Lookup(list,rADC);

  • #if NTC_HIGH_PRECISION

  •         deta=list[index]-list[index+1];

  •         t=10*(list[index]-rADC)/deta;

  • [color=#576B95 !important][url=]#endif[/url]

  •         result=(BaseValue+index*10+t);

  • return result;

  • }

8.电池充满判断

在充电电压到达一定数值后,电压变化不会太明显,为精确判断充电是否完成,因此根据最后充电电流的变化来确认充电是否完成,当电流降低达到一定值之后便认为是充电完成。


  • [color=#576B95 !important][url=]#define[/url] CHARGE_DONE_CURRENT         190//充电电流小于190即判定为充满

  • uint8_t Is_Charge_Done(void)

  • {

  • return Charge_Finish;

  • }

  • void Reset_Charge_Done(void)

  • {

  • Charge_Finish =0;

  • }

  • //判断语句,在合适的位置进行判断,如果充电电流小于190,并且电量不小于99%

  • if(Get_ChargeCurrent() < CHARGE_DONE_CURRENT &&Get_Battery_Percent() >=99){

  • if(Charge_Ctrl_Flag()){ //如果充电脚使能

  • if(!Is_Charge_Done()){

  •                         charge_time=Get_sysTime();        //充电完成时间

  •                         Charge_Finish =1;

  •                 }                        

  •         }                                                

  • }

9.电量计算

最后是电量的计算,通过调用此函数就可以将电池电量百分比计算出来。


  • typedef enum{

  • NO_BATTERY =0,

  • BATTERY_LEVEL_1,

  • BATTERY_LEVEL_2,

  • BATTERY_LEVEL_3,

  • BATTERY_LEVEL_4,

  • BATTERY_LEVEL_5,

  • }BatteryLevel;  //五个等级

  • BatteryLevel batteryLevel;

  • //每个等级对应的电池电压

  • [color=#576B95 !important][url=]#define[/url] LOW_POWER_VAL               (23.1f*6.0f/7.0f)//22.8

  • [color=#576B95 !important][url=]#define[/url] POWER_LEVEL1_VAL            (24.9f*6.0f/7.0f)//24.2

  • [color=#576B95 !important][url=]#define[/url] POWER_LEVEL2_VAL            (25.4f*6.0f/7.0f)//24.8

  • [color=#576B95 !important][url=]#define[/url] POWER_LEVEL3_VAL            (26.0f*6.0f/7.0f)//25.6

  • [color=#576B95 !important][url=]#define[/url] POWER_LEVEL4_VAL            (26.8f*6.0f/7.0f)//26.5

  • [color=#576B95 !important][url=]#define[/url] POWER_LEVEL5_VAL            (27.8f*6.0f/7.0f)//27.8

  • /*延时充电等级*/

  • [color=#576B95 !important][url=]#define[/url] BATTERY_DELAY_LEVEL_11

  • [color=#576B95 !important][url=]#define[/url] BATTERY_DELAY_LEVEL_22

  • uint8_t battery_percent;//电池百分比

  • uint8_t battery_cur_count;//静态计数

  • uint32_t battery_refrece;//电池时间参考

  • static float battery_val;//获取电池电压

  • static const float battery_rate[6] ={LOW_POWER_VAL,POWER_LEVEL1_VAL,POWER_LEVEL2_VAL,POWER_LEVEL3_VAL,POWER_LEVEL4_VAL,POWER_LEVEL5_VAL};//每个等级对应的电池电压数组

  • bool is_first=true;//为第一次运行减少误差

  • static uint32_t battery_updata_rate;//更新速率等级

  • static uint8_t bat_diff_count=0;//与上次的差异值

  • static uint8_t delay_charge =0;//充电延时

  • void Delay_Charge_Set(uint8_t delay_level)

  • {

  •         delay_charge = delay_level;

  • }

  • void Delay_Charge_Reset(void)

  • {

  •         delay_charge =0;

  • }

  • uint8_t Delay_Charge_Get(void)

  • {

  • return delay_charge;

  • }

  • void Charge_Ctrl_Enable(void)

  • {

  • GPIO_SetBits(CHARGE_CTRL_GPIO,CHARGE_CTRL_GPIO_PIN);

  • }

  • void Charge_Ctrl_Disable(void)

  • {

  • GPIO_ResetBits(CHARGE_CTRL_GPIO,CHARGE_CTRL_GPIO_PIN);

  • }

  • uint8_t Charge_Ctrl_Flag(void)

  • {

  • return GPIO_ReadOutputDataBit(CHARGE_CTRL_GPIO,CHARGE_CTRL_GPIO_PIN);

  • }

  • /*获取电池电量百分比*/

  • uint8_t Get_Battery_Percent(void)

  • {

  • int  i =0;

  • if(is_first){ //第一次跑机,延时获取

  •                 while(i++<20){

  •                 battery_val =Get_BatteryVal();        //获取电池电压

  • if(battery_val <3.6f){//最小电压               

  •                 }elseif((battery_val >4.3f) && ChargeAccess()){

  •                 }else{

  •                         break;

  •                 }

  •                 DELAY_WaitmS(100);//第一次获取数据延时一小段时间

  •                 }

  •         }elseif(delay_charge && ChargeAccess()){ //主要在充电完成之后

  • if((Get_sysTime() - Get_Charge_Start_Time() >5*1000) && (Delay_Charge_Get() != BATTERY_DELAY_LEVEL_2)){ //延时级别1,正常延时5秒充电使能

  •                         battery_val =Get_BatteryVal();        

  • if(battery_val <= battery_rate[BATTERY_LEVEL_5]){ //如果电池电压未到满压,立即开始充电

  •                                 Charge_Ctrl_Enable();//开始充电

  •                                 Delay_Charge_Reset();//清清除延时等级

  • //延时再次判断

  •                                 DELAY_WaitmS(200);

  •                                 battery_val =Get_BatteryVal();        

  • if(battery_val >4.8f || Read_NTC_Temperature(NTC3435_10K,Get_Battery_Temper(),-200)/10) >40){ //充电过温保护,关闭充电

  •                                         Charge_Ctrl_Disable();

  •                                         Delay_Charge_Set(BATTERY_DELAY_LEVEL_2);

  •                                 }

  •                         }

  •                 }

  • if(Get_sysTime() - Get_Charge_Start_Time() >60*1000){ //延时级别2,过温保护延时1分钟充电使能

  •                         Charge_Ctrl_Enable();

  •                         Delay_Charge_Reset();

  •                 }

  •         }

  • battery_val =Get_BatteryVal();        

  • //动态调整电压值

  • if(battery_val ){//可要可不要

  •                         battery_val +=0.02f;               

  • }else {

  •                         battery_val +=0;

  • }

  • //如果是充电脚,用于在充电的过程中电池电压相较于实际电压略高的问题

  • if(Charge_Ctrl_Flag()){

  •                         (battery_val > POWER_LEVEL5_VAL) ? (battery_val -=0.05f) : ((battery_val > POWER_LEVEL4_VAL) ? (battery_val -=0.15f) : (battery_val -=0.2f));

  • }

  • //电池数据更新时间,根据不同电压调整电量衰减时间

  • if(battery_percent <=20 ){

  •                         battery_updata_rate =1;

  • }elseif(battery_percent >=80){

  •                         battery_updata_rate=16;

  •                 }elseif(battery_percent >=50){

  •         battery_updata_rate =8;

  •                 }elseif(Charge_Ctrl_Flag()){ //充电速率

  •                         battery_updata_rate =10;

  • }else{

  •                         battery_updata_rate =4;

  • }               

  • if((Get_sysTime() - battery_refrece > (uint32_t)(1000* battery_updata_rate)) || is_first){        

  • if (battery_val >= battery_rate[BATTERY_LEVEL_4]){

  • /* 充电时: 80%-90%电压判断,90%-100% 电流判断 */

  • if (Charge_Ctrl_Flag()) {

  • if (battery_val >= battery_rate[BATTERY_LEVEL_5]) {

  •                                                         battery_cur_count=(uint8_t)((11-(Get_ChargeCurrent()/100))+90);

  • if (Is_Charge_Done()) {

  •                                                                 battery_cur_count=100;

  •                                                         } elseif (battery_cur_count>99) {

  • if (Is_Charge_Done()) {

  •                                                                         battery_cur_count=100;

  •                                                                 } else {

  •                                                                         battery_cur_count=100;

  •                                                                 }

  •                                                         }

  •                                                         batteryLevel = BATTERY_LEVEL_5;

  •                                                 } elseif (battery_val >= battery_rate[BATTERY_LEVEL_4]) {

  •                                                         battery_cur_count= (uint8_t) ((battery_val - battery_rate[BATTERY_LEVEL_4]) /(battery_rate[BATTERY_LEVEL_5] - battery_rate[BATTERY_LEVEL_4]) *10+80);

  •                                                         batteryLevel = BATTERY_LEVEL_4;

  •                                                 }

  •                                 } else {

  • /* 耗电时,全部按电压判断 */

  • if(battery_val >= battery_rate[BATTERY_LEVEL_4]){

  •                                                         battery_cur_count= (uint8_t)((battery_val - battery_rate[BATTERY_LEVEL_4])/(battery_rate[BATTERY_LEVEL_5] - battery_rate[BATTERY_LEVEL_4])*20+80);

  •                                                         batteryLevel = BATTERY_LEVEL_4;

  • if (Is_Charge_Done() && Charge_Ctrl_Flag()) { //充电完成并且有充电标志的情况下才显示100%

  •                                                                 batteryLevel = BATTERY_LEVEL_5;

  •                                                                 battery_cur_count=100;

  •                                                         } elseif (battery_cur_count>99) {

  • if (Is_Charge_Done()) {

  •                                                                         batteryLevel = BATTERY_LEVEL_5;

  •                                                                         battery_cur_count=100;

  •                                                                 } else {

  •                                                                         battery_cur_count=100;

  •                                                                 }

  •                                                         }

  •                                         }

  •                                 }

  • } elseif(battery_val >= battery_rate[BATTERY_LEVEL_3]){

  •                                 battery_cur_count= (uint8_t)((battery_val - battery_rate[BATTERY_LEVEL_3])/(battery_rate[BATTERY_LEVEL_4] - battery_rate[BATTERY_LEVEL_3])*20+60);

  •                                 batteryLevel = BATTERY_LEVEL_3;

  • }elseif(battery_val >= battery_rate[BATTERY_LEVEL_2]){

  • battery_cur_count= (uint8_t)((battery_val - battery_rate[BATTERY_LEVEL_2])/(battery_rate[BATTERY_LEVEL_3] - battery_rate[BATTERY_LEVEL_2])*20+40);

  • batteryLevel = BATTERY_LEVEL_2;

  • }elseif(battery_val >= battery_rate[BATTERY_LEVEL_1]){

  • battery_cur_count= (uint8_t)((battery_val - battery_rate[BATTERY_LEVEL_1])/(battery_rate[BATTERY_LEVEL_2] - battery_rate[BATTERY_LEVEL_1])*20+20);

  • batteryLevel = BATTERY_LEVEL_1;

  • }elseif(battery_val >= battery_rate[NO_BATTERY]){

  • battery_cur_count= (uint8_t)((battery_val - battery_rate[NO_BATTERY])/(battery_rate[BATTERY_LEVEL_1] - battery_rate[NO_BATTERY])*20+0);

  • batteryLevel =NO_BATTERY;

  • }else{

  •                                 battery_cur_count=1;

  •                 }

  • //如果跟上一次的百分比不一致

  • if (battery_cur_count != battery_percent){

  •                                 bat_diff_count++;

  •                 } else {

  •                                 bat_diff_count=0;

  •                 }

  • if (!is_first) { //非首次运行

  • //如果不同次数大于2次

  • if (bat_diff_count>=2){

  •                                 bat_diff_count=0;

  • if (ChargeAccess() && battery_cur_count> battery_percent){

  • ++battery_percent;

  •                                 } elseif (battery_cur_count< battery_percent){

  •                                         --battery_percent;

  •                                 }

  •                         }

  •                 } else {

  • is_first=false;

  • //如果需要做掉电保存还可以在此处进行Flash的电量读取

  •                 }

  • //限定范围在1~100

  •                         battery_percent = battery_percent>100?  100: battery_percent;

  • battery_percent = battery_percent<1  ?  1: battery_percent;

  • battery_refrece =Get_sysTime();        //获取系统时间为下一次电量计算做准备

  • }

  • return battery_percent;

  • }

.
您需要登录后才可以回帖 登录 | 请使用中文注册

本版积分规则

QQ|Archiver|手机版|家电维修论坛 ( 蜀ICP备19011473号-4 川公网安备51102502000164号 )

GMT+8, 2025-11-25 01:48 , Processed in 0.310741 second(s), 23 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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