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.

BQ27441-G1: Battery Fuel Management IC set Capacity

Part Number: BQ27441-G1

Tool/software:

Hello,

I have been working with the BQ27441 for an embedded project. I have gotten it to work quiet well but I am coming up short with writing the new capacity. I have tested the chip and reading the Voltage, Temperature, and current are all correct and what is expected. I am writing this code in Python but when I read the capacity it still reading as the default value. 

When I set the battery capacity I do the following;

unseal

enter extended mode 

Write the register for the battery capacity

exit extended mode

seal 

This does not produce any errors and the device works and gives me readings, but the capacity has not been updated. I am attaching the full code for this sample incase anyone in the future needs any help with getting started for a Raspberry pi or other Linux distros.

import smbus2
import time

# I2C address of BQ27441
BQ27441_I2C_ADDR = 0x55  

# Register addresses
CONTROL_REG = 0x00       # Control register
EXTENDED_DATA_CTRL = 0x3E  # Extended Data Control register
DESIGN_CAP_REG = 0x4B    # Design Capacity register
EXIT_CFGUPDATE_REG = 0x44  # Exit Config Mode
SOC_REG = 0x1C           # State of Charge register
VOLTAGE_REG = 0x04       # Battery Voltage register
CURRENT_REG = 0x10       # Average Current register
TEMP_REG = 0x02          # Battery Temperature register
REMAINING_CAP_REG = 0x0A  # Remaining Capacity register
SOH_Reg = 0x20 #State of health 

# Control Subcommands
UNSEAL_KEY_1 = 0x8000
SEAL_COMMAND = 0x0020
ENTER_CFGUPDATE = 0x0013

# Battery capacity to set (mAh)
BATTERY_CAPACITY = 4500  

# Initialize I2C bus
bus = smbus2.SMBus(1)

def write_register(reg, value):
    """Writes a 16-bit value to the specified BQ27441 register."""
    data = [value & 0xFF, (value >> 8) & 0xFF]  # Low byte first
    bus.write_i2c_block_data(BQ27441_I2C_ADDR, reg, data)

def read_register(reg):
    """Reads a 16-bit value from the specified BQ27441 register."""
    data = bus.read_i2c_block_data(BQ27441_I2C_ADDR, reg, 2)
    return data[0] | (data[1] << 8)

def unseal_battery():
    """Unseals the BQ27441 to allow writing to protected registers."""
    write_register(CONTROL_REG, UNSEAL_KEY_1)
    time.sleep(0.1)

def seal_battery():
    """Seals the BQ27441 to protect configuration settings."""
    write_register(CONTROL_REG, SEAL_COMMAND)
    time.sleep(0.1)

def enter_extended_mode():
    """Enables Extended Mode for configuration changes."""
    write_register(CONTROL_REG, ENTER_CFGUPDATE)  # Enter Config Mode
    time.sleep(0.1)

def exit_extended_mode():
    """Exits Extended Mode and applies changes."""
    write_register(EXIT_CFGUPDATE_REG, 0x00)  # Exit Extended Mode
    time.sleep(0.1)

def set_battery_capacity(capacity):
    """Sets the battery capacity in Extended Mode."""
    unseal_battery()  # Unseal before modifying protected settings
    enter_extended_mode()
    
    # Access Design Capacity Block
    write_register(EXTENDED_DATA_CTRL, 0x13)
    time.sleep(0.1)

    # Write new battery capacity
    write_register(DESIGN_CAP_REG, capacity)
    time.sleep(0.1)

    exit_extended_mode()
    seal_battery()  # Reseal after configuration
    return read_register(DESIGN_CAP_REG) == capacity

def get_battery_percentage():
    """Reads the battery's state of charge (SOC) in percentage."""
    return read_register(SOC_REG)

def get_battery_voltage():
    """Reads the battery voltage in millivolts (mV)."""
    return read_register(VOLTAGE_REG)

def get_current_draw():
    """Reads the battery current draw in mA (negative = discharge, positive = charge)."""
    raw_current = read_register(CURRENT_REG)
    if raw_current > 32767:  # Convert two's complement for negative values
        raw_current -= 65536
    return raw_current

