This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

BQ - 27441-G1B : Setting Design capacity using I2C programming doesn't change it

Other Parts Discussed in Thread: EV2400, BQSTUDIO

Dear Forum, 

I am using a BQ27441 G1B, fuel gauge on the EVM. I also have a EV2400 interface adapter, but for our purposes we need to operate the board programmatically. So I have connected it to a microcontroller board (Raspberry-Pi) using I2C, which happened alright. I am able to read battery voltage, temperature, Full Charge Capacity, Remaining Battery Capacity etc by issuing the relevant command using the register address. 

My problem is, before gauging any battery, we need to set its design capacity, as this is a variable parameter, and changes with each battery, provided by the manufacturer, usually written on the battery. The TRM has a whole section describing how it can be updated. Specifically, sec - 3.1, pg 14 of SLUUAC9A–December 2013–Revised May 2015. These are 14 steps that ultimately should lead to updation of design capacity as entered by the user. I am able to execute all the steps correctly. The way I know that the steps are executed correctly is: 

1. The write statements don't return any error and get executed. 

2. The read statements  - except the un-documented read to read-back newly set design capacity - give anticipated outputs.

Below is the code that I use, and later a sample output. The steps to update the design capacity after the comment - "Unseal the gauge". The comments on the right edge of the line //# 1, 2... represent the corresponding steps as given in the TRM - Pg-14. 

// Will need compile flags -lm -lwiringPi -lncurses

#define BQ27441_ADDR 0x55 		// taken from datasheet - page 13
#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <math.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <time.h>
#include <sys/time.h>
#include <curses.h>
#include <wiringPi.h>

typedef unsigned char byte;

int deviceDescriptor;

/* This function initializes the I2C device*/
void init_i2c(char *DeviceName)
{	
        printf("Initialising i2c device \n");
        deviceDescriptor=open(DeviceName, O_RDWR);

        if (deviceDescriptor == -1) {
                printf("Error opening device '%s' \n",DeviceName);
                exit(-1);
        }
}

/* This function sends data to the I2C device*/
void I2cSendData(byte addr,byte *data,int len)
{
        if(ioctl(deviceDescriptor,I2C_SLAVE, addr))
                printf("I2cSendData_device : IOCTL Problem \n");

        write(deviceDescriptor,data,len);
}

/* This function reads data from the I2C device*/
void I2cReadData(byte addr,byte *data,int len)
{
        if(ioctl(deviceDescriptor,I2C_SLAVE, addr))
                printf("I2cReadData_device : IOCTL Problem \n");

        read(deviceDescriptor,data,len);
}

/* Convert the number to hexadecimal representation */
void to_hex_16(char *output, unsigned n)
{
        static const char hex_digits[] = "0123456789abcdef";
        output[0] = hex_digits[(n >> 12) & 0xF];
        output[1] = hex_digits[(n >> 8) & 0xF];
        output[2] = hex_digits[(n >> 4) & 0xF];
        output[3] = hex_digits[(n & 0xF)];
        output[4] = '\0';
}

