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.

MSP430F5529: MSP430F5529 communication issue over I2C with Battery BoosterPack BOOSTXL-BATPAKII

Part Number: BQ27441-G1MSP430F5529BOOSTXL-BATPAKMKII

Hello All,

The way I have set it up is MSP430 will be master communicating with slave battery booster pack. I have developed the source code which has two functions to initialize the I2C communication and another one to get battery capacity. The code runs and I can see on Logic it get correct read and write address as well. This is my first project with I2C, so I don't know much stuff about it. On the second byte for command code, I receive NAK. And I don't receive any returned data as well. Any help to figure out what's going wrong will be greatly appreciated. Thanks in advance!

/*
 * Initialize the I2C communication in between MSP430F5529 LP and Battery
 * booster pack
 */
static void ioport_i2c_init(void)
{
    // Assign I2C pins P4.2 (SCL) and P4.1 (SDA)
    P4SEL |= (BIT2 | BIT1);
    //    P4DIR |= (BIT1);

    // SMCLK, Software reset enable
    UCB1CTL1 |= UCSSEL_2 | UCSWRST;

    // Master mode, I2C mode, Synchronous mode, Address slave with 10-bit
    UCB1CTL0 = UCMST | UCMODE_3 | UCSYNC;// | UCSLA10;

    // Baud rate: SMCLK(25MHz)/(400Khz)=62.5=0x3E
    UCB1BR0 = 0x3E;
    UCB1BR1 = 0x00;

    // Slave address
    UCB1I2CSA = 0x55;

    // Clear SW reset, to enable operation
    UCB1CTL1 &= ~UCSWRST;
}

/*
 * This function fetches the battery capacity remaining.
 *
 * @return
 * The compensated battery capacity remaining. Units are mAh.
 */
