/**
 * \file    sys_bat_intf.c
 * \brief   DPM battery interface
 *
 * \par     Copyright (c) 2016 UWE Technologies Corp., Ltd. All Rights Reserved.
 * \author  James
 * \date    2016-02-29
 * \version 
 * 
 * \history 
 *  Date        Note
 *  2016-02-29  Original Creation
 * 
 */

#include "libc.h"
#include "drivers.h"
#include "usbpd.h"
#include "usbpd_config.h"
#include "pe_state.h"
#include "sys_bat_intf.h"
#include "sys_config.h"
#include "sys_pd_hook.h"
#include "sys_pd_config.h"

volatile SYS_BAT_INFO_t g_sys_bat_info;

void sys_bat_init(void)
{
#if SYS_POWER_CHIP_SC8812A_ENABLED
    // Enable FACTORY
	SYS_BAT_REG_FIELD_SET(CTRL2_SET_REG,FACTORY,1);
#endif /* SYS_POWER_CHIP_SC8812A_ENABLED */
    
#if SYS_POWER_BANK_BATTERY_CELL_CFG
    SYS_BAT_REG_FIELD_SET(VBAT_SET_REG,VCELL_SET,SYS_BAT_DEF_CELL_VOLT);
    SYS_BAT_REG_FIELD_SET(VBAT_SET_REG,CSEL, usbpd_cfg_port_bat_cell_get());
    /* Max charge voltage
     * default set as 4200 mV for 1s, 8400 mV for 2s, 12600 mV for 3s or 
     * 16800 mV for 4s
     */
    //sys_bat_max_rated_charge_volt_set(SYS_BAT_CELL_VOLT* (usbpd_cfg_port_bat_cell_get()+1));
    sys_bat_max_rated_charge_volt_set(SYS_BAT_MAX_VOLT);
#endif /* SYS_POWER_BANK_BATTERY_CELL_CFG */

    SYS_BAT_REG_SET(VINREG_SET, SYS_BAT_VINREG_VAL_TO_REG(SYS_BAT_CELL_VOLT));
	
	// Enable ibat current
	//SYS_BAT_REG_FIELD_SET(CTRL1_SET_REG,ICHAR_SEL,1);
	
	// Disable auto-termination charge
	SYS_BAT_REG_FIELD_SET(CTRL1_SET_REG,DIS_TERM,1);

    // Enable discharge OVP 
    SYS_BAT_REG_FIELD_SET(CTRL1_SET_REG,DIS_OVP,FALSE);

    // FB_SEL: internal 
    SYS_BAT_REG_FIELD_SET(CTRL1_SET_REG,FB_SEL,0);

    // Change Ratio, RSEN_SET:10mO, IBAT:12x, IBUS:6A, VBAT:12.5x, VBUS:12.5x 
    SYS_BAT_REG_SET(RATIO_REG,0x34);

    // Configure default current limitation to 1500mA
    SYS_BAT_REG_SET(IBUS_LIM_SET_REG, SYS_BAT_IBUS_LIM_VAL_TO_REG(CURRENT_1500mA));

    // Interrupt mask, enabled VBUS_short, EOC, OTP
    SYS_BAT_REG_SET(MASK_REG,0x8E);

    // Enable ADC
    SYS_BAT_REG_FIELD_SET(CTRL3_SET_REG,AD_START,TRUE);
    SYS_BAT_REG_FIELD_SET(CTRL3_SET_REG,DIS_SHORTFOLDBACK,TRUE); /* disable IBUS current foldback, yzhang 20180319 */
	//sys_bat_dis_foldback_set(TRUE); 
    // 1/10, modified for discharge and charge concurrency
    SYS_BAT_REG_FIELD_SET(CTRL3_SET_REG,EOC_SET,1);

    sys_bat_charging_current_reach_peak_flag_set(FALSE);
}

