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.

bq27621 : Changing Capacity Parameter

I'm on Kinetis K02 Processor .  The following is the code to change the battery capacity to 500 mAh.  I'm following the same sequence outlined in sluuad4c.pdf.

I2C rate is 400 kHz.

bool bq27621ChangeCapacityParameter (uint16_t capacityVal)
{
i2c_status_t i2cStatus;
uint8_t txBuf[3];
uint8_t checkSum, tempMod;
uint16_t flagVal = 0;
uint8_t msbCap, lsbCap,
prvMsbCap, prvLsbCap,
prvCheckSum;


// UNSEALED
txBuf [0] = 0x00;
txBuf [1] = UNSEAL_CODE_L;
txBuf [2] = UNSEAL_CODE_H;

i2cStatus = I2C_DRV_MasterSendDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
NULL, 0, (const uint8_t*)txBuf, 3, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;

i2cStatus = I2C_DRV_MasterSendDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
NULL, 0, (const uint8_t*)txBuf, 3, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;


// Enter CFG Update
txBuf [0] = BQ27621_CTRL1_ADDR;
txBuf [1] = SET_CFGUPDATE & 0xff;
txBuf [2] = (SET_CFGUPDATE >> 8) & 0xff;

i2cStatus = I2C_DRV_MasterSendDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
NULL, 0, (const uint8_t*)txBuf, 3, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;

// Check Flag bit 4
for (int i = 2000; (i > 0) && !(flagVal & 0x10); i--)
{
_bq27621ReadFlag(&flagVal);
}

//Bit never get set..ignored for now
//if ((flagVal & 0x10) != 0x10)
// return false;

// Enable block data memory control
txBuf [0] = BLOCKDATACONTROL;
txBuf [1] = 0x00;

i2cStatus = I2C_DRV_MasterSendDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
NULL, 0, (const uint8_t*)txBuf, 2, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;


// Access State subclass: Design Capacity parameter
txBuf [0] = DATACLASS;
txBuf [1] = 0x52;

i2cStatus = I2C_DRV_MasterSendDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
NULL, 0, (const uint8_t*)txBuf, 2, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;


// Access location block in subclass
txBuf [0] = DATABLOCK;
txBuf [1] = 0x00;

i2cStatus = I2C_DRV_MasterSendDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
NULL, 0, (const uint8_t*)txBuf, 2, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;

// Read previous CheckSum - Getting 0x70 
i2cStatus = I2C_DRV_MasterReceiveDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
(const uint8_t *)(BLOCKDATACHECKSUM), 1, &prvCheckSum, 1, DEVICE_I2C_TIMEOUT);

// Read previous Design Capacity @ 0x43 (offset = 3) - getting 0x00
i2cStatus = I2C_DRV_MasterReceiveDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
(const uint8_t *)(DESIGNCAPACITY_H), 1, &prvMsbCap, 1, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;

//Getting 0x70 back

i2cStatus = I2C_DRV_MasterReceiveDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
(const uint8_t *)(DESIGNCAPACITY_L), 1, &prvLsbCap, 1, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;

msbCap = (capacityVal >> 8) & 0xFF;
lsbCap = capacityVal & 0xFF;

if ((prvMsbCap != (msbCap)) || (prvLsbCap != lsbCap))
{
// Write new Design Capacity
txBuf [0] = DESIGNCAPACITY_H;
txBuf [1] = msbCap;

i2cStatus = I2C_DRV_MasterSendDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
NULL, 0, (const uint8_t*)txBuf, 2, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;

txBuf [0] = DESIGNCAPACITY_L;
txBuf [1] = lsbCap;

i2cStatus = I2C_DRV_MasterSendDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
NULL, 0, (const uint8_t*)txBuf, 2, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;


// Compute and write checksum
tempMod = (255 - prvCheckSum - prvMsbCap - prvLsbCap) % 256;
checkSum = 255 - ((tempMod + msbCap + lsbCap) % 256);

txBuf [0] = BLOCKDATACHECKSUM;
txBuf [1] = checkSum;

//Getting NACK

i2cStatus = I2C_DRV_MasterSendDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
NULL, 0, (const uint8_t*)txBuf, 2, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;
}

// Exit CFG with soft reset
txBuf [0] = BQ27621_CTRL1_ADDR;
txBuf [2] = (SOFT_RESET) & 0xff;
txBuf [1] = SOFT_RESET & 0xff;

i2cStatus = I2C_DRV_MasterSendDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
NULL, 0, (const uint8_t*)txBuf, 3, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;


// Confirm CFG Update Exit
_bq27621ReadFlag(&flagVal);
if ((flagVal & 0x10) == 0x10)
return false;


