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.

BQ40Z50: Need help initializing the chip using a microcontroller over I2C

Part Number: BQ40Z50
Other Parts Discussed in Thread: BQ24074, EV2400, GPCCHEM

Hello, 

We are using the BQ40Z50 in our system, an ESP32 microcontroller, and the BQ24074 charger. 

During manufacturing QA / testing, we're running into the BQ40Z50s all being in a "Permanent Fail" state and are unsure how to resolve this. For example, the PFStatus() command returns 0X17171717 (hex representation of the bits) which, if I'm reading the technical reference correctly, constitutes pretty much every single PF protection being tripped. 

Battery status shows: 
SEC0: 1
SEC1: 1
CHG-DISABLED:0 
DSG-DISABLE:0
Perm Fail: 1,
Shutdown: 1
CHG FET: 1
DSG FET: 1


Disconnecting/reconnecting the battery or power does not fix this 


Questions:

1. Am I missing something critical? 

2. In production, what SBS commands should be sent on boot to initialize the BQ40Z50 properly? 

3. In Manufacturing  / QA testing, what commands should be sent to initialize the BQ40Z50 prior to shipping? 


Thanks,
Amir

  • Hello Amir,

    Can you share the .gg file with your configuration information?

    The gauge is SMBus and not I2C, sometimes when the code 0x171717 is received it means you are trying to use I2C instead of SMBus.

    Sincerely,

    Wyatt Keller

  • Hi, 

    We're using a custom PCB with the chip soldered on, communicating via I2C with an ESP32 
    These values are what I"m pulling off the I2C line, but I've shared in Hex format to save space 

    Let me know if you need circuit schematics 

    I can also share the code I'm using to communicate with the BQ40Z50

  • Hello Amir,

    You must configure the gauge with EV2400 first to upload the chem ID which has proprietary information (EV2400 is only way to get the golden image with chem ID)

    If you have not gone through the proper configuration steps it could cause many issues with the gauge.

    1. Use GPCCHEM to find your chem ID
    2. Upload the chem ID
    3. Perform a learning cycle
    4. Configure any other application specific settings for protections and gauging
    5. Export the final golden image to upload in production

    I see there are libraries for SMBus using ESP32, if you are using I2C to communicate you will get the wrong data.

    Please reference this application note, it discusses why it is not compatilble: https://www.ti.com/lit/pdf/sloa132

    Sincerely,

    Wyatt Keller

  • Thank you, Wyatt

    I'm using the technical reference here: https://www.ti.com/lit/ug/sluua43a/sluua43a.pdf 
    Which states that I can use I2C to communicate with the fuel gauge? 
    It states that the device provides an I2C slave address as well as an SMBus slave address? 

    1) How Can I just disable protections for now to facilitate QA testing? The battery packs themselves have redundant BMS'es

    2) How can I adjust the Settings registers? I see all the settings I want to change, such as disabling TS1-4 in the Data Flash Values section but I'm unsure what commands are used to write to them. Or do I just write to the "register start address" directly? 

    3) When I try to send 0x0022 (FET_EN) followed by CHG_EN or DSG_EN nothing seems to happen, although the FET_EN bit is flipped to 1 
    Here's what I'm reading from the status registers: 
    Battery Status: 0X5AD7 (0110 1101 0101 0111)
    Operation Status: 0XE8E86D54
    Manufacturing Status: 0X6D57
    PF Status: 0X83836D53

    The operation and manufacturing statuses suggest: 

    SEC0: 1
    SEC1: 0       
    XCHG:1 
    XDSG:1 
    Perm Fail: 0
    Shutdown: 1
    CHGFET: 1
    DSGFET: 0

    FET_EN: 1

  • Update, 

    I think I'm able to write to the dataflash via instructions in the TRM here: www.ti.com/.../sluua43a.pdf
    12.1.60 0x4000–0x5FFF Data Flash Access()

    What is a quick way to sanity-check that the device is responding as intended over the custom I2C bus I'm using?

    For reference, here is some of the code I'm using to access, FuelGaugeChip is an Adafruit BusIO object for the actual i2c comms 

    uint32_t FuelGauge::executeMfgrCommand(const uint8_t* desired_command, uint8_t numBytes) {
      // Create a buffer to store the command data
      uint8_t cmd_data[2] = {desired_command[1], desired_command[0]};
      LUMO_LOGD("Sending Command: %#X", cmd_data[1]);
      // Create a buffer to store the data returned from fuel gauge
      uint8_t  received_data[numBytes];
      uint32_t result{0};
    
      // Execute the command
      FuelGaugeChip.write_then_read(cmd_data, sizeof(cmd_data), received_data, sizeof(received_data), true);
      delay(500);
      // The data is returned as a 16-bit value in two bytes, with the
      // high byte in the first element of the received_data array and the low
      // byte in the second element. To combine these two bytes into a single
      // 16-bit value, we can use bit shifting and bitwise OR operations.
      if (numBytes == 2) {
        return (result |= bytesToInt16(received_data));
      } else if (numBytes == 4) {
        return bytesToInt32(received_data);
      } else if (numBytes == 1) {
        return (result |= received_data[0]);
      } else {
        LUMO_LOGD("Error parsing values from fuel_gauge");
        return 0;
      }
    }
    
    // Execute a single word command on the BQ40Z50 and read a specified number of bytes
    // Returns the data read from the BQ40Z50
    uint32_t FuelGauge::executeCommand(const uint8_t command, uint8_t numBytes) {
      // Create a buffer to hold the data read from the BQ40Z50
      uint8_t* data = new uint8_t[numBytes];
      uint32_t result{0};
      // Create a buffer to store the command data
      // TODO: support longer cmd_data for seal/unseal operations?
      uint8_t cmd_data[1] = {command};
      LUMO_LOGD("Sending Command: %#x", cmd_data[0]);
      // Use the write_then_read() method to execute the command
      FuelGaugeChip.write_then_read(cmd_data, sizeof(cmd_data), data, numBytes, 0x00);
      delay(500);
      if (numBytes == 2) {
        return (result |= bytesToInt16(data));
      } else if (numBytes == 4) {
        return bytesToInt32(data);
      } else if (numBytes == 1) {
        return (result |= data[0]);
      } else {
        LUMO_LOGE("Error parsing values from fuel_gauge!");
        return 0;
      }
    }
    
    void FuelGauge::updateTemperatureEnable(bool enable) {
      // Create a buffer to hold the command data
      uint8_t cmd_data[4] = {0x40, 0x4E, 0x49, enable ? 0x01 : 0x00};
      // Send the command
      blockWrite(cmd_data, sizeof(cmd_data));
    }
    
    void FuelGauge::blockWrite(uint8_t* data, uint8_t numBytes) {
      uint32_t block;
      if (numBytes == 2) {
        block = bytesToInt16(data);
      } else if (numBytes == 4) {
        block = bytesToInt32(data);
      } else if (numBytes == 1) {
        block = data[0];
      } else {
        LUMO_LOGE("Error parsing input values for Block Write!");
        return;
      }
      LUMO_LOGD("\n\t\t Writing %#X", block);
      FuelGaugeChip.write(data, numBytes);
    }
    // Convert a buffer of bytes into a 16-bit integer
    uint16_t FuelGauge::bytesToInt16(uint8_t* data) {
      return ((uint16_t)data[1] << 8) | (uint16_t)data[0];
    }
    
    // Convert a buffer of bytes into a 32-bit integer
    int32_t FuelGauge::bytesToInt32(uint8_t* data) {
      return ((uint32_t)data[3] << 24) | ((uint32_t)data[2] << 16) | ((uint32_t)data[1] << 8) | (uint32_t)data[0];
    }

  • Hello Amir,

    Can you provide a snippet or section number of the section referring to the gauge using I2C? It may be a bug in the TRM.

    The BQ40Z50 uses SMBus for communication and can also transmit SMBus Charge Voltage and Charge Current to the charger. It does not use I2C and it won't be compatible with all commands (as mentioned in the app note linked in the last response). Maybe with some of the more basic commands, and I'm sure you could make wrapper functions around an I2C library to get it working, but we cannot assist with the driver writing.

    The best way I can recommend you to debug this type of issue with the communication packet structure is to use the EV2400 with a logic analyzer and then your code and compare the two.

    Sincerely,

    Wyatt Keller