马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?请使用中文注册
×
如何在系统中通过硬件电路和软件算法计算锂电池的电量百分比,涉及电池电压、电流检测、ADC数据处理以及充电状态监控等关键步骤。
一、说明本文讲述锂电池在系统中进行充放电过程中进行电量百分比计算,仅供参考,电池电量百分比计算相对于来说是略微复杂,不仅需要分压电路并且还需要根据电池电压的大小来进行分级,因此除去最基础的ADC驱动(不同平台配置不同),整个充放电系统流程都有涉及到。 二、电路1.电池电压检测电路在电量百分比计算中,硬件电路尤为重要,简单来说就是对电池进行分压,运用串联电阻分压的特性,防止电压超过最高电压烧坏MCU的情况出现,检测电池电压。
2.充电电压检测电路检测充电电压。
3.充电电流检测电路检测充电电流电压。
三、软件计算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采集充电电压同上 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.当前充电基准电流基准电流在充电其实设置即可。 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;
}
. |