This is a library for the MAX17055 Li+ Battery Fuel Gauge.

Fork of max17055 by Central Applications - Mbed Code repo

max17055.cpp

Committer:
fneirab
Date:
2018-02-06
Revision:
9:f29d5e49b190
Parent:
8:ca8765c30ed2
Child:
11:bdbd3104995b

File content as of revision 9:f29d5e49b190:

/******************************************************************//**
* @file max17055.cpp
*
* @author Felipe Neira - Maxim Integrated - TTS
*
* @version 1.2
*
* Started: 9JAN18
*
* Updated: 
* New functions improved for continious display. Change copyright notice for 2018.
* 
* 
*
/*******************************************************************************
* Copyright (C) 2018 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************
*/

#include "mbed.h"
#include "max17055.h"

#define DRV_NAME "max17055"

/* CONFIG register bits */
#define MAX17055_CONFIG_ALRT_EN         (1 << 2)
#define MAX17055_CONFIG2_LDMDL          (1 << 5)

/* STATUS register bits */
#define MAX17055_STATUS_BST             (1 << 3)
#define MAX17055_STATUS_POR             (1 << 1)

/* POR Mask */
#define MAX17055_POR_MASK               (0xFFFD)

/* MODELCFG register bits */
#define MAX17055_MODELCFG_REFRESH       (1 << 15)

/* TALRTTH register bits */
#define MIN_TEMP_ALERT                  0
#define MAX_TEMP_ALERT                  8

/* FSTAT register bits */
#define MAX17055_FSTAT_DNR              (1)

/* STATUS interrupt status bits */
#define MAX17055_STATUS_ALRT_CLR_MASK   (0x88BB)
#define MAX17055_STATUS_SOC_MAX_ALRT    (1 << 14)
#define MAX17055_STATUS_TEMP_MAX_ALRT   (1 << 13)
#define MAX17055_STATUS_VOLT_MAX_ALRT   (1 << 12)
#define MAX17055_STATUS_SOC_MIN_ALRT    (1 << 10)
#define MAX17055_STATUS_TEMP_MIN_ALRT   (1 << 9)
#define MAX17055_STATUS_VOLT_MIN_ALRT   (1 << 8)
#define MAX17055_STATUS_CURR_MAX_ALRT   (1 << 6)
#define MAX17055_STATUS_CURR_MIN_ALRT   (1 << 2)

#define MAX17055_VMAX_TOLERANCE     50 /* 50 mV */


///////////////////////////////////////////////////////////////////////////////

MAX17055::MAX17055(I2C &i2c):
    m_i2cBus(i2c)
{
    //empty block
}

///////////////////////////////////////////////////////////////////////////////
MAX17055::~MAX17055()
{
    //empty block
}


///////////////////////////////////////////////////////////////////////////////

/**
* \brief        Write a value to a MAX17055 register
* \par          Details
*               This function writes a value to a MAX17055 register
*
* \param[in]    reg_addr - register address
* \param[in]    reg_data - register data
*
* \retval       1 on success
*/

int MAX17055::writeReg(Registers_e reg_addr, uint16_t reg_data)
{

    uint16_t mask = 0x00FF;
    uint8_t dataLSB;
    uint8_t dataMSB;

    dataLSB = reg_data & mask;
    dataMSB = (reg_data >> 8) & mask;

    char addr_plus_data[3] = {reg_addr, dataLSB, dataMSB};

    if ( m_i2cBus.write(I2C_W_ADRS, addr_plus_data, 3, false) == 0)
        return 1;
    else
        return 0;

}
///////////////////////////////////////////////////////////////////////////////
/**
* \brief        Read a MAX17055 register
* \par          Details
*               This function reads a MAX17055 register
*
* \param[in]    reg_addr   - register address
* \param[out]   &value   - pointer that stores the register data
*
* \retval       1 on success
*/


int32_t MAX17055::readReg(Registers_e reg_addr, uint16_t &value)
{
    int32_t result;
    uint16_t mask = 0x00FF;
    char local_data[1];
    local_data[0] = reg_addr;
    char read_data[2];

    result = m_i2cBus.write(I2C_W_ADRS, local_data, 1);
    if(result == 0) {
        result = m_i2cBus.read(I2C_R_ADRS, read_data , 2, false);
        if (result == 0) {
            value = ( ((read_data[1] & mask) << 8) + (read_data[0]));
            result = 1;
        }
    }

    return result;

}

