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.

BQ76940: I2C Communication issue

Part Number: BQ76940

Hi experts,

I'm designing 13s BMS using BQ7694003. I'm having trouble writing to BQ registers. The IC doesn't give an ACK on the 9th cycle. The SDA line isn't pulled LOW during the 9th cycle. The same issue had been brought up earlier https://e2e.ti.com/support/power-management/f/196/t/323329  But neither the solution nor the cause was pointed out. I changed the BMS IC but still the issue wasn't solved.

I have studied the code given at http://www.ti.com/lit/zip/sluc583  and the ap note http://www.ti.com/lit/pdf/slva626 and accordingly have written my code. 

In my I2C  setup - 

Host MCU -> MASTER (always)
BQ76940 -> SLAVE (always)

I believe this setup is right because BQ doesn't generate SCL, hence it cannot be a MASTER in any event (tx or rx). Thus, it'll always be a SLAVE and MCU will be the MASTER always because it generates SCL. Is my understanding correct?

Moving on to I2C software. To explain my software flow, I will consider the example of CC_CFG register

BQ_addr = 0x08 or 0000 1000
CC_CFG : addr = 0x0B or 0000 1011
                  data = 0x19 or 0001 1001
 CRC = 0x89 or 1000 1001

CRC poly: x8 + x2 + x + 1 = 1 0000 0111
CRC is calculated over: BQ_addr(8bit)  |  CC_CFG(8bit)  |  DATA(8bit) 

                                          0000 1000  |  0000 1011  |  0001 1001

since the power of CRC poly is 8, 8 zeroes are appended to the above binary number and then CRC is calculated
I got 0x89 as the CRC for the above case

The whole I2C frame is then transmitted to the BQ. The BQ sends an ACK after every byte it receives. The frame format is
START  |  SLAVE_ADDR(7)+W(1)   |  REGISTER_ADDR(8)  DATA(8)  |  CRC(8)  |  STOP

(n)  -> indicates the number of bits

SLAVE_ADDR + W = 0001 0000 (BQ_ADDR left shifted by 1)
REGISTER_ADDR = 0000 1011
DATA = 0001 1001
CRC = 1000 1001

SLAVE_ADDR is the BQ address left shifted by 1 bit. 8th bit of this field is zero which indicates MASTER is in transmission mode (MT mode). Thus, the MASTER will write data into the SLAVE's register.

Now the problem is, BQ isn't sending an ACK on the 9th cycle after SLAVE_ADDR + W. SLAVE_ADDR + W byte is transmitted successfully but a NACK is given by the BQ on 9th cycle, SDA isn't pulled LOW. 
Below are the voltage readings I observed at different pins of the BQ IC - 

(ALL VOLTAGES ARE WRT GROUND)

TS1 = 0.0 V (after bootup)
TS2 = 20.2 V
TS3 = 36.4 V
CAP1 = 3.28 V
CAP2 = 23.7 V
CAP3 = 40.01 V
REGSRC = 18.4 V
VC5X = 20.4 V [C1,C2,C3,C4,C5]
VC10X = 36.7 V [C6,C7.C8.C10]
BAT = 53.2 V [C11,C12,C13,C15]
PACK+ = 53.3 V

I have used 3 10k NTC thermistors, each soldered between TS1-gnd, TS2-VC5X, TS3-VC10X. When I press the BOOT button, the TS1 voltage goes up to 2.7 V. Also, BOOT signal is applied after connecting the battery. I2C communication is started after BQ boots up.
I have also used a 3.3 - 5v bidirectional logic level shifter to enable safe and correct communication between the 5V MCU and 3.3v BQ IC.

