This class provides APIs to all of the registers of the TI BQ35100 battery gauge, as used on the u-blox C030 primary battery shield.
Dependents: example-battery-gauge-bq35100
Revision 2:4c699a813451, committed 2017-11-10
- Comitter:
- RobMeades
- Date:
- Fri Nov 10 17:07:06 2017 +0000
- Parent:
- 1:ee7cc8d75283
- Commit message:
- Functionality at equivalent level to the BQ27411 battery driver. Good enough for now.
Changed in this revision
diff -r ee7cc8d75283 -r 4c699a813451 TESTS/unit_tests/default/main.cpp --- a/TESTS/unit_tests/default/main.cpp Thu Nov 09 22:55:13 2017 +0000 +++ b/TESTS/unit_tests/default/main.cpp Fri Nov 10 17:07:06 2017 +0000 @@ -21,8 +21,9 @@ #define MAX_CURRENT_READING_MA 2000 #define MIN_CURRENT_READING_MA -2000 #define MIN_CAPACITY_READING_UAH 0 -#define MAX_CAPACITY_READING_UAH 30000000 -#define SET_DESIGN_CAPACITY_MAH 32177 +#define MAX_CAPACITY_READING_UAH 32177000 // Some randomly chosen +#define SET_DESIGN_CAPACITY_MAH 32177 // values that match + // ---------------------------------------------------------------- // PRIVATE VARIABLES @@ -199,6 +200,158 @@ TEST_ASSERT(pBatteryGauge->getRemainingPercentage(NULL)); } +// Test behaviours with gauging on +void test_gauging() { + BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100(); + uint32_t capacityUAh = MIN_CAPACITY_READING_UAH - 1; + + // Call should fail if the battery gauge has not been initialised + TEST_ASSERT_FALSE(pBatteryGauge->enableGauge()); + TEST_ASSERT_FALSE(pBatteryGauge->disableGauge()); + TEST_ASSERT_FALSE(pBatteryGauge->isGaugeEnabled()); + + // Normal case, gauging should be off to begin with + TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN)); + TEST_ASSERT_FALSE(pBatteryGauge->isGaugeEnabled()); + + // Enable gauge, without non-volatile storage + + TEST_ASSERT(pBatteryGauge->enableGauge()); + TEST_ASSERT(pBatteryGauge->getUsedCapacity(&capacityUAh)); + printf ("Used capacity %.3f mAh.\n", ((float) capacityUAh) / 1000); + // Range check + TEST_ASSERT((capacityUAh >= MIN_CAPACITY_READING_UAH) && (capacityUAh <= MAX_CAPACITY_READING_UAH)); + TEST_ASSERT(pBatteryGauge->isGaugeEnabled()); + TEST_ASSERT(pBatteryGauge->disableGauge()); + TEST_ASSERT_FALSE(pBatteryGauge->isGaugeEnabled()); + + // Enable gauge, with non-volatile storage + TEST_ASSERT(pBatteryGauge->enableGauge(true)); + TEST_ASSERT(pBatteryGauge->getUsedCapacity(&capacityUAh)); + printf ("Used capacity %.3f mAh.\n", ((float) capacityUAh) / 1000); + // Range check + // TODO: any way to check that the non-volatileness has worked? + TEST_ASSERT((capacityUAh >= MIN_CAPACITY_READING_UAH) && (capacityUAh <= MAX_CAPACITY_READING_UAH)); + TEST_ASSERT(pBatteryGauge->isGaugeEnabled()); + TEST_ASSERT(pBatteryGauge->disableGauge()); + TEST_ASSERT_FALSE(pBatteryGauge->isGaugeEnabled()); +} + +// Test the new battery call +void test_new_battery() { + BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100(); + uint32_t originalDesignCapacity; + uint32_t newDesignCapacity = SET_DESIGN_CAPACITY_MAH; + uint32_t readDesignCapacity = 0; + + // Call should fail if the battery gauge has not been initialised + TEST_ASSERT_FALSE(pBatteryGauge->newBattery(1000)); + + // Normal case + TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN)); + // Get the original design capacity so that we can set it back + // at the end + TEST_ASSERT(pBatteryGauge->getDesignCapacity(&originalDesignCapacity)); + printf ("Design capacity was originally %d mAh.\n", (unsigned int) originalDesignCapacity); + + // Avoid the old and new values being the same + if (originalDesignCapacity == newDesignCapacity) { + newDesignCapacity--; + } + + // Now add the new battery + TEST_ASSERT(pBatteryGauge->newBattery(newDesignCapacity)); + printf ("New battery added with design capacity %d mAh.\n", (unsigned int) newDesignCapacity); + + // Read the value back and check that it's been set + TEST_ASSERT(pBatteryGauge->getDesignCapacity(&readDesignCapacity)); + printf ("Design capacity was read as %d mAh.\n", (unsigned int) readDesignCapacity); + TEST_ASSERT(readDesignCapacity = newDesignCapacity) + + // Put the original value back + TEST_ASSERT(pBatteryGauge->setDesignCapacity(originalDesignCapacity)); + printf ("Design capacity returned to %d mAh.\n", (unsigned int) originalDesignCapacity); +} + +// Test get/set config +void test_advanced_get_set_config() { + BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100(); + int32_t address = 0x4036; // Manufacturer Info Block A01, a 1 byte field + char originalValue; + char oldValue; + char newValue; + + // Calls should fail if the battery gauge has not been initialised + TEST_ASSERT_FALSE(pBatteryGauge->advancedGetConfig(address, &originalValue, sizeof(originalValue))); + TEST_ASSERT_FALSE(pBatteryGauge->advancedSetConfig(address, &newValue, sizeof(newValue))); + + // Initialise the battery gauge + TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN)); + + // Normal case + TEST_ASSERT(pBatteryGauge->advancedGetConfig(address, &originalValue, sizeof(originalValue))); + // Invert the result and write it back + oldValue = originalValue; + printf ("Original value was 0x%02x.\n", oldValue); + newValue = ~oldValue; + printf ("Setting it to 0x%02x.\n", newValue); + TEST_ASSERT(pBatteryGauge->advancedSetConfig(address, &newValue, sizeof(newValue))); + // Read it and check that it has changed + TEST_ASSERT(pBatteryGauge->advancedGetConfig(address, &oldValue, sizeof(oldValue))); + printf ("Read back 0x%02x.\n", oldValue); + TEST_ASSERT_EQUAL_UINT8(newValue, oldValue); + + // Put the original value back again + TEST_ASSERT(pBatteryGauge->advancedSetConfig(address, &originalValue, sizeof(originalValue))); +} + +// Send a control word +void test_advanced_control() { + BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100(); + uint16_t controlWord = 0x0003; // get HW version + uint16_t response = 0; + + // Call should fail if the battery gauge has not been initialised + TEST_ASSERT_FALSE(pBatteryGauge->advancedSendControlWord(controlWord, &response)); + + // Initialise the battery gauge + TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN)); + + // Normal case + TEST_ASSERT(pBatteryGauge->advancedSendControlWord(controlWord, &response)); + // FW version must be 0x00a8 + TEST_ASSERT_EQUAL_UINT16(0x00a8, response); + + // The parameter is allowed to be null + TEST_ASSERT(pBatteryGauge->advancedSendControlWord(controlWord, NULL)); +} + +// Read using a standard command +void test_advanced_get() { + BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100(); + uint8_t address = 0x06; // Temperature + uint16_t value = 0; + int32_t temperatureC = -1; + + // Call should fail if the battery gauge has not been initialised + TEST_ASSERT_FALSE(pBatteryGauge->advancedGet(address, &value)); + + // Initialise the battery gauge + TEST_ASSERT(pBatteryGauge->init(gpI2C, GAUGE_ENABLE_PIN)); + + // Normal case + TEST_ASSERT(pBatteryGauge->advancedGet(address, &value)); + // Get the temperature via the standard API command + TEST_ASSERT(pBatteryGauge->getTemperature(&temperatureC)); + // Convert the value returned into a temperature reading and compare + // it with the real answer, allowing a 1 degree tolerance in case + // it has changed between readings. + TEST_ASSERT_INT32_WITHIN (1, temperatureC, ((int32_t) value / 10) - 273); + + // The parameter is allowed to be null + TEST_ASSERT(pBatteryGauge->advancedGet(address, NULL)); +} + // Test that the security mode of the chip can be changed void test_advanced_security_mode() { BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100(); @@ -263,7 +416,7 @@ TEST_ASSERT(pBatteryGauge->advancedSetSecurityMode(securityMode)); } -// Reset the BQ35100 battery gauge chip at the outset +// Reset the BQ35100 battery gauge chip void test_advanced_reset() { BatteryGaugeBq35100 * pBatteryGauge = new BatteryGaugeBq35100(); BatteryGaugeBq35100::SecurityMode securityMode; @@ -299,7 +452,7 @@ utest::v1::status_t test_setup(const size_t number_of_cases) { // Setup Greentea, timeout is long enough to run these tests with // DEBUG_BQ35100 defined - GREENTEA_SETUP(480, "default_auto"); + GREENTEA_SETUP(250, "default_auto"); return verbose_test_setup_handler(number_of_cases); } @@ -313,6 +466,11 @@ Case("Design capacity read/set", test_design_capacity), Case("Remaining capacity read", test_remaining_capacity), Case("Remaining precentage read", test_remaining_percentage), + Case("Gauging", test_gauging), + Case("New battery", test_new_battery), + Case("Advanced get/set config", test_advanced_get_set_config), + Case("Advanced control", test_advanced_control), + Case("Advanced get", test_advanced_get), Case("Advanced security mode", test_advanced_security_mode), Case("Advanced reset", test_advanced_reset) };
diff -r ee7cc8d75283 -r 4c699a813451 battery_gauge_bq35100.h --- a/battery_gauge_bq35100.h Thu Nov 09 22:55:13 2017 +0000 +++ b/battery_gauge_bq35100.h Fri Nov 10 17:07:06 2017 +0000 @@ -33,7 +33,7 @@ * NOTE: the battery shield board on the C030 platform will not work * at the default I2C clock frequency. */ -#define I2C_CLOCK_FREQUENCY 250 +#define I2C_CLOCK_FREQUENCY 100 /** Settling time after gaugeEnable is set high */ #define GAUGE_ENABLE_SETTLING_TIME_MS 10 @@ -66,70 +66,78 @@ ~BatteryGaugeBq35100(void); /** Initialise the BQ35100 chip. Once initialised - * the chip is put into its lowest power state. Any API call - * will awaken the chip from this state and then return it once - * more to the lowest possible power state. - * @param pI2c a pointer to the I2C instance to use. - * @param gaugeEnable the gauge enable pin (will be set high to enable the chip). - * @param address 7-bit I2C address of the battery gauge chip. - * @param sealCodes the two 16 bit seal codes (step 1 in the higher word, step 2 in the - * lower word, NOT byte reversed) that will unseal the device if it is sealed. - * @return true if successful, otherwise false. - */ - bool init(I2C * pI2c, PinName gaugeEnable, uint8_t address = BATTERY_GAUGE_BQ35100_ADDRESS, + * the chip is put into its lowest power state. Any API call + * will awaken the chip from this state and then return it once + * more to the lowest possible power state. + * @param pI2c a pointer to the I2C instance to use. + * @param gaugeEnable the gauge enable pin (will be set high to enable the chip). + * @param address 7-bit I2C address of the battery gauge chip. + * @param sealCodes the two 16 bit seal codes (step 1 in the higher word, step 2 in the + * lower word, NOT byte reversed) that will unseal the device if it is sealed. + * @return true if successful, otherwise false. + */ + bool init(I2C * pI2c, PinName gaugeEnable = NC, uint8_t address = BATTERY_GAUGE_BQ35100_ADDRESS, uint32_t sealCodes = SEAL_CODES_DEFAULT); /** Switch on the battery gauge. Battery gauging must be switched on - * for the battery capacity and percentage readings to be valid. The - * chip will consume more when battery gauging is switched on. - * @return true if successful, otherwise false. - */ - bool enableGauge(void); + * for the battery capacity and percentage readings to be valid. The + * chip will consume more when battery gauging is switched on. + * @param nonVolatile if set to true then the chip will add the + * accumulated capacity values to those taken + * previously in non-volatile memory when + * disableGauge() is called. + * @return true if successful, otherwise false. + */ + bool enableGauge(bool nonVolatile = false); - /** Switch off the battery gauge. - * @return true if successful, otherwise false. - */ + /** Switch off the battery gauge. If gauging to non-volatile + * memory was switched on, the accumulated capacity values + * will be stored in non-volatile memory. Please see the warning + * in section 5.1.1 of the TI BQ35100 technical reference manual + * concerning how frequently this should be done. + * @return true if successful, otherwise false. + */ bool disableGauge(void); /** Check whether battery gauging is enabled or not. - * @return true if battery gauging is enabled, otherwise false. - */ + * @return true if battery gauging is enabled, otherwise false. + */ bool isGaugeEnabled(void); /** Set the designed capacity of the cell. - * @param capacityMAh the capacity. - * @return true if successful, otherwise false. - */ + * @param capacityMAh the capacity. + * @return true if successful, otherwise false. + */ bool setDesignCapacity(uint32_t capacityMAh); /** Get the designed capacity of the cell. - * @param pCapacityMAh a palce to put the capacity. - * @return true if successful, otherwise false. - */ + * @param pCapacityMAh a place to put the capacity. + * @return true if successful, otherwise false. + */ bool getDesignCapacity(uint32_t *pCapacityMAh); /** Read the temperature of the BQ35100 chip. - * @param pTemperatureC place to put the temperature reading. - * @return true if successful, otherwise false. - */ + * @param pTemperatureC place to put the temperature reading. + * @return true if successful, otherwise false. + */ bool getTemperature(int32_t *pTemperatureC); /** Read the voltage of the battery. - * @param pVoltageMV place to put the voltage reading. - * @return true if successful, otherwise false. - */ + * @param pVoltageMV place to put the voltage reading. + * @return true if successful, otherwise false. + */ bool getVoltage(int32_t *pVoltageMV); /** Read the current flowing from the battery. - * @param pCurrentMA place to put the current reading. - * @return true if successful, otherwise false. - */ + * @param pCurrentMA place to put the current reading. + * @return true if successful, otherwise false. + */ bool getCurrent(int32_t *pCurrentMA); /** Read the battery capacity used in uAh (NOT mAh). - * @param pCapacityUAh place to put the capacity reading. - * @return true if successful, otherwise false. - */ + * @param pCapacityUAh place to put the capacity reading. + * @return true if successful, otherwise false. + */ bool getUsedCapacity(uint32_t *pCapacityUAh); /** Get the remaining battery capacity in uAh (NOT mAh). @@ -141,33 +149,89 @@ bool getRemainingCapacity(uint32_t *pCapacityUAh); /** Get the remaining battery capacity in percent. - * NOTE: this relies on the Design Capacity of the battery - * having been set correctly. - * @param pBatteryPercentage place to put the reading. - * @return true if successful, otherwise false. - */ + * NOTE: this relies on the Design Capacity of the battery + * having been set correctly. + * @param pBatteryPercentage place to put the reading. + * @return true if successful, otherwise false. + */ bool getRemainingPercentage(int32_t *pBatteryPercentage); + /** Indicate that a new battery has been inserted. + * @param capacityMAh the capacity of the battery. + * @return true if successful, otherwise false. + */ + bool newBattery(uint32_t capacityMAh); + + /** An advanced function to read configuration data from the BQ35100 chip memory. + * Please refer to the TI BQ35100 technical reference manual for details of the + * address space.This function will unseal the device (using the seal codes + * passed into init()) in order to perform the read from data flash and will + * restore the previous security state afterwards. + * @param address the address of the data within the class. + * @param pData a place to put the read data. + * @param length the size of the place to put the data block. + * @return true if successful, otherwise false. + */ + bool advancedGetConfig(int32_t address, char * pData, int32_t length); + + /** An advanced function to write configuration data to the BQ35100 chip memory. + * Please refer to the TI BQ35100 technical reference manual for details of the + * address space. This function will unseal the device (using the seal codes + * passed into init()) in order to perform the write to data flash and will + * restore the previous security state afterwards. However, if the write + * operation requires full access (e.g. to change the seal codes) then + * the security mode of the device must be changed (through a call to + * advancedGetSecurityMode()) first. If this function is used to change the seal + * or full access codes for the chip then init() should be called once more to + * update the codes used by this driver. + * @param address the address to write to. + * @param pData a pointer to the data to be written. + * @param length the size of the data to be written. + * @return true if successful, otherwise false. + */ + bool advancedSetConfig(int32_t address, const char * pData, int32_t length); + + /** Send a control word (see section 11.1 of the BQ35100 technical reference manual). + * @param controlWord the control word to send. + * @param pDataReturned a place to put the word of data that could be returned, + * depending on which control word is used (may be NULL). + * @return true if successful, otherwise false. + */ + bool advancedSendControlWord(uint16_t controlWord, uint16_t *pDataReturned); + + /** Read two bytes starting at a given address on the chip. + * See sections 11.3 to 11.18 of the BQ35100 technical reference manual for the list + * of addresses. + * NOTE: this is not a read from data flash, for that you need the advancedGetConfig() + * method. + * @param address the start address to read from. For instance, for temperature this is 0x06. + * @param pDataReturned a place to put the word of data returned. + * @return true if successful, otherwise false. + */ + bool advancedGet(uint8_t address, uint16_t *pDataReturned); + /** Advanced function to get the security mode of the chip. - * @return the security mode. - */ + * @return the security mode. + */ SecurityMode advancedGetSecurityMode(void); - /** Set the security mode of the chip. SECURITY_MODE_UNSEALED mode allows - * writes to the chip and read access to certain proteced areas while - * SECURITY_MODE_FULL_ACCESS, in addition, allows the security codes - * to be updated. All of the functions in this class are able to work with a - * SEALED chip, provided the correct codes are provided to the init() function. - * NOTE: it is only possible to move to SECURITY_MODE_FULL_ACCESS from - * SECURITY_MODE_UNSEALED. - * @return true if successful, otherwise false. - */ + /** Advanced function to set the security mode of the chip. + * SECURITY_MODE_UNSEALED mode allows writes to the chip and read access to + * certain proteced areas while SECURITY_MODE_FULL_ACCESS, in addition, + * allows the security codes to be updated. All of the functions in this + * class are able to work with a SEALED chip, provided the correct codes + * are provided to the init() function. + * NOTE: it is only possible to move to SECURITY_MODE_FULL_ACCESS from + * SECURITY_MODE_UNSEALED. + * @return true if successful, otherwise false. + */ bool advancedSetSecurityMode(SecurityMode securityMode); - /** Do a hard reset of the chip, reinitialising RAM data to defaults from ROM. - * Note: the security mode of the chip is unaffected. - * @return true if successful, otherwise false. - */ + /** Advanced function to perform a hard reset of the chip, reinitialising RAM + * data to defaults from ROM. + * Note: the security mode of the chip is unaffected. + * @return true if successful, otherwise false. + */ bool advancedReset(void); protected: @@ -183,8 +247,6 @@ uint32_t gFullAccessCodes; /** Flag to indicate device is ready. */ bool gReady; - /** Flag to indicate that monitor mode is active. */ - bool gGaugeOn; /** Read two bytes starting at a given address. * Note: gpI2c should be locked before this is called. @@ -208,7 +270,7 @@ * @param length the size of the place to put the data block. * @return true if successful, otherwise false. */ - bool readExtendedData(int32_t address, int32_t length, char * pData); + bool readExtendedData(int32_t address, char * pData, int32_t length); /** Write an extended data block. * Note: gpI2c should be locked before this is called. @@ -217,7 +279,7 @@ * @param length the size of the data to be written. * @return true if successful, otherwise false. */ - bool writeExtendedData(int32_t address, int32_t length, const char * pData); + bool writeExtendedData(int32_t address, const char * pData, int32_t length); /** Get the security mode of the chip. * Note: gpI2c should be locked before this is called.
diff -r ee7cc8d75283 -r 4c699a813451 bq35100.cpp --- a/bq35100.cpp Thu Nov 09 22:55:13 2017 +0000 +++ b/bq35100.cpp Fri Nov 10 17:07:06 2017 +0000 @@ -20,8 +20,8 @@ */ /** Define these to print debug information. */ -#define DEBUG_BQ35100 -#define DEBUG_BQ35100_BLOCK_DATA +//#define DEBUG_BQ35100 +//#define DEBUG_BQ35100_BLOCK_DATA #include <mbed.h> #include <battery_gauge_bq35100.h> @@ -34,33 +34,20 @@ // COMPILE-TIME MACROS // ---------------------------------------------------------------- -/** How many loops to wait for a configuration update to be permitted. - * Experience suggests that the limit really does need to be this large. */ -#define CONFIG_UPDATE_LOOPS 200 - -/** How long to delay when running around the config update loop. */ -#define CONFIG_UPDATE_LOOP_DELAY_MS 100 - -/** How long to wait for each loop while device is initialising. */ -#define INIT_LOOP_WAIT_MS 100 - -/** The maximum number of init loops to wait for. */ -#define INIT_LOOP_COUNT 10 - /** How long to wait for a security mode change to succeed. */ #define SET_SECURITY_MODE_RETRY_SECONDS 5 -// ---------------------------------------------------------------- -// PRIVATE VARIABLES -// ---------------------------------------------------------------- - +/** How long to wait for accumulated capacity data to be written + * to data flash when gauging is disabled */ +#define GAUGE_COMPLETE_WAIT_MS 10000 + // ---------------------------------------------------------------- // GENERIC PRIVATE FUNCTIONS // ---------------------------------------------------------------- // Read two bytes from an address. // Note: gpI2c should be locked before this is called. -bool BatteryGaugeBq35100::getTwoBytes (uint8_t registerAddress, uint16_t *pBytes) +bool BatteryGaugeBq35100::getTwoBytes(uint8_t registerAddress, uint16_t *pBytes) { bool success = false; char data[3]; @@ -84,7 +71,7 @@ } // Compute the checksum over an address plus the block of data. -uint8_t BatteryGaugeBq35100::computeChecksum (const char * pData, int32_t length) +uint8_t BatteryGaugeBq35100::computeChecksum(const char * pData, int32_t length) { uint8_t checkSum = 0; uint8_t x = 0; @@ -125,7 +112,7 @@ // Read data of a given length from a given address. // Note: gpI2c should be locked before this is called. -bool BatteryGaugeBq35100::readExtendedData (int32_t address, int32_t length, char * pData) +bool BatteryGaugeBq35100::readExtendedData(int32_t address, char * pData, int32_t length) { int32_t lengthRead; bool success = false; @@ -212,11 +199,12 @@ // Write data of a given length to a given address. // Note: gpI2c should be locked before this is called. -bool BatteryGaugeBq35100::writeExtendedData(int32_t address, int32_t length, const char * pData) +bool BatteryGaugeBq35100::writeExtendedData(int32_t address, const char * pData, int32_t length) { bool success = false; SecurityMode securityMode = getSecurityMode(); char data[3 + 32]; + uint16_t controlStatus; if ((gpI2c != NULL) && (length <= 32) && (address >= 0x4000) && (address < 0x4400) && (pData != NULL)) { #ifdef DEBUG_BQ35100 @@ -248,8 +236,14 @@ data[0] = 0x61; if (gpI2c->write(gAddress, &(data[0]), 2) == 0) { - // TODO check for successful write - success = true; + // Read the control status register to see if a bad + // flash write has been detected (bit 15) + data[0] = 0; + if ((gpI2c->write(gAddress, &(data[0]), 1) == 0) && + getTwoBytes(0, &controlStatus) && + (((controlStatus >> 15) & 0x01) != 0x01)) { + success = true; + } #ifdef DEBUG_BQ35100 printf("BatteryGaugeBq35100 (I2C 0x%02x): write successful.\n", gAddress >> 1); #endif @@ -296,22 +290,20 @@ char data[1]; uint16_t controlStatus; - data[0] = 0; // Set address to first register for Control - - // Send a control command to read the control status register - if (gpI2c->write(gAddress, &(data[0]), 1) == 0) { - if (getTwoBytes(0, &controlStatus)) { - // Bits 13 and 14 of the high byte represent the security status, - // 01 = full access - // 10 = unsealed access - // 11 = sealed access - securityMode = (SecurityMode) ((controlStatus >> 13) & 0x03); + // Read the control status register + data[0] = 0; + if ((gpI2c->write(gAddress, &(data[0]), 1) == 0) && + getTwoBytes(0, &controlStatus)) { + // Bits 13 and 14 of the high byte represent the security status, + // 01 = full access + // 10 = unsealed access + // 11 = sealed access + securityMode = (SecurityMode) ((controlStatus >> 13) & 0x03); #ifdef DEBUG_BQ35100 - printf("BatteryGaugeBq35100 (I2C 0x%02x): security mode is 0x%02x (control status 0x%04x).\r\n", gAddress >> 1, securityMode, controlStatus); + printf("BatteryGaugeBq35100 (I2C 0x%02x): security mode is 0x%02x (control status 0x%04x).\r\n", gAddress >> 1, securityMode, controlStatus); #endif - } } - + return securityMode; } @@ -329,7 +321,7 @@ // to change security mode if a previous security mode change // happend only a few seconds ago, hence the retry here for (int32_t x = 0; (x < SET_SECURITY_MODE_RETRY_SECONDS) && !success; x++) { - data[0] = 0; // Set address to first register for Control) + data[0] = 0x3e; // Set address to ManufacturerAccessControl switch (securityMode) { case SECURITY_MODE_SEALED: // Just seal the chip @@ -383,36 +375,17 @@ } // Make sure that the device is awake and has taken a reading. -// Note: the function does its own locking of gpI2C so that it isn't -// locked for the entire time we wait for ADC readings to complete. bool BatteryGaugeBq35100::makeAdcReading(void) { bool success = false; - uint16_t controlStatus; - char data[1]; - // Wait for INITCOMP to be set - data[0] = 0; // Set address to first register for Control - - gpI2c->lock(); - // Raise the pin - *pGaugeEnable = 1; - wait_ms(GAUGE_ENABLE_SETTLING_TIME_MS); - for (int x = 0; !success && (x < INIT_LOOP_COUNT); x++) { - if (gpI2c->write(gAddress, &(data[0]), 1) == 0) { - if (getTwoBytes(0, &controlStatus)) { -#ifdef DEBUG_BQ35100 - printf("BatteryGaugeBq35100 (I2C 0x%02x): read 0x%04x as CONTROL_STATUS.\n", gAddress >> 1, controlStatus); -#endif - // Bit 7 is INITCOMP - if (((controlStatus >> 7) & 0x01) == 0x01) { - success = true; - } - } - wait_ms (INIT_LOOP_WAIT_MS); + if (isGaugeEnabled()) { + success = true; + } else { + if (enableGauge() && disableGauge()) { + success = true; } } - gpI2c->unlock(); return success; } @@ -429,16 +402,11 @@ gReady = false; gSealCodes = 0; gFullAccessCodes = 0; - gGaugeOn = false; } // Destructor. BatteryGaugeBq35100::~BatteryGaugeBq35100(void) { - if (pGaugeEnable) { - *pGaugeEnable = 0; - delete pGaugeEnable; - } } // Initialise ourselves. @@ -451,8 +419,10 @@ gAddress = address << 1; gSealCodes = sealCodes; - pGaugeEnable = new DigitalOut(gaugeEnable, 1); - wait_ms(GAUGE_ENABLE_SETTLING_TIME_MS); + if (gaugeEnable != NC) { + pGaugeEnable = new DigitalOut(gaugeEnable, 1); + wait_ms(GAUGE_ENABLE_SETTLING_TIME_MS); + } if (gpI2c != NULL) { gpI2c->lock(); @@ -467,7 +437,7 @@ getTwoBytes(0x40, &answer)) { // Read from MACData address if (answer == 0x00a8) { // Read the full access codes, in case we need them - if (readExtendedData(0x41d0, sizeof (data), &(data[0]))) { + if (readExtendedData(0x41d0, &(data[0]), sizeof (data))) { // The four bytes are the full access codes gFullAccessCodes = ((uint32_t) data[0] << 24) + ((uint32_t) data[1] << 16) + ((uint32_t) data[2] << 8) + data[3]; #ifdef DEBUG_BQ35100 @@ -483,10 +453,7 @@ #endif } - if (!gGaugeOn) { - *pGaugeEnable = 0; - } - + disableGauge(); gpI2c->unlock(); } @@ -502,14 +469,65 @@ } // Switch on the battery capacity monitor. -bool BatteryGaugeBq35100::enableGauge(void) +bool BatteryGaugeBq35100::enableGauge(bool nonVolatile) { + bool accumulatedCapacityOn = false; bool success = false; - + char data[3]; + char opConfig; + uint16_t controlStatus; + if (gReady) { - *pGaugeEnable = 1; - wait_ms(GAUGE_ENABLE_SETTLING_TIME_MS); - success = true; + if (nonVolatile) { + // Read the OpConfig register which is at address 0x41b1 + if (readExtendedData(0x41b1, &opConfig, sizeof (opConfig))) { +#ifdef DEBUG_BQ35100 + printf("BatteryGaugeBq35100 (I2C 0x%02x): OpConfig is 0x%02x.\n", gAddress >> 1, opConfig); +#endif + // AccumulatedCapacity is achieved by setting GMSEL 1:0 (in bits 0 and 1) to 00 + if ((opConfig & 0x03) != 0) { + opConfig &= ~0x03; + // Write the new value back + accumulatedCapacityOn = writeExtendedData(0x41b1, &opConfig, sizeof (opConfig)); +#ifdef DEBUG_BQ35100 + if (accumulatedCapacityOn) { + printf("BatteryGaugeBq35100 (I2C 0x%02x): AccumulatedCapacity enabled, OpConfig becomes 0x%02x.\r\n", gAddress >> 1, opConfig); + } +#endif + } else { + accumulatedCapacityOn = true; + } + } + } + + if (accumulatedCapacityOn || !nonVolatile) { + if (pGaugeEnable) { + *pGaugeEnable = 1; + wait_ms(GAUGE_ENABLE_SETTLING_TIME_MS); + } + gpI2c->lock(); + data[0] = 0x3e; // Set address to ManufacturerAccessControl + data[1] = 0x11; // First byte of GAUGE_START sub-command (0x11) + data[2] = 0x00; // Second byte of GAUGE_START sub-command (0x00) (register address will auto-increment) + if (gpI2c->write(gAddress, &(data[0]), 3) == 0) { + // Wait for GA bit of CONTROL_STATUS (bit 0) to become 1 + data[0] = 0; + if (gpI2c->write(gAddress, &(data[0]), 1) == 0) { + for (int x = 0; (x < GAUGE_COMPLETE_WAIT_MS / 10) && !success; x++) { + if (getTwoBytes(0, &controlStatus)) { + if ((controlStatus & 0x01) == 0x01) { + success = true; + } else { + wait_ms(10); + } + } else { + wait_ms(10); + } + } + } + } + gpI2c->unlock(); + } } return success; @@ -518,11 +536,68 @@ // Switch off the battery capacity monitor. bool BatteryGaugeBq35100::disableGauge(void) { + bool accumulatedCapacityOn = false; bool success = false; + char data[3]; + char opConfig; + uint16_t controlStatus; if (gReady) { - *pGaugeEnable = 0; - success = true; + if (isGaugeEnabled()) { + // Read the OpConfig register which is at address 0x41b1 + if (readExtendedData(0x41b1, &opConfig, sizeof (opConfig))) { +#ifdef DEBUG_BQ35100 + printf("BatteryGaugeBq35100 (I2C 0x%02x): OpConfig is 0x%02x.\n", gAddress >> 1, opConfig); +#endif + // Check if AccumulatedCapacity is on + if ((opConfig & 0x03) == 0) { + accumulatedCapacityOn = true; + } + + // Send GAUGE_STOP + gpI2c->lock(); + data[0] = 0x3e; // Set address to ManufacturerAccessControl + data[1] = 0x12; // First byte of GAUGE_STOP sub-command (0x12) + data[2] = 0x00; // Second byte of GAUGE_STOP sub-command (0x00) (register address will auto-increment) + if (gpI2c->write(gAddress, &(data[0]), 3) == 0) { + // Wait for GA bit of CONTROL_STATUS (bit 0) to become 0 and, + // if AccumulatedCapacity was on, wait for G_DONE + // (bit 6 in CONTROL_STATUS) to be set + data[0] = 0; + if (gpI2c->write(gAddress, &(data[0]), 1) == 0) { + for (int x = 0; (x < GAUGE_COMPLETE_WAIT_MS / 10) && !success; x++) { + if (getTwoBytes(0, &controlStatus)) { + if ((controlStatus & 0x01) == 0) { + if (accumulatedCapacityOn) { + if (((controlStatus >> 6) & 0x01) == 0x01) { + success = true; +#ifdef DEBUG_BQ35100 + printf("BatteryGaugeBq35100 (I2C 0x%02x): AccumulatedCapacity data written to non-volatile memory.\r\n", gAddress >> 1); +#endif + } else { + wait_ms(10); + } + } else { + success = true; + } + } else { + wait_ms(10); + } + } else { + wait_ms(10); + } + } + } + } + gpI2c->unlock(); + } + } else { + success = true; + } + + if (pGaugeEnable) { + *pGaugeEnable = 0; + } } return success; @@ -531,13 +606,25 @@ // Determine whether battery gauging is enabled. bool BatteryGaugeBq35100::isGaugeEnabled(void) { - bool isEnabled = false; - + bool gaugeEnabled = false; + char data[1]; + uint16_t controlStatus; + if (gReady) { - isEnabled = true; + // Check the GA bit (bit 0) in CONTROL_STATUS + data[0] = 0; + if ((gpI2c->write(gAddress, &(data[0]), 1) == 0) && + getTwoBytes(0, &controlStatus)) { +#ifdef DEBUG_BQ35100 + printf("BatteryGaugeBq35100 (I2C 0x%02x): read 0x%04x as CONTROL_STATUS.\n", gAddress >> 1, controlStatus); +#endif + if ((controlStatus & 0x01) == 0x01) { + gaugeEnabled = true; + } + } } - return isEnabled; + return gaugeEnabled; } // Set the designed capacity of the cell. @@ -553,7 +640,7 @@ data[1] = capacityMAh; // Lower byte of design capacity // Write to the "Cell Design Capacity mAh" address in data flash - if (writeExtendedData(0x41fe, sizeof(data), &(data[0]))) { + if (writeExtendedData(0x41fe, &(data[0]), sizeof(data))) { success = true; #ifdef DEBUG_BQ35100 @@ -603,7 +690,7 @@ int32_t temperatureC = 0; uint16_t data; - if (gReady && (gGaugeOn || makeAdcReading())) { + if (gReady && makeAdcReading()) { gpI2c->lock(); // Read from the temperature register address if (getTwoBytes (0x06, &data)) { @@ -621,10 +708,6 @@ #endif } - if (!gGaugeOn) { - *pGaugeEnable = 0; - } - gpI2c->unlock(); } @@ -637,7 +720,7 @@ bool success = false; uint16_t data = 0; - if (gReady && (gGaugeOn || makeAdcReading())) { + if (gReady && makeAdcReading()) { gpI2c->lock(); // Read from the voltage register address if (getTwoBytes (0x08, &data)) { @@ -653,10 +736,6 @@ #endif } - if (!gGaugeOn) { - *pGaugeEnable = 0; - } - gpI2c->unlock(); } @@ -668,9 +747,9 @@ { bool success = false; int32_t currentMA = 0; - uint16_t data; + uint16_t data = 0; - if (gReady && (gGaugeOn || makeAdcReading())) { + if (gReady && makeAdcReading()) { gpI2c->lock(); // Read from the average current register address if (getTwoBytes (0x0c, &data)) { @@ -685,10 +764,6 @@ #endif } - if (!gGaugeOn) { - *pGaugeEnable = 0; - } - gpI2c->unlock(); } @@ -702,7 +777,7 @@ char bytes[5]; uint32_t data; - if (gReady && (gGaugeOn || makeAdcReading())) { + if (gReady && makeAdcReading()) { gpI2c->lock(); // Read four bytes from the AccummulatedCapacity register address @@ -728,10 +803,6 @@ #endif } - if (!gGaugeOn) { - *pGaugeEnable = 0; - } - gpI2c->unlock(); } @@ -779,8 +850,9 @@ uint32_t usedCapacityUAh; int32_t batteryPercentage; - // First, get the designed capacity + // First, get the designed capacity (which is actually in mAh) if (getDesignCapacity(&designCapacityUAh)) { + // Convert to uAh designCapacityUAh *= 1000; // Then get the used capacity if (getUsedCapacity(&usedCapacityUAh)) { @@ -805,6 +877,129 @@ return success; } +// Indicate that a new battery has been inserted. +bool BatteryGaugeBq35100::newBattery(uint32_t capacityMAh) +{ + bool success = false; + char data[3]; + + if (gReady) { + if (setDesignCapacity(capacityMAh)) { + gpI2c->lock(); + // Send a control command to indicate NEW_BATTERY + data[0] = 0x3e; // Set address to ManufacturerAccessControl + data[1] = 0x13; // First byte of NEW_BATTERY sub-command (0x13) + data[2] = 0xA6; // Second byte of NEW_BATTERY sub-command (0xA6) (register address will auto-increment) + if (gpI2c->write(gAddress, &(data[0]), 3) == 0) { + success = true; +#ifdef DEBUG_BQ35100 + printf("BatteryGaugeBq35100 (I2C 0x%02x): new battery set.\n", gAddress >> 1); +#endif + } + + gpI2c->unlock(); + } + } + + return success; +} + +// Read configuration data. +bool BatteryGaugeBq35100::advancedGetConfig(int32_t address, char * pData, int32_t length) +{ + bool success = false; + + if (gReady) { + gpI2c->lock(); + + success = readExtendedData(address, pData, length); + + gpI2c->unlock(); + } + + return success; +} + +// Write configuration data. +bool BatteryGaugeBq35100::advancedSetConfig(int32_t address, const char * pData, int32_t length) +{ + bool success = false; + + if (gReady) { + gpI2c->lock(); + + success = writeExtendedData(address, pData, length); + + gpI2c->unlock(); + } + + return success; +} + +// Send a control word. +bool BatteryGaugeBq35100::advancedSendControlWord(uint16_t controlWord, uint16_t *pDataReturned) +{ + bool success = false; + char data[3]; + + if (gReady) { + gpI2c->lock(); + + // Send the control command + data[0] = 0x3e; // Set address to ManufacturerAccessControl + data[1] = (char) controlWord; // First byte of controlWord + data[2] = (char) (controlWord >> 8); // Second byte of controlWord + if (gpI2c->write(gAddress, &(data[0]), 3) == 0) { + // Read the two bytes returned if requested + if (pDataReturned != NULL) { + if (getTwoBytes(0x40, pDataReturned)) { // Read from MACData + success = true; +#ifdef DEBUG_BQ35100 + printf("BatteryGaugeBq35100 (I2C 0x%02x): sent control word 0x%04x, read back 0x%04x.\n", gAddress >> 1, controlWord, *pDataReturned); +#endif + } + } else { + success = true; +#ifdef DEBUG_BQ35100 + printf("BatteryGaugeBq35100 (I2C 0x%02x): sent control word 0x%04x.\n", gAddress >> 1, controlWord); +#endif + } + } + + gpI2c->unlock(); + } + + return success; +} + +// Read two bytes starting at a given address on the chip. +bool BatteryGaugeBq35100::advancedGet(uint8_t address, uint16_t *pDataReturned) +{ + bool success = false; + uint16_t value = 0; + + if (gReady) { + // Make sure there's a recent reading, as most + // of these commands involve the chip having done one + if (makeAdcReading()) { + gpI2c->lock(); + // Read the data + if (getTwoBytes(address, &value)) { + success = true; +#ifdef DEBUG_BQ35100 + printf("BatteryGaugeBq35100 (I2C 0x%02x): read 0x%04x from addresses 0x%02x and 0x%02x.\n", gAddress >> 1, value, address, address + 1); +#endif + if (pDataReturned != NULL) { + *pDataReturned = value; + } + } + gpI2c->unlock(); + } + } + + return success; +} + // Get the security mode of the chip. BatteryGaugeBq35100::SecurityMode BatteryGaugeBq35100::advancedGetSecurityMode(void) { @@ -813,17 +1008,8 @@ if (gReady) { gpI2c->lock(); - if (!gGaugeOn) { - *pGaugeEnable = 1; - wait_ms(GAUGE_ENABLE_SETTLING_TIME_MS); - } + securityMode = getSecurityMode(); - securityMode = getSecurityMode(); - - if (!gGaugeOn) { - *pGaugeEnable = 0; - } - gpI2c->unlock(); } @@ -838,17 +1024,8 @@ if (gReady) { gpI2c->lock(); - if (!gGaugeOn) { - *pGaugeEnable = 1; - wait_ms(GAUGE_ENABLE_SETTLING_TIME_MS); - } - success = setSecurityMode(securityMode); - if (!gGaugeOn) { - *pGaugeEnable = 0; - } - gpI2c->unlock(); } @@ -862,14 +1039,14 @@ SecurityMode securityMode; char data[3]; - if (gReady && (gpI2c != NULL)) { + if (gReady) { gpI2c->lock(); securityMode = getSecurityMode(); // Must be inside lock() // Handle unsealing, as this command only works when unsealed if (setSecurityMode(SECURITY_MODE_UNSEALED)) { // Send a RESET sub-command - data[0] = 0x3e; // Set address to first register for ManufacturerAccessControl + data[0] = 0x3e; // Set address to ManufacturerAccessControl data[1] = 0x41; // First byte of RESET sub-command (0x41) data[2] = 0x00; // Second byte of RESET sub-command (0x00) (register address will auto-increment) @@ -885,10 +1062,6 @@ success = false; } } - - if (!gGaugeOn) { - *pGaugeEnable = 0; - } gpI2c->unlock(); }