// Return to SEALED
txBuf [0] = 0x00;
txBuf [1] = SEAL_CODE_L;
txBuf [2] = SEAL_CODE_H;

i2cStatus = I2C_DRV_MasterSendDataBlocking(BOARD_I2C_INSTANCE, &i2cDevice,
NULL, 0, (const uint8_t*)txBuf, 3, DEVICE_I2C_TIMEOUT);
if (i2cStatus != kStatus_I2C_Success)
return false;

return true;
}

  • Please follow the examples in the attached code.

    #example code bq27621 - absolutely no warranties implied
    
    #define GAUGE_DEVICE_ADDRESS 0xAA
    
    #define SET_CFGUPDATE 	0x0013
    #define EXIT_RESIM 	0x0044
    #define SOFT_RESET 	0x0042
    #define RESET		0x0041
    #define SET_HIBERNATE	0x0011
    
    #define CMD_DATA_CLASS 	0x3E
    #define CMD_DATA_BLOCK 	0x3F
    #define CMD_BLOCK_DATA	0x40
    #define CMD_CHECK_SUM 	0x60
    #define CMD_FLAGS 	0x06
    
    #define DC_STATE	0x52
    #define DC_STATE_LENGTH	38
    
    #define CFGUPD 0x0010
    
    
    // nI2C = handle to your I2C driver (if required)
    // nRegister = gauge register
    // pData = pointer to data block which will hold the data from the gauge
    // nLength = length
    int i2c_read(int nI2C, unsigned char nRegister, unsigned char *pData, unsigned char nLength)
    {
    	// implement your I2C read function here. Make sure you follow the timing requirements from the datasheet
    }
    
    // nI2C = handle to your I2C driver (if required)
    // nRegister = gauge register
    // pData = pointer to data block which holds the data for the gauge
    // nLength = length
    int i2c_write(int nI2C, unsigned char nRegister, unsigned char *pData, unsigned char nLength)
    {
    	// implement your I2C write function here. Make sure you follow the timing requirements from the datasheet 
    }
    
    // issue a control command (nSubCmd = sub command) to the gauge
    unsigned int gauge_control(int nI2C, unsigned int nSubCmd)
    {
    	unsigned int nResult = 0;
    
    	char pData[2];
    
    	pData[0] = nSubCmd & 0xFF;
    	pData[1] = (nSubCmd >> 8) & 0xFF;
    	
    	i2c_write(nI2C, 0x00, pData, 2); // issue control and sub command
    
    	i2c_read(nI2C, 0x00, pData, 2); // read data
    
    	nResult = (pData[1] << 8) | pData[0];
    
    	return nResult; 
    }
    
    // read 2 bytes from the gauge (nCmd = command)
    unsigned int gauge_cmd_read(int nI2C, unsigned char nCmd)
    {
    	unsigned char pData[2];
    
    	i2c_read(nI2C, nCmd, pData, 2);
    
    	return (pData[1] << 8) | pData[0];
    } 
    
    // write 2 bytes to the gauge (nCmd = command, nData = 16 bit parameter)
    unsigned int gauge_cmd_write(int nI2C, unsigned char nCmd, unsigned int nData)
    {
    	unsigned char pData[2];
    
    	pData[0] = nData & 0xFF;
    	pData[1] = (nData >> 8) & 0xFF;
    
    	i2c_write(nI2C, nCmd, pData, 2);
    
    	return (pData[1] << 8) | pData[0];
    } 
    
    #define MAX_ATTEMPTS 5
    // exit CFG_UPDATE mode with one of the supported exit commands (e.g. EXIT_RESIM)
    int gauge_exit(int nI2C, unsigned int nCmd)
    {
    	unsigned int nFlags;
    	int nAttempts = 0;
    	gauge_control(nI2C, nCmd);
    
    	do
    	{
    		nFlags = gauge_cmd_read(nI2C, CMD_FLAGS);
    		if (nFlags & CFGUPD) usleep(500000);
    	} while ((nFlags & CFGUPD) && (nAttempts++ < MAX_ATTEMPTS));
    
    	return (nAttempts < MAX_ATTEMPTS);
    }
    
    // enter CFG_UPDATE mode
    int gauge_cfg_update(int nI2C)
    {
    	unsigned int nFlags;
    	int nAttempts = 0;
    	gauge_control(nI2C, SET_CFGUPDATE);
    
    	do
    	{
    		nFlags = gauge_cmd_read(nI2C, CMD_FLAGS);
    		if (!(nFlags & CFGUPD)) usleep(500000);
    	} while (!(nFlags & CFGUPD) && (nAttempts++ < MAX_ATTEMPTS));
    
    	return (nAttempts < MAX_ATTEMPTS);
    }
    
    // read a data class.
    // nDataClass = data class number
    // pData = raw data (for the whole data class)
    // nLength = length of the whole data class
    int gauge_read_data_class(int nI2C, unsigned char nDataClass, unsigned char *pData, unsigned char nLength)
    {
    	unsigned char nRemainder = nLength;
    	unsigned int nOffset = 0;
    	unsigned char nDataBlock = 0x00;
    	unsigned int nData;
    
    	if (nLength < 1) return 0;
    
    	do
    	{
    
    		nLength = nRemainder;
    		if (nLength > 32)
    		{
    			nRemainder = nLength - 32;
    			nLength = 32;
    		}
    		else nRemainder = 0;
    
    		nData = (nDataBlock << 8) | nDataClass;
    		gauge_cmd_write(nI2C, CMD_DATA_CLASS, nData);
    
    		i2c_read(nI2C, CMD_BLOCK_DATA, pData, nLength);
    
    		pData += nLength;
    		nDataBlock++;
    	} while (nRemainder > 0);
    
    	return nLength;
    }
    
    unsigned char check_sum(unsigned char *pData, unsigned char nLength)
    {
    	unsigned char nSum = 0x00;
    	unsigned char n;
    
    	for (n = 0; n < nLength; n++)
    		nSum += pData[n];
    
    	nSum = 0xFF - nSum;
    
    	return nSum;
    }
    
    // write a data class.
    // nDataClass = data class number
    // pData = raw data (for the whole data class)
    // nLength = length of the whole data class
    int gauge_write_data_class(int nI2C, unsigned char nDataClass, unsigned char *pData, unsigned char nLength)
    {
    	unsigned char nRemainder = nLength;
    	unsigned int nOffset = 0;
    	unsigned char pCheckSum[2] = {0x00, 0x00};
    	unsigned int nData;
    	unsigned char nDataBlock = 0x00;
    
    	if (nLength < 1) return 0;
    
    	do
    	{
    		nLength = nRemainder;
    		if (nLength > 32)
    		{
    			nRemainder = nLength - 32;
    			nLength = 32;
    		}
    		else nRemainder = 0;
    
    		nData = (nDataBlock << 8) | nDataClass;
    		gauge_cmd_write(nI2C, CMD_DATA_CLASS, nData);
    
    		i2c_write(nI2C, CMD_BLOCK_DATA, pData, nLength);
    		pCheckSum[0] = check_sum(pData, nLength);
    		i2c_write(nI2C, CMD_CHECK_SUM, pCheckSum, 1);
    
    		usleep(10000);
    
    		gauge_cmd_write(nI2C, CMD_DATA_CLASS, nData);
    		i2c_read(nI2C, CMD_CHECK_SUM, pCheckSum + 1, 1);
    		if (pCheckSum[0] != pCheckSum[1])
    			printf("gauge_write_data_class(): CheckSum mismatch 0x%02X vs. 0x%02X\n\r", pCheckSum[0], pCheckSum[1]);
    
    		pData += nLength;
    		nDataBlock++;
    	} while (nRemainder > 0);
    
    	return nLength;
    }
    
    void print_data(unsigned char *pData, unsigned int nLength)
    {
    	unsigned int n;
    
    	for (n = 0; n < nLength; n++)
    	{
    		printf("%02X ", pData[n]);
    		if (!((n + 1) % 16)) printf("\n\r");
    	}
    
    	printf("\n\r");
    }
    
    
    Examples:
    
    read flags:
    	nResult = gauge_cmd_read(nI2C, CMD_FLAGS);
    	printf("FLAGS = 0x%04X\n\r", nResult);
    	
    
    read data class DC_STATE:
    	gauge_read_data_class(nI2C, DC_STATE, pData, DC_STATE_LENGTH);
    	printf("Data Class 'State' (0x52):\n\r");
    	print_data(pData, DC_STATE_LENGTH); 
    
    write data class DC_STATE:
    	gauge_cfg_update(nI2C);
    	gauge_write_data_class(nI2C, DC_STATE, pClass52Data, sizeof(pClass52Data));
    	gauge_exit(nI2C, EXIT_RESIM);
    
    

  • We got side track on another problem.

    I was able use the example code . However, I'm never getting the flag bit being set after sending SET_CFGUPDATE.
    I went back to the original code and notice the same problem.
  • I continue the code regardless of the flag bit.
    I proceeded to read dataclass 2 and 82. The result of both action matched the data in the SLUUAD4C document except for the first byte was not return in both instances.
  • You should not proceed without the correct flags. I tested the code example extensively and I did not observe problems with the flags. Please debug your system to make sure that the flags are set correctly.