///////////////////////////////////////////////////////////////////////////////
/**
* \brief        Write and Verify a MAX17055 register
* \par          Details
*               This function wites and verifies if the writing process was successful
*
* \param[in]    reg_addr     - register address
* \param[out]   reg_data     - the variable that contains the data to write
*                                to the register address
*
* \retval       1 on success
*/


int MAX17055::write_and_verify_reg(Registers_e reg_addr, uint16_t reg_data)
{
    int retries = 8;
    int ret;
    int statusRead;
    int statusWrite;
    uint16_t read_data;

    do {
        statusWrite = writeReg(reg_addr, reg_data);
        if (statusWrite != 1)
            ret = -1;
        wait_ms(3);
        statusRead = readReg(reg_addr, read_data);
        if  (statusRead != 1)
            ret = -2;
        if (read_data != reg_data) {
            ret = -3;
            retries--;
        }
    } while (retries && read_data != reg_data);

    if (ret<0)
        return ret;
    else
        return 1;
}

////////////////////////////////////////////////////////////////////////////////

/**
* \brief        Initialise Function for MAX17055
* \par          Details
*               This function intitializes the MAX17055
*
* \retval       1 on success
*               0 if device is not present
*              -1 if errors exist
*              -2 if POR is not set
*/


int MAX17055::init(platform_data des_data)
{

    int status, ret;
    int time_out = 10;
    uint16_t read_data, hibcfg_value, reg;


    status = readReg(VERSION_REG, read_data);
    if (status == 0)
        return status;  //Device is not present in the i2c Bus

    /* Step 0: Check for POR */
    /* Skip load model if POR bit is cleared */

    if (check_POR_func() == -1)
        return -2;  //POR is not set. Skip Initialization.

    /* Step 1: Check if FStat.DNR == 0 */
    // Do not continue until FSTAT.DNR == 0
    ret = poll_flag_clear(FSTAT_REG, MAX17055_FSTAT_DNR, time_out);
    if (ret < 0) {
        //printf("Unsuccessful init: Data Not Ready!\n");
        return ret;
    }

    /* Force exit from hibernate */
    hibcfg_value = forcedExitHyberMode();

    //printf("step 1 check \r\n");

    /* Step 2: Initialize configuration */
    switch (1) {
        case MODEL_LOADING_OPTION1:
            /* Step 2.1: Option 1 EZ Config */
            EZconfig_init(des_data);

            /* Poll ModelCFG.ModelRefresh bit for clear */
            ret = poll_flag_clear(MODELCFG_REG, MAX17055_MODELCFG_REFRESH, time_out);
            if(ret < 0) {
                //dev_err(priv->dev, "Option1 model refresh not completed!\n");
                return ret;
            }

            break; 
    }
    /* Restore original HibCfg */
    writeReg(HIBCFG_REG, hibcfg_value);
    //printf("Last section check \r\n");


    /* Optional step - alert threshold initialization */
    //set_alert_thresholds(priv);

    /* Clear Status.POR */
    ret = clear_POR_bit();
    if (ret == -1)
        return ret;    
    return 1;
}

////////////////////////////////////////////////////////////////////////////////

/**
* \brief        Check POR function
* \par          Details
*               This function check is there was a power on reset event for the MAX17055
*
* \retval       1 for no POR detected
*              -1 for POR detected
*/
int MAX17055::check_POR_func()
{
    uint16_t read_data;
    
    readReg(STATUS_REG, read_data);

    if (!(read_data & MAX17055_STATUS_POR ) )
        return -1;  //POR is not set.
    else 
        return 1;
}

////////////////////////////////////////////////////////////////////////////////

/**
* \brief        clear POR bit function
* \par          Details
*               This function clear the idicating bit for POR - MAX17055
*
* \retval       1 for Success
*              -1 for errors
*/
int MAX17055::clear_POR_bit()
{
    int status, ret;
    uint16_t read_data, hibcfg_value, reg;


    status = readReg(STATUS_REG, read_data);
    if (status != 1)
        return -1;  //Device is not present in the i2c Bus
    status = write_and_verify_reg(STATUS_REG, (read_data & MAX17055_POR_MASK));
    if (status != 1)
        return -1;
    else 
        return 1;


}

///////////////////////////////////////////////////////////////////////////////

/**
* \brief        Poll Flag clear
* \par          Details
*               This function clears status flags for the MAX17055
*
* \param[in]    reg_addr - register address
* \param[in]    mask - register address
* \param[in]    timeout - register data
*
* \retval       1 on success
*              -1 on Failure
*/

