找回密码
 立即注册
查看: 2932|回复: 19

关于充放电电量指示灯不一致的问题

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2023-12-25 15:38:27

39

主题

204

回帖

651

积分

高级会员

积分
651
发表于 2023-3-1 14:44:50 | 显示全部楼层 |阅读模式
大家好,我现在使用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;
                                }
                        }       
        }

如下是电池充放电曲线图:
关于充放电电量指示灯不一致的问题-1.png

如下是根据电池充放电划分的电压和电量之间的对应关系
关于充放电电量指示灯不一致的问题-2.png

如下是ADC检测电池电压的线路图:
关于充放电电量指示灯不一致的问题-3.png




回复

使用道具 举报 送花

0

主题

3

回帖

22

积分

新手上路

积分
22
发表于 2023-3-2 14:57:37 来自手机 | 显示全部楼层
不做差分么?基准2v差分到0-2.5v,完事adc准一些。8h1k10位ad.电池电压变化范围是2.5-4.2v,1.7你在分压,分辨率不够吧。
  • 打卡等级:偶尔看看III
  • 打卡总天数:51
  • 最近打卡:2025-05-02 10:07:51

73

主题

5883

回帖

1万

积分

超级版主

积分
12079
发表于 2023-3-2 19:01:29 | 显示全部楼层
楼主,你可以检测一下电池充电和不充电时的端电压,还有放电时的端电压。
因为电池有内阻(包括保护板的电阻),所以以上3中状态下,电池端电压是不同的,据此显示就会不同,否则就要根据电流来修正。
比较好的方式是用库仑计的方式显示(即实际电量,mAH)。
  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2023-12-25 15:38:27

39

主题

204

回帖

651

积分

高级会员