// vbus_fb_value, unit mv
u16_t sys_bat_vbus_fb_val_get()
{
    u8_t regv[2] = {0,0};
    u16_t val;
    drv_i2c_reg_mget(SYS_BAT_PWR_CTRL_I2C, SYS_BAT_BASE_ADDR,VBUS_FB_VALUE_REG,2,&regv[0]);
    val = regv[0];
    regv[1] = (regv[1] & VBUS_FB_VALUE2_REG_VBUS_FB_VALUE2_FLDSET) >> VBUS_FB_VALUE2_REG_VBUS_FB_VALUE2;
    return (u16_t)((val*4+regv[1]+1)*25);
}

// vbat_fb_value, unit mv
u16_t sys_bat_vbat_fb_val_get()
{
    u8_t regv[2] = {0,0};
    u16_t val;
    drv_i2c_reg_mget(SYS_BAT_PWR_CTRL_I2C, SYS_BAT_BASE_ADDR,VBAT_FB_VALUE_REG,2,&regv[0]);
    val = regv[0];
    regv[1] = (regv[1] & VBAT_FB_VALUE2_REG_VBAT_FB_VALUE_2_FLDSET) >> VBAT_FB_VALUE2_REG_VBAT_FB_VALUE_2;
    return (u16_t)((val*4+regv[1]+1)*25);
}

// ibus_value, unit mA
u16_t sys_bat_ibus_val_get()
{
    u8_t regv[2] = {0,0};
    u16_t val;
    drv_i2c_reg_mget(SYS_BAT_PWR_CTRL_I2C, SYS_BAT_BASE_ADDR,IBUS_VALUE_REG,2,&regv[0]);
    val = regv[0];
    regv[1] = (regv[1] & IBUS_VALUE2_REG_IBUS_VALUE2_FLDSET) >> IBUS_VALUE2_REG_IBUS_VALUE2;
    return (u16_t)((val*4+regv[1]+1)*10);
}

// ibus_value, unit mA
u16_t sys_bat_ibat_val_get()
{
    u8_t regv[2] = {0,0};
    u16_t val;
    drv_i2c_reg_mget(SYS_BAT_PWR_CTRL_I2C, SYS_BAT_BASE_ADDR,IBAT_VALUE_REG,2,&regv[0]);
    val = regv[0];
    regv[1] = (regv[1] & IBAT_VALUE2_REG_IBAT_VALUE2_FLDSET) >> IBAT_VALUE2_REG_IBAT_VALUE2;
    return (u16_t)((val*4+regv[1]+1)*20);
}

// adin_value, unit mV
u16_t sys_bat_adin_val_get()
{
    u8_t regv[2] = {0,0};
    u16_t val;
    drv_i2c_reg_mget(SYS_BAT_PWR_CTRL_I2C, SYS_BAT_BASE_ADDR,ADIN_VALUE_REG,2,&regv[0]);
    val = regv[0];
    regv[1] = (regv[1] & ADIN_VALUE_2_REG_ADIN_VALUE2_FLDSET) >> ADIN_VALUE_2_REG_ADIN_VALUE2;
    return (u16_t)((val*4+regv[1]+1)*2U);
}


void sys_bat_discharge_vbus_ibus_set(u32_t vbus, u32_t ibus)
{
    SYS_BAT_REG_FIELD_SET(CTRL0_SET_REG,EN_OTG,TRUE);
    SYS_BAT_REG_SET(IBUS_LIM_SET_REG, SYS_BAT_IBUS_LIM_VAL_TO_REG(ibus));
    SYS_BAT_REG_SET(VBUSREF_I_SET_REG, (u8_t)SYS_BAT_VBUSREF_I_SET_VAL_TO_REG1(vbus));
    SYS_BAT_REG_FIELD_SET(VBUSREF_I_SET2_REG, VBUSREF_I_SET2, (u8_t)SYS_BAT_VBUSREF_I_SET_VAL_TO_REG2(vbus));
#if 0 /* CLI_ENABLED */
    if (sys_cfg_dbg_get() > SYS_DEBUG_LEVEL_2)
    {
        printf("REG:V1:%X,V2:%X,I:%X\n", 
            SYS_BAT_REG_GET(VBUSREF_I_SET_REG), 
            SYS_BAT_REG_GET(VBUSREF_I_SET2_REG), 
            SYS_BAT_REG_GET(IBUS_LIM_SET_REG));
    }
#endif /* CLI_ENABLED */
}