uint8_t ioport_get_battery_capacity(void)
{
    // Transmitter, Send start condition
    UCB1CTL1 |= UCTR | UCTXSTT;

    //Poll for transmit interrupt flag.
    while(!(UCB1IFG & UCTXIFG))                 // If UCTXIFG == 0, then wait
    {
        ;                                       // Wait for TXBUF to be empty
    }

    if(UCB1IFG & UCTXIFG)
    {
        //Send first byte of data
        UCB1TXBUF = 0x02;
        printf("Sent 0x0d \n");
    }
    else
    {
        printf("Transmit Interrupt pending \n");
    }

    //Poll for transmit interrupt flag.
    while(!(UCB1IFG & UCTXIFG))                 // If UCTXIFG == 0, then wait
    {
        ;                                       // Wait for TXBUF to be empty
    }

    if(UCB1IFG & UCTXIFG)
    {
        //Send second byte of data
        UCB1TXBUF = 0x03;
        printf("Sent 0x0c \n");
    }
    else
    {
        printf("Transmit Interrupt pending \n");
    }

    /*
    //Poll for transmit interrupt flag.
    while(!(UCB1IFG & UCTXIFG))                 // If UCTXIFG == 0, then wait
    {
        ;                                       // Wait for TXBUF to be empty
    }

    if(UCB1IFG & UCTXIFG)
    {
        //Send third byte of data
        UCB1TXBUF = 0x01;
        printf("Sent 0x01 \n");
    }
    else
    {
        printf("Transmit Interrupt pending \n");
    }
    */

    // Send stop condition
    UCB1CTL1 |= UCTXSTP;

    ////////////////////////////////////////////////////////////////////////////
    // Reception part when master receives the response

    // Set master in Receive mode, Send start condition
    UCB1CTL1 &= ~UCTR;
    UCB1CTL1 |= UCTXSTT;

    //Poll for Start bit to complete
    while(UCB1CTL1 & UCTXSTT)
    {
        ;
    }

    //Polling RXIFG0 if RXIE is not enabled
    while(UCB1IFG & UCRXIFG)
    {
        //Read a byte from the data received
        unsigned char PRxData = UCB1RXBUF;
        printf("Data Received: %c \n",PRxData);

    }

    //Send stop condition.
    UCB1CTL1 |= UCTXSTP;

    //Wait for Stop to finish
    while(UCB1CTL1 & UCTXSTP)
    {
        ;
    }

    return 0;
}

  • Hi Anand,

    Just at first glance I see you're trying to send 0x0D and 0x0C but are actually sending 0x02 and 0x03:

        if(UCB1IFG & UCTXIFG)
        {
            //Send first byte of data
            UCB1TXBUF = 0x02;
            printf("Sent 0x0d \n");
        }
        else
        {
            printf("Transmit Interrupt pending \n");
        }
    
        //Poll for transmit interrupt flag.
        while(!(UCB1IFG & UCTXIFG))                 // If UCTXIFG == 0, then wait
        {
            ;                                       // Wait for TXBUF to be empty
        }
    
        if(UCB1IFG & UCTXIFG)
        {
            //Send second byte of data
            UCB1TXBUF = 0x03;
            printf("Sent 0x0c \n");
        }
        else
        {
            printf("Transmit Interrupt pending \n");
        }

    Best regards, 
    Caleb Overbay

  • Well just like the remaining capacity of a battery, the booster pack support several other commands too. So I was just trying with those but no luck. Sorry for the confusion, but the command I am interested in RemainingCapacity() with is 0c0C and 0x0D.

  • Hi Aand,

    Thanks for the clarification. Have you been using a logic analyzer to observe the I2C bus? Can you provide screenshots of what you're seeing? I'll take a deeper look into your code and make sure you're meeting all the requirements necessary to interface with the bq27441-G1.

    Best regards,
    Caleb Overbay
  • Hello Caleb,

    Please find attached screenshots from my logic analyzer. I have two battery booster packs and two screenshot shows output received by running code on each of those. To add to my confusion, the output received is different. I think the one which sends back ACK on address 0xAA at least shows some progress whereas other one doesn't acknowledge the address sent. See if you can get anything from these about what's going wrong.

  • Hi Anand, 

    Thank you for providing the screenshots. The write with a NAK in response is peculiar to me. Have you ensured you're using the 10K pull-up resistors recommended in the BQ274411-G1 datasheet?

    After looking at your code, I see a few things wrong with it. To execute the intended command you can send 0x0C OR 0x0D and you need to follow the following format specified in the BQ274411-G1 datasheet:

    What the MSP430 in the above diagram is doing:

    1. Sending start condition
    2. Sending address 0x55
    3. Sending WRITE bit
    4. Sending command 0x0C OR 0x0D
    5. Receiving ACK from BQ device
    6. Sending start condition
    7. Sending address 0x55
    8. Sending READ bit
    9. Receiving ACK from BQ device
    10. Receiving data from BQ device
    11. Sending ACK
    12. Receiving data from BQ device
    13. Sending NACK
    14. Sending STOP

    This can be accomplished by adopting the following code into your application:

    /**
      * @brief  Receive data from bq27441-g1 through i2c bus
      * @param  
      * @retval  0 : Operation normal
      *         -1 : Could not read data
      */
    int USCI_I2C_READ(char *buffer, int num, int cmd)
    {
        char *PRxData;
        int i=0;
    
        // Transmitter, Send start condition with addr + write
        UCB1CTL1 |= UCTR | UCTXSTT;
    
        /* Send command to bq27441 */
        if(UCB1IFG & UCTXIFG)
        {
            UCB1TXBUF = cmd;
        }
    
        //Timeout
        for (i=0; i<1000; i++) {
        	if(UCB1IFG & UCTXIFG)
        		break;
        }
    
    
        /* I2C could not send command
         * So we could not read data from bq27441
         * So we just return
         */
        if (i == 1000) {
        	return -1;
        }
        /* Start of RX buffer */
        PRxData = buffer;
    
        // Receiver, Send restart condition with addr + read
        UCB1CTL1 &= ~UCTR;
        UCB1CTL1 |= UCTXSTT;
    
        while (num > 0) {
        	for (i=0; i<1000; i++) {
    			if (UCB1IFG & UCRXIFG) {
    				/* Load TX buffer */
    				*PRxData = UCB1RXBUF;
    
    				/* Decrement TX byte counter */
    				num--;
    				PRxData++;

    UCB1IFG &= ~UCRXIFG; break; } } if (i == 1000) { return -1; } } //Send stop condition. UCB1CTL1 |= UCTXSTP; /* Clear USCI_B1 TX int flag */ UCB1IFG &= ~UCTXIFG; return 0; }

    Can you try this out and let me know the results?

    Best regards, 
    Caleb Overbay

  • Thanks Caleb for sending the source code example. I have updated my code according to it, and my new code is as follows:

    /*
     * This function fetches the battery capacity remaining.
     *
     * @return
     * The compensated battery capacity remaining. Units are mAh.
     */
    uint8_t ioport_get_battery_capacity(void)
    {
        // Transmitter, Send start condition
        UCB1CTL1 |= UCTR | UCTXSTT;
    
        //Poll for transmit interrupt flag.
        while(!(UCB1IFG & UCTXIFG))
        {
            ;
        }
    
        /* Send command to bq27441 */
        if(UCB1IFG & UCTXIFG)
        {
            //Send first byte of data
            UCB1TXBUF = 0x0c;
        }
    
        int i;
        //Timeout
        for (i=0; i<1000; i++)
        {
            if(UCB1IFG & UCTXIFG)
                break;
        }
    
        /* I2C could not send command, So we could not read data from bq27441
         * So just return
         */
        if (i == 1000)
        {
            return -1;
        }
    
        ////////////////////////////////////////////////////////////////////////////
        // Set master in Receive mode, Send start condition
        UCB1CTL1 &= ~UCTR;
        UCB1CTL1 |= UCTXSTT;
    
        //Read a byte from the data received
        char *PRxData;
        char *buffer;
    
        /* Start of RX buffer */
        PRxData = buffer;
    
        for (i=0; i<1000; i++)
        {
            if (UCB1IFG & UCRXIFG)
            {
                /* Load TX buffer */
                *PRxData = UCB1RXBUF;
    
                PRxData++;
    
                UCB1IFG &= ~UCRXIFG;
                break;
            }
        }
    
        if (i == 1000)
        {
            return -1;
        }
    
        for (i=0; i<1000; i++)
        {
            if (UCB1IFG & UCRXIFG)
            {
                /* Load TX buffer */
                *PRxData = UCB1RXBUF;
    
                PRxData++;
    
                UCB1IFG &= ~UCRXIFG;
                break;
            }
        }
    
        if (i == 1000)
        {
            return -1;
        }
    
    
        printf("Data Received: %s \n",buffer);
    
        //Send stop condition.
        UCB1CTL1 |= UCTXSTP;
    
        /* Clear USCI_B1 TX int flag */
        UCB1IFG &= ~UCTXIFG;
    
        return 0;
    }

    Now after the first call to ioport_get_battery_capacity() I receive data 0x03 as first byte. I am not sure whether incoming data is 1 byte or 2. But looks like its doing something. It will be great if you can help me with interpreting the incoming data. Thanks for your help Caleb!

  • Hi Anand,

    The data returned from the BQ27441-G1 is 2-bytes in length so you will need to read from the device twice. So for example, in the code I supplied the while loop that reads from the I2C buffer would need to execute twice (i.e. num = 2 first pass through the loop).

    I recommend taking a look at the corresponding MSP430G2 Launchpad code for the BOOTXL-BATTPACK located here: 

    While it's not for the exact MSP430 or battery fuel gauge you're trying to implement, they are very similar and will help provide insights into interfacing these two devices. 

    Best regards, 
    Caleb Overbay

  • Hello Caleb,

    I have incorporated the code sample that you have sent to me. And these are the results I am getting which still dont make sense. (Code is command I sent to booster pack, and result is what I get back).
    Code: 0x00, Result: 3072 (CONTROL_STATUS)
    Code: 0x02, Result: 49152 (Temperature: 0.1K)
    Code: 0x04, Result: 44811 (Voltage: 0 to 6000 mV)
    Code: 0x06, Result: 9997 (Flags)
    Code: 0x08, Result: 15361 (NominalAvailableCapacity: mAh)
    Code: 0x0a, Result: 15365 (FullAvailableCapacity: mAh)
    Code: 0x0c, Result: 5 (RemainingCapacity: mAh)
    Code: 0x0e, Result: 0 (FullChargeCapacity: mAh)
    Code: 0x10, Result: 22272 (AverageCurrent: mA)
    Code: 0x12, Result: 65021 (StandbyCurrent)
    Code: 0x14, Result: 19967 (MaxLoadCurrent)
    Code: 0x18, Result: 45568 (AveragePower)
    Code: 0x1c, Result: 0 (StateOfCharge)
    Code: 0x1e, Result: 49152 (IntTemperature)
    Code: 0x20, Result: 11 (StateOfHealth()

    Small other doubts that I have are whether in incoming 2 bytes for these commands, whether MSB will be being received first or LSB. That might need some changes in transBytes2Int() from the sample code you sent. Thanks...
  • Anand,

    The gauge uses little endian for commands and big endian for data memory.

    Your results look incorrect.

    Please use the examples from www.ti.com/.../slua801.pdf
  • Thanks for sharing that resource. One more problem that I am facing is most of the times, after sending first write instruction, booster pack doesn't acknowledge it. Do we need to wake the booster pack or it is always functioning? (For more information, please find attached screenshot from the logic analyzer.) So I can't communicate with it further. What might be causing this problem? 

  • Hi Anand,

    What value pull-up resistors are you using? Also, please check out this new application report on debugging common eUSCI and USCI serial communication issues: Solutions to Common eUSCI and USCI Serial Communication Issues on MSP430 MCUs

    Best regards,

    Caleb Overbay

  • Hi Anand,

    Were you able to solve the communication issues you were experiencing?

    Best regards,
    Caleb Overbay

**Attention** This is a public forum