This class provides APIs to all of the registers of the TI BQ27441 battery gauge, as used on the u-blox C030 board. The caller should instantiate an I2C interface and pass this to init(), which will initialise the chip and place it into its lowest power state. When battery gauging is enabled, the getRemainingCapacity()/getRemainingPercentage() API calls may be used; otherwise the chip will be maintained in its lowest power state until a voltage/current/temperature reading is requested.

Dependents:   example-battery-gauge-bq27441

Committer:
breadboardbasics
Date:
Wed Dec 13 17:14:51 2017 +0000
Revision:
6:998cc334f8f2
Parent:
5:63b325f2c21a
Fixes current = 0 problem and adds power measurement capability

Who changed what in which revision?

UserRevisionLine numberNew contents of line
rob.meades@u-blox.com 1:566163f17cde 1 /* mbed Microcontroller Library
rob.meades@u-blox.com 1:566163f17cde 2 * Copyright (c) 2017 u-blox
rob.meades@u-blox.com 1:566163f17cde 3 *
rob.meades@u-blox.com 1:566163f17cde 4 * Licensed under the Apache License, Version 2.0 (the "License");
rob.meades@u-blox.com 1:566163f17cde 5 * you may not use this file except in compliance with the License.
rob.meades@u-blox.com 1:566163f17cde 6 * You may obtain a copy of the License at
rob.meades@u-blox.com 1:566163f17cde 7 *
rob.meades@u-blox.com 1:566163f17cde 8 * http://www.apache.org/licenses/LICENSE-2.0
rob.meades@u-blox.com 1:566163f17cde 9 *
rob.meades@u-blox.com 1:566163f17cde 10 * Unless required by applicable law or agreed to in writing, software
rob.meades@u-blox.com 1:566163f17cde 11 * distributed under the License is distributed on an "AS IS" BASIS,
rob.meades@u-blox.com 1:566163f17cde 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rob.meades@u-blox.com 1:566163f17cde 13 * See the License for the specific language governing permissions and
rob.meades@u-blox.com 1:566163f17cde 14 * limitations under the License.
rob.meades@u-blox.com 1:566163f17cde 15 */
rob.meades@u-blox.com 1:566163f17cde 16
rob.meades@u-blox.com 1:566163f17cde 17 /**
rob.meades@u-blox.com 1:566163f17cde 18 * @file bq27441.cpp
rob.meades@u-blox.com 1:566163f17cde 19 * This file defines the API to the TI BQ27441 battery gauge chip.
rob.meades@u-blox.com 1:566163f17cde 20 */
rob.meades@u-blox.com 1:566163f17cde 21
RobMeades 2:93310a83401a 22 /** Define these to print debug information. */
rob.meades@u-blox.com 1:566163f17cde 23 //#define DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 24 //#define DEBUG_BQ27441_BLOCK_DATA
rob.meades@u-blox.com 1:566163f17cde 25
rob.meades@u-blox.com 1:566163f17cde 26 #include <mbed.h>
rob.meades@u-blox.com 1:566163f17cde 27 #include <battery_gauge_bq27441.h>
rob.meades@u-blox.com 1:566163f17cde 28
rob.meades@u-blox.com 1:566163f17cde 29 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 30 # include <stdio.h>
rob.meades@u-blox.com 1:566163f17cde 31 #endif
rob.meades@u-blox.com 1:566163f17cde 32
rob.meades@u-blox.com 1:566163f17cde 33 // ----------------------------------------------------------------
rob.meades@u-blox.com 1:566163f17cde 34 // COMPILE-TIME MACROS
rob.meades@u-blox.com 1:566163f17cde 35 // ----------------------------------------------------------------
rob.meades@u-blox.com 1:566163f17cde 36
RobMeades 2:93310a83401a 37 /** How many loops to wait for a configuration update to be permitted.
RobMeades 2:93310a83401a 38 * Experience suggests that the limit really does need to be this large. */
rob.meades@u-blox.com 1:566163f17cde 39 #define CONFIG_UPDATE_LOOPS 200
rob.meades@u-blox.com 1:566163f17cde 40
RobMeades 2:93310a83401a 41 /** How long to delay when running around the config update loop. */
rob.meades@u-blox.com 1:566163f17cde 42 #define CONFIG_UPDATE_LOOP_DELAY_MS 100
rob.meades@u-blox.com 1:566163f17cde 43
RobMeades 2:93310a83401a 44 /** How long to wait for all the ADC readings to be performed. */
rob.meades@u-blox.com 1:566163f17cde 45 #define ADC_READ_WAIT_MS 1000
rob.meades@u-blox.com 1:566163f17cde 46
RobMeades 2:93310a83401a 47 /** Delay after the last config update before we can unseal the
RobMeades 2:93310a83401a 48 * chip again (see section 6.4.5.1.1 of the BQ27441 technical
RobMeades 2:93310a83401a 49 * reference manual). */
rob.meades@u-blox.com 1:566163f17cde 50 #define UNSEAL_DELAY_MS 4000
rob.meades@u-blox.com 1:566163f17cde 51
rob.meades@u-blox.com 1:566163f17cde 52 // ----------------------------------------------------------------
rob.meades@u-blox.com 1:566163f17cde 53 // PRIVATE VARIABLES
rob.meades@u-blox.com 1:566163f17cde 54 // ----------------------------------------------------------------
rob.meades@u-blox.com 1:566163f17cde 55
rob.meades@u-blox.com 1:566163f17cde 56 // ----------------------------------------------------------------
rob.meades@u-blox.com 1:566163f17cde 57 // GENERIC PRIVATE FUNCTIONS
rob.meades@u-blox.com 1:566163f17cde 58 // ----------------------------------------------------------------
rob.meades@u-blox.com 1:566163f17cde 59
RobMeades 2:93310a83401a 60 // Read two bytes from an address.
rob.meades@u-blox.com 1:566163f17cde 61 // Note: gpI2c should be locked before this is called.
rob.meades@u-blox.com 1:566163f17cde 62 bool BatteryGaugeBq27441::getTwoBytes (uint8_t registerAddress, uint16_t *pBytes)
rob.meades@u-blox.com 1:566163f17cde 63 {
rob.meades@u-blox.com 1:566163f17cde 64 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 65 char data[3];
rob.meades@u-blox.com 1:566163f17cde 66
rob.meades@u-blox.com 1:566163f17cde 67 if (gpI2c != NULL) {
rob.meades@u-blox.com 1:566163f17cde 68 // Send a command to read from registerAddress
rob.meades@u-blox.com 1:566163f17cde 69 data[0] = registerAddress;
rob.meades@u-blox.com 1:566163f17cde 70 data[1] = 0;
rob.meades@u-blox.com 1:566163f17cde 71 data[2] = 0;
rob.meades@u-blox.com 1:566163f17cde 72
rob.meades@u-blox.com 1:566163f17cde 73 if ((gpI2c->write(gAddress, &(data[0]), 1) == 0) &&
rob.meades@u-blox.com 1:566163f17cde 74 (gpI2c->read(gAddress, &(data[1]), 2) == 0)) {
rob.meades@u-blox.com 1:566163f17cde 75 success = true;
rob.meades@u-blox.com 1:566163f17cde 76 if (pBytes) {
rob.meades@u-blox.com 1:566163f17cde 77 *pBytes = (((uint16_t) data[2]) << 8) + data[1];
rob.meades@u-blox.com 1:566163f17cde 78 }
rob.meades@u-blox.com 1:566163f17cde 79 }
rob.meades@u-blox.com 1:566163f17cde 80 }
rob.meades@u-blox.com 1:566163f17cde 81
rob.meades@u-blox.com 1:566163f17cde 82 return success;
rob.meades@u-blox.com 1:566163f17cde 83 }
rob.meades@u-blox.com 1:566163f17cde 84
breadboardbasics 6:998cc334f8f2 85 bool BatteryGaugeBq27441::getTwoBytesSigned (uint8_t registerAddress, int16_t *pBytes)
breadboardbasics 6:998cc334f8f2 86 {
breadboardbasics 6:998cc334f8f2 87 bool success = false;
breadboardbasics 6:998cc334f8f2 88 char data[3];
breadboardbasics 6:998cc334f8f2 89
breadboardbasics 6:998cc334f8f2 90 if (gpI2c != NULL) {
breadboardbasics 6:998cc334f8f2 91 // Send a command to read from registerAddress
breadboardbasics 6:998cc334f8f2 92 data[0] = registerAddress;
breadboardbasics 6:998cc334f8f2 93 data[1] = 0;
breadboardbasics 6:998cc334f8f2 94 data[2] = 0;
breadboardbasics 6:998cc334f8f2 95
breadboardbasics 6:998cc334f8f2 96 if ((gpI2c->write(gAddress, &(data[0]), 1) == 0) &&
breadboardbasics 6:998cc334f8f2 97 (gpI2c->read(gAddress, &(data[1]), 2) == 0)) {
breadboardbasics 6:998cc334f8f2 98 success = true;
breadboardbasics 6:998cc334f8f2 99 if (pBytes) {
breadboardbasics 6:998cc334f8f2 100 *pBytes = (((int16_t) data[2]) << 8) + data[1];
breadboardbasics 6:998cc334f8f2 101 }
breadboardbasics 6:998cc334f8f2 102 }
breadboardbasics 6:998cc334f8f2 103 }
breadboardbasics 6:998cc334f8f2 104
breadboardbasics 6:998cc334f8f2 105 return success;
breadboardbasics 6:998cc334f8f2 106 }
breadboardbasics 6:998cc334f8f2 107
RobMeades 2:93310a83401a 108 // Compute the checksum over a block of data.
rob.meades@u-blox.com 1:566163f17cde 109 uint8_t BatteryGaugeBq27441::computeChecksum(const char * pData)
rob.meades@u-blox.com 1:566163f17cde 110 {
rob.meades@u-blox.com 1:566163f17cde 111 uint8_t checkSum = 0;
rob.meades@u-blox.com 1:566163f17cde 112 uint8_t x;
rob.meades@u-blox.com 1:566163f17cde 113
rob.meades@u-blox.com 1:566163f17cde 114 if (pData != NULL) {
rob.meades@u-blox.com 1:566163f17cde 115 #ifdef DEBUG_BQ27441_BLOCK_DATA
rob.meades@u-blox.com 1:566163f17cde 116 printf ("BatteryGaugeBq27441 (I2C 0x%02x): computing check sum on data block.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 117 printf (" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
rob.meades@u-blox.com 1:566163f17cde 118 #endif
rob.meades@u-blox.com 1:566163f17cde 119 for (x = 1; x <= 32; x++) {
rob.meades@u-blox.com 1:566163f17cde 120 checkSum += *pData;
rob.meades@u-blox.com 1:566163f17cde 121
rob.meades@u-blox.com 1:566163f17cde 122 #ifdef DEBUG_BQ27441_BLOCK_DATA
rob.meades@u-blox.com 1:566163f17cde 123 if (x % 16 == 8) {
rob.meades@u-blox.com 1:566163f17cde 124 printf ("%02x ", *pData);
rob.meades@u-blox.com 1:566163f17cde 125 } else if (x % 16 == 0) {
rob.meades@u-blox.com 1:566163f17cde 126 printf ("%02x\n", *pData);
rob.meades@u-blox.com 1:566163f17cde 127 } else {
rob.meades@u-blox.com 1:566163f17cde 128 printf ("%02x-", *pData);
rob.meades@u-blox.com 1:566163f17cde 129 }
rob.meades@u-blox.com 1:566163f17cde 130 #endif
rob.meades@u-blox.com 1:566163f17cde 131 pData++;
rob.meades@u-blox.com 1:566163f17cde 132 }
rob.meades@u-blox.com 1:566163f17cde 133
rob.meades@u-blox.com 1:566163f17cde 134 checkSum = 0xff - checkSum;
rob.meades@u-blox.com 1:566163f17cde 135 }
rob.meades@u-blox.com 1:566163f17cde 136
rob.meades@u-blox.com 1:566163f17cde 137 #ifdef DEBUG_BQ27441_BLOCK_DATA
rob.meades@u-blox.com 1:566163f17cde 138 if (x % 16 != 1) {
rob.meades@u-blox.com 1:566163f17cde 139 printf("\n");
rob.meades@u-blox.com 1:566163f17cde 140 }
rob.meades@u-blox.com 1:566163f17cde 141
rob.meades@u-blox.com 1:566163f17cde 142 printf ("BatteryGaugeBq27441 (I2C 0x%02x): check sum is 0x%02x.\n", gAddress >> 1, checkSum);
rob.meades@u-blox.com 1:566163f17cde 143 #endif
rob.meades@u-blox.com 1:566163f17cde 144
rob.meades@u-blox.com 1:566163f17cde 145 return checkSum;
rob.meades@u-blox.com 1:566163f17cde 146 }
rob.meades@u-blox.com 1:566163f17cde 147
RobMeades 2:93310a83401a 148 // Read data of a given length and class ID.
rob.meades@u-blox.com 1:566163f17cde 149 // Note: gpI2c should be locked before this is called.
rob.meades@u-blox.com 1:566163f17cde 150 bool BatteryGaugeBq27441::readExtendedData(uint8_t subClassId, int32_t offset, int32_t length, char * pData)
rob.meades@u-blox.com 1:566163f17cde 151 {
rob.meades@u-blox.com 1:566163f17cde 152 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 153 bool wasSealed = false;
rob.meades@u-blox.com 1:566163f17cde 154 char block[32];
rob.meades@u-blox.com 1:566163f17cde 155 char data[3];
rob.meades@u-blox.com 1:566163f17cde 156
rob.meades@u-blox.com 1:566163f17cde 157 if ((gpI2c != NULL) && (length <= 32) && (pData != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 158
rob.meades@u-blox.com 1:566163f17cde 159 // The offset + length combination must not cross a 32-byte boundary
rob.meades@u-blox.com 1:566163f17cde 160 if (offset / 32 == (offset + length - 1) / 32) {
rob.meades@u-blox.com 1:566163f17cde 161
rob.meades@u-blox.com 1:566163f17cde 162 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 5:63b325f2c21a 163 printf("BatteryGaugeBq27441 (I2C 0x%02x): preparing to read %d byte(s) from offset %d of sub-class %d.\n", gAddress >> 1, (int) length, (int) offset, subClassId);
rob.meades@u-blox.com 1:566163f17cde 164 #endif
rob.meades@u-blox.com 1:566163f17cde 165 // Handle unsealing
rob.meades@u-blox.com 1:566163f17cde 166 wasSealed = isSealed();
rob.meades@u-blox.com 1:566163f17cde 167 if (!wasSealed || unseal(gSealCode)) {
rob.meades@u-blox.com 1:566163f17cde 168
rob.meades@u-blox.com 1:566163f17cde 169 // Enable Block Data Control (0x61)
rob.meades@u-blox.com 1:566163f17cde 170 data[0] = 0x61;
rob.meades@u-blox.com 1:566163f17cde 171 data[1] = 0;
rob.meades@u-blox.com 1:566163f17cde 172
rob.meades@u-blox.com 1:566163f17cde 173 if (gpI2c->write(gAddress, &(data[0]), 2) == 0) {
rob.meades@u-blox.com 1:566163f17cde 174 // Write sub-class ID using Data Block Class (0x3e)
rob.meades@u-blox.com 1:566163f17cde 175 data[0] = 0x3e;
rob.meades@u-blox.com 1:566163f17cde 176 data[1] = subClassId;
rob.meades@u-blox.com 1:566163f17cde 177
rob.meades@u-blox.com 1:566163f17cde 178 if (gpI2c->write(gAddress, &(data[0]), 2) == 0) {
rob.meades@u-blox.com 1:566163f17cde 179 // Write offset using Block Offset (0x3f) and then
rob.meades@u-blox.com 1:566163f17cde 180 // read the data block
rob.meades@u-blox.com 1:566163f17cde 181 data[0] = 0x3f;
rob.meades@u-blox.com 1:566163f17cde 182 data[1] = offset / 32;
rob.meades@u-blox.com 1:566163f17cde 183
rob.meades@u-blox.com 1:566163f17cde 184 if ((gpI2c->write(gAddress, &(data[0]), 2, true) == 0) &&
rob.meades@u-blox.com 1:566163f17cde 185 (gpI2c->read(gAddress, &(block[0]), 32) == 0)) {
rob.meades@u-blox.com 1:566163f17cde 186 // Compute the block checksum and then read it
rob.meades@u-blox.com 1:566163f17cde 187 data[2] = computeChecksum(&(block[0]));
rob.meades@u-blox.com 1:566163f17cde 188 data[0] = 0x60;
rob.meades@u-blox.com 1:566163f17cde 189 data[1] = 0;
rob.meades@u-blox.com 1:566163f17cde 190
rob.meades@u-blox.com 1:566163f17cde 191 if ((gpI2c->write(gAddress, &(data[0]), 1, true) == 0) &&
rob.meades@u-blox.com 1:566163f17cde 192 (gpI2c->read(gAddress, &(data[1]), 1) == 0)) {
rob.meades@u-blox.com 1:566163f17cde 193
rob.meades@u-blox.com 1:566163f17cde 194 // Does the checksum match?
rob.meades@u-blox.com 1:566163f17cde 195 if (data[1] == data[2]) {
rob.meades@u-blox.com 1:566163f17cde 196 // If so read the new data from the block data area at 0x40 plus
rob.meades@u-blox.com 1:566163f17cde 197 // the offset (plus the Block Offset already written)
rob.meades@u-blox.com 1:566163f17cde 198 data[0] = 0x40 + (offset % 32);
rob.meades@u-blox.com 1:566163f17cde 199
rob.meades@u-blox.com 1:566163f17cde 200 if ((gpI2c->write(gAddress, &(data[0]), 1, true) == 0) &&
rob.meades@u-blox.com 1:566163f17cde 201 (gpI2c->read(gAddress, pData, length) == 0)) {
rob.meades@u-blox.com 1:566163f17cde 202 success = true;
rob.meades@u-blox.com 1:566163f17cde 203 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 204 printf("BatteryGaugeBq27441 (I2C 0x%02x): read successful.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 205 #endif
rob.meades@u-blox.com 1:566163f17cde 206 } else {
rob.meades@u-blox.com 1:566163f17cde 207 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 5:63b325f2c21a 208 printf("BatteryGaugeBq27441 (I2C 0x%02x): couldn't read all %d bytes of config.\r", gAddress >> 1, (int) length);
rob.meades@u-blox.com 1:566163f17cde 209 #endif
rob.meades@u-blox.com 1:566163f17cde 210 }
rob.meades@u-blox.com 1:566163f17cde 211 } else {
rob.meades@u-blox.com 1:566163f17cde 212 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 213 printf("BatteryGaugeBq27441 (I2C 0x%02x): checksum on data block incorrect (expected 0x%02x, received 0x%02x).\n", gAddress >> 1, data[2], data[1]);
rob.meades@u-blox.com 1:566163f17cde 214 #endif
rob.meades@u-blox.com 1:566163f17cde 215 }
rob.meades@u-blox.com 1:566163f17cde 216 } else {
rob.meades@u-blox.com 1:566163f17cde 217 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 218 printf("BatteryGaugeBq27441 (I2C 0x%02x): unable to read Block Data Checksum for config.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 219 #endif
rob.meades@u-blox.com 1:566163f17cde 220 }
rob.meades@u-blox.com 1:566163f17cde 221 } else {
rob.meades@u-blox.com 1:566163f17cde 222 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 223 printf("BatteryGaugeBq27441 (I2C 0x%02x): unable to write Block Offset (%d), or maybe read the data block, for config.\n", gAddress >> 1, data[1]);
rob.meades@u-blox.com 1:566163f17cde 224 #endif
rob.meades@u-blox.com 1:566163f17cde 225 }
rob.meades@u-blox.com 1:566163f17cde 226 } else {
rob.meades@u-blox.com 1:566163f17cde 227 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 228 printf("BatteryGaugeBq27441 (I2C 0x%02x): unable set sub-class ID (0x%02x) for config.\n", gAddress >> 1, subClassId);
rob.meades@u-blox.com 1:566163f17cde 229 #endif
rob.meades@u-blox.com 1:566163f17cde 230 }
rob.meades@u-blox.com 1:566163f17cde 231 } else {
rob.meades@u-blox.com 1:566163f17cde 232 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 233 printf("BatteryGaugeBq27441 (I2C 0x%02x): unable to set Block Data Control for config.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 234 #endif
rob.meades@u-blox.com 1:566163f17cde 235 }
rob.meades@u-blox.com 1:566163f17cde 236 } else {
rob.meades@u-blox.com 1:566163f17cde 237 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 238 printf("BatteryGaugeBq27441 (I2C 0x%02x): unable to unseal chip.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 239 #endif
rob.meades@u-blox.com 1:566163f17cde 240 }
rob.meades@u-blox.com 1:566163f17cde 241 } else {
rob.meades@u-blox.com 1:566163f17cde 242 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 5:63b325f2c21a 243 printf("BatteryGaugeBq27441 (I2C 0x%02x): offset (%d) is in different 32 byte block to offset + length (%d) [length is %d].\n", gAddress >> 1, (int) offset, (int) (offset + length), (int) length);
rob.meades@u-blox.com 1:566163f17cde 244 #endif
rob.meades@u-blox.com 1:566163f17cde 245 }
rob.meades@u-blox.com 1:566163f17cde 246 }
rob.meades@u-blox.com 1:566163f17cde 247
rob.meades@u-blox.com 1:566163f17cde 248 // Reseal if required, and fail if it fails
rob.meades@u-blox.com 1:566163f17cde 249 if (wasSealed) {
rob.meades@u-blox.com 1:566163f17cde 250 if (!seal()) {
rob.meades@u-blox.com 1:566163f17cde 251 success = false;
rob.meades@u-blox.com 1:566163f17cde 252 }
rob.meades@u-blox.com 1:566163f17cde 253 }
rob.meades@u-blox.com 1:566163f17cde 254
rob.meades@u-blox.com 1:566163f17cde 255 return success;
rob.meades@u-blox.com 1:566163f17cde 256 }
rob.meades@u-blox.com 1:566163f17cde 257
RobMeades 2:93310a83401a 258 // Write data of a given length and class ID to a given offset. This code taken from
rob.meades@u-blox.com 1:566163f17cde 259 // section 3.1 of the SLUUAC9A application technical reference manual with hints from:
rob.meades@u-blox.com 1:566163f17cde 260 // https://github.com/sparkfun/SparkFun_BQ27441_Arduino_Library/blob/master/src/SparkFunBQ27441.cpp.
rob.meades@u-blox.com 1:566163f17cde 261 // Note: gpI2c should be locked before this is called.
rob.meades@u-blox.com 1:566163f17cde 262 bool BatteryGaugeBq27441::writeExtendedData(uint8_t subClassId, int32_t offset, int32_t length, const char * pData)
rob.meades@u-blox.com 1:566163f17cde 263 {
rob.meades@u-blox.com 1:566163f17cde 264 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 265 bool wasSealed = false;
rob.meades@u-blox.com 1:566163f17cde 266 char data[3 + 32];
rob.meades@u-blox.com 1:566163f17cde 267 char block[32];
rob.meades@u-blox.com 1:566163f17cde 268 uint16_t answer;
rob.meades@u-blox.com 1:566163f17cde 269 uint32_t count = 0;
rob.meades@u-blox.com 1:566163f17cde 270
rob.meades@u-blox.com 1:566163f17cde 271 if ((gpI2c != NULL) && (length <= 32) && (pData != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 272
rob.meades@u-blox.com 1:566163f17cde 273 // The offset + length combination must not cross a 32-byte boundary
rob.meades@u-blox.com 1:566163f17cde 274 if (offset / 32 == (offset + length - 1) / 32) {
rob.meades@u-blox.com 1:566163f17cde 275 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 5:63b325f2c21a 276 printf("BatteryGaugeBq27441 (I2C 0x%02x): preparing to write %d byte(s) to offset %d of sub-class %d.\n", gAddress >> 1, (int) length, (int) offset, subClassId);
rob.meades@u-blox.com 1:566163f17cde 277 #endif
rob.meades@u-blox.com 1:566163f17cde 278 // Handle unsealing
rob.meades@u-blox.com 1:566163f17cde 279 wasSealed = isSealed();
rob.meades@u-blox.com 1:566163f17cde 280 if (!wasSealed || unseal(gSealCode)) {
rob.meades@u-blox.com 1:566163f17cde 281
rob.meades@u-blox.com 1:566163f17cde 282 // Send Config Update (command 0x13)
rob.meades@u-blox.com 1:566163f17cde 283 data[0] = 0;
rob.meades@u-blox.com 1:566163f17cde 284 data[1] = 0x13;
rob.meades@u-blox.com 1:566163f17cde 285 data[2] = 0;
rob.meades@u-blox.com 1:566163f17cde 286
rob.meades@u-blox.com 1:566163f17cde 287 if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
rob.meades@u-blox.com 1:566163f17cde 288 // Wait for CONFIGUPDATE to be set in Flags register (bit 4)
rob.meades@u-blox.com 1:566163f17cde 289 count = 0;
rob.meades@u-blox.com 1:566163f17cde 290 do {
rob.meades@u-blox.com 1:566163f17cde 291 answer = 0;
rob.meades@u-blox.com 1:566163f17cde 292 getTwoBytes(0x06, &answer);
rob.meades@u-blox.com 1:566163f17cde 293 count++;
rob.meades@u-blox.com 1:566163f17cde 294 wait_ms(CONFIG_UPDATE_LOOP_DELAY_MS);
rob.meades@u-blox.com 1:566163f17cde 295 } while (((answer & (1 << 4)) == 0) && (count < CONFIG_UPDATE_LOOPS));
rob.meades@u-blox.com 1:566163f17cde 296
rob.meades@u-blox.com 1:566163f17cde 297 if ((answer & (1 << 4)) != 0) {
rob.meades@u-blox.com 1:566163f17cde 298 // Enable Block Data Control (0x61)
rob.meades@u-blox.com 1:566163f17cde 299 data[0] = 0x61;
rob.meades@u-blox.com 1:566163f17cde 300 data[1] = 0;
rob.meades@u-blox.com 1:566163f17cde 301
rob.meades@u-blox.com 1:566163f17cde 302 if (gpI2c->write(gAddress, &(data[0]), 2) == 0) {
rob.meades@u-blox.com 1:566163f17cde 303 // Write sub-class ID using Data Block Class (0x3e)
rob.meades@u-blox.com 1:566163f17cde 304 data[0] = 0x3e;
rob.meades@u-blox.com 1:566163f17cde 305 data[1] = subClassId;
rob.meades@u-blox.com 1:566163f17cde 306
rob.meades@u-blox.com 1:566163f17cde 307 if (gpI2c->write(gAddress, &(data[0]), 2) == 0) {
rob.meades@u-blox.com 1:566163f17cde 308 // Write offset using Block Offset (0x3f) and then
rob.meades@u-blox.com 1:566163f17cde 309 // read the data block
rob.meades@u-blox.com 1:566163f17cde 310 data[0] = 0x3f;
rob.meades@u-blox.com 1:566163f17cde 311 data[1] = offset / 32;
rob.meades@u-blox.com 1:566163f17cde 312
rob.meades@u-blox.com 1:566163f17cde 313 if ((gpI2c->write(gAddress, &(data[0]), 2, true) == 0) &&
rob.meades@u-blox.com 1:566163f17cde 314 (gpI2c->read(gAddress, &(block[0]), 32) == 0)) {
rob.meades@u-blox.com 1:566163f17cde 315 // Compute the existing block checksum and then read it
rob.meades@u-blox.com 1:566163f17cde 316 data[2] = computeChecksum(&(block[0]));
rob.meades@u-blox.com 1:566163f17cde 317 data[0] = 0x60;
rob.meades@u-blox.com 1:566163f17cde 318 data[1] = 0;
rob.meades@u-blox.com 1:566163f17cde 319
rob.meades@u-blox.com 1:566163f17cde 320 if ((gpI2c->write(gAddress, &(data[0]), 1, true) == 0) &&
rob.meades@u-blox.com 1:566163f17cde 321 (gpI2c->read(gAddress, &(data[1]), 1) == 0)) {
rob.meades@u-blox.com 1:566163f17cde 322
rob.meades@u-blox.com 1:566163f17cde 323 // Does the checksum match?
rob.meades@u-blox.com 1:566163f17cde 324 if (data[1] == data[2]) {
rob.meades@u-blox.com 1:566163f17cde 325 // If so write the new data to the block data area at 0x40 plus the offset (plus the Block Offset already written)
rob.meades@u-blox.com 1:566163f17cde 326 // NOTE: I tried doing this as two separate writes, one of the offset and then another of the
rob.meades@u-blox.com 1:566163f17cde 327 // data block (so that I could use pData directly rather than having to extend the local
rob.meades@u-blox.com 1:566163f17cde 328 // data array by 32) but the chip didn't like that, hence we have to copy pData into the
rob.meades@u-blox.com 1:566163f17cde 329 // local array and do a single contiguous write.
rob.meades@u-blox.com 1:566163f17cde 330 data[0] = 0x40 + (offset % 32);
rob.meades@u-blox.com 1:566163f17cde 331 memcpy (&(data[1]), pData, length);
rob.meades@u-blox.com 1:566163f17cde 332
rob.meades@u-blox.com 1:566163f17cde 333 if (gpI2c->write(gAddress, &(data[0]), length + 1) == 0) {
rob.meades@u-blox.com 1:566163f17cde 334 // Also write the data into our local block variable
rob.meades@u-blox.com 1:566163f17cde 335 // on top of the previously read data and use
rob.meades@u-blox.com 1:566163f17cde 336 // that to compute the new block checksum, then write it
rob.meades@u-blox.com 1:566163f17cde 337 memcpy (&(block[offset % 32]), pData, length);
rob.meades@u-blox.com 1:566163f17cde 338 data[0] = 0x60;
rob.meades@u-blox.com 1:566163f17cde 339 data[1] = computeChecksum(&(block[0]));
rob.meades@u-blox.com 1:566163f17cde 340
rob.meades@u-blox.com 1:566163f17cde 341 if (gpI2c->write(gAddress, &(data[0]), 2) == 0) {
rob.meades@u-blox.com 1:566163f17cde 342 // Exit config mode with SOFT_RESET command 0x42
rob.meades@u-blox.com 1:566163f17cde 343 data[0] = 0;
rob.meades@u-blox.com 1:566163f17cde 344 data[1] = 0x42;
rob.meades@u-blox.com 1:566163f17cde 345 data[2] = 0;
rob.meades@u-blox.com 1:566163f17cde 346
rob.meades@u-blox.com 1:566163f17cde 347 if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
rob.meades@u-blox.com 1:566163f17cde 348 // Finally, wait for CONFIGUPDATE to be unset in Flags register (bit 4)
rob.meades@u-blox.com 1:566163f17cde 349 count = 0;
rob.meades@u-blox.com 1:566163f17cde 350 do {
rob.meades@u-blox.com 1:566163f17cde 351 answer = 0xFFFF;
rob.meades@u-blox.com 1:566163f17cde 352 getTwoBytes(0x06, &answer);
rob.meades@u-blox.com 1:566163f17cde 353 count++;
rob.meades@u-blox.com 1:566163f17cde 354 wait_ms(CONFIG_UPDATE_LOOP_DELAY_MS);
rob.meades@u-blox.com 1:566163f17cde 355 } while (((answer & (1 << 4)) != 0) && (count < CONFIG_UPDATE_LOOPS));
rob.meades@u-blox.com 1:566163f17cde 356
rob.meades@u-blox.com 1:566163f17cde 357 if ((answer & (1 << 4)) == 0) {
rob.meades@u-blox.com 1:566163f17cde 358 success = true;
rob.meades@u-blox.com 1:566163f17cde 359 // Wait around for 4 seconds if there has been a config update
rob.meades@u-blox.com 1:566163f17cde 360 // and there is a danger that we might have to unseal the device
rob.meades@u-blox.com 1:566163f17cde 361 // Note: would be nice if we could note the time and do other
rob.meades@u-blox.com 1:566163f17cde 362 // things but the timer is restarted when certain commands are sent,
rob.meades@u-blox.com 1:566163f17cde 363 // so it is better to be dumb
rob.meades@u-blox.com 1:566163f17cde 364 if (wasSealed) {
rob.meades@u-blox.com 1:566163f17cde 365 wait_ms(UNSEAL_DELAY_MS);
rob.meades@u-blox.com 1:566163f17cde 366 }
rob.meades@u-blox.com 1:566163f17cde 367 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 368 printf("BatteryGaugeBq27441 (I2C 0x%02x): write successful.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 369 #endif
rob.meades@u-blox.com 1:566163f17cde 370 } else {
rob.meades@u-blox.com 1:566163f17cde 371 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 372 printf("BatteryGaugeBq27441 (I2C 0x%02x): Flags register didn't show config update flag unset in time (0x%04x).\n", gAddress >> 1, answer);
rob.meades@u-blox.com 1:566163f17cde 373 #endif
rob.meades@u-blox.com 1:566163f17cde 374 }
rob.meades@u-blox.com 1:566163f17cde 375 } else {
rob.meades@u-blox.com 1:566163f17cde 376 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 377 printf("BatteryGaugeBq27441 (I2C 0x%02x): unable to write config update exit after update.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 378 #endif
rob.meades@u-blox.com 1:566163f17cde 379 }
rob.meades@u-blox.com 1:566163f17cde 380 } else {
rob.meades@u-blox.com 1:566163f17cde 381 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 382 printf("BatteryGaugeBq27441 (I2C 0x%02x): checksum on modified data block incorrect (0x%02x) during config update.\n", gAddress >> 1, data[1]);
rob.meades@u-blox.com 1:566163f17cde 383 #endif
rob.meades@u-blox.com 1:566163f17cde 384 }
rob.meades@u-blox.com 1:566163f17cde 385 } else {
rob.meades@u-blox.com 1:566163f17cde 386 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 5:63b325f2c21a 387 printf("BatteryGaugeBq27441 (I2C 0x%02x): couldn't write all %d bytes during config update.\n", gAddress >> 1, (int) length);
rob.meades@u-blox.com 1:566163f17cde 388 #endif
rob.meades@u-blox.com 1:566163f17cde 389 }
rob.meades@u-blox.com 1:566163f17cde 390 } else {
rob.meades@u-blox.com 1:566163f17cde 391 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 392 printf("BatteryGaugeBq27441 (I2C 0x%02x): checksum on read data block incorrect (expected 0x%02x, received 0x%02x).\n", gAddress >> 1, data[2], data[1]);
rob.meades@u-blox.com 1:566163f17cde 393 #endif
rob.meades@u-blox.com 1:566163f17cde 394 }
rob.meades@u-blox.com 1:566163f17cde 395 } else {
rob.meades@u-blox.com 1:566163f17cde 396 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 397 printf("BatteryGaugeBq27441 (I2C 0x%02x): unable to read Block Data Checksum for config update.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 398 #endif
rob.meades@u-blox.com 1:566163f17cde 399 }
rob.meades@u-blox.com 1:566163f17cde 400 } else {
rob.meades@u-blox.com 1:566163f17cde 401 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 402 printf("BatteryGaugeBq27441 (I2C 0x%02x): unable to write Block Offset (%d), or possibly read the data block, for config update.\n", gAddress >> 1, data[1]);
rob.meades@u-blox.com 1:566163f17cde 403 #endif
rob.meades@u-blox.com 1:566163f17cde 404 }
rob.meades@u-blox.com 1:566163f17cde 405 } else {
rob.meades@u-blox.com 1:566163f17cde 406 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 407 printf("BatteryGaugeBq27441 (I2C 0x%02x): unable to set sub-class ID (0x%02x) for config update.\r", gAddress >> 1, subClassId);
rob.meades@u-blox.com 1:566163f17cde 408 #endif
rob.meades@u-blox.com 1:566163f17cde 409 }
rob.meades@u-blox.com 1:566163f17cde 410 } else {
rob.meades@u-blox.com 1:566163f17cde 411 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 412 printf("BatteryGaugeBq27441 (I2C 0x%02x): unable to set Block Data Control for config update.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 413 #endif
rob.meades@u-blox.com 1:566163f17cde 414 }
rob.meades@u-blox.com 1:566163f17cde 415 } else {
rob.meades@u-blox.com 1:566163f17cde 416 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 417 printf("BatteryGaugeBq27441 (I2C 0x%02x): Flags register didn't show config update flag set in time (0x%04x).\n", gAddress >> 1, answer);
rob.meades@u-blox.com 1:566163f17cde 418 #endif
rob.meades@u-blox.com 1:566163f17cde 419 }
rob.meades@u-blox.com 1:566163f17cde 420 } else {
rob.meades@u-blox.com 1:566163f17cde 421 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 422 printf("BatteryGaugeBq27441 (I2C 0x%02x): unable to write to control register for config update.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 423 #endif
rob.meades@u-blox.com 1:566163f17cde 424 }
rob.meades@u-blox.com 1:566163f17cde 425 } else {
rob.meades@u-blox.com 1:566163f17cde 426 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 427 printf("BatteryGaugeBq27441 (I2C 0x%02x): unable to unseal chip.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 428 #endif
rob.meades@u-blox.com 1:566163f17cde 429 }
rob.meades@u-blox.com 1:566163f17cde 430 } else {
rob.meades@u-blox.com 1:566163f17cde 431 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 5:63b325f2c21a 432 printf("BatteryGaugeBq27441 (I2C 0x%02x): offset (%d) is in different 32 byte block to offset + length (%d) [length is %d].\n", gAddress >> 1, (int) offset, (int) (offset + length), (int) length);
rob.meades@u-blox.com 1:566163f17cde 433 #endif
rob.meades@u-blox.com 1:566163f17cde 434 }
rob.meades@u-blox.com 1:566163f17cde 435 }
rob.meades@u-blox.com 1:566163f17cde 436
rob.meades@u-blox.com 1:566163f17cde 437 // If the write succeeded the exit from config update state will
rob.meades@u-blox.com 1:566163f17cde 438 // re-seal things. If it failed, we need to re-seal the device
rob.meades@u-blox.com 1:566163f17cde 439 // manually if it was previously sealed.
rob.meades@u-blox.com 1:566163f17cde 440 if (!success && wasSealed) {
rob.meades@u-blox.com 1:566163f17cde 441 if (!seal()) {
rob.meades@u-blox.com 1:566163f17cde 442 success = false;
rob.meades@u-blox.com 1:566163f17cde 443 }
rob.meades@u-blox.com 1:566163f17cde 444 }
rob.meades@u-blox.com 1:566163f17cde 445
rob.meades@u-blox.com 1:566163f17cde 446 return success;
rob.meades@u-blox.com 1:566163f17cde 447 }
rob.meades@u-blox.com 1:566163f17cde 448
rob.meades@u-blox.com 1:566163f17cde 449
RobMeades 2:93310a83401a 450 // Check if the chip is SEALED or UNSEALED.
rob.meades@u-blox.com 1:566163f17cde 451 // Note: gpI2c should be locked before this is called.
rob.meades@u-blox.com 1:566163f17cde 452 bool BatteryGaugeBq27441::isSealed(void)
rob.meades@u-blox.com 1:566163f17cde 453 {
rob.meades@u-blox.com 1:566163f17cde 454 bool sealed = false;
rob.meades@u-blox.com 1:566163f17cde 455 char data[3];
rob.meades@u-blox.com 1:566163f17cde 456 uint16_t controlStatus;
rob.meades@u-blox.com 1:566163f17cde 457
rob.meades@u-blox.com 1:566163f17cde 458 // Send a control command to read the control status register
rob.meades@u-blox.com 1:566163f17cde 459 data[0] = 0x00; // Set address to first register for control
rob.meades@u-blox.com 1:566163f17cde 460 data[1] = 0x00; // First byte of CONTROL_STATUS sub-command (0x00)
rob.meades@u-blox.com 1:566163f17cde 461 data[2] = 0x00; // Second byte of CONTROL_STATUS sub-command (0x00) (register address will auto-increment)
rob.meades@u-blox.com 1:566163f17cde 462
rob.meades@u-blox.com 1:566163f17cde 463 if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
rob.meades@u-blox.com 1:566163f17cde 464 if (getTwoBytes (0, &controlStatus)) {
rob.meades@u-blox.com 1:566163f17cde 465 // Bit 5 of the high byte is set to 1 if the device is sealed
rob.meades@u-blox.com 1:566163f17cde 466 if (controlStatus & (1 << 13)) {
rob.meades@u-blox.com 1:566163f17cde 467 sealed = true;
rob.meades@u-blox.com 1:566163f17cde 468 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 469 printf("BatteryGaugeBq27441 (I2C 0x%02x): is sealed (control status 0x%04x).\r\n", gAddress >> 1, controlStatus);
rob.meades@u-blox.com 1:566163f17cde 470 } else {
rob.meades@u-blox.com 1:566163f17cde 471 printf("BatteryGaugeBq27441 (I2C 0x%02x): is unsealed (control status 0x%04x).\r\n", gAddress >> 1, controlStatus);
rob.meades@u-blox.com 1:566163f17cde 472 #endif
rob.meades@u-blox.com 1:566163f17cde 473 }
rob.meades@u-blox.com 1:566163f17cde 474 }
rob.meades@u-blox.com 1:566163f17cde 475 }
rob.meades@u-blox.com 1:566163f17cde 476
rob.meades@u-blox.com 1:566163f17cde 477 return sealed;
rob.meades@u-blox.com 1:566163f17cde 478 }
rob.meades@u-blox.com 1:566163f17cde 479
RobMeades 2:93310a83401a 480 // Put the chip into SEALED mode.
rob.meades@u-blox.com 1:566163f17cde 481 // Note: gpI2c should be locked before this is called.
rob.meades@u-blox.com 1:566163f17cde 482 bool BatteryGaugeBq27441::seal(void)
rob.meades@u-blox.com 1:566163f17cde 483 {
rob.meades@u-blox.com 1:566163f17cde 484 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 485 char data[3];
rob.meades@u-blox.com 1:566163f17cde 486 uint16_t controlStatus;
rob.meades@u-blox.com 1:566163f17cde 487
rob.meades@u-blox.com 1:566163f17cde 488 // Send a SEALED sub-command
rob.meades@u-blox.com 1:566163f17cde 489 data[0] = 0x00; // Set address to first register for control
rob.meades@u-blox.com 1:566163f17cde 490 data[1] = 0x20; // First byte of SEALED sub-command (0x20)
rob.meades@u-blox.com 1:566163f17cde 491 data[2] = 0x00; // Second byte of SEALED sub-command (0x00) (register address will auto-increment)
rob.meades@u-blox.com 1:566163f17cde 492
rob.meades@u-blox.com 1:566163f17cde 493 if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
rob.meades@u-blox.com 1:566163f17cde 494 // Check for success by reading the control status register
rob.meades@u-blox.com 1:566163f17cde 495 data[0] = 0x00; // Set address to first register for control
rob.meades@u-blox.com 1:566163f17cde 496 data[1] = 0x00; // First byte of CONTROL_STATUS sub-command (0x00)
rob.meades@u-blox.com 1:566163f17cde 497 data[2] = 0x00; // Second byte of CONTROL_STATUS sub-command (0x00) (register address will auto-increment)
rob.meades@u-blox.com 1:566163f17cde 498
rob.meades@u-blox.com 1:566163f17cde 499 if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
rob.meades@u-blox.com 1:566163f17cde 500 if (getTwoBytes (0, &controlStatus)) {
rob.meades@u-blox.com 1:566163f17cde 501 // Bit 5 of the high byte is set to 1 if the device is sealed
rob.meades@u-blox.com 1:566163f17cde 502 if (controlStatus & (1 << 13)) {
rob.meades@u-blox.com 1:566163f17cde 503 success = true;
rob.meades@u-blox.com 1:566163f17cde 504 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 505 printf("BatteryGaugeBq27441 (I2C 0x%02x): now sealed.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 506 } else {
rob.meades@u-blox.com 1:566163f17cde 507 printf("BatteryGaugeBq27441 (I2C 0x%02x): seal failed (control status 0x%04x).\n", gAddress >> 1, controlStatus);
rob.meades@u-blox.com 1:566163f17cde 508 #endif
rob.meades@u-blox.com 1:566163f17cde 509 }
rob.meades@u-blox.com 1:566163f17cde 510 }
rob.meades@u-blox.com 1:566163f17cde 511 }
rob.meades@u-blox.com 1:566163f17cde 512 }
rob.meades@u-blox.com 1:566163f17cde 513
rob.meades@u-blox.com 1:566163f17cde 514 return success;
rob.meades@u-blox.com 1:566163f17cde 515 }
rob.meades@u-blox.com 1:566163f17cde 516
RobMeades 2:93310a83401a 517 // Send the seal code to the device to unseal it.
rob.meades@u-blox.com 1:566163f17cde 518 // Note: gpI2c should be locked before this is called.
rob.meades@u-blox.com 1:566163f17cde 519 bool BatteryGaugeBq27441::unseal(uint16_t sealCode)
rob.meades@u-blox.com 1:566163f17cde 520 {
rob.meades@u-blox.com 1:566163f17cde 521 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 522 char data[3];
rob.meades@u-blox.com 1:566163f17cde 523 uint16_t controlStatus;
rob.meades@u-blox.com 1:566163f17cde 524
rob.meades@u-blox.com 1:566163f17cde 525 // Send the unseal code to the control register
rob.meades@u-blox.com 1:566163f17cde 526 data[0] = 0x00; // Set address to first register for control
rob.meades@u-blox.com 1:566163f17cde 527 data[1] = (char) sealCode; // First byte of code
rob.meades@u-blox.com 1:566163f17cde 528 data[2] = (char) (sealCode >> 8); // Second byte of code (register address will auto-increment)
rob.meades@u-blox.com 1:566163f17cde 529
rob.meades@u-blox.com 1:566163f17cde 530
rob.meades@u-blox.com 1:566163f17cde 531 if ((gpI2c->write(gAddress, &(data[0]), 3) == 0) &&
rob.meades@u-blox.com 1:566163f17cde 532 (gpI2c->write(gAddress, &(data[0]), 3) == 0)) {
rob.meades@u-blox.com 1:566163f17cde 533 // Check for success by reading the control status register
rob.meades@u-blox.com 1:566163f17cde 534 data[0] = 0x00; // Set address to first register for control
rob.meades@u-blox.com 1:566163f17cde 535 data[1] = 0x00; // First byte of CONTROL_STATUS sub-command (0x00)
rob.meades@u-blox.com 1:566163f17cde 536 data[2] = 0x00; // Second byte of CONTROL_STATUS sub-command (0x00) (register address will auto-increment)
rob.meades@u-blox.com 1:566163f17cde 537
rob.meades@u-blox.com 1:566163f17cde 538 if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
rob.meades@u-blox.com 1:566163f17cde 539 if (getTwoBytes (0, &controlStatus)) {
rob.meades@u-blox.com 1:566163f17cde 540 // Bit 5 of the high byte is 0 if the device is unsealed
rob.meades@u-blox.com 1:566163f17cde 541 if ((controlStatus & (1 << 13)) == 0) {
rob.meades@u-blox.com 1:566163f17cde 542 success = true;
rob.meades@u-blox.com 1:566163f17cde 543 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 544 printf("BatteryGaugeBq27441 (I2C 0x%02x): now unsealed with seal code 0x%04x (control status 0x%04x).\n", gAddress >> 1, sealCode, controlStatus);
rob.meades@u-blox.com 1:566163f17cde 545 } else {
rob.meades@u-blox.com 1:566163f17cde 546 printf("BatteryGaugeBq27441 (I2C 0x%02x): unseal failed (with seal code 0x%04x, control status 0x%04x).\n", gAddress >> 1, sealCode, controlStatus);
rob.meades@u-blox.com 1:566163f17cde 547 #endif
rob.meades@u-blox.com 1:566163f17cde 548 }
rob.meades@u-blox.com 1:566163f17cde 549 }
rob.meades@u-blox.com 1:566163f17cde 550 }
rob.meades@u-blox.com 1:566163f17cde 551 }
rob.meades@u-blox.com 1:566163f17cde 552
rob.meades@u-blox.com 1:566163f17cde 553 return success;
rob.meades@u-blox.com 1:566163f17cde 554 }
rob.meades@u-blox.com 1:566163f17cde 555
RobMeades 2:93310a83401a 556 // Make sure that the device is awake and has taken a reading.
rob.meades@u-blox.com 1:566163f17cde 557 // Note: the function does its own locking of gpI2C so that it isn't
rob.meades@u-blox.com 1:566163f17cde 558 // locked for the entire time we wait for ADC readings to complete.
rob.meades@u-blox.com 1:566163f17cde 559 bool BatteryGaugeBq27441::makeAdcReading(void)
rob.meades@u-blox.com 1:566163f17cde 560 {
rob.meades@u-blox.com 1:566163f17cde 561 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 562 char data[3];
rob.meades@u-blox.com 1:566163f17cde 563
rob.meades@u-blox.com 1:566163f17cde 564 // Send CLEAR_HIBERNATE
rob.meades@u-blox.com 1:566163f17cde 565 data[0] = 0x00; // Set address to first register for control
rob.meades@u-blox.com 1:566163f17cde 566 data[1] = 0x12; // First byte of CLEAR_HIBERNATE sub-command (0x12)
rob.meades@u-blox.com 1:566163f17cde 567 data[2] = 0x00; // Second byte of CLEAR_HIBERNATE sub-command (0x00) (register address will auto-increment)
rob.meades@u-blox.com 1:566163f17cde 568
rob.meades@u-blox.com 1:566163f17cde 569 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 570 success = (gpI2c->write(gAddress, &(data[0]), 3) == 0);
rob.meades@u-blox.com 1:566163f17cde 571 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 572 wait_ms (ADC_READ_WAIT_MS);
rob.meades@u-blox.com 1:566163f17cde 573
rob.meades@u-blox.com 1:566163f17cde 574 return success;
rob.meades@u-blox.com 1:566163f17cde 575 }
rob.meades@u-blox.com 1:566163f17cde 576
RobMeades 2:93310a83401a 577 // Set Hibernate mode.
rob.meades@u-blox.com 1:566163f17cde 578 // Note: gpI2c should be locked before this is called.
rob.meades@u-blox.com 1:566163f17cde 579 bool BatteryGaugeBq27441::setHibernate(void)
rob.meades@u-blox.com 1:566163f17cde 580 {
rob.meades@u-blox.com 1:566163f17cde 581 char data[3];
rob.meades@u-blox.com 1:566163f17cde 582
rob.meades@u-blox.com 1:566163f17cde 583 // Send SET_HIBERNATE
rob.meades@u-blox.com 1:566163f17cde 584 data[0] = 0x00; // Set address to first register for control
rob.meades@u-blox.com 1:566163f17cde 585 data[1] = 0x11; // First byte of SET_HIBERNATE sub-command (0x11)
rob.meades@u-blox.com 1:566163f17cde 586 data[2] = 0x00; // Second byte of SET_HIBERNATE sub-command (0x00) (register address will auto-increment)
rob.meades@u-blox.com 1:566163f17cde 587
rob.meades@u-blox.com 1:566163f17cde 588 return (gpI2c->write(gAddress, &(data[0]), 3) == 0);
rob.meades@u-blox.com 1:566163f17cde 589 }
rob.meades@u-blox.com 1:566163f17cde 590
rob.meades@u-blox.com 1:566163f17cde 591 //----------------------------------------------------------------
rob.meades@u-blox.com 1:566163f17cde 592 // PUBLIC FUNCTIONS
rob.meades@u-blox.com 1:566163f17cde 593 // ----------------------------------------------------------------
rob.meades@u-blox.com 1:566163f17cde 594
RobMeades 2:93310a83401a 595 // Constructor.
rob.meades@u-blox.com 1:566163f17cde 596 BatteryGaugeBq27441::BatteryGaugeBq27441(void)
rob.meades@u-blox.com 1:566163f17cde 597 {
rob.meades@u-blox.com 1:566163f17cde 598 gpI2c = NULL;
rob.meades@u-blox.com 1:566163f17cde 599 gReady = false;
rob.meades@u-blox.com 1:566163f17cde 600 gGaugeOn = false;
rob.meades@u-blox.com 1:566163f17cde 601 gSealCode = 0;
rob.meades@u-blox.com 1:566163f17cde 602 }
rob.meades@u-blox.com 1:566163f17cde 603
RobMeades 2:93310a83401a 604 // Destructor.
rob.meades@u-blox.com 1:566163f17cde 605 BatteryGaugeBq27441::~BatteryGaugeBq27441(void)
rob.meades@u-blox.com 1:566163f17cde 606 {
rob.meades@u-blox.com 1:566163f17cde 607 }
rob.meades@u-blox.com 1:566163f17cde 608
RobMeades 2:93310a83401a 609 // Initialise ourselves.
rob.meades@u-blox.com 1:566163f17cde 610 bool BatteryGaugeBq27441::init (I2C * pI2c, uint8_t address, uint16_t sealCode)
rob.meades@u-blox.com 1:566163f17cde 611 {
rob.meades@u-blox.com 1:566163f17cde 612 uint16_t answer;
rob.meades@u-blox.com 1:566163f17cde 613 char data[4];
rob.meades@u-blox.com 1:566163f17cde 614
rob.meades@u-blox.com 1:566163f17cde 615 gpI2c = pI2c;
rob.meades@u-blox.com 1:566163f17cde 616 gAddress = address << 1;
rob.meades@u-blox.com 1:566163f17cde 617 gSealCode = sealCode;
rob.meades@u-blox.com 1:566163f17cde 618
rob.meades@u-blox.com 1:566163f17cde 619 if (gpI2c != NULL) {
rob.meades@u-blox.com 1:566163f17cde 620 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 621
rob.meades@u-blox.com 1:566163f17cde 622 // Send a control command to read the firmware version
rob.meades@u-blox.com 1:566163f17cde 623 data[0] = 0x00; // Set address to first register for control
rob.meades@u-blox.com 1:566163f17cde 624 data[1] = 0x02; // First byte of FW_VERSION sub-command (0x02)
rob.meades@u-blox.com 1:566163f17cde 625 data[2] = 0x00; // Second byte of FW_VERSION sub-command (0x00) (register address will auto-increment)
rob.meades@u-blox.com 1:566163f17cde 626
rob.meades@u-blox.com 1:566163f17cde 627 if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
rob.meades@u-blox.com 1:566163f17cde 628 if (getTwoBytes (0, &answer)) {
rob.meades@u-blox.com 1:566163f17cde 629 // The expected response is 0x0109
rob.meades@u-blox.com 1:566163f17cde 630 if (((answer >> 8) == 0x01) && ((answer & 0xff) == 0x09)) {
rob.meades@u-blox.com 1:566163f17cde 631 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 632 if (readExtendedData(112, 0, 4, &(data[0]))) {
rob.meades@u-blox.com 1:566163f17cde 633 printf("BatteryGaugeBq27441 (I2C 0x%02x): seal code stored in chip is 0x%02x%02x%02x%02x.\n", gAddress >> 1, data[0], data[1], data[2], data[3]);
rob.meades@u-blox.com 1:566163f17cde 634 }
rob.meades@u-blox.com 1:566163f17cde 635 #endif
rob.meades@u-blox.com 1:566163f17cde 636 // Set the Sleep Current to the maximum value so that, if
rob.meades@u-blox.com 1:566163f17cde 637 // we tell the chip to go to sleep mode, it will do so
rob.meades@u-blox.com 1:566163f17cde 638 // straight away. Sleep Current is offset 31 in the State
rob.meades@u-blox.com 1:566163f17cde 639 // sub-class (82) and max value is 1000. Since offset 31
rob.meades@u-blox.com 1:566163f17cde 640 // and a length of 2 crosses a 32 bytes boundary this needs
rob.meades@u-blox.com 1:566163f17cde 641 // two separate writes.
rob.meades@u-blox.com 1:566163f17cde 642 data[0] = (char) (1000 >> 8);
rob.meades@u-blox.com 1:566163f17cde 643 data[1] = (char) 1000;
rob.meades@u-blox.com 1:566163f17cde 644
rob.meades@u-blox.com 1:566163f17cde 645 if (writeExtendedData(82, 31, 1, &(data[0])) &&
rob.meades@u-blox.com 1:566163f17cde 646 writeExtendedData(82, 32, 1, &(data[1]))) {
rob.meades@u-blox.com 1:566163f17cde 647 // Now enter Hibernate mode to minimise power consumption
rob.meades@u-blox.com 1:566163f17cde 648 // Need to set either the Hibernate Current or Hibernate Voltage value
rob.meades@u-blox.com 1:566163f17cde 649 // to max, otherwise we won't hibernate, then set the SET_HIBERNATE
rob.meades@u-blox.com 1:566163f17cde 650 // bit. Here we set Hibernate V element (offset 9) in the Power
rob.meades@u-blox.com 1:566163f17cde 651 // sub-class (68) to its max value (see section 6.4.1.6.2 of the
rob.meades@u-blox.com 1:566163f17cde 652 // BQ27441 technical reference manual).
rob.meades@u-blox.com 1:566163f17cde 653 // Note: the cell must also be "relaxed" for this to occur and so
rob.meades@u-blox.com 1:566163f17cde 654 // the chip may still not enter Hibernate mode for a little while.
rob.meades@u-blox.com 1:566163f17cde 655 data[0] = (char) (5000 >> 8);
rob.meades@u-blox.com 1:566163f17cde 656 data[1] = (char) 5000;
rob.meades@u-blox.com 1:566163f17cde 657
rob.meades@u-blox.com 1:566163f17cde 658 if (writeExtendedData(68, 9, 2, &(data[0]))) {
rob.meades@u-blox.com 1:566163f17cde 659 // Now send SET_HIBERNATE
rob.meades@u-blox.com 1:566163f17cde 660 gReady = setHibernate();
rob.meades@u-blox.com 1:566163f17cde 661 }
rob.meades@u-blox.com 1:566163f17cde 662 }
rob.meades@u-blox.com 1:566163f17cde 663 }
rob.meades@u-blox.com 1:566163f17cde 664 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 665 printf("BatteryGaugeBq27441 (I2C 0x%02x): read 0x%04x as FW_VERSION, expected 0x0109.\n", gAddress >> 1, answer);
rob.meades@u-blox.com 1:566163f17cde 666 #endif
rob.meades@u-blox.com 1:566163f17cde 667 }
rob.meades@u-blox.com 1:566163f17cde 668 }
rob.meades@u-blox.com 1:566163f17cde 669 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 670 }
rob.meades@u-blox.com 1:566163f17cde 671
rob.meades@u-blox.com 1:566163f17cde 672 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 673 if (gReady) {
rob.meades@u-blox.com 1:566163f17cde 674 printf("BatteryGaugeBq27441 (I2C 0x%02x): handler initialised.\r\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 675 } else {
rob.meades@u-blox.com 1:566163f17cde 676 printf("BatteryGaugeBq27441 (I2C 0x%02x): init NOT successful.\r\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 677 }
rob.meades@u-blox.com 1:566163f17cde 678 #endif
rob.meades@u-blox.com 1:566163f17cde 679
rob.meades@u-blox.com 1:566163f17cde 680 return gReady;
rob.meades@u-blox.com 1:566163f17cde 681 }
rob.meades@u-blox.com 1:566163f17cde 682
RobMeades 2:93310a83401a 683 // Switch on the battery capacity monitor.
rob.meades@u-blox.com 1:566163f17cde 684 bool BatteryGaugeBq27441::enableGauge (bool isSlow)
rob.meades@u-blox.com 1:566163f17cde 685 {
rob.meades@u-blox.com 1:566163f17cde 686 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 687 char data[3];
rob.meades@u-blox.com 1:566163f17cde 688
rob.meades@u-blox.com 1:566163f17cde 689 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 690 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 691 // Make sure that we are not in hibernate
rob.meades@u-blox.com 1:566163f17cde 692 data[0] = 0x00; // Set address to first register for control
rob.meades@u-blox.com 1:566163f17cde 693 data[1] = 0x12; // First byte of CLEAR_HIBERNATE sub-command (0x12)
rob.meades@u-blox.com 1:566163f17cde 694 data[2] = 0x00; // Second byte of CLEAR_HIBERNATE sub-command (0x00)
rob.meades@u-blox.com 1:566163f17cde 695 if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
rob.meades@u-blox.com 1:566163f17cde 696 gGaugeOn = true;
rob.meades@u-blox.com 1:566163f17cde 697 // Read the OpConfig register which is in the Registers sub-class
rob.meades@u-blox.com 1:566163f17cde 698 // (64) at offset 0.
rob.meades@u-blox.com 1:566163f17cde 699 if (readExtendedData(64, 0, 2, &(data[0]))) {
rob.meades@u-blox.com 1:566163f17cde 700 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 701 printf("BatteryGaugeBq27441 (I2C 0x%02x): OpConfig is 0x%02x%02x.\r\n", gAddress >> 1, data[0], data[1]);
rob.meades@u-blox.com 1:566163f17cde 702 #endif
rob.meades@u-blox.com 1:566163f17cde 703 // SLEEP mode is bit 5 of the low byte of OpConfig. In SLEEP
rob.meades@u-blox.com 1:566163f17cde 704 // mode a reading is taken every 20 seconds.
rob.meades@u-blox.com 1:566163f17cde 705 if (isSlow && ((data[1] & (1 << 5)) == 0)) {
rob.meades@u-blox.com 1:566163f17cde 706 // Set the SLEEP bit 'cos it's not set and needs to be
rob.meades@u-blox.com 1:566163f17cde 707 data[1] |= 1 << 5;
rob.meades@u-blox.com 1:566163f17cde 708 // Write the new value back
rob.meades@u-blox.com 1:566163f17cde 709 success = writeExtendedData(64, 0, 2, &(data[0]));
rob.meades@u-blox.com 1:566163f17cde 710 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 711 if (success) {
rob.meades@u-blox.com 1:566163f17cde 712 printf("BatteryGaugeBq27441 (I2C 0x%02x): OpConfig becomes 0x%02x%02x.\r\n", gAddress >> 1, data[0], data[1]);
rob.meades@u-blox.com 1:566163f17cde 713 }
rob.meades@u-blox.com 1:566163f17cde 714 #endif
rob.meades@u-blox.com 1:566163f17cde 715 } else if (!isSlow && ((data[1] & (1 << 5)) != 0)) {
rob.meades@u-blox.com 1:566163f17cde 716 // Clear the SLEEP bit 'cos it's set and shouldn't be
rob.meades@u-blox.com 1:566163f17cde 717 data[1] &= ~(1 << 5);
rob.meades@u-blox.com 1:566163f17cde 718 // Write the new value back
rob.meades@u-blox.com 1:566163f17cde 719 success = writeExtendedData(64, 0, 2, &(data[0]));
rob.meades@u-blox.com 1:566163f17cde 720 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 721 if (success) {
rob.meades@u-blox.com 1:566163f17cde 722 printf("BatteryGaugeBq27441 (I2C 0x%02x): OpConfig becomes 0x%02x%02x.\r\n", gAddress >> 1, data[0], data[1]);
rob.meades@u-blox.com 1:566163f17cde 723 }
rob.meades@u-blox.com 1:566163f17cde 724 #endif
rob.meades@u-blox.com 1:566163f17cde 725 } else {
rob.meades@u-blox.com 1:566163f17cde 726 success = true;
rob.meades@u-blox.com 1:566163f17cde 727 }
rob.meades@u-blox.com 1:566163f17cde 728 }
rob.meades@u-blox.com 1:566163f17cde 729 }
rob.meades@u-blox.com 1:566163f17cde 730
rob.meades@u-blox.com 1:566163f17cde 731 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 732 }
rob.meades@u-blox.com 1:566163f17cde 733
rob.meades@u-blox.com 1:566163f17cde 734 return success;
rob.meades@u-blox.com 1:566163f17cde 735 }
rob.meades@u-blox.com 1:566163f17cde 736
RobMeades 2:93310a83401a 737 // Switch off the battery capacity monitor.
rob.meades@u-blox.com 1:566163f17cde 738 bool BatteryGaugeBq27441::disableGauge (void)
rob.meades@u-blox.com 1:566163f17cde 739 {
rob.meades@u-blox.com 1:566163f17cde 740 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 741
rob.meades@u-blox.com 1:566163f17cde 742 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 743 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 744 // Send SET_HIBERNATE
rob.meades@u-blox.com 1:566163f17cde 745 if (setHibernate()) {
rob.meades@u-blox.com 1:566163f17cde 746 success = true;
rob.meades@u-blox.com 1:566163f17cde 747 gGaugeOn = false;
rob.meades@u-blox.com 1:566163f17cde 748 }
rob.meades@u-blox.com 1:566163f17cde 749
rob.meades@u-blox.com 1:566163f17cde 750 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 751 }
rob.meades@u-blox.com 1:566163f17cde 752
rob.meades@u-blox.com 1:566163f17cde 753 return success;
rob.meades@u-blox.com 1:566163f17cde 754 }
rob.meades@u-blox.com 1:566163f17cde 755
rob.meades@u-blox.com 3:ebd56471d57c 756 // Determine whether battery gauging is enabled.
rob.meades@u-blox.com 3:ebd56471d57c 757 bool BatteryGaugeBq27441::isGaugeEnabled(void)
rob.meades@u-blox.com 3:ebd56471d57c 758 {
rob.meades@u-blox.com 3:ebd56471d57c 759 return gGaugeOn;
rob.meades@u-blox.com 3:ebd56471d57c 760 }
rob.meades@u-blox.com 3:ebd56471d57c 761
rob.meades@u-blox.com 5:63b325f2c21a 762 // Disable the battery detect pin.
rob.meades@u-blox.com 5:63b325f2c21a 763 bool BatteryGaugeBq27441::disableBatteryDetect (void)
rob.meades@u-blox.com 5:63b325f2c21a 764 {
rob.meades@u-blox.com 5:63b325f2c21a 765 bool success = false;
rob.meades@u-blox.com 5:63b325f2c21a 766 char data[3];
rob.meades@u-blox.com 5:63b325f2c21a 767
rob.meades@u-blox.com 5:63b325f2c21a 768 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 5:63b325f2c21a 769 gpI2c->lock();
rob.meades@u-blox.com 5:63b325f2c21a 770 // Read the OpConfig register which is in the Registers sub-class
rob.meades@u-blox.com 5:63b325f2c21a 771 // (64) at offset 0.
rob.meades@u-blox.com 5:63b325f2c21a 772 if (readExtendedData(64, 0, 2, &(data[0]))) {
rob.meades@u-blox.com 5:63b325f2c21a 773 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 5:63b325f2c21a 774 printf("BatteryGaugeBq27441 (I2C 0x%02x): OpConfig is 0x%02x%02x.\r\n", gAddress >> 1, data[0], data[1]);
rob.meades@u-blox.com 5:63b325f2c21a 775 #endif
rob.meades@u-blox.com 5:63b325f2c21a 776 // Battery Insertion Enabled is bit 5 of the high byte of OpConfig.
rob.meades@u-blox.com 5:63b325f2c21a 777 // 1 means that the battery input pin is enabled
rob.meades@u-blox.com 5:63b325f2c21a 778 if (((data[0] & (1 << 5)) != 0)) {
rob.meades@u-blox.com 5:63b325f2c21a 779 // Clear the BIE bit 'cos it's set and is shouldn't be
rob.meades@u-blox.com 5:63b325f2c21a 780 data[0] &= ~(1 << 5);
rob.meades@u-blox.com 5:63b325f2c21a 781 // Write the new value back
rob.meades@u-blox.com 5:63b325f2c21a 782 if (writeExtendedData(64, 0, 2, &(data[0]))) {
rob.meades@u-blox.com 5:63b325f2c21a 783 // Send the Battery Inserted message as we can't do gauging otherwise
rob.meades@u-blox.com 5:63b325f2c21a 784 data[0] = 0x00; // Set address to first register for control
rob.meades@u-blox.com 5:63b325f2c21a 785 data[1] = 0x0C; // First byte of BAT_INSERT sub-command (0x0C)
rob.meades@u-blox.com 5:63b325f2c21a 786 data[2] = 0x00; // Second byte of BAT_INSERT sub-command (0x00)
rob.meades@u-blox.com 5:63b325f2c21a 787 success = gpI2c->write(gAddress, &(data[0]), 3) == 0;
rob.meades@u-blox.com 5:63b325f2c21a 788 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 5:63b325f2c21a 789 if (success) {
rob.meades@u-blox.com 5:63b325f2c21a 790 printf("BatteryGaugeBq27441 (I2C 0x%02x): BIE disabled, BAT_INSERT sent, OpConfig becomes 0x%02x%02x.\r\n", gAddress >> 1, data[0], data[1]);
rob.meades@u-blox.com 5:63b325f2c21a 791 }
rob.meades@u-blox.com 5:63b325f2c21a 792 #endif
rob.meades@u-blox.com 5:63b325f2c21a 793 }
rob.meades@u-blox.com 5:63b325f2c21a 794 } else {
rob.meades@u-blox.com 5:63b325f2c21a 795 success = true;
rob.meades@u-blox.com 5:63b325f2c21a 796 }
rob.meades@u-blox.com 5:63b325f2c21a 797 }
rob.meades@u-blox.com 5:63b325f2c21a 798
rob.meades@u-blox.com 5:63b325f2c21a 799 // Set hibernate again if we are not monitoring
rob.meades@u-blox.com 5:63b325f2c21a 800 if (!gGaugeOn) {
rob.meades@u-blox.com 5:63b325f2c21a 801 setHibernate();
rob.meades@u-blox.com 5:63b325f2c21a 802 }
rob.meades@u-blox.com 5:63b325f2c21a 803
rob.meades@u-blox.com 5:63b325f2c21a 804 gpI2c->unlock();
rob.meades@u-blox.com 5:63b325f2c21a 805 }
rob.meades@u-blox.com 5:63b325f2c21a 806
rob.meades@u-blox.com 5:63b325f2c21a 807 return success;
rob.meades@u-blox.com 5:63b325f2c21a 808 }
rob.meades@u-blox.com 5:63b325f2c21a 809
rob.meades@u-blox.com 5:63b325f2c21a 810 // Enable the battery detect pin.
rob.meades@u-blox.com 5:63b325f2c21a 811 bool BatteryGaugeBq27441::enableBatteryDetect (void)
rob.meades@u-blox.com 5:63b325f2c21a 812 {
rob.meades@u-blox.com 5:63b325f2c21a 813 bool success = false;
rob.meades@u-blox.com 5:63b325f2c21a 814 char data[3];
rob.meades@u-blox.com 5:63b325f2c21a 815
rob.meades@u-blox.com 5:63b325f2c21a 816 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 5:63b325f2c21a 817 gpI2c->lock();
rob.meades@u-blox.com 5:63b325f2c21a 818 // Read the OpConfig register which is in the Registers sub-class
rob.meades@u-blox.com 5:63b325f2c21a 819 // (64) at offset 0.
rob.meades@u-blox.com 5:63b325f2c21a 820 if (readExtendedData(64, 0, 2, &(data[0]))) {
rob.meades@u-blox.com 5:63b325f2c21a 821 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 5:63b325f2c21a 822 printf("BatteryGaugeBq27441 (I2C 0x%02x): OpConfig is 0x%02x%02x.\r\n", gAddress >> 1, data[0], data[1]);
rob.meades@u-blox.com 5:63b325f2c21a 823 #endif
rob.meades@u-blox.com 5:63b325f2c21a 824 // Battery Insertion Enabled is bit 5 of the high byte of OpConfig.
rob.meades@u-blox.com 5:63b325f2c21a 825 // 1 means that the battery input pin is enabled
rob.meades@u-blox.com 5:63b325f2c21a 826 if (((data[0] & (1 << 5)) == 0)) {
rob.meades@u-blox.com 5:63b325f2c21a 827 // Set the BIE bit 'cos it's not set and needs to be
rob.meades@u-blox.com 5:63b325f2c21a 828 data[0] |= 1 << 5;
rob.meades@u-blox.com 5:63b325f2c21a 829 // Write the new value back
rob.meades@u-blox.com 5:63b325f2c21a 830 success = writeExtendedData(64, 0, 2, &(data[0]));
rob.meades@u-blox.com 5:63b325f2c21a 831 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 5:63b325f2c21a 832 if (success) {
rob.meades@u-blox.com 5:63b325f2c21a 833 printf("BatteryGaugeBq27441 (I2C 0x%02x): BIE enabled, OpConfig becomes 0x%02x%02x.\r\n", gAddress >> 1, data[0], data[1]);
rob.meades@u-blox.com 5:63b325f2c21a 834 }
rob.meades@u-blox.com 5:63b325f2c21a 835 #endif
rob.meades@u-blox.com 5:63b325f2c21a 836 } else {
rob.meades@u-blox.com 5:63b325f2c21a 837 success = true;
rob.meades@u-blox.com 5:63b325f2c21a 838 }
rob.meades@u-blox.com 5:63b325f2c21a 839 }
rob.meades@u-blox.com 5:63b325f2c21a 840
rob.meades@u-blox.com 5:63b325f2c21a 841 // Set hibernate again if we are not monitoring
rob.meades@u-blox.com 5:63b325f2c21a 842 if (!gGaugeOn) {
rob.meades@u-blox.com 5:63b325f2c21a 843 setHibernate();
rob.meades@u-blox.com 5:63b325f2c21a 844 }
rob.meades@u-blox.com 5:63b325f2c21a 845
rob.meades@u-blox.com 5:63b325f2c21a 846 gpI2c->unlock();
rob.meades@u-blox.com 5:63b325f2c21a 847 }
rob.meades@u-blox.com 5:63b325f2c21a 848
rob.meades@u-blox.com 5:63b325f2c21a 849 return success;
rob.meades@u-blox.com 5:63b325f2c21a 850 }
rob.meades@u-blox.com 5:63b325f2c21a 851
RobMeades 2:93310a83401a 852 // Check whether a battery has been detected or not.
rob.meades@u-blox.com 1:566163f17cde 853 bool BatteryGaugeBq27441::isBatteryDetected (void)
rob.meades@u-blox.com 1:566163f17cde 854 {
rob.meades@u-blox.com 1:566163f17cde 855 bool isDetected = false;
rob.meades@u-blox.com 1:566163f17cde 856 uint16_t data;
rob.meades@u-blox.com 1:566163f17cde 857
rob.meades@u-blox.com 1:566163f17cde 858 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 859
rob.meades@u-blox.com 1:566163f17cde 860 // Make sure there's a recent reading
rob.meades@u-blox.com 1:566163f17cde 861 if (gGaugeOn || makeAdcReading()) {
rob.meades@u-blox.com 1:566163f17cde 862 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 863 // Read from the flags register address
rob.meades@u-blox.com 1:566163f17cde 864 if (getTwoBytes (0x06, &data)) {
rob.meades@u-blox.com 1:566163f17cde 865
rob.meades@u-blox.com 1:566163f17cde 866 // If bit 3 is set then a battery has been detected
rob.meades@u-blox.com 1:566163f17cde 867 if (data & (1 << 3)) {
rob.meades@u-blox.com 1:566163f17cde 868 isDetected = true;
rob.meades@u-blox.com 1:566163f17cde 869 }
rob.meades@u-blox.com 1:566163f17cde 870
rob.meades@u-blox.com 1:566163f17cde 871 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 872 if (isDetected) {
rob.meades@u-blox.com 1:566163f17cde 873 printf("BatteryGaugeBq27441 (I2C 0x%02x): battery detected (flags 0x%04x).\n", gAddress >> 1, data);
rob.meades@u-blox.com 1:566163f17cde 874 } else {
rob.meades@u-blox.com 1:566163f17cde 875 printf("BatteryGaugeBq27441 (I2C 0x%02x): battery NOT detected (flags 0x%04x).\n", gAddress >> 1, data);
rob.meades@u-blox.com 1:566163f17cde 876 }
rob.meades@u-blox.com 1:566163f17cde 877 #endif
rob.meades@u-blox.com 1:566163f17cde 878 }
rob.meades@u-blox.com 1:566163f17cde 879
rob.meades@u-blox.com 1:566163f17cde 880 // Set hibernate again if we are not monitoring
rob.meades@u-blox.com 1:566163f17cde 881 if (!gGaugeOn) {
rob.meades@u-blox.com 1:566163f17cde 882 setHibernate();
rob.meades@u-blox.com 1:566163f17cde 883 }
rob.meades@u-blox.com 1:566163f17cde 884
rob.meades@u-blox.com 1:566163f17cde 885 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 886 }
rob.meades@u-blox.com 1:566163f17cde 887 }
rob.meades@u-blox.com 1:566163f17cde 888
rob.meades@u-blox.com 1:566163f17cde 889 return isDetected;
rob.meades@u-blox.com 1:566163f17cde 890 }
rob.meades@u-blox.com 1:566163f17cde 891
RobMeades 2:93310a83401a 892 // Get the temperature of the chip.
rob.meades@u-blox.com 1:566163f17cde 893 bool BatteryGaugeBq27441::getTemperature (int32_t *pTemperatureC)
rob.meades@u-blox.com 1:566163f17cde 894 {
rob.meades@u-blox.com 1:566163f17cde 895 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 896 int32_t temperatureC = 0;
rob.meades@u-blox.com 1:566163f17cde 897 uint16_t data;
rob.meades@u-blox.com 1:566163f17cde 898
rob.meades@u-blox.com 1:566163f17cde 899 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 900 // Make sure there's a recent reading
rob.meades@u-blox.com 1:566163f17cde 901 if (gGaugeOn || makeAdcReading()) {
rob.meades@u-blox.com 1:566163f17cde 902 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 903 // Read from the temperature register address
rob.meades@u-blox.com 1:566163f17cde 904 if (getTwoBytes (0x02, &data)) {
rob.meades@u-blox.com 1:566163f17cde 905 success = true;
rob.meades@u-blox.com 1:566163f17cde 906
rob.meades@u-blox.com 1:566163f17cde 907 // The answer is in units of 0.1 K, so convert to C
rob.meades@u-blox.com 1:566163f17cde 908 temperatureC = ((int32_t) data / 10) - 273;
rob.meades@u-blox.com 1:566163f17cde 909
rob.meades@u-blox.com 1:566163f17cde 910 if (pTemperatureC) {
rob.meades@u-blox.com 1:566163f17cde 911 *pTemperatureC = temperatureC;
rob.meades@u-blox.com 1:566163f17cde 912 }
rob.meades@u-blox.com 1:566163f17cde 913
rob.meades@u-blox.com 1:566163f17cde 914 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 5:63b325f2c21a 915 printf("BatteryGaugeBq27441 (I2C 0x%02x): chip temperature %.1f K, so %d C.\n", gAddress >> 1, ((float) data) / 10, (int) temperatureC);
rob.meades@u-blox.com 1:566163f17cde 916 #endif
rob.meades@u-blox.com 1:566163f17cde 917 }
rob.meades@u-blox.com 1:566163f17cde 918
rob.meades@u-blox.com 1:566163f17cde 919 // Return to sleep if we are allowed to
rob.meades@u-blox.com 1:566163f17cde 920 if (!gGaugeOn && !setHibernate()) {
rob.meades@u-blox.com 1:566163f17cde 921 success = false;
rob.meades@u-blox.com 1:566163f17cde 922 }
rob.meades@u-blox.com 1:566163f17cde 923
rob.meades@u-blox.com 1:566163f17cde 924 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 925 }
rob.meades@u-blox.com 1:566163f17cde 926 }
rob.meades@u-blox.com 1:566163f17cde 927
rob.meades@u-blox.com 1:566163f17cde 928 return success;
rob.meades@u-blox.com 1:566163f17cde 929 }
rob.meades@u-blox.com 1:566163f17cde 930
RobMeades 2:93310a83401a 931 // Get the voltage of the battery.
rob.meades@u-blox.com 1:566163f17cde 932 bool BatteryGaugeBq27441::getVoltage (int32_t *pVoltageMV)
rob.meades@u-blox.com 1:566163f17cde 933 {
rob.meades@u-blox.com 1:566163f17cde 934 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 935 uint16_t data = 0;
rob.meades@u-blox.com 1:566163f17cde 936
rob.meades@u-blox.com 1:566163f17cde 937 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 938 // Make sure there's a recent reading
rob.meades@u-blox.com 1:566163f17cde 939 if (gGaugeOn || makeAdcReading()) {
rob.meades@u-blox.com 1:566163f17cde 940 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 941 // Read from the voltage register address
rob.meades@u-blox.com 1:566163f17cde 942 if (getTwoBytes (0x04, &data)) {
rob.meades@u-blox.com 1:566163f17cde 943 success = true;
rob.meades@u-blox.com 1:566163f17cde 944
rob.meades@u-blox.com 1:566163f17cde 945 // The answer is in mV
rob.meades@u-blox.com 1:566163f17cde 946 if (pVoltageMV) {
rob.meades@u-blox.com 1:566163f17cde 947 *pVoltageMV = (int32_t) data;
rob.meades@u-blox.com 1:566163f17cde 948 }
rob.meades@u-blox.com 1:566163f17cde 949
rob.meades@u-blox.com 1:566163f17cde 950 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 951 printf("BatteryGaugeBq27441 (I2C 0x%02x): battery voltage %.3f V.\n", gAddress >> 1, ((float) data) / 1000);
rob.meades@u-blox.com 1:566163f17cde 952 #endif
rob.meades@u-blox.com 1:566163f17cde 953 }
rob.meades@u-blox.com 1:566163f17cde 954
rob.meades@u-blox.com 1:566163f17cde 955 // Return to sleep if we are allowed to
rob.meades@u-blox.com 1:566163f17cde 956 if (!gGaugeOn && !setHibernate()) {
rob.meades@u-blox.com 1:566163f17cde 957 success = false;
rob.meades@u-blox.com 1:566163f17cde 958 }
rob.meades@u-blox.com 1:566163f17cde 959
rob.meades@u-blox.com 1:566163f17cde 960 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 961 }
rob.meades@u-blox.com 1:566163f17cde 962 }
rob.meades@u-blox.com 1:566163f17cde 963
rob.meades@u-blox.com 1:566163f17cde 964 return success;
rob.meades@u-blox.com 1:566163f17cde 965 }
rob.meades@u-blox.com 1:566163f17cde 966
breadboardbasics 6:998cc334f8f2 967 // Get the power output of the battery
breadboardbasics 6:998cc334f8f2 968 bool BatteryGaugeBq27441::getPower (int32_t *pPowerMW)
breadboardbasics 6:998cc334f8f2 969 {
breadboardbasics 6:998cc334f8f2 970 bool success = false;
breadboardbasics 6:998cc334f8f2 971 int16_t data = 0;
breadboardbasics 6:998cc334f8f2 972
breadboardbasics 6:998cc334f8f2 973 if (gReady && (gpI2c != NULL)) {
breadboardbasics 6:998cc334f8f2 974 // Make sure there's a recent reading
breadboardbasics 6:998cc334f8f2 975 if (gGaugeOn || makeAdcReading()) {
breadboardbasics 6:998cc334f8f2 976 gpI2c->lock();
breadboardbasics 6:998cc334f8f2 977 // Read from the power register address
breadboardbasics 6:998cc334f8f2 978 if (getTwoBytesSigned (0x18, &data)) {
breadboardbasics 6:998cc334f8f2 979 success = true;
breadboardbasics 6:998cc334f8f2 980
breadboardbasics 6:998cc334f8f2 981 // The answer is in mW
breadboardbasics 6:998cc334f8f2 982 if (pPowerMW) {
breadboardbasics 6:998cc334f8f2 983 *pPowerMW = (int32_t) data;
breadboardbasics 6:998cc334f8f2 984 }
breadboardbasics 6:998cc334f8f2 985
breadboardbasics 6:998cc334f8f2 986 }
breadboardbasics 6:998cc334f8f2 987
breadboardbasics 6:998cc334f8f2 988 // Return to sleep if we are allowed to
breadboardbasics 6:998cc334f8f2 989 if (!gGaugeOn && !setHibernate()) {
breadboardbasics 6:998cc334f8f2 990 success = false;
breadboardbasics 6:998cc334f8f2 991 }
breadboardbasics 6:998cc334f8f2 992
breadboardbasics 6:998cc334f8f2 993 gpI2c->unlock();
breadboardbasics 6:998cc334f8f2 994 }
breadboardbasics 6:998cc334f8f2 995 }
breadboardbasics 6:998cc334f8f2 996
breadboardbasics 6:998cc334f8f2 997 return success;
breadboardbasics 6:998cc334f8f2 998 }
breadboardbasics 6:998cc334f8f2 999
RobMeades 2:93310a83401a 1000 // Get the current flowing from the battery.
rob.meades@u-blox.com 1:566163f17cde 1001 bool BatteryGaugeBq27441::getCurrent (int32_t *pCurrentMA)
rob.meades@u-blox.com 1:566163f17cde 1002 {
rob.meades@u-blox.com 1:566163f17cde 1003 bool success = false;
breadboardbasics 6:998cc334f8f2 1004 int16_t data;
rob.meades@u-blox.com 1:566163f17cde 1005
rob.meades@u-blox.com 1:566163f17cde 1006 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 1007 // Make sure there's a recent reading
rob.meades@u-blox.com 1:566163f17cde 1008 if (gGaugeOn || makeAdcReading()) {
rob.meades@u-blox.com 1:566163f17cde 1009 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 1010 // Read from the average current register address
breadboardbasics 6:998cc334f8f2 1011 if (getTwoBytesSigned (0x10, &data)) {
rob.meades@u-blox.com 1:566163f17cde 1012 success = true;
rob.meades@u-blox.com 1:566163f17cde 1013
rob.meades@u-blox.com 1:566163f17cde 1014 if (pCurrentMA) {
breadboardbasics 6:998cc334f8f2 1015 *pCurrentMA = (int32_t) data;
rob.meades@u-blox.com 1:566163f17cde 1016 }
rob.meades@u-blox.com 1:566163f17cde 1017 }
rob.meades@u-blox.com 1:566163f17cde 1018
rob.meades@u-blox.com 1:566163f17cde 1019 // Return to sleep if we are allowed to
rob.meades@u-blox.com 1:566163f17cde 1020 if (!gGaugeOn && !setHibernate()) {
rob.meades@u-blox.com 1:566163f17cde 1021 success = false;
rob.meades@u-blox.com 1:566163f17cde 1022 }
rob.meades@u-blox.com 1:566163f17cde 1023
rob.meades@u-blox.com 1:566163f17cde 1024 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 1025 }
rob.meades@u-blox.com 1:566163f17cde 1026 }
rob.meades@u-blox.com 1:566163f17cde 1027
rob.meades@u-blox.com 1:566163f17cde 1028 return success;
rob.meades@u-blox.com 1:566163f17cde 1029 }
rob.meades@u-blox.com 1:566163f17cde 1030
RobMeades 2:93310a83401a 1031 // Get the remaining battery capacity.
rob.meades@u-blox.com 1:566163f17cde 1032 bool BatteryGaugeBq27441::getRemainingCapacity (int32_t *pCapacityMAh)
rob.meades@u-blox.com 1:566163f17cde 1033 {
rob.meades@u-blox.com 1:566163f17cde 1034 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 1035 uint16_t data = 0;
rob.meades@u-blox.com 1:566163f17cde 1036
rob.meades@u-blox.com 1:566163f17cde 1037 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 1038 // Make sure there's a recent reading
rob.meades@u-blox.com 1:566163f17cde 1039 if (gGaugeOn || makeAdcReading()) {
rob.meades@u-blox.com 1:566163f17cde 1040 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 1041 // Read from the RemainingCapacity register address
rob.meades@u-blox.com 1:566163f17cde 1042
rob.meades@u-blox.com 1:566163f17cde 1043 if (getTwoBytes (0x0c, &data)) {
rob.meades@u-blox.com 1:566163f17cde 1044 success = true;
rob.meades@u-blox.com 1:566163f17cde 1045
rob.meades@u-blox.com 1:566163f17cde 1046 // The answer is in mAh
rob.meades@u-blox.com 1:566163f17cde 1047 if (pCapacityMAh) {
rob.meades@u-blox.com 1:566163f17cde 1048 *pCapacityMAh = (int32_t) data;
rob.meades@u-blox.com 1:566163f17cde 1049 }
rob.meades@u-blox.com 1:566163f17cde 1050
rob.meades@u-blox.com 1:566163f17cde 1051 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 1052 printf("BatteryGaugeBq27441 (I2C 0x%02x): remaining battery capacity %u mAh.\n", gAddress >> 1, data);
rob.meades@u-blox.com 1:566163f17cde 1053 #endif
rob.meades@u-blox.com 1:566163f17cde 1054 }
rob.meades@u-blox.com 1:566163f17cde 1055
rob.meades@u-blox.com 1:566163f17cde 1056 // Return to sleep if we are allowed to
rob.meades@u-blox.com 1:566163f17cde 1057 if (!gGaugeOn && !setHibernate()) {
rob.meades@u-blox.com 1:566163f17cde 1058 success = false;
rob.meades@u-blox.com 1:566163f17cde 1059 }
rob.meades@u-blox.com 1:566163f17cde 1060
rob.meades@u-blox.com 1:566163f17cde 1061 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 1062 }
rob.meades@u-blox.com 1:566163f17cde 1063 }
rob.meades@u-blox.com 1:566163f17cde 1064
rob.meades@u-blox.com 1:566163f17cde 1065 return success;
rob.meades@u-blox.com 1:566163f17cde 1066 }
rob.meades@u-blox.com 1:566163f17cde 1067
RobMeades 2:93310a83401a 1068 // Get the battery percentage remaining
rob.meades@u-blox.com 1:566163f17cde 1069 bool BatteryGaugeBq27441::getRemainingPercentage (int32_t *pBatteryPercent)
rob.meades@u-blox.com 1:566163f17cde 1070 {
rob.meades@u-blox.com 1:566163f17cde 1071 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 1072 uint16_t data = 0;
rob.meades@u-blox.com 1:566163f17cde 1073
rob.meades@u-blox.com 1:566163f17cde 1074 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 1075 // Make sure there's a recent reading
rob.meades@u-blox.com 1:566163f17cde 1076 if (gGaugeOn || makeAdcReading()) {
rob.meades@u-blox.com 1:566163f17cde 1077 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 1078
rob.meades@u-blox.com 1:566163f17cde 1079 // Wake up and take a reading if we have to
rob.meades@u-blox.com 1:566163f17cde 1080 if (!gGaugeOn && !setHibernate()) {
rob.meades@u-blox.com 1:566163f17cde 1081 success = false;
rob.meades@u-blox.com 1:566163f17cde 1082 }
rob.meades@u-blox.com 1:566163f17cde 1083
rob.meades@u-blox.com 1:566163f17cde 1084 // Read from the StateOfCharge register address
rob.meades@u-blox.com 1:566163f17cde 1085 if (getTwoBytes (0x1c, &data)) {
rob.meades@u-blox.com 1:566163f17cde 1086 success = true;
rob.meades@u-blox.com 1:566163f17cde 1087
rob.meades@u-blox.com 1:566163f17cde 1088 if (pBatteryPercent) {
rob.meades@u-blox.com 1:566163f17cde 1089 *pBatteryPercent = (int32_t) data;
rob.meades@u-blox.com 1:566163f17cde 1090 }
rob.meades@u-blox.com 1:566163f17cde 1091
rob.meades@u-blox.com 1:566163f17cde 1092 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 1093 printf("BatteryGaugeBq27441 (I2C 0x%02x): remaining battery percentage %u%%.\n", gAddress >> 1, data);
rob.meades@u-blox.com 1:566163f17cde 1094 #endif
rob.meades@u-blox.com 1:566163f17cde 1095 }
rob.meades@u-blox.com 1:566163f17cde 1096
rob.meades@u-blox.com 1:566163f17cde 1097 // Return to sleep if we are allowed to
rob.meades@u-blox.com 1:566163f17cde 1098 if (!gGaugeOn && !setHibernate()) {
rob.meades@u-blox.com 1:566163f17cde 1099 success = false;
rob.meades@u-blox.com 1:566163f17cde 1100 }
rob.meades@u-blox.com 1:566163f17cde 1101
rob.meades@u-blox.com 1:566163f17cde 1102 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 1103 }
rob.meades@u-blox.com 1:566163f17cde 1104 }
rob.meades@u-blox.com 1:566163f17cde 1105
rob.meades@u-blox.com 1:566163f17cde 1106 return success;
rob.meades@u-blox.com 1:566163f17cde 1107 }
rob.meades@u-blox.com 1:566163f17cde 1108
RobMeades 2:93310a83401a 1109 // Advanced function to read a configuration data block.
rob.meades@u-blox.com 1:566163f17cde 1110 bool BatteryGaugeBq27441::advancedGetConfig(uint8_t subClassId, int32_t offset, int32_t length, char * pData)
rob.meades@u-blox.com 1:566163f17cde 1111 {
rob.meades@u-blox.com 1:566163f17cde 1112 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 1113
rob.meades@u-blox.com 1:566163f17cde 1114 if (gReady && (gpI2c != NULL) && (pData != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 1115 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 1116 // Read the extended configuration data
rob.meades@u-blox.com 1:566163f17cde 1117 success = readExtendedData(subClassId, offset, length, pData);
rob.meades@u-blox.com 1:566163f17cde 1118 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 1119 if (success) {
rob.meades@u-blox.com 5:63b325f2c21a 1120 printf("BatteryGaugeBq27441 (I2C 0x%02x): read extended data with subClassId %d from offset %d.\n", gAddress >> 1, subClassId, (int) offset);
rob.meades@u-blox.com 1:566163f17cde 1121 }
rob.meades@u-blox.com 1:566163f17cde 1122 #endif
rob.meades@u-blox.com 1:566163f17cde 1123
rob.meades@u-blox.com 1:566163f17cde 1124 // Return to sleep if we are allowed to
rob.meades@u-blox.com 1:566163f17cde 1125 if (!gGaugeOn && !setHibernate()) {
rob.meades@u-blox.com 1:566163f17cde 1126 success = false;
rob.meades@u-blox.com 1:566163f17cde 1127 }
rob.meades@u-blox.com 1:566163f17cde 1128
rob.meades@u-blox.com 1:566163f17cde 1129 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 1130 }
rob.meades@u-blox.com 1:566163f17cde 1131
rob.meades@u-blox.com 1:566163f17cde 1132 return success;
rob.meades@u-blox.com 1:566163f17cde 1133 }
rob.meades@u-blox.com 1:566163f17cde 1134
RobMeades 2:93310a83401a 1135 // Advanced function to write a configuration data block.
rob.meades@u-blox.com 1:566163f17cde 1136 bool BatteryGaugeBq27441::advancedSetConfig(uint8_t subClassId, int32_t offset, int32_t length, const char * pData)
rob.meades@u-blox.com 1:566163f17cde 1137 {
rob.meades@u-blox.com 1:566163f17cde 1138 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 1139
rob.meades@u-blox.com 1:566163f17cde 1140 if (gReady && (gpI2c != NULL) && (pData != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 1141 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 1142 // Write the extended configuration data
rob.meades@u-blox.com 1:566163f17cde 1143 success = writeExtendedData(subClassId, offset, length, pData);
rob.meades@u-blox.com 1:566163f17cde 1144 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 1145 if (success) {
rob.meades@u-blox.com 5:63b325f2c21a 1146 printf("BatteryGaugeBq27441 (I2C 0x%02x): written %d byte(s) of extended data with subClassId %d from offset %d.\n", gAddress >> 1, (int) length, subClassId, (int) offset);
rob.meades@u-blox.com 1:566163f17cde 1147 }
rob.meades@u-blox.com 1:566163f17cde 1148 #endif
rob.meades@u-blox.com 1:566163f17cde 1149
rob.meades@u-blox.com 1:566163f17cde 1150 // Return to sleep if we are allowed to
rob.meades@u-blox.com 1:566163f17cde 1151 if (!gGaugeOn && !setHibernate()) {
rob.meades@u-blox.com 1:566163f17cde 1152 success = false;
rob.meades@u-blox.com 1:566163f17cde 1153 }
rob.meades@u-blox.com 1:566163f17cde 1154
rob.meades@u-blox.com 1:566163f17cde 1155 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 1156 }
rob.meades@u-blox.com 1:566163f17cde 1157
rob.meades@u-blox.com 1:566163f17cde 1158 return success;
rob.meades@u-blox.com 1:566163f17cde 1159 }
rob.meades@u-blox.com 1:566163f17cde 1160
RobMeades 2:93310a83401a 1161 // Send a control word.
rob.meades@u-blox.com 1:566163f17cde 1162 bool BatteryGaugeBq27441::advancedSendControlWord (uint16_t controlWord, uint16_t *pDataReturned)
rob.meades@u-blox.com 1:566163f17cde 1163 {
rob.meades@u-blox.com 1:566163f17cde 1164 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 1165 char data[3];
rob.meades@u-blox.com 1:566163f17cde 1166
rob.meades@u-blox.com 1:566163f17cde 1167 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 1168 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 1169 // Send the control command
rob.meades@u-blox.com 1:566163f17cde 1170 data[0] = 0x00; // Set address to first register for control
rob.meades@u-blox.com 1:566163f17cde 1171 data[1] = (char) controlWord; // First byte of controlWord
rob.meades@u-blox.com 1:566163f17cde 1172 data[2] = (char) (controlWord >> 8); // Second byte of controlWord
rob.meades@u-blox.com 1:566163f17cde 1173 if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
rob.meades@u-blox.com 1:566163f17cde 1174 // Read the two bytes returned if requested
rob.meades@u-blox.com 1:566163f17cde 1175 if (pDataReturned != NULL) {
rob.meades@u-blox.com 1:566163f17cde 1176 if (getTwoBytes(0, pDataReturned)) {
rob.meades@u-blox.com 1:566163f17cde 1177 success = true;
rob.meades@u-blox.com 1:566163f17cde 1178 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 1179 printf("BatteryGaugeBq27441 (I2C 0x%02x): sent control word 0x%04x, read back 0x%04x.\n", gAddress >> 1, controlWord, *pDataReturned);
rob.meades@u-blox.com 1:566163f17cde 1180 #endif
rob.meades@u-blox.com 1:566163f17cde 1181 }
rob.meades@u-blox.com 1:566163f17cde 1182 } else {
rob.meades@u-blox.com 1:566163f17cde 1183 success = true;
rob.meades@u-blox.com 1:566163f17cde 1184 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 1185 printf("BatteryGaugeBq27441 (I2C 0x%02x): sent control word 0x%04x.\n", gAddress >> 1, controlWord);
rob.meades@u-blox.com 1:566163f17cde 1186 #endif
rob.meades@u-blox.com 1:566163f17cde 1187 }
rob.meades@u-blox.com 1:566163f17cde 1188 }
rob.meades@u-blox.com 1:566163f17cde 1189 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 1190 }
rob.meades@u-blox.com 1:566163f17cde 1191
rob.meades@u-blox.com 1:566163f17cde 1192 return success;
rob.meades@u-blox.com 1:566163f17cde 1193 }
rob.meades@u-blox.com 1:566163f17cde 1194
RobMeades 2:93310a83401a 1195 // Read two bytes starting at a given address on the chip.
rob.meades@u-blox.com 1:566163f17cde 1196 bool BatteryGaugeBq27441::advancedGet (uint8_t address, uint16_t *pDataReturned)
rob.meades@u-blox.com 1:566163f17cde 1197 {
rob.meades@u-blox.com 1:566163f17cde 1198 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 1199 uint16_t value;
rob.meades@u-blox.com 1:566163f17cde 1200
rob.meades@u-blox.com 1:566163f17cde 1201 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 1202 // Make sure there's a recent reading, as most
rob.meades@u-blox.com 1:566163f17cde 1203 // of these commands involve the chip having done one
rob.meades@u-blox.com 1:566163f17cde 1204 if (gGaugeOn || makeAdcReading()) {
rob.meades@u-blox.com 1:566163f17cde 1205 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 1206 // Read the data
rob.meades@u-blox.com 1:566163f17cde 1207 if (getTwoBytes(address, &value)) {
rob.meades@u-blox.com 1:566163f17cde 1208 success = true;
rob.meades@u-blox.com 1:566163f17cde 1209 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 1210 printf("BatteryGaugeBq27441 (I2C 0x%02x): read 0x%04x from addresses 0x%02x and 0x%02x.\n", gAddress >> 1, value, address, address + 1);
rob.meades@u-blox.com 1:566163f17cde 1211 #endif
rob.meades@u-blox.com 1:566163f17cde 1212 if (pDataReturned != NULL) {
rob.meades@u-blox.com 1:566163f17cde 1213 *pDataReturned = value;
rob.meades@u-blox.com 1:566163f17cde 1214 }
rob.meades@u-blox.com 1:566163f17cde 1215 }
rob.meades@u-blox.com 1:566163f17cde 1216 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 1217 }
rob.meades@u-blox.com 1:566163f17cde 1218 }
rob.meades@u-blox.com 1:566163f17cde 1219
rob.meades@u-blox.com 1:566163f17cde 1220 return success;
rob.meades@u-blox.com 1:566163f17cde 1221 }
rob.meades@u-blox.com 1:566163f17cde 1222
RobMeades 2:93310a83401a 1223 // Check if the chip is SEALED or UNSEALED.
rob.meades@u-blox.com 1:566163f17cde 1224 bool BatteryGaugeBq27441::advancedIsSealed()
rob.meades@u-blox.com 1:566163f17cde 1225 {
rob.meades@u-blox.com 1:566163f17cde 1226 bool sealed = false;
rob.meades@u-blox.com 1:566163f17cde 1227
rob.meades@u-blox.com 1:566163f17cde 1228 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 1229 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 1230 // Check for sealedness
rob.meades@u-blox.com 1:566163f17cde 1231 sealed = isSealed();
rob.meades@u-blox.com 1:566163f17cde 1232
rob.meades@u-blox.com 1:566163f17cde 1233 // Return to sleep if we are allowed to
rob.meades@u-blox.com 1:566163f17cde 1234 if (!gGaugeOn) {
rob.meades@u-blox.com 1:566163f17cde 1235 setHibernate();
rob.meades@u-blox.com 1:566163f17cde 1236 }
rob.meades@u-blox.com 1:566163f17cde 1237
rob.meades@u-blox.com 1:566163f17cde 1238 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 1239 }
rob.meades@u-blox.com 1:566163f17cde 1240
rob.meades@u-blox.com 1:566163f17cde 1241 return sealed;
rob.meades@u-blox.com 1:566163f17cde 1242 }
rob.meades@u-blox.com 1:566163f17cde 1243
RobMeades 2:93310a83401a 1244 // Put the chip into SEALED mode.
rob.meades@u-blox.com 1:566163f17cde 1245 bool BatteryGaugeBq27441::advancedSeal()
rob.meades@u-blox.com 1:566163f17cde 1246 {
rob.meades@u-blox.com 1:566163f17cde 1247 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 1248
rob.meades@u-blox.com 1:566163f17cde 1249 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 1250 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 1251 // Seal
rob.meades@u-blox.com 1:566163f17cde 1252 success = seal();
rob.meades@u-blox.com 1:566163f17cde 1253
rob.meades@u-blox.com 1:566163f17cde 1254 // Return to sleep if we are allowed to
rob.meades@u-blox.com 1:566163f17cde 1255 if (!gGaugeOn && !setHibernate()) {
rob.meades@u-blox.com 1:566163f17cde 1256 success = false;
rob.meades@u-blox.com 1:566163f17cde 1257 }
rob.meades@u-blox.com 1:566163f17cde 1258
rob.meades@u-blox.com 1:566163f17cde 1259 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 1260 }
rob.meades@u-blox.com 1:566163f17cde 1261
rob.meades@u-blox.com 1:566163f17cde 1262 return success;
rob.meades@u-blox.com 1:566163f17cde 1263 }
rob.meades@u-blox.com 1:566163f17cde 1264
RobMeades 2:93310a83401a 1265 // Unseal the device.
rob.meades@u-blox.com 1:566163f17cde 1266 bool BatteryGaugeBq27441::advancedUnseal(uint16_t sealCode)
rob.meades@u-blox.com 1:566163f17cde 1267 {
rob.meades@u-blox.com 1:566163f17cde 1268 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 1269 char updateStatus;
rob.meades@u-blox.com 1:566163f17cde 1270
rob.meades@u-blox.com 1:566163f17cde 1271 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 1272 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 1273 // Unseal and read the Update Status value
rob.meades@u-blox.com 1:566163f17cde 1274 if (unseal(sealCode) && readExtendedData(82, 2, 1, &updateStatus)) {
rob.meades@u-blox.com 1:566163f17cde 1275 // If the update status value has the top bit set then the chip will
rob.meades@u-blox.com 1:566163f17cde 1276 // reseal itself on the next reset, so this bit needs to be cleared to
rob.meades@u-blox.com 1:566163f17cde 1277 // unseal it properly
rob.meades@u-blox.com 1:566163f17cde 1278 if ((updateStatus & (1 << 7)) != 0) {
rob.meades@u-blox.com 1:566163f17cde 1279 updateStatus &= ~(1 << 7);
rob.meades@u-blox.com 1:566163f17cde 1280 success = writeExtendedData(82, 2, 1, &updateStatus);
rob.meades@u-blox.com 1:566163f17cde 1281 } else {
rob.meades@u-blox.com 1:566163f17cde 1282 success = true;
rob.meades@u-blox.com 1:566163f17cde 1283 }
rob.meades@u-blox.com 1:566163f17cde 1284 }
rob.meades@u-blox.com 1:566163f17cde 1285
rob.meades@u-blox.com 1:566163f17cde 1286 // Return to sleep if we are allowed to
rob.meades@u-blox.com 1:566163f17cde 1287 if (!gGaugeOn && !setHibernate()) {
rob.meades@u-blox.com 1:566163f17cde 1288 success = false;
rob.meades@u-blox.com 1:566163f17cde 1289 }
rob.meades@u-blox.com 1:566163f17cde 1290
rob.meades@u-blox.com 1:566163f17cde 1291 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 1292 }
rob.meades@u-blox.com 1:566163f17cde 1293
rob.meades@u-blox.com 1:566163f17cde 1294 return success;
rob.meades@u-blox.com 1:566163f17cde 1295 }
rob.meades@u-blox.com 1:566163f17cde 1296
RobMeades 2:93310a83401a 1297 // Do a hard reset of the chip.
rob.meades@u-blox.com 1:566163f17cde 1298 bool BatteryGaugeBq27441::advancedReset(void)
rob.meades@u-blox.com 1:566163f17cde 1299 {
rob.meades@u-blox.com 1:566163f17cde 1300 bool success = false;
rob.meades@u-blox.com 1:566163f17cde 1301 bool wasSealed;
rob.meades@u-blox.com 1:566163f17cde 1302 char data[3];
rob.meades@u-blox.com 1:566163f17cde 1303
rob.meades@u-blox.com 1:566163f17cde 1304 if (gReady && (gpI2c != NULL)) {
rob.meades@u-blox.com 1:566163f17cde 1305 gpI2c->lock();
rob.meades@u-blox.com 1:566163f17cde 1306
rob.meades@u-blox.com 1:566163f17cde 1307 // Handle unsealing, as this command only works when unsealed
rob.meades@u-blox.com 1:566163f17cde 1308 wasSealed = isSealed();
rob.meades@u-blox.com 1:566163f17cde 1309 if (!wasSealed || unseal(gSealCode)) {
rob.meades@u-blox.com 1:566163f17cde 1310 // Send a RESET sub-command
rob.meades@u-blox.com 1:566163f17cde 1311 data[0] = 0x00; // Set address to first register for control
rob.meades@u-blox.com 1:566163f17cde 1312 data[1] = 0x41; // First byte of RESET sub-command (0x41)
rob.meades@u-blox.com 1:566163f17cde 1313 data[2] = 0x00; // Second byte of RESET sub-command (0x00) (register address will auto-increment)
rob.meades@u-blox.com 1:566163f17cde 1314
rob.meades@u-blox.com 1:566163f17cde 1315 if (gpI2c->write(gAddress, &(data[0]), 3) == 0) {
rob.meades@u-blox.com 1:566163f17cde 1316 success = true;
rob.meades@u-blox.com 1:566163f17cde 1317 #ifdef DEBUG_BQ27441
rob.meades@u-blox.com 1:566163f17cde 1318 printf("BatteryGaugeBq27441 (I2C 0x%02x): chip hard reset.\n", gAddress >> 1);
rob.meades@u-blox.com 1:566163f17cde 1319 #endif
rob.meades@u-blox.com 1:566163f17cde 1320 }
rob.meades@u-blox.com 1:566163f17cde 1321
rob.meades@u-blox.com 1:566163f17cde 1322 // Handle re-sealing and fail if we need to re-seal but can't
rob.meades@u-blox.com 1:566163f17cde 1323 if (wasSealed && !seal()) {
rob.meades@u-blox.com 1:566163f17cde 1324 success = false;
rob.meades@u-blox.com 1:566163f17cde 1325 }
rob.meades@u-blox.com 1:566163f17cde 1326 }
rob.meades@u-blox.com 1:566163f17cde 1327
rob.meades@u-blox.com 1:566163f17cde 1328 // Return to sleep if we are allowed to
rob.meades@u-blox.com 1:566163f17cde 1329 if (!gGaugeOn && !setHibernate()) {
rob.meades@u-blox.com 1:566163f17cde 1330 success = false;
rob.meades@u-blox.com 1:566163f17cde 1331 }
rob.meades@u-blox.com 1:566163f17cde 1332
rob.meades@u-blox.com 1:566163f17cde 1333 gpI2c->unlock();
rob.meades@u-blox.com 1:566163f17cde 1334 }
rob.meades@u-blox.com 1:566163f17cde 1335
rob.meades@u-blox.com 1:566163f17cde 1336 return success;
rob.meades@u-blox.com 1:566163f17cde 1337 }
rob.meades@u-blox.com 1:566163f17cde 1338
RobMeades 2:93310a83401a 1339 /* End Of File */