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.

BQ4050: Dataflash block reads work but single byte registers fail

Part Number: BQ4050

Hi,

We are developing a tool to program our devices on the production line. It perform the following sequence:

  1. Read the gg.csv file exported by Battery Management Studio with our final configuration
  2. Read the data flash from the device using the block read command (0x44)
  3. If the values don't match, write the new values

We have observed the following:

  • All block reads are 32 bytes
  • Block reads work perfectly for any register longer than one byte. 
  • All single byte register reads fail. They seem incompatible with block read.
  • I can't find any reference in the register guide to dataflash "word read". It covers word write and block read/write.
  • Note the sequence below is missing the second 0x44 byte issued before the read starts.

  • Update:
    By creating a special condition for 1-byte registers we can now read 525 out of 539 registers correctly.

    Dear TI, what's the deal with different IO behaviour for different registers?

  • By using the same hack for 1-byte and odd-addresses we can now read all but 2 registers.

    Did everyone else have to reverse engineer this?

  • The last registers are read-only (Manufacturer name, device name) so we're going to say this is resolved. Here's my code from my PC app:

    // Fill in packet
    byte BQ4050_BLOCK_READ_CMD = 0x44;
    length = 0;
    if ((reg.DataLength == 1) || ( reg.AddressOffset % 2 != 0 ))
    {
        // Word read
        // - Read command
        payload[length++] = (byte)BQ4050_BLOCK_READ_CMD;
    
        // - Length is fixed at 2 (no explanation)
        payload[length++] = 2;
    
        // - Address
        foreach (byte b in BitConverter.GetBytes(SMBAddr))
            payload[length++] = b;
    
        // - CRC
        byte crc = ComputeCRC(payload, length);
        payload[length++] = crc;
    
        // Send to device
        Send();
        var response = Receive(Timeout);
        if (response == null)
        {
            StatusBox.Log(Name + "Read: No response");
            return false;
        }
    
        StatusBox.Log(Name + "ReadWord: Read addr: 0x" + SMBAddr.ToString("X2") + " Length: " + reg.DataLength);
        // Word read
        int read_offset = response.length - BQ4050_BLOCK_READ_LEN;
        if (reg.DataFlashRawValue[0] != response.payload[read_offset])
        {
            error = true;
            StatusBox.Log("Read: Readback mismatch. Readback: " + response.payload[3].ToString("X2") + " Expected: " + reg.DataFlashRawValue[0].ToString("X2"), Color.Red);
        }
    }
    else
    {
        // Block read:
        // - Read command
        payload[length++] = (byte)BQ4050_BLOCK_READ_CMD;
    
        // - Length
        payload[length++] = (byte)reg.DataLength;
    
        // - Address must be aligned
        int alignment = (byte)(reg.AddressOffset % BQ4050_BLOCK_READ_LEN);
        SMBAddr -= (UInt16)(alignment);
        foreach (byte b in BitConverter.GetBytes(SMBAddr))
            payload[length++] = b;
    
        // - CRC
        byte crc = ComputeCRC(payload, length);
        payload[length++] = crc;
    
        // Send to device
        Send();
        var response = Receive(Timeout);
        if (response == null)
        {
            StatusBox.Log(Name + "Read: No response");
            return false;
        }
    
        StatusBox.Log(Name + "ReadBlock: Read addr: 0x" + SMBAddr.ToString("X2") + " Length: " + reg.DataLength);
        // Block read:
        int read_offset = response.length + alignment - BQ4050_BLOCK_READ_LEN;
        for (int i = 0; i < reg.DataFlashRawValue.Count; i++)
        {
            if (reg.DataFlashRawValue[i] != response.payload[i + read_offset])
            {
                error = true;
                StatusBox.Log("Read: Readback mismatch[" + i + "]. Readback: " + response.payload[i + read_offset].ToString("X2") + " Expected: " + reg.DataFlashRawValue[i].ToString("X2"), Color.Red);
            }
        }
    }

    And here's my MCU code which is mostly pass-through except for setting the read length. I don't think the delays are required but i didn't want to remove them for a one-time operation:

    bool BQ4050FactoryConfigRead( uint8_t *buf, uint16_t *len )
    {
    	uint8_t i = 0;
    	HAL_StatusTypeDef res = HAL_OK;
    
    	PRINTF("TX[%i]: ", *len);
    	for( int i = 0; i < *len; i++)
    		PRINTF("%x ", buf[i]);
    	PRINTF("\n");
    
    	// Write command with address and PEC
    	res = HAL_I2C_Master_Transmit(&hi2c1, BQ4050_ADDRESS, buf, *len, BQ4050_RW_TIMEOUT);
    	if( res != HAL_OK )
    	{
    		HippoAddLog(__FILE__, __FUNCTION__, __LINE__, res, ERR_DESC_I2C);
    		return false;
    	}
    
    	HAL_Delay(20);
    
    	// Write block read command again (no explanation)
    	res = HAL_I2C_Master_Transmit(&hi2c1, BQ4050_ADDRESS, buf, sizeof(uint8_t), BQ4050_RW_TIMEOUT);
    	if( res != HAL_OK )
    	{
    		HippoAddLog(__FILE__, __FUNCTION__, __LINE__, res, ERR_DESC_I2C);
    		return false;
    	}
    
    	// Block read are always 35 bytes
    	// 2 byte command ack + 32 bytes data + 1 byte CRC
    	const int8_t READ_BLOCK_LEN = 35;
    	*len = READ_BLOCK_LEN;
    
    	HAL_Delay(20);
    
    	// Perform read
    	res = HAL_I2C_Master_Receive(&hi2c1, BQ4050_ADDRESS, buf, *len, BQ4050_RW_TIMEOUT);
    	if( res != HAL_OK )
    	{
    		HippoAddLog(__FILE__, __FUNCTION__, __LINE__, res, ERR_DESC_I2C);
    		return false;
    	}
    
    	PRINTF("RX[%i]: ",*len );
    	for( int i = 0; i <*len; i++)
    		PRINTF("%x ", buf[i]);
    	PRINTF("\n");
    
    	return true;
    }

    Hope that saves someone else 2 days!