关于充放电电量指示灯不一致的问题
大家好,我现在使用STC8H1K08设计一个充电盒子,盒子内置一颗可充电的锂电池,容量是500mAh,盒子外面有4个LED电量指示灯,使用单片机内置的ADC检测电池电压,当盒子插入USB 5V充电时,根据电池电量的高低就会显示的对应LED,电池电量越高显示(打开)的LED就越多,反之,越少。当盒子不充电(放电)时,可通过短按盒子上面的按键显示当前电池的电量指示灯。我现在遇到的问题是:盒子充电时和不充电时(放电时)电量指示灯显示不一致的问题,比如盒子在充电时显示两个LED,盒子拔掉USB 5V不充电时通过短按按键却显示3个电量指示灯。我认为正确的做法是盒子充放电显示电量指示灯的数量应该要一致。现向大家请教一下怎么解决此问题,如下是设计资料,谢谢各位指点。如下是盒子在充电时的灯显程序:
if(usb_in==1)
{
vbat=get_vbat_value(0);
if(vbat>4.0947)
{
if(count2==50)//500mS时间到
{
led1=1;
led2=1;
led3=1;
led4=!led4;
count2=0;
}
}
else if(vbat>3.9725)
{
if(count2==50)
{
led1=1;
led2=1;
led3=!led3;
led4=0;
count2=0;
}
}
else if(vbat>3.9105)
{
if(count2==50)
{
led1=1;
led2=!led2;
led3=0;
led4=0;
count2=0;
}
}
else if(vbat>3.0)
{
if(count2==50)
{
led1=!led1;
led2=0;
led3=0;
led4=0;
count2=0;
}
}
}
如下是盒子在不充电时(放电时),通过按键的灯显程序:
if(key_flag==1&&usb_out==1)
{
vbat=get_vbat_value(0);
if(vbat<3.5001)
{
if(count3==50)//盒子在非常低电时会快闪led提示
{
led1=!led1;
led2=0;
led3=0;
led4=0;
count3=0;
key_flag=0;
}
}
else if(vbat<3.6954)
{
led1=1;
led2=0;
led3=0;
led4=0;
if(count3==300)//显示3秒电量指示灯
{
led1=0;
led2=0;
led3=0;
led4=0;
count3=0;
key_flag=0;
}
}
else if(vbat<3.7589)
{
led1=1;
led2=1;
led3=0;
led4=0;
if(count3==300)
{
led1=0;
led2=0;
led3=0;
led4=0;
count3=0;
key_flag=0;
}
}
else if(vbat<3.8929)
{
led1=1;
led2=1;
led3=1;
led4=0;
if(count3==300)
{
led1=0;
led2=0;
led3=0;
led4=0;
count3=0;
key_flag=0;
}
}
else if(vbat<4.2)
{
led1=1;
led2=1;
led3=1;
led4=1;
if(count3==300)
{
led1=0;
led2=0;
led3=0;
led4=0;
count3=0;
key_flag=0;
}
}
}
如下是电池充放电曲线图:
如下是根据电池充放电划分的电压和电量之间的对应关系
如下是ADC检测电池电压的线路图:
不做差分么?基准2v差分到0-2.5v,完事adc准一些。8h1k10位ad.电池电压变化范围是2.5-4.2v,1.7你在分压,分辨率不够吧。 楼主,你可以检测一下电池充电和不充电时的端电压,还有放电时的端电压。
因为电池有内阻(包括保护板的电阻),所以以上3中状态下,电池端电压是不同的,据此显示就会不同,否则就要根据电流来修正。
比较好的方式是用库仑计的方式显示(即实际电量,mAH)。 梁工 发表于 2023-3-2 19:01
楼主,你可以检测一下电池充电和不充电时的端电压,还有放电时的端电压。
因为电池有内阻(包括保护板的电 ...
梁工,您好。3种状态下确实是不一样的,它们的关系:电池充电时的电压>电池不充电时的电压>电池放电时的电压。其实可以概括为两种状态充电盒放电(不充电),它们的关系是,电池充电时的电压>电池放电时的电压。为了降低设计成本,现在不想使用库伦计,参照电池的充放电曲线,电量不用计算那么精确,用单片机内置的ADC可否解决问题? ziyueboy 发表于 2023-3-2 14:57
不做差分么?基准2v差分到0-2.5v,完事adc准一些。8h1k10位ad.电池电压变化范围是2.5-4.2v,1.7你在分压,分 ...
电池电压是单端电压,不用做差分吧,10位AD,分辨率已足够 980578873 发表于 2023-3-2 19:44
梁工,您好。3种状态下确实是不一样的,它们的关系:电池充电时的电压>电池不充电时的电压>电池放电时 ...
如果有检测电流,则根据电池的内阻*电流进行修正电压,来判断电池电压,否则解决不了,因为ADC读到的电压就不是电池真正的电压,叠加了内阻压降了。 梁工 发表于 2023-3-2 22:30
如果有检测电流,则根据电池的内阻*电流进行修正电压,来判断电池电压,否则解决不了,因为ADC读到的电压 ...
梁工,我看到有大佬能解决此问题,没有电流检测,就是通过单片机内置的ADC检测电池电压然后再结合代码算法就能解决问题。 980578873 发表于 2023-3-3 09:58
梁工,我看到有大佬能解决此问题,没有电流检测,就是通过单片机内置的ADC检测电池电压然后再结合代码算 ...
那能否贴出我也学习一下?
只测电池电压、不测电流 就能通过算法抵消电池内阻、保护板内阻的影响,我倒是第一次听说,这么好的算法我真的要学习下。 梁工 发表于 2023-3-3 11:51
那能否贴出我也学习一下?
只测电池电压、不测电流 就能通过算法抵消电池内阻、保护板内阻的影响,我倒是 ...
梁工,贴代码出来了,我没看懂,没办法模仿
/******************************************************************************/
/** \file adc_detection.c
**
** ADC detection.
**
** - 2019-08-21.0By Johnny.Cai
**
******************************************************************************/
/*******************************************************************************
* Include files
******************************************************************************/
#include "adc_detection.h"
extern type_stc_user_system_t gb_user_system;
/*******************************************************************************
* Local pre-processor symbols/macros ('#define')
******************************************************************************/
const uint16_t bat_gauge_level = {
3300, 3488, 3536, 3571, 3603, 3643, 3701, 3777, 3867, 3966, 4098
};/*3401*///20200928
const uint16_t bat_gauge_level2 = {
3614, 3671, 3702, 3733, 3766, 3814, 3842, 3971, 4072, 4187, 4210 //20201105
};
uint32_t sum_bat_adc;
uint16_t gauge_buffer, gauge_buffer_count = 0;
uint8_t bat_adc_initial = 0;
/*******************************************************************************
* Global variable definitions (declared in header file with 'extern') *
******************************************************************************/
/*******************************************************************************
* Local type definitions ('typedef')
******************************************************************************/
/*******************************************************************************
* Local function prototypes ('static')
******************************************************************************/
/*******************************************************************************
* Local variable definitions ('static')
******************************************************************************/
type_stc_adc_detection lc_adc_detection;
/*******************************************************************************
* Function implementation - global ('extern') and local ('static')
******************************************************************************/
/******************************************************************************
** \brief AdcInit
**
** \param null
**
** \return null
******************************************************************************/
void AdcInit(void)
{
stc_adc_cfg_t stcAdcCfg;
stc_adc_cont_cfg_tstcAdcContCfg;
DDL_ZERO_STRUCT(stcAdcCfg);
DDL_ZERO_STRUCT(stcAdcContCfg);
Gpio_SetAnalog(CURRENT_RIGHT_PORT, CURRENT_RIGHT_PIN, TRUE);
Gpio_SetAnalog(CURRENT_LEFT_PORT, CURRENT_LEFT_PIN, TRUE);
Gpio_SetAnalog(VOLTAGE_BAT_PORT, VOLTAGE_BAT_PIN, TRUE);
Adc_Enable();
M0P_BGR->CR_f.BGR_EN = 0x1u;//BGR enable
M0P_BGR->CR_f.TS_EN = 0x0u; //internal temperature disable
delay100us(1);
stcAdcCfg.enAdcOpMode = AdcContMode; //continue mode
stcAdcCfg.enAdcClkSel = AdcClkSysTDiv4; //PCLK/4
stcAdcCfg.enAdcSampTimeSel = AdcSampTime12Clk; //12 clocks for sample
stcAdcCfg.enAdcRefVolSel = RefVolSelInBgr1p5; //refrence: internal 2.5V(avdd>3V,SPS<=200kHz)
//stcAdcCfg.enAdcRefVolSel = RefVolSelAVDD; //refrence: AVDD
stcAdcCfg.bAdcInBufEn = FALSE; //voltage follower enable, SPS(sample rate) must <=200K
stcAdcCfg.enAdcTrig0Sel = AdcTrigDisable;
stcAdcCfg.enAdcTrig1Sel = AdcTrigDisable;
Adc_Init(&stcAdcCfg);
stcAdcContCfg.enAdcContModeCh = VOLTAGE_BAT_CHN; //channel BAT
stcAdcContCfg.u8AdcSampCnt = 0x0Fu; //sample timers(timers = 0x0F+1)
stcAdcContCfg.bAdcResultAccEn = TRUE; //enable ACC
Adc_ConfigContMode(&stcAdcCfg, &stcAdcContCfg);
}
void AdcUninit(void)
{
M0P_BGR->CR_f.BGR_EN = 0x0u;
Adc_Disable();
}
/******************************************************************************
** \brief ADCSample
**
** \param ch sample channel
**
** \return u32adcresult ADC result
******************************************************************************/
uint32_t ADCSample(en_adc_samp_ch_sel_t ch)
{
uint32_t u32adcresult;
M0P_ADC->CR2 &= 0xFFFFFF00;
M0P_ADC->CR2 |= 1 << ch;
M0P_ADC->CR0_f.SEL = ch;
delay100us(1);
Adc_Start(); //ADC start
while(FALSE == M0P_ADC->IFR_f.CONT_INTF);//wait for ADC complete
M0P_ADC->ICLR_f.CONT_INTC = 0; //clear the flag
Adc_GetAccResult(&u32adcresult);
Adc_ClrAccResult();
return u32adcresult;
}
/******************************************************************************
** \brief HandsetLCurrent
**
** \param null
**
** \return current max=255mA
******************************************************************************/
uint8_t HandsetLCurrent(void)
{
uint32_t temp;
temp = ADCSample(CURRENT_LEFT_CHN);
temp *= REFERENCE_ADC;
temp /= CURRENT_RESISTANCE;
temp /= 65536;
return (uint8_t)temp;
}
/******************************************************************************
** \brief HandsetRCurrent
**
** \param null
**
** \return current max=255mA
******************************************************************************/
uint8_t HandsetRCurrent(void)
{
uint32_t temp;
temp = ADCSample(CURRENT_RIGHT_CHN);
temp *= REFERENCE_ADC;
temp /= CURRENT_RESISTANCE;
temp /= 65536;
return (uint8_t)temp;
}
/******************************************************************************
** \brief BatGauge
**
** \param null
**
** \return gauge max=100
******************************************************************************/
uint8_t BatGauge(uint8_t charge)
{
uint32_t temp;
uint8_t level;
const uint16_t *vaule_level;
if(charge)
{
vaule_level = bat_gauge_level2;
}
else
{
vaule_level = bat_gauge_level;
}
temp = ADCSample(VOLTAGE_BAT_CHN);
temp *= REFERENCE_ADC / 4;
temp *= VOLTAGE_DIVIDE_TOTAL;
temp /= VOLTAGE_DIVIDE_MODULUS;
temp /= 16384;
if(temp < vaule_level)
temp = vaule_level;
gauge_buffer = temp; //递推滤波算法 191226f
if(gauge_buffer_count == BUFFER_NUM)
{
gauge_buffer_count = 0;
}
sum_bat_adc = 0;
for(uint8_t loop = 0; loop < BUFFER_NUM; loop++)
{
sum_bat_adc += gauge_buffer;
}
temp = sum_bat_adc / BUFFER_NUM;
if(temp < vaule_level)
temp = vaule_level;
/****************************************************/
for(level = 1; level <= 10; level++)
{
if(temp < vaule_level)
{
temp = vaule_level - temp;
temp = temp * 10 / (vaule_level - vaule_level);
temp = level * 10 - temp;
return (uint8_t)temp;
}
}
return 100;
}
/******************************************************************************
** \brief BatDetect
**
** \param charge TRUE:battery charging
**
** \return gauge of battery max=100
******************************************************************************/
uint8_t BatDetect(uint8_t charge)
{
uint8_t gauge = BatGauge(charge); //get the bat gauge
if(charge)
{
lc_adc_detection.flag_f.bat_low = 0;
lc_adc_detection.flag_f.bat_none = 0;
lc_adc_detection.bat_low_cnt = 0;
lc_adc_detection.bat_none_cnt = 0;
if(gb_user_system.bat_th_gauge < 80)
{
if(gb_user_system.bat_th_gauge < gauge)
{
if(gauge > 80)
{
gb_user_system.bat_th_gauge = 80;
}
else
{
gb_user_system.bat_th_gauge = gauge;
}
}
}
if(gb_user_system.bat_th_gauge < 90)
{
if(++lc_adc_detection.gauge_charge_cnt >= GAUGE_LOW_CHARGE_UP_TIME)
{
lc_adc_detection.gauge_charge_cnt = 0;
gb_user_system.bat_th_gauge++;
}
}
else
{
if(++lc_adc_detection.gauge_charge_cnt >= GAUGE_HIGH_CHARGE_UP_TIME)
{
lc_adc_detection.gauge_charge_cnt = 0;
if(gb_user_system.bat_th_gauge < 98)
{
gb_user_system.bat_th_gauge++;
}
}
}
}
//else if(gb_user_system.flag_f.adc_init_finish) //adc初始化完成之后才判断低电量
else
{
if(gauge < gb_user_system.bat_th_gauge) //充电时电量只能增加,放电时电量只能减小 200108f
{
gb_user_system.bat_th_gauge = gauge;
}
//if(gauge <= BAT_GAUGE_LOW)
if(gb_user_system.bat_th_gauge <= BAT_GAUGE_LOW)
{
if(lc_adc_detection.bat_low_cnt >= 5)
{
lc_adc_detection.flag_f.bat_low = 1;
}
else
{
lc_adc_detection.bat_low_cnt++;
}
}
else
{
lc_adc_detection.bat_low_cnt = 0;
}
if(gb_user_system.bat_th_gauge <= BAT_GAUGE_NONE)
{
if(lc_adc_detection.bat_none_cnt >= 5)
{
lc_adc_detection.flag_f.bat_none = 1;
}
else
{
lc_adc_detection.bat_none_cnt++;
}
}
else
{
lc_adc_detection.bat_none_cnt = 0;
}
}
return gb_user_system.bat_th_gauge;
}
/******************************************************************************
** \brief Bat Flag Get
**
** \param null
**
** \return flag
******************************************************************************/
_Bool BatLowFlagGet(void)
{
return lc_adc_detection.flag_f.bat_low;
}
_Bool BatNoneFlagGet(void)
{
return lc_adc_detection.flag_f.bat_none;
}
980578873 发表于 2023-3-3 19:40
梁工,贴代码出来了,我没看懂,没办法模仿
/******************************************************** ...
缺乏解释的代码很难看懂的,我以为有原理说明。一般来说,仅仅测量电压是没法修正内阻造成的偏差的。
页:
[1]
2