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.

BQ76942: Cannot Enter Full Access Mode for OTP Programming. Help!

Part Number: BQ76942

Tool/software:

I'm using the BQ76942 and am trying to use OTP programming but cannot enter FULL ACCESS mode. 

I've been using the chip on these boards for months now with no issues. The hardware is working and well understood. 

Page 125 in the TRM describes the process for Data Memory Access and I believe I am executing this correctly. I've verified this with my LA as a check. I'm including this code so you can have a look. 

What state does the IC have to be in to enter full access (other than first going through UNSEALED.) Do I enter CFG_UPDATE before or after going through the process to enter FULL ACCESS? Should I do the register reset before or after? 

Any help you can give is appreciated! I'm including the relevant code. Perhaps there is some easy mistake in that. 

Function calls for FULLACCESS:

CommandSubcommands(BQ769x2_RESET);  // Resets the BQ769x2 registers

	    	writeDataMemoryAccess(0x9257, 0x0414, 2);
	    	delayUS(60000);
	    	writeDataMemoryAccess(0x9259, 0x3672, 2);
	    	delayUS(60000);
	    	writeDataMemoryAccess(0x925B, 0xFFFF, 2);
	    	delayUS(60000);
	    	writeDataMemoryAccess(0x925D, 0xFFFF, 2);
	    	delayUS(60000);

	    	uint8_t battery_status_data = checkBQAccessMode();
			if(battery_status_data != 4)
			{
//				while(1)
//				{
//					LEDControl("R", 100, 4);
//				}
			}
			delayUS(60000);

			// Enter CONFIGUPDATE mode (Subcommand 0x0090) - It is required to be in CONFIG_UPDATE mode to program the device RAM settings
			// See TRM for full description of CONFIG_UPDATE mode
			CommandSubcommands(SET_CFGUPDATE);
			delayUS(60000);

			//Use OTP_WR_CHECK() to determine if the chip is ready for OTP programming. OTP WR CHECK will return
			uint8_t otp_write_status = OTPWriteCheck();
			delayUS(60000);
			if(otp_write_status == 0X80){ //Write to the desired OTP Register and send the OTP Write command
				test = 1;
			}

The writeDataMemoryAccess() function:

void writeDataMemoryAccess(uint16_t memory_address, uint16_t value, uint8_t size){
    uint8_t address[2] = {0};
    address[0] = memory_address & 0xFF;        // Lower byte of address
    address[1] = (memory_address >> 8) & 0xFF; // Upper byte of address

    uint8_t data[32] = {0};
    uint8_t i;

    // Prepare data in little endian format
    for(i = 0; i < size; i++){
        data[i] = (value >> (i * 8)) & 0xFF;
    }

    // Prepare the buffer for checksum calculation
    uint8_t checksum_buffer[2 + size]; // 0x3E, 0x3F, and data buffer
    checksum_buffer[0] = address[0];   // 0x3E
    checksum_buffer[1] = address[1];   // 0x3F
    for(i = 0; i < size; i++){
        checksum_buffer[2 + i] = data[i]; // Data bytes starting at 0x40
    }

    // Calculate checksum using existing Checksum function
    uint8_t checksum = Checksum(checksum_buffer, 2 + size);

    // Length calculation: data length + 4 (for 0x3E, 0x3F, 0x60, 0x61)
    uint8_t length = size + 4;

    // Write lower byte of address to 0x3E
    I2C_WriteReg(0x3E, &address[0], 1);
    // Write upper byte of address to 0x3F
    I2C_WriteReg(0x3F, &address[1], 1);
    // Write data to 0x40
    I2C_WriteReg(0x40, data, size);
    // Write checksum to 0x60
    I2C_WriteReg(0x60, &checksum, 1);
    // Write length to 0x61
    I2C_WriteReg(0x61, &length, 1);
}