int MAX17055::poll_flag_clear (Registers_e reg_addr, int mask, int timeout)
{
    uint16_t data;
    int ret;


    do {
        wait_ms(1);
        ret = readReg(reg_addr, data);
        if(ret < 0)
            return ret;

        if(!(data & mask))
            return 1;

        timeout -= 1;
    } while(timeout > 0);

    return -1;
}



////////////////////////////////////////////////////////////////////////////////

/**
* \brief        Get Internal Temperature Function for MAX17055
* \par          Details
*               This function sends a request to access the internal
*               of the MAX17055
*
*
* \retval      temperature value
*              -1 if errors exist
*/


int MAX17055::get_temperature()
{

    int ret;
    uint16_t data;

    ret = readReg(TEMP_REG, data);
    if (ret < 0)
        return ret;

    /* The value is signed. */
    if (data & 0x8000)
        data |= 0xFFFF0000;

    /* The value is converted into centigrade scale */
    /* Units of LSB = 1 / 256 degree Celsius */
    data >>= 8;

    return data ;
}


////////////////////////////////////////////////////////////////////////////////

/**
* \brief        Forced Exit Hibernate Mode Function for MAX17055
* \par          Details
*               This function executes a force exit from hibernate mode.
*
* \retval       returns HibCFG original value before forced Exit Hybernate mode
*
*/


uint16_t MAX17055::forcedExitHyberMode()
{
    uint16_t hibcfg;

    /* Force exit from hibernate */
    //STEP 0: Store original HibCFG value
    readReg(HIBCFG_REG, hibcfg);

    //STEP 1: Write to Soft-Wakeup Commannd Register
    writeReg(VFSOC0_QH0_LOCK_REG, 0x90); //Soft-Wakeup from hybernate

    //STEP 2: Write to Hibernate Configuration register
    writeReg(HIBCFG_REG, 0x0); //disable hibernate mode

    //STEP 3:Write to Soft-Wakeup Commannd Register
    writeReg(VFSOC0_QH0_LOCK_REG, 0x0); //Clear All commnads

    return hibcfg;
}


////////////////////////////////////////////////////////////////////////////////

/**
* \brief        EZ COnfing Init function
* \par          Details
*               This function implements the steps for the EZ confing m5 FuelGauge
*
* \retval       1 on success
*
*/


uint16_t MAX17055::EZconfig_init(platform_data des_data)
{
    const int charger_th = 4275;
    const int chg_V_high = 51200;
    const int chg_V_low = 44138;
    const int param_EZ_FG1 = 0x8400;
    const int param_EZ_FG2 = 0x8000;
    uint16_t dpacc, ret;


    /* Step 2.1: Option 1 EZ Config */
    ret = writeReg(DESIGNCAP_REG, des_data.designcap);
    ret = writeReg(DQACC_REG, des_data.designcap >> 5);  //DesignCap divide by 32
    ret = writeReg(ICHGTERM_REG, des_data.ichgterm);
    ret = writeReg(VEMPTY_REG, des_data.vempty);

    if (des_data.vcharge > charger_th) {
        dpacc = (des_data.designcap >> 5) * chg_V_high / des_data.designcap;
        ret = writeReg(DPACC_REG, dpacc);
        ret = writeReg(MODELCFG_REG, param_EZ_FG1); //Why 0x8400??
    } else {
        dpacc = (des_data.designcap >> 5) * chg_V_low / des_data.designcap;
        ret = writeReg(DPACC_REG, dpacc);
        ret = writeReg(MODELCFG_REG, param_EZ_FG2);
    }


    return ret;

}


////////////////////////////////////////////////////////////////////////////////

/**
* \brief        Get State Of Charge(SOC) Function for MAX17055
* \par          Details
*               This function sends a request to access the internal
*               of the MAX17055
*
* \param[in]    reg_addr     - register address
* \param[out]   reg_data     - SOC data from the REPSOC_REG register
* \retval       1 on success
*
*              -1 if errors exist
*/


int MAX17055::get_SOC()
{

    int ret;
    uint16_t data;

    ret = readReg(REPSOC_REG, data);
    if (ret < 0)
        return ret;

    data = data >> 8; /* RepSOC LSB: 1/256 % */

    return data;
}

////////////////////////////////////////////////////////////////////////////////