积分
651
发表于 2023-3-2 19:44:32 | 显示全部楼层
梁*** 发表于 2023-3-2 19:01
楼主,你可以检测一下电池充电和不充电时的端电压,还有放电时的端电压。
因为电池有内阻(包括保护板的电 ...

梁工,您好。3种状态下确实是不一样的,它们的关系:电池充电时的电压>电池不充电时的电压>电池放电时的电压。其实可以概括为两种状态充电盒放电(不充电),它们的关系是,电池充电时的电压>电池放电时的电压。为了降低设计成本,现在不想使用库伦计,参照电池的充放电曲线,电量不用计算那么精确,用单片机内置的ADC可否解决问题?

点评

如果有检测电流,则根据电池的内阻*电流进行修正电压,来判断电池电压,否则解决不了,因为ADC读到的电压就不是电池真正的电压,叠加了内阻压降了。  详情 回复 发表于 2023-3-2 22:30
  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2023-12-25 15:38:27

39

主题

204

回帖

651

积分

高级会员

积分
651
发表于 2023-3-2 19:46:22 | 显示全部楼层
ziyu*** 发表于 2023-3-2 14:57
不做差分么?基准2v差分到0-2.5v,完事adc准一些。8h1k10位ad.电池电压变化范围是2.5-4.2v,1.7你在分压,分 ...

电池电压是单端电压,不用做差分吧,10位AD,分辨率已足够
  • 打卡等级:偶尔看看III
  • 打卡总天数:51
  • 最近打卡:2025-05-02 10:07:51

73

主题

5883

回帖

1万

积分

超级版主

积分
12079
发表于 2023-3-2 22:30:04 | 显示全部楼层
98057*** 发表于 2023-3-2 19:44
梁工,您好。3种状态下确实是不一样的,它们的关系:电池充电时的电压>电池不充电时的电压>电池放电时 ...

如果有检测电流,则根据电池的内阻*电流进行修正电压,来判断电池电压,否则解决不了,因为ADC读到的电压就不是电池真正的电压,叠加了内阻压降了。
  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2023-12-25 15:38:27

39

主题

204

回帖

651

积分

高级会员

积分
651
发表于 2023-3-3 09:58:05 | 显示全部楼层
梁*** 发表于 2023-3-2 22:30
如果有检测电流,则根据电池的内阻*电流进行修正电压,来判断电池电压,否则解决不了,因为ADC读到的电压 ...

梁工,我看到有大佬能解决此问题,没有电流检测,就是通过单片机内置的ADC检测电池电压然后再结合代码算法就能解决问题。

点评

那能否贴出我也学习一下? 只测电池电压、不测电流 就能通过算法抵消电池内阻、保护板内阻的影响,我倒是第一次听说,这么好的算法我真的要学习下。  详情 回复 发表于 2023-3-3 11:51
  • 打卡等级:偶尔看看III
  • 打卡总天数:51
  • 最近打卡:2025-05-02 10:07:51

73

主题

5883

回帖

1万

积分

超级版主

积分
12079
发表于 2023-3-3 11:51:14 | 显示全部楼层
98057*** 发表于 2023-3-3 09:58
梁工,我看到有大佬能解决此问题,没有电流检测,就是通过单片机内置的ADC检测电池电压然后再结合代码算 ...

那能否贴出我也学习一下?
只测电池电压、不测电流 就能通过算法抵消电池内阻、保护板内阻的影响,我倒是第一次听说,这么好的算法我真的要学习下。
  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2023-12-25 15:38:27

39

主题

204

回帖

651

积分

高级会员

积分
651
发表于 2023-3-3 19:40:21 | 显示全部楼层
梁*** 发表于 2023-3-3 11:51
那能否贴出我也学习一下?
只测电池电压、不测电流 就能通过算法抵消电池内阻、保护板内阻的影响,我倒是 ...

梁工,贴代码出来了,我没看懂,没办法模仿
/******************************************************************************/
/** \file adc_detection.c
**
** ADC detection.
**
**   - 2019-08-2  1.0  By 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[11] = {
    3300, 3488, 3536, 3571, 3603, 3643, 3701, 3777, 3867, 3966, 4098
};/*3401*/  //20200928
const uint16_t bat_gauge_level2[11] = {
    3614, 3671, 3702, 3733, 3766, 3814, 3842, 3971, 4072, 4187, 4210    //20201105
};
uint32_t        sum_bat_adc;
uint16_t        gauge_buffer[BUFFER_NUM], 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 [in]  null
**
** \return null
******************************************************************************/
void AdcInit(void)
{
    stc_adc_cfg_t       stcAdcCfg;
    stc_adc_cont_cfg_t  stcAdcContCfg;

    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 [in]  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[0])
        temp = vaule_level[0];

    gauge_buffer[gauge_buffer_count++] = 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[loop];
    }
    temp = sum_bat_adc / BUFFER_NUM;

    if(temp < vaule_level[0])
        temp = vaule_level[0];

    /****************************************************/
    for(level = 1; level <= 10; level++)
    {
        if(temp < vaule_level[level])
        {
            temp = vaule_level[level] - temp;
            temp = temp * 10 / (vaule_level[level] - vaule_level[level - 1]);
            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;
}


点评

缺乏解释的代码很难看懂的,我以为有原理说明。一般来说,仅仅测量电压是没法修正内阻造成的偏差的。  详情 回复 发表于 2023-3-3 22:51
  • 打卡等级:偶尔看看III
  • 打卡总天数:51
  • 最近打卡:2025-05-02 10:07:51

73

主题

5883

回帖

1万

积分

超级版主

积分
12079
发表于 2023-3-3 22:51:23 | 显示全部楼层
98057*** 发表于 2023-3-3 19:40
梁工,贴代码出来了,我没看懂,没办法模仿
/******************************************************** ...

缺乏解释的代码很难看懂的,我以为有原理说明。一般来说,仅仅测量电压是没法修正内阻造成的偏差的。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-2 15:47 , Processed in 0.134311 second(s), 114 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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