So, what can be cause of the mentoned issue? Why is BQ not sending an ACK? (PS: I dont have access to an EVM board, so dont ask to test the I2C on an EVM)

  • Hi Pranit,

    It looks like you may have calculated the CRC using the unshifted address: 0000 1000 | 0000 1011 | 0001 1001
    If you calculate using the actual bytes you send to the device (0001 0000 | 0000 1011 | 0001 1001), the CRC should be 0x7A

    Matt
  • Hi Matt

    In that case, shouldn't I get a NACK after the transmission of CRC byte? Why am I getting NACK after the SLA+W byte, even though it is sent correctly

  • Hi Pranit,

    To really debug this, you need to capture waveforms with an oscilloscope. You should capture waveforms on the BQ76940 side of the level shifter.

    Matt
  • ISSUE RESOLVED!

    After weeks of debugging, I have finally got the BMS communicating with the MCU. So I will post the checklist, things to be done, precautions and steps to follow for debugging

    My setup is:
    MCU: Arduino Promini (5v variant)
    BMS: BQ7694003 (3.3v variant)
    Pack: 13-15s configurable

    HARDWARE DEBUGGING

    1. Starting off with the schematic, make sure to follow datasheets and reference designs related to BQ. I found TIDR773 and TIDA00792 to be extremely useful. TIDR uses P-ch MOSFETs
    for balancing whereas TIDA00792 uses N-ch MOSFETs for balancing. The preference is upto you. Rest everything is conceptually similar in both designs.

    2. Make sure to place capacitors close to the ic pins where required. Keep the load circuitry away from the sensitive digital circuitry to minimise the noise coupling into the digital circuitry. 

    3. A complete PCB layer dedicated to ground will reduce noise significantly. 

    4. Although BQ is designed to handle random cell connection, still to be on a safer side, plug in the battery with the GND first, then Cell1, then Cell2, then Cell3 and so on till last cell.

    5. Keep I2C traces short and make sure there is not much of capacitive coupling between the two. Such coupling can lead to incorrect bit recognition during I2C communication

    6. Use Kelvin Connection for R_sense connection. It yields better CC results. https://www.analog.com/en/analog-dialogue/articles/optimize-high-current-sensing-accuracy.html

    7. DO NOT FORGET PULLUP RESISTORS FOR I2C ! else the entire communication will fail. Generally, 4.7k - 10k resistors are used for pullups. Here's a good guide on I2C Communication basics. www.ti.com/lit/an/slva704/slva704.pdf

    8. If you, like me, have logic level differences between BMS and MCU, use a logic level shifter.  In my case, although the 5v MCU was able to read from the 3.3v BMS, it was reading some registers wrongly. So to avoid this, use a logic level shifter. https://learn.sparkfun.com/tutorials/bi-directional-logic-level-converter-hookup-guide/all . In my case, BMS HIGH(max) and MCU HIGH(min) differed by only 0.1v.  So I was relying on luck for the I2C signals to get recognised correctly.

    9. Now most importantly, do check the datasheet and verify nominal voltage readings on the following pins. 
    TS0, TS1, TS2
    CAP1, CAP2, CAP3
    V5X, VC10X, BAT
    REGSRC
    REGOUT

    CAP1 - GND = 3.3V
    CAP2 - VC5X = 3.3V
    CAP3 - VC10X = 3.3V
    REGOUT = 3.3V 

    If the voltage readings on all these pins are correct, then you can relax a bit! Your hardware isn't faulty.

    10. Connect NC pins too! Also, if no a thermistor isnt used, then pulldown the corresponding TSx pin to GND via 10k resistor.

    11. A BOOT signal is needed on TS1 pin to wake up the BQ76940 IC. Dont expect the IC to wakeup on its own :)

    12. Connect the ALERT pin of BQ to an interrupt pin on the MCU. This will help you in quick identification and clearing of faults



    SOFTWARE DEBUGGING

    If you like rock then you'll love s/w debugging because it involves a lot of head-banging :)

    1. Grab yourself a cup of coffee.

    2. Make sure all your pin declarations and ISRs are setup and working. Debug the entire code piece by piece. DO NOT DEBUG A 1000 LINE CODE AT ONCE.

    3. To test the I2C communication, first run an I2C scanner code to find the BQ IC. This will get two things done at once - you can verify the I2C address of the BQ IC, plus, if the device responds, then you can be sure that the device is alive!

    4. Once the BQ is recognised by the MCU, test the I2C read and write functionality. (optional) Read all registers of the BQ device and note them down.
    For all Arduino fanboys out there, use Wire() Library. The sequence for read is - 

    /* READS 1 BYTE */
    byte registerRead(byte regAddress)
    {
    Wire.beginTransmission(bqI2CAddress);
    Wire.write(regAddress);
    Wire.endTransmission();
    Wire.requestFrom(bqI2CAddress, 1);

    return (Wire.read());
    }

     

    /* READS 2 BYTES  */
    int registerDoubleRead(byte regAddress)

    {
    Wire.beginTransmission(bqI2CAddress);
    Wire.write(regAddress);
    Wire.endTransmission();

    Wire.requestFrom(bqI2CAddress, 2);

    byte reg1 = Wire.read();
    byte reg2 = Wire.read();

    int combined = (int)reg1 << 8;
    combined |= reg2;

    return (combined);
    }


    5. Look up the datasheet to find whether your BQ IC is CRC-enabled or not. If CRC isn't enabled, then you can directly write to the BQ registers using the normal I2C communication protocol. IF CRC IS ENABLED, THEN YOU HAVE TO CALCULATE THE CRC AND SEND IT TO BQ, else it wont accept any write requests to its registers.
    Again, TI has a super sexy and simple guide on I2C http://www.ti.com/lit/an/slva704/slva704.pdf 


    /* Write without CRC */
    void registerWrite(byte regAddress, byte regData)

    {
    Wire.beginTransmission(bqI2CAddress);
    Wire.write(regAddress);
    Wire.write(regData);
    Wire.write(0x7A);
    Wire.endTransmission();
    }


    /*Write with CRC*/

    void registerWriteCRC(unsigned char regAddress, unsigned char regData)
    {
    uint8_t values[3] = { (bqI2CAddress << 1), regAddress, regData };
    uint8_t crc = calc_crc(values, 3);

    Wire.beginTransmission(bqI2CAddress);
    Wire.write(regAddress);
    Wire.write(regData);
    Wire.write(crc);
    Wire.endTransmission();
    }

    unsigned char calc_crc(unsigned char *ptr, unsigned char len)
    {
    unsigned char key = 7;
    unsigned char i;
    unsigned char crc = 0;

    while (len-- != 0)
    {
    for (i = 0x80; i != 0; i /= 2)
    {
    if ((crc & 0x80) != 0)
    {
    crc *= 2;
    crc ^= key;
    }
    else
    crc *= 2;

    if ((*ptr & i) != 0)
    crc ^= key;
    }
    ptr++;
    }
    return (crc);
    }

     




    And that it for the debugging session!

    ALL THE BEST FOR YOUR BMS PROJECT. 
    I HOPE THIS HELPS!

  • ISSUE RESOLVED!

    After weeks of debugging, I have finally got the BMS communicating with the MCU. So I will post the checklist, things to be done, precautions and steps to follow for debugging

    My setup is:
    MCU: Arduino Promini (5v variant)
    BMS: BQ7694003 (3.3v variant)
    Pack: 13-15s configurable

    HARDWARE DEBUGGING

    1. Starting off with the schematic, make sure to follow datasheets and reference designs related to BQ. I found TIDR773 and TIDA00792 to be extremely useful. TIDR uses P-ch MOSFETs
    for balancing whereas TIDA00792 uses N-ch MOSFETs for balancing. The preference is upto you. Rest everything is conceptually similar in both designs.

    2. Make sure to place capacitors close to the ic pins where required. Keep the load circuitry away from the sensitive digital circuitry to minimise the noise coupling into the digital circuitry. 

    3. A complete PCB layer dedicated to ground will reduce noise significantly. 

    4. Although BQ is designed to handle random cell connection, still to be on a safer side, plug in the battery with the GND first, then Cell1, then Cell2, then Cell3 and so on till last cell.

    5. Keep I2C traces short and make sure there is not much of capacitive coupling between the two. Such coupling can lead to incorrect bit recognition during I2C communication

    6. Use Kelvin Connection for R_sense connection. It yields better CC results. https://www.analog.com/en/analog-dialogue/articles/optimize-high-current-sensing-accuracy.html

    7. DO NOT FORGET PULLUP RESISTORS FOR I2C ! else the entire communication will fail. Generally, 4.7k - 10k resistors are used for pullups. Here's a good guide on I2C Communication basics. www.ti.com/lit/an/slva704/slva704.pdf

    8. If you, like me, have logic level differences between BMS and MCU, use a logic level shifter.  In my case, although the 5v MCU was able to read from the 3.3v BMS, it was reading some registers wrongly. So to avoid this, use a logic level shifter. https://learn.sparkfun.com/tutorials/bi-directional-logic-level-converter-hookup-guide/all . In my case, BMS HIGH(max) and MCU HIGH(min) differed by only 0.1v.  So I was relying on luck for the I2C signals to get recognised correctly.

    9. Now most importantly, do check the datasheet and verify nominal voltage readings on the following pins. 
    TS0, TS1, TS2
    CAP1, CAP2, CAP3
    V5X, VC10X, BAT
    REGSRC
    REGOUT

    CAP1 - GND = 3.3V
    CAP2 - VC5X = 3.3V
    CAP3 - VC10X = 3.3V
    REGOUT = 3.3V 

    If the voltage readings on all these pins are correct, then you can relax a bit! Your hardware isn't faulty.

    10. Connect NC pins too! Also, if no a thermistor isnt used, then pulldown the corresponding TSx pin to GND via 10k resistor.

    11. A BOOT signal is needed on TS1 pin to wake up the BQ76940 IC. Dont expect the IC to wakeup on its own :)

    12. Connect the ALERT pin of BQ to an interrupt pin on the MCU. This will help you in quick identification and clearing of faults



    SOFTWARE DEBUGGING

    If you like rock then you'll love s/w debugging because it involves a lot of head-banging :)

    1. Grab yourself a cup of coffee.

    2. Make sure all your pin declarations and ISRs are setup and working. Debug the entire code piece by piece. DO NOT DEBUG A 1000 LINE CODE AT ONCE.

    3. To test the I2C communication, first run an I2C scanner code to find the BQ IC. This will get two things done at once - you can verify the I2C address of the BQ IC, plus, if the device responds, then you can be sure that the device is alive!

    4. Once the BQ is recognised by the MCU, test the I2C read and write functionality. (optional) Read all registers of the BQ device and note them down.
    For all Arduino fanboys out there, use Wire() Library. The sequence for read is - 

    /* READS 1 BYTE */
    byte registerRead(byte regAddress)
    {
    Wire.beginTransmission(bqI2CAddress);
    Wire.write(regAddress);
    Wire.endTransmission();
    Wire.requestFrom(bqI2CAddress, 1);

    return (Wire.read());
    }

    /* READS 2 BYTES  */
    int registerDoubleRead(byte regAddress)
    {
    Wire.beginTransmission(bqI2CAddress);
    Wire.write(regAddress);
    Wire.endTransmission();

    Wire.requestFrom(bqI2CAddress, 2);

    byte reg1 = Wire.read();
    byte reg2 = Wire.read();

    int combined = (int)reg1 << 8;
    combined |= reg2;

    return (combined);
    }


    5. Look up the datasheet to find whether your BQ IC is CRC-enabled or not. If CRC isn't enabled, then you can directly write to the BQ registers using the normal I2C communication protocol. IF CRC IS ENABLED, THEN YOU HAVE TO CALCULATE THE CRC AND SEND IT TO BQ, else it wont accept any write requests to its registers. 
    Again, TI has a super sexy and simple guide on I2C http://www.ti.com/lit/an/slva704/slva704.pdf 


    /* Write without CRC */
    void registerWrite(byte regAddress, byte regData)

    {
    Wire.beginTransmission(bqI2CAddress);
    Wire.write(regAddress);
    Wire.write(regData);
    Wire.write(0x7A);
    Wire.endTransmission();
    }



    /*Write with CRC*/

    void registerWriteCRC(unsigned char regAddress, unsigned char regData)
    {
    uint8_t values[3] = { (bqI2CAddress << 1), regAddress, regData };
    uint8_t crc = calc_crc(values, 3);

    Wire.beginTransmission(bqI2CAddress);
    Wire.write(regAddress);
    Wire.write(regData);
    Wire.write(crc);
    Wire.endTransmission();
    }

    unsigned char calc_crc(unsigned char *ptr, unsigned char len)
    {
    unsigned char key = 7;
    unsigned char i;
    unsigned char crc = 0;

    while (len-- != 0)
    {
    for (i = 0x80; i != 0; i /= 2)
    {
    if ((crc & 0x80) != 0)
    {
    crc *= 2;
    crc ^= key;
    }
    else
    crc *= 2;

    if ((*ptr & i) != 0)
    crc ^= key;
    }
    ptr++;
    }
    return (crc);
    }

     

    For BQ769x0, the CRC polynomial is x^8 + x^2 + x + 1. Key is 7. https://www.digikey.com/eewiki/display/microcontroller/CRC+Basics 

    Feel free to understand the steps the CRC calculation. Also, the transmission sequence and CRC calculation is a bit different when using CRC, so make sure to checkout the datasheet for more details.

    CRC is calculated over the bytes which are sent

    6. Make sure you convert the bytes received into actual values (volts, amperes etc). Look up the datasheet for formulae. As I had mentioned earlier, you got to be like rock! Writing the software will involve a lot of 

    bit banging and head-banging. You will have to make yourself comfortable in the world of HEX, DEC and BIN

    7. To check whether the WRITE function works or not, simply write a value to a register and read back the register. If your MCU reads the same value which you had written earlier, then the WRITE functionality is

    working and so is the READ functionality!

    8. Make sure to clear XREADY bit when it is set. Doing so will help you recognise the events when the BQ IC faces some issues l(e.g. excessive system transients)




    And that it for the debugging session!

    ALL THE BEST FOR YOUR BMS PROJECT. 
    I HOPE THIS HELPS!