def get_battery_temperature():
    """Reads the battery temperature in Celsius."""
    raw_temp = read_register(TEMP_REG)  # Temperature in 0.1 Kelvin
    temp_celsius = (raw_temp / 10.0) - 273.15  # Convert to Celsius
    return round(temp_celsius, 2)

def get_remaining_capacity():
    """Reads the remaining battery capacity in mAh."""
    return read_register(REMAINING_CAP_REG)

def get_state_of_health():
    """Reads state of battery"""
    soh_data = read_register(SOH_Reg)
    return soh_data & 0xFF

# Main execution
if __name__ == "__main__":
    print("Initializing BQ27441...")

    if set_battery_capacity(BATTERY_CAPACITY):
        print(f"Battery capacity set to {BATTERY_CAPACITY} mAh")
    else:
        print("Failed to set battery capacity!")

    while True:
        battery_percentage = get_battery_percentage()
        battery_voltage = get_battery_voltage()
        current_draw = get_current_draw()
        battery_temperature = get_battery_temperature()
        remaining_capacity = get_remaining_capacity()
        state_of_health = get_state_of_health()

        print(f"Battery Percentage: {battery_percentage}%")
        print(f"Battery Voltage: {battery_voltage} mV")
        print(f"Current Draw: {current_draw} mA")
        print(f"Battery Temperature: {battery_temperature}°C")
        print(f"Remaining Capacity: {remaining_capacity} mAh")
        print(f"State of Health: {state_of_health}")
        print("-" * 40)

        time.sleep(2)  # Delay before next reading

  • Hello,

    When you say extended mode, are you referring to CONFIG UPDATE mode?

    Regards,

    Adrian

  • Hello Adrian,  Thank you for looking at my question.  To answer your question, Yes its enter the device in Config Update Mode to send the new data. I have looked at the TRM and updated the code but I am still unable to update the capacity. If you look in my Update Capacity I, Write the unseal command twice, enter ConfigUpdate, I wait until bit 4 is set in the Flags register, enable block RAM update, update the capacity register, checksum update, I check the checksum, then do a soft reset but when I look at capacity remaining I'm still seeing 1232 (mAh) which is indicating the initial value is still set. 

     

    import time
    from smbus2 import SMBus
    
    # Define I2C address and registers
    BQ27441_I2C_ADDR = 0x55  # Default I2C address
    CONTROL_REG = 0x00
    FLAGS_REG = 0x06
    BLOCK_DATA_CONTROL = 0x61
    DATA_BLOCK_CLASS = 0x3E
    DATA_BLOCK_OFFSET = 0x3F
    CHECKSUM_REG = 0x60
    SOFT_RESET = 0x0042
    
    # Battery information registers
    DESIGN_CAPACITY_REG = 0x4A  # Accessed via RAM update (Subclass 82)
    REMAINING_CAPACITY_REG = 0x0A
    STATE_OF_CHARGE_REG = 0x1C
    VOLTAGE_REG = 0x04
    TEMPERATURE_REG = 0x02  # Needs conversion to Celsius
    
    # Unseal and sealing keys
    UNSEAL_KEY = [0x80, 0x00, 0x80, 0x00]
    SEAL_KEY = [0x20, 0x00]
    
    # Subclass ID for Design Capacity
    DESIGN_CAPACITY_CLASS = 0x52  # Subclass 82
    
    # I2C bus number (modify as needed)
    I2C_BUS = 1
    
    def write_word(bus, reg, value):
        """Write a word (2 bytes) to a register."""
        bus.write_word_data(BQ27441_I2C_ADDR, reg, value)
    
    def write_bytes(bus, reg, values):
        """Write multiple bytes to a register."""
        bus.write_i2c_block_data(BQ27441_I2C_ADDR, reg, values)
    
    def read_word(bus, reg):
        """Read a word (2 bytes) from a register."""
        return bus.read_word_data(BQ27441_I2C_ADDR, reg)
    
    def read_bytes(bus, reg, length):
        """Read multiple bytes from a register."""
        return bus.read_i2c_block_data(BQ27441_I2C_ADDR, reg, length)
    
    def calculate_checksum(data):
        """Calculate checksum according to BQ27441 TRM."""
        checksum = 255 - (sum(data) % 256)
        return checksum & 0xFF
    
    def read_battery_info():
        """Read and print battery parameters."""
        with SMBus(I2C_BUS) as bus:
            capacity = read_word(bus, DESIGN_CAPACITY_REG)
            remaining_capacity = read_word(bus, REMAINING_CAPACITY_REG)
            soc = read_word(bus, STATE_OF_CHARGE_REG) & 0xFF  # SOC is a percentage
            voltage = read_word(bus, VOLTAGE_REG)
            temperature = read_word(bus, TEMPERATURE_REG)  # Needs conversion
    
            # Convert temperature (reported in 0.1 Kelvin) to Celsius
            temperature_c = (temperature * 0.1) - 273.15
    
            print("\nBattery Information:")
            print(f"- Design Capacity: {capacity} mAh")
            print(f"- Remaining Capacity: {remaining_capacity} mAh")
            print(f"- Battery Percentage: {soc}%")
            print(f"- Voltage: {voltage} mV")
            print(f"- Temperature: {temperature_c:.2f} °C\n")
    
    def update_capacity(new_capacity):
        """Update the fuel gauge capacity and read battery info."""
        with SMBus(I2C_BUS) as bus:
            print("Unsealing device...")
            write_bytes(bus, CONTROL_REG, UNSEAL_KEY[:2])
            time.sleep(0.5)
            write_bytes(bus, CONTROL_REG, UNSEAL_KEY[2:])
            time.sleep(0.5)
    
            print("Entering config update mode...")
            write_bytes(bus, CONTROL_REG, [0x13, 0x00])
            time.sleep(0.5)
    
            # Wait until bit 4 is set in FLAGS_REG
            while not (read_word(bus, FLAGS_REG) & 0x0010):
                time.sleep(0.5)
    
            print("Enabling block RAM update...")
            write_bytes(bus, BLOCK_DATA_CONTROL, [0x00])
    
            print("Selecting Design Capacity subclass...")
            write_bytes(bus, DATA_BLOCK_CLASS, [DESIGN_CAPACITY_CLASS])
    
            print("Selecting first 32-byte block...")
            write_bytes(bus, DATA_BLOCK_OFFSET, [0x00])
    
            print("Reading initial checksum...")
            old_checksum = read_bytes(bus, CHECKSUM_REG, 1)[0]
    
            print("Reading current settings...")
            block_data = read_bytes(bus, 0x40, 32)
    
            # Update design capacity (assume it's stored at offset 10-11)
            block_data[10] = new_capacity & 0xFF
            block_data[11] = (new_capacity >> 8) & 0xFF
    
            print("Writing new values...")
            for i in range(len(block_data)):
                write_bytes(bus, 0x4C + i, [block_data[i]])
                time.sleep(0.01)  # Small delay for stability
    
            print("Calculating new checksum...")
            new_checksum = calculate_checksum(block_data)
            write_bytes(bus, CHECKSUM_REG, [new_checksum])
    
            time.sleep(2)  # Wait 2s
    
            print("Verifying new checksum...")
            updated_checksum = read_bytes(bus, CHECKSUM_REG, 1)[0]
            print("Updated Checksum  "+str(updated_checksum))
            print("New Checksum " + str(new_checksum))
            if new_checksum == updated_checksum:
                print("Checksum matches. Applying soft reset...")
                write_bytes(bus, CONTROL_REG, [SOFT_RESET & 0xFF, (SOFT_RESET >> 8) & 0xFF])
            else:
                print("Checksum mismatch! Update failed.")
                return
    
            time.sleep(0.5)
    
            print("Sealing the device...")
            write_bytes(bus, CONTROL_REG, SEAL_KEY)
    
            print("Update complete!")
    
        # After updating, print battery information
        read_battery_info()
    
    # Example usage: Set new capacity to 3000mAh
    update_capacity(3000)