/**
* \brief        Get Average State Of Charge(SOC) Function for MAX17055
* \par          Details
*               This function sends a request to access the internal
*               of the MAX17055
*
* \param[in]    reg_addr     - register address
* \param[out]   
* \retval       data     - avSOC data from the AVSOC_REG register
*              
*/


int MAX17055::get_avSOC()
{

    int ret;
    uint16_t data;

    ret = readReg(AVSOC_REG, data);
    if (ret < 0)
        return ret;

    data = data >> 8; /* avSOC LSB: 1/256 % */

    return data;
}

////////////////////////////////////////////////////////////////////////////////

/**
* \brief        Get State Of Charge(SOC) Function for MAX17055
* \par          Details
*               This function sends a request to access the internal
*               of the MAX17055
*
* \param[in]    reg_addr     - register address
* \param[out]   reg_data     - SOC data from the REPSOC_REG register
* \retval       1 on success
*
*              -1 if errors exist
*/


int MAX17055::get_mixSOC()
{

    int ret;
    uint16_t data;

    ret = readReg(MIXSOC_REG, data);
    if (ret < 0)
        return ret;

    data = data >> 8; /* RepSOC LSB: 1/256 % */

    return data;
}
///////////////////////////////////////////////////////////////////////////////

/**
* \brief        Get the remaining Time to Empty(TTE) Function for MAX17055
* \par          Details
*               This function sends a request to access the internal register
*               of the MAX17055
*
* \retval      tte_data  - Time to Empty data from the TTE_REG register
*
*              -1 if errors exist
*/

int MAX17055::get_atTTE()
{

    int ret;
    uint16_t data;

    ret = readReg(ATTTE_REG, data);
    if (ret < 0)
        return ret;
    else
        data = (data * 45) >> 3; /* TTE LSB: 5.625 sec */

    return data;
}

///////////////////////////////////////////////////////////////////////////////

/**
* \brief        Get the remaining Time to Full(TTF) Function for MAX17055
* \par          Details
*               This function sends a request to access the internal register
*               of the MAX17055
*
* \retval      ttf_data  - Time to Empty data from the TTF_REG register
*
*              -1 if errors exist
*/

int MAX17055::get_TTF()
{

    int ret;
    uint16_t ttf_data;

    ret = readReg(TTF_REG, ttf_data);
    if (ret < 0)
        return ret;
    else
        ttf_data = (ttf_data * 45) >> 3; /* TTF LSB: 5.625 sec */

    return ttf_data;
}

////////////////////////////////////////////////////////////////////////////

/**
* \brief        Get voltage of the cell Function for MAX17055
* \par          Details
*               This function sends a request to access the internal register
*               of the MAX17055 to read the voltage of the cell
*
* \retval      vcell_data  - vcell data from the VCELL_REG register
*
*              -1 if errors exist
*/


int MAX17055::get_Vcell()
{

    int ret;
    uint16_t vcell_data;

    ret = readReg(VCELL_REG, vcell_data);
    if (ret < 0)
        return ret;
    else
        //printf("Vcell Reg= %d \r\n",vcell_data);
        ret = lsb_to_uvolts(vcell_data);
    //printf("Vcell Conv= %d \r\n",ret);
    return ret;

}


////////////////////////////////////////////////////////////////////////////

/**
* \brief        Get current Function for MAX17055
* \par          Details
*               This function sends a request to access the internal register
*               of the MAX17055 to read the current register.
*
* \retval      curr_data  - vcell data from the VCELL_REG register
*
*              -1 if errors exist
*/


int MAX17055::get_Current( platform_data des_data )
{

    int ret,design_rsense;
    uint16_t data;

    ret = readReg(CURRENT_REG, data);
    if (ret < 0)
        return ret;
    else
    design_rsense = des_data.rsense;
    ret = raw_current_to_uamps((uint32_t)data, design_rsense);
    return ret;

}


////////////////////////////////////////////////////////////////////////////

/**
* \brief        Get Average Current Function for MAX17055
* \par          Details
*               This function sends a request to access the internal register
*               of the MAX17055 to read the average current register.
*
* \retval      curr_data  - vcell data from the AVGCURRENT_REG register
*
*              -1 if errors exist
*/


int MAX17055::get_AvgCurrent( platform_data des_data )
{

    int ret, design_rsense;
    uint16_t data;
    uint32_t aveCurr_data;

    ret = readReg(AVGCURRENT_REG, data);
    if (ret < 0)
        return ret;
    else
    aveCurr_data = data;
    design_rsense = des_data.rsense;
    aveCurr_data = raw_current_to_uamps(aveCurr_data, design_rsense);
    return aveCurr_data;

}