void sys_bat_ibus_set(u32_t ibus)
{
    SYS_BAT_REG_SET(IBUS_LIM_SET_REG, SYS_BAT_IBUS_LIM_VAL_TO_REG(ibus));
}


void sys_bat_charge_enable(bool_t en)
{
    u8_t cap_pos;
    u32_t power;

    if (en)
    {
        if (dpm_cable_detect_done_get() && (PE_SNK_READY == pe_state_get()) && (dpm_rx_src_cap_num_get()))
        {
            cap_pos = dpm_src_obj_pos_get();
            UPD_DATA_MSG_SOURCE_CAP_FIXED_SUPPLY_t *p_src_cap_fixed = (UPD_DATA_MSG_SOURCE_CAP_FIXED_SUPPLY_t *)dpm_rx_src_cap_object_get(cap_pos-1);
            if (POWER_SUPPLY_TYPE_BATTERY == p_src_cap_fixed->supply_type)
            {
                UPD_DATA_MSG_SOURCE_CAP_BATTERY_SUPPLY_t *p_src_cap_battery = (UPD_DATA_MSG_SOURCE_CAP_BATTERY_SUPPLY_t *)dpm_rx_src_cap_object_get(cap_pos-1);
                sys_bat_rated_charging_volt_set(p_src_cap_battery->max_volt);
                sys_bat_rated_charging_current_set((u32_t)(p_src_cap_battery->max_allow_pwr) * POWER_DIV * 1000 / (sys_bat_rated_charging_volt_get() * VOLTAGE_DIV) / CURRENT_DIV);
            }
            else if (POWER_SUPPLY_TYPE_VARIABLE == p_src_cap_fixed->supply_type)
            {
                UPD_DATA_MSG_SOURCE_CAP_VARIABLE_SUPPLY_t *p_src_cap_vari = (UPD_DATA_MSG_SOURCE_CAP_VARIABLE_SUPPLY_t *)dpm_rx_src_cap_object_get(cap_pos-1);
                sys_bat_rated_charging_volt_set(p_src_cap_vari->max_volt);
                sys_bat_rated_charging_current_set(p_src_cap_vari->max_curr);
            }
            else {
                sys_bat_rated_charging_volt_set(p_src_cap_fixed->volt);
                sys_bat_rated_charging_current_set(p_src_cap_fixed->max_curr);
#if CLI_ENABLED
                if (sys_cfg_dbg_get() > SYS_DEBUG_LEVEL_1) printf("POS:%u,MAX:%u,V:%u\n",cap_pos,p_src_cap_fixed->max_curr,p_src_cap_fixed->volt);
#endif /* CLI_ENABLED */
            }
            sys_bat_rated_charging_volt_set(sys_bat_rated_charging_volt_get()*VOLTAGE_DIV);
            sys_bat_rated_charging_current_set(SYS_POWER_BANK_CHARGE_RATIO*sys_bat_rated_charging_current_get()*CURRENT_DIV);
#if CLI_ENABLED
            if (sys_cfg_dbg_get() > SYS_DEBUG_LEVEL_1) printf("IBUS INC0:%umA,REG:%x\n",sys_bat_rated_charging_current_get(),(u8_t)SYS_BAT_IBUS_LIM_VAL_TO_REG(sys_bat_rated_charging_current_get()));
#endif /* CLI_ENABLED */

            power = sys_bat_rated_charging_volt_get() * sys_bat_rated_charging_current_get() / 1000;
            if (power > SYS_BAT_MAX_CHARGE_POWER)
            {
                sys_bat_rated_charging_current_set((u32_t)SYS_BAT_MAX_CHARGE_POWER * 1000 / sys_bat_rated_charging_volt_get());
            }
        }
        else {
            sys_bat_rated_charging_volt_set(VSAFE_5V);
            sys_bat_rated_charging_current_set(CURRENT_500mA);
        }

        if (sys_bat_rated_charging_current_get() > SYS_BAT_MAX_IBUS_CURRENT)
            sys_bat_rated_charging_current_set(SYS_BAT_MAX_IBUS_CURRENT);

        sys_bat_charging_start_tick_set(g_sys_pd_info.continuous_timer_tick);
        if (pe_prs_in_prog_get())
        {
            SYS_BAT_REG_SET(IBUS_LIM_SET_REG, 0);
            sys_bat_charging_current_reach_peak_flag_set(FALSE);
        }
        else {
            if (sys_bat_rated_charging_current_get() > CURRENT_500mA)
            {
                SYS_BAT_REG_SET(IBUS_LIM_SET_REG, SYS_BAT_IBUS_LIM_VAL_TO_REG(CURRENT_500mA));
                sys_bat_charging_current_reach_peak_flag_set(FALSE);
            }
            else {
                SYS_BAT_REG_SET(IBUS_LIM_SET_REG, SYS_BAT_IBUS_LIM_VAL_TO_REG(sys_bat_rated_charging_current_get()));
                sys_bat_charging_current_reach_peak_flag_set(TRUE);
            }            
        }
        SYS_BAT_REG_FIELD_SET(CTRL0_SET_REG,EN_OTG,FALSE);
#if CLI_ENABLED
        if (sys_cfg_dbg_get() > SYS_DEBUG_LEVEL_1) printf("IBUS INC1:%umA,REG:%x\n",sys_bat_rated_charging_current_get(),SYS_BAT_REG_GET(IBUS_LIM_SET_REG));
#endif /* CLI_ENABLED */
    }
    else {
        // disable charge input
        dpm_vbus_source_en_set(FALSE);
        sys_bat_charging_start_tick_set(0);
        sys_bat_charging_current_reach_peak_flag_set(FALSE);
        sys_bat_rated_charging_volt_set(0);
        sys_bat_rated_charging_current_set(0);
    }
}