/* Computes the checksum by adding the values in the register and then subtracting from 255 */ static int checksum(byte *check_data) { int sum = 0; int ii = 0; for(ii = 0; ii < 32; ii++) sum += check_data[ii+62]; sum &= 0xFF; return 0xFF - sum; } /* getliner() reads one line from standard input and copies it to line array * (but no more than max chars) * It does not place the terminating \n line array. * Returns line length, or 0 for empty line, or EOF for end-of-file. */ int getliner(char line[], int max) { int nch = 0; int c; max = max - 1; /* Leave room for '\0' */ while ((c = getchar()) != EOF) { if (c == '\n') break; if (nch < max) { line[nch] = c; nch = nch + 1; } } if (c == EOF && nch == 0) return EOF; line[nch] = '\0'; return nch; } int main(int argc, char **argv) { int i, voltage, design_capacity, new_design_capacity, new_design_cap_hex; int des_cap[10], cksum = 0; byte data[100], writeData[100], unseal_data[10], cfgupdate_data[10], flag_data[10], flag_out[10]; byte block_data_control[10], data_block_class[10], data_block[10], block_data_checksum[10]; byte block_data_checksum_data[10], design_capacity_loc[10], design_capacity_data[10]; byte soft_reset[10], seal_data[10]; float remaining_batt_cap = 0.0; float full_charge_cap = 0.0; float soc = 0.0; float temp = 0.0; float current = 0.0; char new_design_cap[7], a[10], b[10], tmp[10]; printf("Inside main \n"); init_i2c("/dev/i2c-1"); writeData[0] = 0x00; writeData[1] = 0x04; unseal_data[0] = 0x00; unseal_data[1] = 0x00; unseal_data[2] = 0x80; cfgupdate_data[0] = 0x00; cfgupdate_data[1] = 0x13; cfgupdate_data[2] = 0x00; flag_data[0] = 0x06; block_data_control[0] = 0x61; block_data_control[1] = 0x00; data_block_class[0] = 0x3E; data_block_class[1] = 0x52; data_block[0] = 0x3F; data_block[1] = 0x00; block_data_checksum[0] = 0x60; design_capacity_loc[0] = 0x4A; soft_reset[0] = 0x00; soft_reset[1] = 0x42; soft_reset[2] = 0x00; seal_data[0] = 0x00; seal_data[1] = 0x20; seal_data[2] = 0x00; /* Unseal the gauge - Refer TRM - Pg-14 */ I2cSendData(BQ27441_ADDR, unseal_data, 3); // #1 I2cSendData(BQ27441_ADDR, unseal_data, 3); delay(5); printf("The gauge seems to be unsealed. \n"); I2cSendData(BQ27441_ADDR, cfgupdate_data, 3); // #2 delay(1000); I2cSendData(BQ27441_ADDR, flag_data, 1); // #3 delay(5); I2cReadData(BQ27441_ADDR, flag_out, 1); printf("The flag_out is: %x \n", flag_out[0]); if (CHECK_BIT(flag_out[0], 4)) { printf("The gauge is ready to be configured \n"); I2cSendData(BQ27441_ADDR, block_data_control, 2); // #4 delay(5); I2cSendData(BQ27441_ADDR, data_block_class, 2); // #5 delay(5); I2cSendData(BQ27441_ADDR, data_block, 2); // #6 delay(5); I2cSendData(BQ27441_ADDR, block_data_checksum, 1); // #7 delay(5); I2cReadData(BQ27441_ADDR, block_data_checksum_data, 1); delay(5); printf("The checksum_data: %x \n", block_data_checksum_data[0]); if (block_data_checksum_data[0] == 0xE8) { printf("The checksum is as expected. Config will proceed. \n"); I2cSendData(BQ27441_ADDR, design_capacity_loc, 1); // #8 delay(5); I2cReadData(BQ27441_ADDR, design_capacity_data, 2); delay(5); //printf("Design capacity data: %x and %x \n", design_capacity_data[0], design_capacity_data[1]); design_capacity = design_capacity_data[0]*16*16 + design_capacity_data[1]; delay(5); printf("The current design capacity is: %d mAh \n", design_capacity); printf("Set new design capacity in mAh (ENTER to continue) ?"); getliner(new_design_cap, 7); if (new_design_cap != EOF && new_design_cap[0] != 0) { printf("Trying to update the design capacity \n"); new_design_capacity = atoi(new_design_cap); // #9 printf("Trying to set new design capacity to: %d \n", new_design_capacity); to_hex_16(tmp, new_design_capacity); for(i = 0; i <= 3; i++) { printf("Output at position %d has %c \n", i, tmp[i]); } des_cap[0] = design_capacity_loc[0]; des_cap[1] = (tmp[0] - '0')*16 + (tmp[1] - '0'); des_cap[2] = (tmp[2] - '0')*16 + (tmp[3] - '0'); printf("Des cap 0: %d ", des_cap[0]); printf("Des cap 1: %d ", des_cap[1]); printf("Des cap 2: %d ", des_cap[2]); I2cSendData(BQ27441_ADDR, des_cap, 3); delay(1000); cksum = checksum(data); // #10 delay(1000); printf("New Checksum found is: %x ", cksum); block_data_checksum[1] = cksum; // #11 I2cSendData(BQ27441_ADDR, block_data_checksum, 2); delay(5); I2cSendData(BQ27441_ADDR, soft_reset, 3); // #12 delay(1000); //printf("Design Cap data 0: %x", data[72]); //printf("Design Cap data :1 %x", data[73]); //design_capacity = data[72]*16*16 + data[73]; I2cSendData(BQ27441_ADDR, flag_data, 1); // #13 delay(5); I2cReadData(BQ27441_ADDR, flag_out, 1); printf("The flag_out is: %x \n", flag_out[0]); if(!CHECK_BIT(flag_out[0], 4)) { printf("CFGUPDTE has been exited, configuration done. \n"); I2cSendData(BQ27441_ADDR, seal_data, 1); // #14 delay(5); printf("Gauge has been sealed and is ready for operation \n"); } //printf("New capacity set as: %d mAh \n", design_capacity); } else { printf("Design capacity left unchanged. Now at %d mAh \n", design_capacity); } } else { printf("The checksum is not as expected. Config halt. \n"); } } else { printf("Cannot proceed with configuration. \n"); printf("The CFGUPDATE MODE has not been enabled yet. \n"); } while(true) { /* Reading the device registers */ I2cSendData(BQ27441_ADDR, writeData, 2); I2cReadData(BQ27441_ADDR, data, 100); voltage = data[4]*16*16 + data[3]; remaining_batt_cap = data[12]*16*16 + data[11]; full_charge_cap = data[14]*16*16 + data[13]; soc = (remaining_batt_cap/full_charge_cap)*100; temp = (data[2]*16*16 + data[1])/10.0 - 273.0; current = data[16]*16*16 + data[15]; printf("Voltage: %d mV\n", voltage); printf("Current: %f mA\n", current); printf("Remaining Battery Capacity: %f mAh\n", remaining_batt_cap); printf("Full Charge Capacity: %f mAh\n", full_charge_cap); printf("State of Charge: %f p.c. \n", soc); printf("Temperature: %f Deg C\n", temp); delay(10000); } close(deviceDescriptor); endwin(); return 0; }

OUTPUT

Inside main 
Initialising i2c device 
The gauge seems to be unsealed. 
The flag_out is: 98 
The gauge is ready to be configured 
The checksum_data: e8 
The checksum is as expected. Config will proceed. 
The current design capacity is: 1000 mAh 
Set new design capacity in mAh (ENTER to continue) ?1300    -> (User Input)
Trying to update the design capacity 
Trying to set new design capacity to: 1300 
Output at position 0 has 0 
Output at position 1 has 5 
Output at position 2 has 1 
Output at position 3 has 4 
Des cap 0: 74 Des cap 1: 5 Des cap 2: 20 New Checksum found is: f2 The flag_out is: 88 
CFGUPDTE has been exited, configuration done. 
Gauge has been sealed and is ready for operation 
Voltage: 3932  mV
Current: 0.000000  mA
Remaining Battery Capacity: 589.000000  mAh
Full Charge Capacity: 927.000000 mAh
State of Charge: 63.538296 p.c. 
Temperature: 23.900000  Deg C

As you can see, even after I enter the new design capacity to 1300 mAh, the gauge continues to use the Full charge capacity and remaining battery capacity values based on the preset values of 1000 mAh. And when I run the program again, it reports the design capacity again as 1000 mAh. 

The device is connected to linux and I am using i2c-tools to debug the gauge. If you have any means of debugging, or any other register read/write operation that could help in debugging, it would be great to know. 

Other comments on code are also welcome.

PS: I was able to successfully do this using the bqStudio. And a change in design capacity reflected a change in Full charge capacity and remaining battery capacity etc. 

  • Has anyone ever updated design capacity, terminate voltage or any other settable parameter **without** the use of EV2400 and bqStudio directly from the micro-controller using I2C programming ?
  • I can confirm that the changes can be affected by using the bqStudio and then switching to my micro-controller.

    Also, the changes are lost when battery is disconnected.

    What baffles me is, if this configuration only happens thru bqStudio and EV2400 then why explain the steps in the TRM. The fact that step by step procedure is explained with expected results, males me want to try to achieve this.

    - Thanks
    Chintan Pathak
  • No I have not, I am trying now, and I still don't have an answer! I am not getting valid values read from config update and other functions needed to get proper readings from the voltage and current measurement functions!