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

Files at this revision

API Documentation at this revision

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

TESTS/unit_tests/default/main.cpp Show annotated file Show diff for this revision Revisions of this file
battery_gauge_bq35100.h Show annotated file Show diff for this revision Revisions of this file
bq35100.cpp Show annotated file Show diff for this revision Revisions of this file
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();
     }