void sys_bat_charge_current_update()
{
    u8_t current_from, current_to;

    if (!sys_bat_charging_current_reach_peak_flag_get())
    {
        if ((sys_bat_charging_start_tick_get()) && 
            ((g_sys_pd_info.continuous_timer_tick - sys_bat_charging_start_tick_get()) > 5))
        {
            current_to = (u8_t)SYS_BAT_IBUS_LIM_VAL_TO_REG(sys_bat_rated_charging_current_get());
            current_from = SYS_BAT_REG_GET(IBUS_LIM_SET_REG);
            if ((current_from + 5) < current_to)
            {
                SYS_BAT_REG_SET(IBUS_LIM_SET_REG, (current_from+5));
            }
            else {
                SYS_BAT_REG_SET(IBUS_LIM_SET_REG, current_to);
            }
            if (current_from == current_to)
            {
                sys_bat_charging_current_reach_peak_flag_set(TRUE);
            }
#if CLI_ENABLED
            if (sys_cfg_dbg_get() > SYS_DEBUG_LEVEL_1) printf("IBUS INC2:%umA,REG:%x\n",sys_bat_rated_charging_current_get(),SYS_BAT_REG_GET(IBUS_LIM_SET_REG));
#endif /* CLI_ENABLED */
        }
    }
}


u32_t sys_bat_ocv_rate(u32_t ocv_min, u32_t ocv_max, u8_t scale)
{
    return round_u32(((double)(sys_bat_current_vbat_val_get() - ocv_min)) / (ocv_max - ocv_min) * scale);
}

#if SYS_CAPACITY_PARA_SAVE_TO_FLASH
u32_t sys_bat_cap_saved_check(u32_t soc)
{
    if (sys_capacity_para_cfg_loaded())
    {
        if (sys_capacity_reset_flag_get())
        {
            return sys_capacity_get();
        }
        else {
            sys_capacity_reset_flag_set(TRUE);
            if (soc > sys_capacity_get())
            {
                if ((soc - sys_capacity_get()) <= SYS_BAT_SAVING_CAP_DIFF)
                {
                    return sys_capacity_get();
                }
            }
            else {
                if ((sys_capacity_get() - soc) <= SYS_BAT_SAVING_CAP_DIFF)
                {
                    return sys_capacity_get();
                }
            }
        }
    }
    return soc;
}
#endif /* SYS_CAPACITY_PARA_SAVE_TO_FLASH */


