This is a library for the MAX17055 Li+ Battery Fuel Gauge.
Fork of max17055 by
max17055.cpp
- Committer:
- fneirab
- Date:
- 2018-02-27
- Revision:
- 11:bdbd3104995b
- Parent:
- 9:f29d5e49b190
- Child:
- 12:519a18fc3b28
File content as of revision 11:bdbd3104995b:
/******************************************************************//** * @file max17055.cpp * * @author Felipe Neira - Maxim Integrated - TTS * * @version 1.4 * * Started: 6FEB18 * * Updated: * Modifications to reflect applicable functions and .h file association * * * /******************************************************************************* * 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" ///not used // /* CONFIG register bits */ // #define MAX17055_CONFIG_ALfRT_EN (1 << 2) ///not used // #define MAX17055_CONFIG2_LDMDL (1 << 5) ///not used // /* STATUS register bits */ // #define MAX17055_STATUS_BST (1 << 3) ///not used // #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 ///not used // #define MAX_TEMP_ALERT 8 ///not used /* 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 */ /* LIBRARY FUNCTION SUCCESS*/ #define F_SUCCESS_0 0 /* LIBRARY FUNCTION ERROR CODES */ #define F_ERROR_1 -1 //-1 if read/write errors exist #define F_ERROR_2 -2 //-2 if device is not present #define F_ERROR_3 -3 //-3 if function error #define F_ERROR_4 -4 //-4 if other error #define F_ERROR_5 -5 //-5 if POR not detected /** * @brief max17055 Constructor * @details max17055 Constructor with battery and i2c as parameters */ MAX17055::MAX17055(I2C &i2c): m_i2cBus(i2c) { //empty block } /** * @brief Fuel Gauge Destructor */ MAX17055::~MAX17055() { //empty block } /** * @brief Writes a register. * * @param[in] reg_addr The register address * @param[in] reg_data The register data * * @retval 0 on success * @retval non-0 for errors */ 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) == F_SUCCESS_0) return F_SUCCESS_0; else return F_ERROR_1; } /** * @brief Reads from MAX17055 register. * * @param[in] reg_addr The register address * @param value The value * * @retval 0 on success * @retval non-0 for errors */ 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 == F_SUCCESS_0) { result = m_i2cBus.read(I2C_R_ADRS, read_data , 2, false); if (result == F_SUCCESS_0) { value = ( ((read_data[1] & mask) << 8) + (read_data[0])); result = F_SUCCESS_0; } } 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 0 on success * @retval non-0 for errors */ 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 != F_SUCCESS_0) ret = F_ERROR_1; wait_ms(3); statusRead = readReg(reg_addr, read_data); if (statusRead != F_SUCCESS_0) ret = F_ERROR_1; if (read_data != reg_data) { ret = F_ERROR_1; retries--; } } while (retries && read_data != reg_data); if (ret!=F_SUCCESS_0) return ret; else return F_SUCCESS_0; } /** * @brief Initialization Function for MAX17055. * @par Details * This function intitializes the MAX17055 for the implementation of the EZconfig model.\n * The libraty needs to be customized for the implementation of customize model.\n * * @retval 0 on success * @retval non-0 for errors */ 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 != F_SUCCESS_0) return status; /* Step 0: Check for POR */ /* Skip load model if POR bit is cleared */ if (check_POR_func() == F_ERROR_5) return F_ERROR_5; //POR not detected. 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 < F_SUCCESS_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 < F_SUCCESS_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 < F_SUCCESS_0) return ret; //See errors return F_SUCCESS_0; } /** * @brief Check POR function * @par Details * This function check is there was a power on reset event for the * MAX17055 * * @retval 0 on success (POR detected) * @retval non-0 for errors (POR not detected) * */ int MAX17055::check_POR_func() { uint16_t read_data; readReg(STATUS_REG, read_data); if (!(read_data & MAX17055_STATUS_POR ) ) return F_ERROR_5; //POR not detected. else return F_SUCCESS_0; } /** * @brief clear POR bit function * @par Details * This function clear the idicating bit for POR - MAX17055 * * @retval 0 for Success * @retval non-0 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 != F_SUCCESS_0) return F_ERROR_2; //Device is not present in the i2c Bus status = write_and_verify_reg(STATUS_REG, (read_data & MAX17055_POR_MASK)); if (status != F_SUCCESS_0) return F_ERROR_1; //read or write error else return F_SUCCESS_0; } /** * @brief Poll Flag clear Function. * @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 0 on success * @retval non-0 negative for errors */ 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 < F_SUCCESS_0) return F_ERROR_1; if(!(data & mask)) return F_SUCCESS_0; timeout -= 1; } while(timeout > 0); return F_ERROR_4; } /** * @brief Get Temperature Function from the MAX17055 TEMP register. * @par Details * This function sends a request to access the TEMP register * of the MAX17055, which reflects the temperature measured for the fuel gauge. * The temperature values will reflect the Config Register (0x1D) selections for Tsel bit (D15). * For this library the setting are for die temperature. * The MAX32620FTHR thermistor bias pin is not connected. The biasing of the thermistor is * done by the MAX77650. See MAX77650 library for how to enable the thermistor biasing. * * * @retval temp - Temperature value from TEMP register in °C * @retval non-0 negative values check for errors */ int MAX17055::get_temperature() { int ret; uint16_t temp; ret = readReg(TEMP_REG, temp); if (ret < F_SUCCESS_0) return ret; /* The value is signed. */ if (temp & 0x8000) temp |= 0xFFFF0000; /* The value is converted into centigrade scale */ /* Units of LSB = 1 / 256 degree Celsius */ temp >>= 8; return temp; } /** * @brief Forced Exit Hibernate Mode Function for MAX17055 * @par Details * This function executes a force exit from hibernate mode. * * @retval 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 Initialization function * @par Details * This function implements the steps for the EZ confing m5 FuelGauge * @param[in] des_data - Plataform_data struct with information about the deisgn. * @retval 0 on success * @retval non-zero for errors */ uint16_t MAX17055::EZconfig_init(platform_data des_data) { ///EZ config values 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 reported State Of Charge(SOC) Function from MAX17055 Fuel Gauge. * @par Details * This function sends a request to access the RepSOC register * of the MAX17055. RepSOC is the reported state-of-charge percentage output of the fuel gauge. * * @retval soc_data - Reported SOC data from the RepSOC register in % value. * @retval non-0 negative values check for errors */ int MAX17055::get_SOC() { int ret; uint16_t soc_data; ret = readReg(REPSOC_REG, soc_data); if (ret < F_SUCCESS_0) return ret; soc_data = soc_data >> 8; /* RepSOC LSB: 1/256 % */ return soc_data; } /** * @brief Get Average State Of Charge(SOC) Function from MAX17055 Fuel Gauge. * @par Details * This function sends a request to access the avSOC register of the MAX17055. * The AvSOC registers hold the calculated available capacity and percentage of the * battery based on all inputs from the ModelGauge m5 algorithm including empty * compensation. These registers provide unfiltered results. Jumps in the reported * values can be caused by abrupt changes in load current or temperature. * * @retval avSOC_data - Average SOC data from the AVSOC register in % value. * @retval non-0 negative values check for errors */ int MAX17055::get_avSOC() { int ret; uint16_t avSOC_data; ret = readReg(AVSOC_REG, avSOC_data); if (ret < F_SUCCESS_0) return ret; //Check errors if data is not correct avSOC_data = avSOC_data >> 8; /* avSOC LSB: 1/256 % */ return avSOC_data; } /** * @brief Get mix State Of Charge(SOC) Function for MAX17055 Fuel Gauge. * @par Details * This function sends a request to access mixSOC register * of the MAX17055. The MixSOC registers holds the calculated * remaining capacity and percentage of the cell before any empty compensation * adjustments are performed. * * @retval mixSOC_data - Mixed SOC register values from the mixSOC register in % value. * @retval non-0 for errors */ int MAX17055::get_mixSOC() { int ret; uint16_t mixSOC_data; ret = readReg(MIXSOC_REG, mixSOC_data); if (ret < F_SUCCESS_0) return ret; mixSOC_data = mixSOC_data >> 8; /* RepSOC LSB: 1/256 % */ return mixSOC_data; } /** * @brief Get the Time to Empty(TTE) Function form MAX17055 Fuel Gauge. * @par Details * This function sends a request to access the TTE register * of the MAX17055 * The TTE register holds the estimated time to empty for the * application under present temperature and load conditions. The TTE value is * determined by relating AvCap with AvgCurrent. The corresponding AvgCurrent * filtering gives a delay in TTE, but provides more stable results. * * @retval tte_data - Time to Empty data from the TTE register in seconds. * @retval non-0 negative values check for errors */ int MAX17055::get_TTE() { int ret; uint16_t tte_data; ret = readReg(TTE_REG, tte_data); if (ret < F_SUCCESS_0) return ret; else tte_data = (tte_data * 45) >> 3; /* TTE LSB: 5.625 sec */ return tte_data; } /** * @brief Get the at Time to Empty(atTTE) value Function for MAX17055 Fuel Gauge. * @par Details * This function sends a request to access the internal register * of the MAX17055 * * @retval atTTE_data - Time to Empty data from the atTTE register in seconds. * @retval non-0 negative values check for errors */ int MAX17055::get_atTTE() { int ret; uint16_t atTTE_data; ret = readReg(ATTTE_REG, atTTE_data); if (ret < F_SUCCESS_0) return ret; //Check for errors else atTTE_data = (atTTE_data * 45) >> 3; /* TTE LSB: 5.625 sec */ return atTTE_data; } /** * @brief Get the Time to Full(TTE) values Function for MAX17055 Fuel Gauge. * @par Details * This function sends a request to access the internal register of the MAX17055 * The TTF register holds the estimated time to full for the application * under present conditions. The TTF value is determined by learning the * constant current and constant voltage portions of the charge cycle based * on experience of prior charge cycles. Time to full is then estimate * by comparing present charge current to the charge termination current. * Operation of the TTF register assumes all charge profiles are consistent in the application. * * @retval ttf_data - Time to Full data from the TTF register in seconds. * @retval non-0 negative values check for errors */ int MAX17055::get_TTF() { int ret; uint16_t ttf_data; ret = readReg(TTF_REG, ttf_data); if (ret < F_SUCCESS_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 Fuel Gauge. * @par Details * This function sends a request to access the VCell Register * of the MAX17055 to read the measured voltage from the cell. * * @retval vcell_data - vcell data from the VCELL_REG register in uVolts. * @retval non-0 negative values check for errors */ int MAX17055::get_Vcell() { int ret; uint16_t vcell_data; ret = readReg(VCELL_REG, vcell_data); if (ret < F_SUCCESS_0) return ret; else ret = lsb_to_uvolts(vcell_data); return ret; } /** * @brief Get current Function for MAX17055 Fuel Gauge. * @par Details * This function sends a request to access the CURRENT register * of the MAX17055 to read the current redings. * * @param[in] des_data - Plataform_data struct with information about the deisgn. * * @retval curr_data - current data from the CURRENT register in uAmps. * @retval non-0 negative values check for errors. */ int MAX17055::get_Current( platform_data des_data ) { int ret,design_rsense; uint16_t curr_data; ret = readReg(CURRENT_REG, curr_data); if (ret < F_SUCCESS_0) return ret; else design_rsense = des_data.rsense; ret = raw_current_to_uamps((uint32_t)curr_data, design_rsense); return ret; } /** * @brief Get average current Function for MAX17055 Fuel Gauge. * @par Details * This function sends a request to access the aveCURRENT register * of the MAX17055 to read the average current redings. * * @param[in] des_data - Plataform_data struct with information about the deisgn. * * @retval aveCurr_data - current data from the AVGCURRENT register in uAmps. * @retval non-0 negative values check for errors. */ 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 < F_SUCCESS_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 conv_2_uvolts - value converted lsb to uvolts */ int MAX17055:: lsb_to_uvolts(uint16_t lsb) { int conv_2_uvolts; conv_2_uvolts = (lsb * 625) / 8; /* 78.125uV per bit */ return conv_2_uvolts; } /** * @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 implementen in the design } return res; } /** * @brief Save Learned Parameters Function for battery Fuel Gauge model. * @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. * * @param[in] FG_params Fuel Gauge Parameters based on design details. * * @retval 0 for success * @retval non-0 negative for errors */ int MAX17055::save_Params(saved_FG_params_t FG_params) { int ret, value; uint16_t data[5]; ///STEP 1. Checks if the cycel register bit 2 has changed. ret = readReg(CYCLES_REG, data[3]); if (ret < F_SUCCESS_0) return ret; else { value = data[3]; } ///STEP 2.Save the capacity parametes for the specific battery. ret = readReg(RCOMP0_REG, data[0]); if (ret < F_SUCCESS_0) return ret; else FG_params.rcomp0 = data[0]; ret = readReg(TEMPCO_REG, data[1]); if (ret < F_SUCCESS_0) return ret; else FG_params.temp_co = data[1]; ret = readReg(FULLCAPREP_REG, data[2]); if (ret < F_SUCCESS_0) return ret; else FG_params.full_cap_rep = data[2]; FG_params.cycles = data[3]; ret = readReg(FULLCAPNOM_REG, data[4]); if (ret < F_SUCCESS_0) return ret; else FG_params.full_cap_nom = data[4]; return ret; } /** * @brief Resotore Parameters Function for battery Fuel Gauge model. * @par Details * If power is lost, then the capacity information * can be easily restored with this function. * * @param[in] FG_params Struct for Fuel Gauge Parameters * @retval 0 for success * @retval non-0 negative for errors */ 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? ///STEP 1. 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);//check the type of wait ///STEP 2. Restore FullCap ret = readReg(FULLCAPNOM_REG, fullcapnom_data); if (ret < F_SUCCESS_0) return ret; ret = readReg(MIXSOC_REG, temp_data); //check if Error in sofware guide register incorrect if (ret < F_SUCCESS_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); ///STEP 3. 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); ///STEP 4. Restore Cycles register ret = write_and_verify_reg(CYCLES_REG, FG_params.cycles); if (ret < F_SUCCESS_0) return ret; return ret; } /** * @brief Function to Save Average Current to At Rate register. * @par Details * For User frendliness display of TTE, avSOC, avCAP * write the average current to At Rate registers every 10sec * when the battery is in use. * NOTE: do not use this function when the Battery is charging. * * @retval 0 for success * @retval non-0 negative for errors */ int MAX17055::avCurr_2_atRate() { int ret; uint16_t avCurr_data; ret = readReg(AVGCURRENT_REG, avCurr_data); if (ret < F_SUCCESS_0) return ret; //Write avCurrent to atRate Register ret = write_and_verify_reg(ATRATE_REG, avCurr_data); if (ret < F_SUCCESS_0) return ret; return ret; }