The I2C_WriteReg() function is just a pass through to STM32 HAL_I2C_Mem_Write() function. 

  • Hello Ryan,

    Just to clarify, is the part UNSEALED? Were you able to enter FULL ACCESS Mode before?

    Best Regards,
    Alexis

  • Hi Alexis, 

    Ok so it turns out that was an issue in my code. I have the chip in FULL ACCESS and bit 7 of the Battery Status register is 0. 

    However, when I put the chip in to CFG UPDATE the OTP_WR_STATUS register reads 0xFF. What could be causing that? Before I put the chip in to CFG UPDATE it reads 0x20 (which I believe is because we are not in CFG UPDATE). 

    Ryan

  • If there's any way we can get some info on this soon I would very much appreciate it! I'm on a time crunch to get this sorted out and tested. Thanks in advance. 

  • Hello Ryan,

    There is a chance you are reading the register too quickly.  Can you trying to add a 2ms wait time before reading the results to see if that helps?

    Section 2 Subcommands from the BQ769x2 Software Development Guide (Rev. B) talks about this.

    Best Regards,
    Alexis

  • Alexis, 

    I added 2ms of delay before each of my I2C transactions just to be safe. That doesn't seem to make a difference. 

    Here is my code in case that helps. 

    It certainly doesn't seem like the chip can possibly report 0xFF accurately as that would indicate undervoltage and overvoltage, over temp and undertemp, etc. 

    //Create some flags to diagnose OTP
    	    	uint8_t shutdown_stack_voltage_alread_written, not_in_full_access, registers_not_programmed_successfully, otp_wr_check_nfg, OTPB_write_bit_data_nfg;
    
    	    	/*
    	    	1. Check whether OTP programming has already been done on the device by reading one of the programmed
    	    	registers. When power is applied, registers will either report the default values or the values programmed in
    	    	OTP if OTP has been programmed. If OTP programming has not been done, proceed to the next steps.
    			*/
    	    	uint8_t data[2];
    	    	HAL_Delay(2);
    	    	BQ769x2_GetRegister(ShutdownStackVoltage, data, 2);
    	    	uint16_t shutdown_stack_voltage = ((data[1] << 8) | data[0]) * 10; //reports in mA
    
    	    	//2. Read the 0x12 Battery Status[SEC1,SEC0] bits to verify the device is in FULL ACCESS mode (0x01).
    	    	HAL_Delay(2);
    			writeDataMemoryAccess(0x9257, 0x0414, 2);
    			delayUS(60000);
    			writeDataMemoryAccess(0x9259, 0x3672, 2);
    			delayUS(60000);
    			writeDataMemoryAccess(0x925B, 0xFFFF, 2);
    			delayUS(60000);
    			writeDataMemoryAccess(0x925D, 0xFFFF, 2);
    			delayUS(60000);
    
    			HAL_Delay(2);
    	    	uint8_t battery_status_data = checkBQAccessMode();
    	    	if(battery_status_data != 0x01){
    	    		not_in_full_access = 1;
    	    	}
    
    	    	//3. If the device is in FULL ACCESS mode, enter CONFIG_UPDATE mode - (Subcommand 0x0090).
    	    	HAL_Delay(2);
    	    	CommandSubcommands(SET_CFGUPDATE);
    
    	    	//4. Configure the register settings in data memory.
    	    	HAL_Delay(2);
    	    	uint32_t shutdown_stack_voltage_data = 1680; //Shutdown stack voltage in units of 10mV
    	    	BQ769x2_SetRegister(ShutdownStackVoltage, shutdown_stack_voltage_data, 2);
    
    	    	//5. Exit CONFIG_UPDATE mode - (Subcommand 0x0092).
    	    	HAL_Delay(2);
    	    	CommandSubcommands(EXIT_CFGUPDATE);
    
    	    	//6. Read the data memory registers to verify all parameters were written successfully.
    	    	HAL_Delay(2);
    	    	BQ769x2_GetRegister(ShutdownStackVoltage, data, 2);
    	    	shutdown_stack_voltage = ((data[1] << 8) | data[0]) * 10; //reports in mA
    
    	    	//7. Enter CONFIG_UPDATE mode.
    	    	HAL_Delay(2);
    	    	CommandSubcommands(SET_CFGUPDATE);
    
    	    	//8. Check the Battery Status[OTPB] bit is clear to verify OTP programming conditions are met.
    	    	HAL_Delay(2);
    	    	uint8_t OTPB_write_bit_data = checkBQ_OTPB_Bit();
    	    	if(OTPB_write_bit_data != 1){
    	    		OTPB_write_bit_data_nfg = 1;
    	    	}
    
    	    	//9. Read OTP_WR_CHECK() (Subcommand 0x00A0). If this returns a value of 0x80, then OTP programming
    	    	//conditions are met.
    	    	HAL_Delay(2);
    	    	uint8_t otp_write_status = OTPWriteCheck();
    	    	if(otp_write_status == 0x80){
    	    		//Execute the OTP write
    	    	}
    	    	else otp_wr_check_nfg;
    
    	    	//10. If OTP_WR_CHECK indicates conditions are met, send OTP_WRITE() subcommand (0x00A1).
    
    
    	    	//11. Wait 100 ms. Read from 0x40 to check if OTP programming was successful (0x80 indicates success).
    	    	HAL_Delay(200);

  • Hello Ryan,

    Can you send me logic analyzer capture (.sal file) of each step?

    Also, I know some users have added up to 10ms and 100ms to be safe to process commands. Here is one example: BQ76952: OTP Fail. Can you try incrementing the time to see if that helps as well?

    Best Regards,
    Alexis

  • Alexis, 

    I don't have a Salae analyzer (I'm slumming it with PulseView.) Can you open a PulseView output? I'm attaching that. If not, let me know soon and I will put together some screenshots. 

    Logic Output (short delay code).zip

    Best, 
    Ryan 

  • Hello Ryan,

    Unfortunately, I cannot view the file from PulseView. Can you let me know if increasing the time also worked or not?

    Best Regards,
    Alexis

  • Hi Alexis, Sorry for the late reply. Yes I finally found where I had insufficient delay. It was insufficient after writing to the 0x3e/0x3f registers and before reading from 0x60. I'm going to post the code incase it can help someone in the future. Thanks for working with me. 

    uint8_t OTPWrite(){
    	uint16_t command = OTP_WRITE;
    
    	//security keys and Manu_data writes dont work with this function (reading these commands works)
    	//max readback size is 32 bytes i.e. DASTATUS, CUV/COV snapshot
    	uint8_t TX_Reg[4] = { 0x00, 0x00, 0x00, 0x00 };
    	uint8_t TX_Buffer[2] = { 0x00, 0x00 };
    
    	//TX_Reg in little endian format
    	TX_Reg[0] = command & 0xff;
    	TX_Reg[1] = (command >> 8) & 0xff;
    
    	//Access the register data
    	I2C_WriteReg(0x3E, TX_Reg, 2);
    	delayUS(100000);
    	I2C_ReadReg(0x40, RX_32Byte, 32); //RX_32Byte is a global variable
    
    	return RX_32Byte[0]>>7; //Return the bit that confirms good OTP programming
    }