#if 1  /* modified by James Gan - 2018-11-27 */
// 4S
//const u16_t sys_bat_ocv_tab[10] = {16153,15762,15420,15083,14738,14431,14135,13735,13186,12000};
const u16_t sys_bat_ocv_tab[10] = {16175,15900,15600,15300,15050,14775,14500,14200,13775,12800};
// 3S
//const u16_t sys_bat_ocv_tab[10] = {12215,11822,11565,11312,11054,10823,10601,10301,9890,9000};
#else
const u16_t sys_bat_ocv_tab[5] = {15900,15200,14600,13500,12000};
#endif /* #if 0 */

void sys_bat_cap_cali(void)
{
    int i;
    u32_t vbat_val = 0;

#if SYS_BAT_CAP_INTEGRAL_METHOD_ENABLED
    sys_bat_cap_mws_rated_set(SYS_BAT_DEF_RATED_CAP_WS);
    sys_bat_samp_interval_set(SYS_BAT_DEF_SAMP_INTERVAL);
#endif /* SYS_BAT_CAP_INTEGRAL_METHOD_ENABLED */

    for (i = 0; i < 16; i++)
    {
        vbat_val += sys_bat_vbat_fb_val_get();
        rtc_delay_us(100);
    }
    vbat_val >>= 4;

    sys_bat_current_vbat_val_set(vbat_val);
#if (!SYS_BAT_CAP_INTEGRAL_METHOD_ENABLED)
    g_sys_bat_info.avg_vbat_val = sys_bat_current_vbat_val_get();
#endif /* (!SYS_BAT_CAP_INTEGRAL_METHOD_ENABLED) */
  
    // restore cap via open circuit voltage
    // capacity 100%
	if (sys_bat_current_vbat_val_get() >= sys_bat_max_rated_charge_volt_get())
	{
        i = 100;
	}
#if 1  /* modified by James Gan - 2018-11-27 */
    // 90% - 99%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[0]))
	{
		i = 90 + sys_bat_ocv_rate(sys_bat_ocv_tab[0], sys_bat_max_rated_charge_volt_get(), 10);
	}
    // 80% - 89%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[1]))
	{
		i = 80 + sys_bat_ocv_rate(sys_bat_ocv_tab[1], sys_bat_ocv_tab[0], 10);
	}
    // 70% - 79%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[2]))
	{
		i = 70 + sys_bat_ocv_rate(sys_bat_ocv_tab[2], sys_bat_ocv_tab[1], 10);
    }
    // 60% - 69%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[3]))
	{
		i = 60 + sys_bat_ocv_rate(sys_bat_ocv_tab[3], sys_bat_ocv_tab[2], 10);
	}
    // 50% - 59%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[4]))
	{
		i = 50 + sys_bat_ocv_rate(sys_bat_ocv_tab[4], sys_bat_ocv_tab[3], 10);
	}
    // 40% - 49%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[5]))
	{
		i = 40 + sys_bat_ocv_rate(sys_bat_ocv_tab[5], sys_bat_ocv_tab[4], 10);
	}
    // 30% - 39%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[6]))
	{
		i = 30 + sys_bat_ocv_rate(sys_bat_ocv_tab[6], sys_bat_ocv_tab[5], 10);
	}
    // 20% - 29%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[7]))
	{
		i = 20 + sys_bat_ocv_rate(sys_bat_ocv_tab[7], sys_bat_ocv_tab[6], 10);
	}
    // 10% - 19%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[8]))
	{
		i = 10 + sys_bat_ocv_rate(sys_bat_ocv_tab[8], sys_bat_ocv_tab[7], 10);
	}
    // 0% - 9%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[9]))
	{
		i = sys_bat_ocv_rate(sys_bat_ocv_tab[9], sys_bat_ocv_tab[8], 10);
	}
#else
    // 80% - 99%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[0]))
	{
		i = 80 + sys_bat_ocv_rate(sys_bat_ocv_tab[0], sys_bat_max_rated_charge_volt_get(), 20);
	}
    // 60% - 79%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[1]))
	{
		i = 60 + sys_bat_ocv_rate(sys_bat_ocv_tab[1], sys_bat_ocv_tab[0], 20);
	}
    // 40% - 59%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[2]))
	{
		i = 40 + sys_bat_ocv_rate(sys_bat_ocv_tab[2], sys_bat_ocv_tab[1], 20);
    }
    // 20% - 39%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[3]))
	{
		i = 20 + sys_bat_ocv_rate(sys_bat_ocv_tab[3], sys_bat_ocv_tab[2], 20);
	}
    // 0% - 19%
    else if (sys_bat_current_vbat_val_get() >= (sys_bat_ocv_tab[4]))
	{
		i = sys_bat_ocv_rate(sys_bat_ocv_tab[4], sys_bat_ocv_tab[3], 20);
	}
#endif /* #if 0 */
    else {
        i = 0;
    }

    if (i > 97 && i < 100)
    {
        i += 1;
    }

    sys_bat_capacity_set(i);

#if SYS_CAPACITY_PARA_SAVE_TO_FLASH
    i = sys_bat_cap_saved_check(i);
#endif /* SYS_CAPACITY_PARA_SAVE_TO_FLASH */

#if SYS_BAT_CAP_INTEGRAL_METHOD_ENABLED
    sys_bat_cap_mws_set(((f32_t)sys_bat_cap_mws_rated_get()/100)*i);
#endif /* SYS_BAT_CAP_INTEGRAL_METHOD_ENABLED */

#if SYS_CAPACITY_PARA_SAVE_TO_FLASH

#if CLI_ENABLED
	if (SYS_DEBUG_LEVEL_3 <= sys_cfg_dbg_get())
    {
        drv_uart_putstr("vbat:");
        drv_uart_putdec(g_sys_bat_info.current_vbat_val);
        drv_uart_putstr(",save:");
        drv_uart_putdec(sys_capacity_get());
        drv_uart_putstr(",soc:");
        drv_uart_putdec(sys_bat_capacity_get());
#if SYS_BAT_CAP_INTEGRAL_METHOD_ENABLED  /* modified by James Gan - 2018-12-24 */
        drv_uart_putstr(",cap:");
        drv_uart_putdec(sys_bat_cap_mws_get());
#endif /* #if SYS_BAT_CAP_INTEGRAL_METHOD_ENABLED */
        drv_uart_putchar(UART_LF);
	}
#endif     

    // update to the latest
    sys_bat_capacity_set(i);
#endif /* SYS_CAPACITY_PARA_SAVE_TO_FLASH */
}