///////////////////////////////////////////////////////////////////////////////

/**
* \brief        lsb_to_uvolts Converssion Function
* \par          Details
*               This function takes the lsb value of the register and convert it
*               to uvolts
*
* \param[in]   lsb     - value of register lsb
* \retval      lsb  - converted lsb to uvolts
*
*/

int MAX17055:: lsb_to_uvolts(uint16_t lsb)
{
    int store;
    store = (lsb * 625) / 8; /* 78.125uV per bit */
    return store;
}


///////////////////////////////////////////////////////////////////////////////

/**
* \brief        raw_current_to_uamp Converssion Function
* \par          Details
*               This function takes the raw current value of the register and
*               converts it to uamps
*
* \param[in]   curr - raw current value of register
* \retval      res  - converted raw current to uamps - Signed 2's complement
*
*/

int MAX17055::raw_current_to_uamps(uint32_t curr, int rsense_value)
{
    int res = curr;
    /* Negative */
    if (res & 0x8000) {
        res |= 0xFFFF0000;
    } else {
        res *= 1562500 /(rsense_value * 1000); //Change to interact with the rsense of customer
    }
    return res;
}

///////////////////////////////////////////////////////////////////////////////

/**
* \brief        Save Learned Parameters Function
* \par          Details
*               It is recommended to save the learned capacity parameters every
*               time bit 2 of the Cycles register toggles
*               (so that it is saved every 64% change in the battery) 
*               so that if power is lost the values can easily be restored.
*
* \retval      ret int -1 if fail
*
*/

int MAX17055::save_Params(saved_FG_params_t FG_params)
{
    int ret;
    uint16_t data[5];
    ret = readReg(RCOMP0_REG, data[0]);
    if (ret < 0)
        return ret;
    else
        FG_params.rcomp0 = data[0];

    ret = readReg(TEMPCO_REG, data[1]);
    if (ret < 0)
        return ret;
    else
        FG_params.temp_co = data[1];

    ret = readReg(FULLCAPREP_REG, data[2]);
    if (ret < 0)
        return ret;
    else
        FG_params.full_cap_rep = data[2];

    ret = readReg(CYCLES_REG, data[3]);
    if (ret < 0)
        return ret;
    else
        FG_params.cycles = data[3];

    ret = readReg(FULLCAPNOM_REG, data[4]);
    if (ret < 0)
        return ret;
    else
        FG_params.full_cap_nom = data[4];
    return ret;
}

///////////////////////////////////////////////////////////////////////////////

/**
* \brief        Resotore Parameters Function
* \par          Details
*               If power is lost, then the capacity information 
*               can be easily restored with this function
*
* \param[in]   struct saved_FG_params_t 
* \retval      void
*
*/

int MAX17055::restore_Params(saved_FG_params_t FG_params)
{
    int ret;
    uint16_t temp_data, fullcapnom_data, mixCap_calc, dQacc_calc;
    uint16_t dPacc_value = 0x0C80;//Why this vcalue?

    //Restoring capacity parameters 
    write_and_verify_reg(RCOMP0_REG, FG_params.rcomp0);
    write_and_verify_reg(TEMPCO_REG, FG_params.temp_co);
    write_and_verify_reg(FULLCAPNOM_REG, FG_params.full_cap_nom);
    
    wait_ms(350);
    //Restore FullCap

    ret = readReg(FULLCAPNOM_REG, fullcapnom_data);
    if (ret < 0)
        return ret;

    ret = readReg(MIXSOC_REG, temp_data); //check if Error in sofware guide register incorrect
    if (ret < 0)
        return ret;
    
    mixCap_calc = (temp_data*fullcapnom_data)/25600;

    write_and_verify_reg(MIXCAP_REG, mixCap_calc);
    write_and_verify_reg(FULLCAPREP_REG, FG_params.full_cap_rep);
    
    //Write DQACC to 200% of Capacity and DPACC to 200%
    dQacc_calc = (FG_params.full_cap_nom/ 16) ;

    write_and_verify_reg(DPACC_REG, dPacc_value);
    write_and_verify_reg(DQACC_REG, dQacc_calc);

    wait_ms(350);

    //Restore Cycles register
    ret = write_and_verify_reg(CYCLES_REG, FG_params.cycles);
    if (ret < 0)
        return ret;
return ret;
}