#if SYS_BAT_CAP_INTEGRAL_METHOD_ENABLED

volatile SYS_BAT_CAP_INFO_t g_sys_bat_cap = {SYS_BAT_DEF_SAMP_INTERVAL, 0, 0, 0, SYS_BAT_DEF_RATED_CAP_WS, 0, 0,{0, 0, 0, 0, 0, 0, 0}};

void sys_bat_cap_update()
{
    int term_charge_threshold;
    u32_t u32_tmp;
    // calc from t1 to t2 average power

    u32_tmp = (g_sys_bat_info.current_ibat_val + g_sys_bat_cap.ibat_last) >> 1;
    u32_tmp *= ((g_sys_bat_info.current_vbat_val + g_sys_bat_cap.vbat_last) >> 1);
    // Unit: 1 mW
    u32_tmp /= 1000UL;
    // 1 Watt * 200 ms
    u32_tmp *= g_sys_bat_cap.interval;

    g_sys_bat_cap.cap_mws_dt += u32_tmp;

    // update soc per 1 second
    if (g_sys_pd_info.continuous_timer_tick % 10 == 0)
    {
        // Unit: 1 mWs
        g_sys_bat_cap.cap_mws_dt /= 1000UL;

        if (sys_bat_charging_flag_get())
        {
            g_sys_bat_cap.cap_mws += g_sys_bat_cap.cap_mws_dt;
            if (g_sys_bat_cap.cap_mws > g_sys_bat_cap.cap_mws_rated)
                g_sys_bat_cap.cap_mws = g_sys_bat_cap.cap_mws_rated;
        }
        else 
        {
            if (g_sys_bat_cap.cap_mws > g_sys_bat_cap.cap_mws_dt)
            {
                g_sys_bat_cap.cap_mws -= g_sys_bat_cap.cap_mws_dt;
            }
            else 
            {
                g_sys_bat_cap.cap_mws = 0;
            }
        }

        if (g_sys_bat_cap.cap_mws < g_sys_bat_cap.cap_mws_rated)
        {
            // Need to avoid calculation overflow
            g_sys_bat_info.capacity = (u8_t)round_u32((((f32_t)g_sys_bat_cap.cap_mws / g_sys_bat_cap.cap_mws_rated) * 100UL));
            if (g_sys_bat_info.capacity == 100)
            {
                drv_timer_stop(&(g_sys_bat_cap.cap_timer));
            }
        }
        else 
        {
            g_sys_bat_info.capacity = 100;
            drv_timer_stop(&(g_sys_bat_cap.cap_timer));
        }
        if (sys_bat_charging_flag_get())
        {
            if (dpm_rx_src_cap_max_power_get() >= 15000UL)
            {
                term_charge_threshold = 300;
            }
            else 
            {
                term_charge_threshold = 150;
            }
            if ((g_sys_bat_info.capacity > 60) && (g_sys_bat_info.capacity <= 99) && 
                (g_sys_bat_info.current_ibat_val < term_charge_threshold) && 
                ((u32_t)(now() - dpm_cc_connection_changed_time_get()) > 60000000UL))
            {
                if ((drv_timer_stopped(&(g_sys_bat_cap.cap_timer))) && 
                    (!(drv_timer_expired(&(g_sys_bat_cap.cap_timer)))))
                {
                    drv_timer_start(SYS_BAT_CAP_FULL_CHECK_TIMEOUT, &(g_sys_bat_cap.cap_timer), NULL);
                }
                if (drv_timer_expired(&(g_sys_bat_cap.cap_timer)))
                {
                    g_sys_bat_info.capacity += 1;
                    g_sys_bat_cap.cap_mws = (f32_t)g_sys_bat_cap.cap_mws_rated / 100 * g_sys_bat_info.capacity;
                    drv_timer_stop(&(g_sys_bat_cap.cap_timer));
                    if (g_sys_bat_info.capacity == 100)
                    {
                        g_sys_bat_cap.cap_mws = g_sys_bat_cap.cap_mws_rated;
                    }
                }
            }
        }
        else
        {
            if ((g_sys_bat_info.capacity > 1) && (g_sys_bat_info.capacity <= 20) && 
                (g_sys_bat_info.current_ibat_val > 2500))
            {
                if ((drv_timer_stopped(&(g_sys_bat_cap.cap_timer))) && 
                    (!(drv_timer_expired(&(g_sys_bat_cap.cap_timer)))))
                {
                    drv_timer_start(SYS_BAT_CAP_FULL_CHECK_TIMEOUT, &(g_sys_bat_cap.cap_timer), NULL);
                }
                if (drv_timer_expired(&(g_sys_bat_cap.cap_timer)))
                {
                    g_sys_bat_info.capacity -= 1;
                    g_sys_bat_cap.cap_mws = (f32_t)g_sys_bat_cap.cap_mws_rated / 100 * g_sys_bat_info.capacity;
                    drv_timer_stop(&(g_sys_bat_cap.cap_timer));
                }
            }
            if ((g_sys_bat_info.capacity == 0) && (sys_bat_state_get() == SYS_BAT_STATE_DISCHARGE))
            {
                if ((g_sys_bat_info.current_vbat_val > SYS_BAT_DEAD_VOLT) && (!dpm_port_dead_battery_get()))
                {
                    g_sys_bat_info.capacity = 1;
                }
            }
        }

        g_sys_bat_cap.cap_mws_dt = 0;
    }

    g_sys_bat_cap.ibat_last = g_sys_bat_info.current_ibat_val;
    g_sys_bat_cap.vbat_last = g_sys_bat_info.current_vbat_val;
}
#endif /* #if SYS_BAT_CAP_INTEGRAL_METHOD_ENABLED */

