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.

BQ79616-Q1: Unable to unlock OTP Programming

Part Number: BQ79616-Q1
Other Parts Discussed in Thread: MSPM0L1304, BQ79616,

Tool/software: CCS Theia (IDE), MSPM0L1304 (host MCU)

Here is my code (with some irrelevant portions deleted). On lines 187 and 247, it is detected that the OTP programming is locked. I checked to make sure that I was following the correct procedures in the BQ79616 datasheet, but I can't see anything I'm doing wrong. Please help!

/*
 * Copyright (c) 2023, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "ti/devices/msp/m0p/mspm0l130x.h"
#include "ti/driverlib/dl_timerg.h"
#include "ti/driverlib/dl_uart_main.h"
#include "ti/driverlib/m0p/dl_interrupt.h"
#include "ti_msp_dl_config.h"
#include <math.h>
#include <stdint.h>

/* Configurations */
#define BQ79616_WAKE_PORT GPIOA
#define BQ79616_WAKE_PIN DL_GPIO_PIN_0
#define THERMISTOR_CONNECTION_R1 10 // kohms
#define THERMISTOR_T0 298.15 // Kelvins
#define THERMISTOR_R0 10 // kohms
#define THERMISTOR_B_25_85 3434 // Kelvins

/* BQ79616 addresses */
#define BQ79616_DEVICE_ADDR 0x00
#define OTP_ECC_TEST_ADDR 0x034C
#define CONTROL1_ADDR 0x0309
#define CONTROL2_ADDR 0x030A
#define DIR0_ADDR_ADDR 0x0306
#define COMM_CTRL_ADDR 0x0308
#define OTP_CUST1_STAT_ADDR 0x051A
#define OTP_CUST2_STAT_ADDR 0x051B
#define OTP_PROG_UNLOCK1A_ADDR 0x0300
#define OTP_PROG_UNLOCK2A_ADDR 0x0352
#define OTP_PROG_STAT_ADDR 0x0519
#define OTP_PROG_CTRL_ADDR 0x030B
#define DEV_CONF_ADDR 0x0002
#define ACTIVE_CELL_ADDR 0x0003
#define OV_THRESH_ADDR 0x0009
#define UV_DISABLE1_ADDR 0x000C
#define GPIO_CONF1_ADDR 0x000E
#define FAULT_MSK1_ADDR 0x0016
#define PWR_TRANSIT_CONF_ADDR 0x0018
#define ADC_CTRL1_ADDR 0x030D
#define VCELL16_HI_ADDR 0x0568
#define TSREF_HI_ADDR 0x058C
#define BAL_CTRL2_ADDR 0x032F
#define CB_CELL16_CTRL_ADDR 0x0318
#define FAULT_RST1_ADDR 0x0331
#define FAULT_SUMMARY_ADDR 0x052D
#define FAULT_SYS_ADDR 0x0536

/* BQ79616 Error Flags */
#define OTP_FAULT (1 << 0)
#define COMMS_FAULT (1 << 1)
#define POWER_RAIL_FAULT (1 << 2)
#define DIE_TEMP_FAULT (1 << 3)
#define LFO_FREQ_FAULT (1 << 4)

#define BUFFER_SIZE 512
#define MESSAGE_TYPE_NUM_BITS 3

/* Message Types */
#define REQ_INFO 0b000
#define VOLTAGE_THRESHOLD 0b001
#define TEMP_THRESHOLD 0b010
#define BALANCING 0b011
#define VOLTAGE 0b100
#define TEMP 0b101
#define ERROR 0b110
#define INIT 0b111
#define INIT_SUCCESS 0xFF

/* BQ79616 Request Types */
#define SINGLE_DEVICE_READ 0b000
#define SINGLE_DEVICE_WRITE 0b001
#define STACK_READ 0b010
#define STACK_WRITE 0b011
#define BROADCAST_READ 0b100
#define BROADCAST_WRITE 0b101
#define BROADCAST_WRITE_REVERSE 0b110

/* Global Variables */
uint16_t cell_voltages[17]; // measured in mV
volatile bool enable_balancing_req[17];
volatile bool stop_balancing_req[17];
int16_t temp1 = -10000; // measured in 0.01 degrees C
int16_t temp2 = -10000; // measured in 0.01 degrees C
volatile uint16_t ignore_voltage_threshold = 1000; // measured in mV
volatile int16_t ignore_temp_threshold = -2000; // measured in 0.01 degrees C
volatile bool awaiting_data = false;
volatile bool awaiting_faults = false;
volatile uint8_t module_num;
volatile uint8_t num_modules;
volatile uint8_t messages[BUFFER_SIZE];
uint16_t next_message_to_send_index = 0;
volatile uint16_t next_message_to_save_index = 0;
uint8_t bq79616_response_data[128];
volatile uint32_t micros = 0;

/* Function Prototypes */
void wake_bq79616(void);
void auto_address_bq79616(void);
bool program_bq79616_otp(void);
bool program_bq79616_otp_page1(void);
bool program_bq79616_otp_page2(void);
void set_bq79616_otp_settings(void);
void reset_bq79616_faults(void);
void bq79616_faults(void);
void command_bq79616(uint8_t req_type, uint8_t dev_addr, uint16_t reg_addr, uint8_t *data, uint8_t data_len);
uint8_t response_bq79616(void);
uint16_t crc_bq79616(const uint8_t *data, uint8_t length);
void delay_us(uint32_t us);
void micros_counter(void);

int main(void) {
    SYSCFG_DL_init();

    NVIC_EnableIRQ(MICROS_INST_INT_IRQN);
    DL_Interrupt_registerInterrupt(MICROS_INST_INT_IRQN, micros_counter);

    wake_bq79616();
    auto_address_bq79616();
    reset_bq79616_faults();
    program_bq79616_otp();
}

bool program_bq79616_otp(void) {
    if (program_bq79616_otp_page2() || program_bq79616_otp_page1()) {
        return true;
    } else { // neither OTP programming was successful - try one more time
        if (program_bq79616_otp_page2() || program_bq79616_otp_page1()) {
            return true;
        } else { // error - neither OTP programming was successful
            return false;
        }
    }
}

bool program_bq79616_otp_page1(void) {
    uint8_t data[4];

    data[0] = 0b000;
    command_bq79616(SINGLE_DEVICE_READ, BQ79616_DEVICE_ADDR, OTP_CUST1_STAT_ADDR, data, 1);
    if (response_bq79616() == 0) {
        return false;
    }
    if ((bq79616_response_data[0] & 1) == 0) { // page 1 not yet programmed
        // Unlock the OTP programming
        data[0] = 0x02;
        data[1] = 0xB7;
        data[2] = 0x78;
        data[3] = 0xBC;
        command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, OTP_PROG_UNLOCK1A_ADDR, data, 4);
        
        // Check to confirm the OTP unlock procedure is successful
        data[0] = 0b000;
        command_bq79616(SINGLE_DEVICE_READ, BQ79616_DEVICE_ADDR, OTP_PROG_STAT_ADDR, data, 1);
        if (response_bq79616() == 0) {
            return false;
        }
        if ((bq79616_response_data[0] >> 7) != 1) { // error - OTP programming locked
            return false;
        }

        // Select the proper OTP page and start the OTP programming
        data[0] = 0x01;
        command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, OTP_PROG_CTRL_ADDR, data, 1);

        // Do the OTP programming
        set_bq79616_otp_settings();

        // Wait 100 ms for the OTP programming to complete.
        delay_us(100000);

        // Check to ensure there is no error during OTP programming.
        data[0] = 0b000;
        command_bq79616(SINGLE_DEVICE_READ, BQ79616_DEVICE_ADDR, OTP_PROG_STAT_ADDR, data, 1);
        if (response_bq79616() == 0) {
            return false;
        }
        if ((bq79616_response_data[0] & 1) != 1) { // error - unsuccessful OTP programming
            return false;
        }
        command_bq79616(SINGLE_DEVICE_READ, BQ79616_DEVICE_ADDR, OTP_CUST1_STAT_ADDR, data, 1);
        if (response_bq79616() == 0) {
            return false;
        }
        if (bq79616_response_data[0] != 0b00001111) { // error - unsuccessful OTP programming
            return false;
        }

        // Issue a digital reset to reload the registers with the updated OTP values
        data[0] = 0b10;
        command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, CONTROL1_ADDR, data, 1);
    }
    return true;
}

bool program_bq79616_otp_page2(void) {
    uint8_t data[4];

    data[0] = 0b000;
    command_bq79616(SINGLE_DEVICE_READ, BQ79616_DEVICE_ADDR, OTP_CUST2_STAT_ADDR, data, 1);
    if (response_bq79616() == 0) {
        return false;
    }
    if ((bq79616_response_data[0] & 1) == 0) { // page 2 not yet programmed
        // Unlock the OTP programming
        data[0] = 0x7E;
        data[1] = 0x12;
        data[2] = 0x08;
        data[3] = 0x6F;
        command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, OTP_PROG_UNLOCK2A_ADDR, data, 4);
        
        // Check to confirm the OTP unlock procedure is successful
        data[0] = 0b000;
        command_bq79616(SINGLE_DEVICE_READ, BQ79616_DEVICE_ADDR, OTP_PROG_STAT_ADDR, data, 1);
        if (response_bq79616() == 0) {
            return false;
        }
        if ((bq79616_response_data[0] >> 7) != 1) { // error - OTP programming locked
            return false;
        }

        // Select the proper OTP page and start the OTP programming
        data[0] = 0x03;
        command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, OTP_PROG_CTRL_ADDR, data, 1);

        // Do the OTP programming
        set_bq79616_otp_settings();

        // Wait 100 ms for the OTP programming to complete.
        delay_us(100000);

        // Check to ensure there is no error during OTP programming.
        data[0] = 0b000;
        command_bq79616(SINGLE_DEVICE_READ, BQ79616_DEVICE_ADDR, OTP_PROG_STAT_ADDR, data, 1);
        if (response_bq79616() == 0) {
            return false;
        }
        if ((bq79616_response_data[0] & 1) != 1) { // error - unsuccessful OTP programming
            return false;
        }
        command_bq79616(SINGLE_DEVICE_READ, BQ79616_DEVICE_ADDR, OTP_CUST2_STAT_ADDR, data, 1);
        if (response_bq79616() == 0) {
            return false;
        }
        if (bq79616_response_data[0] != 0b10001111) { // error - unsuccessful OTP programming
            return false;
        }

        // Issue a digital reset to reload the registers with the updated OTP values
        data[0] = 0b10;
        command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, CONTROL1_ADDR, data, 1);
    }
    return true;
}

void set_bq79616_otp_settings(void) {
    uint8_t data[2];

    data[0] = 0b1100000;
    command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, DEV_CONF_ADDR, data, 1);

    data[0] = 0xA;
    command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, ACTIVE_CELL_ADDR, data, 1);

    data[0] = 0x2E;
    command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, OV_THRESH_ADDR, data, 1);

    data[0] = 0xFF;
    data[1] = 0xFF;
    command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, UV_DISABLE1_ADDR, data, 2);

    data[0] = 0b00010010;
    command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, GPIO_CONF1_ADDR, data, 1);

    data[0] = 0b11111100;
    data[1] = 0b0011110;
    command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, FAULT_MSK1_ADDR, data, 2);

    data[0] = 0b00000;
    command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, PWR_TRANSIT_CONF_ADDR, data, 1);
}

void reset_bq79616_faults(void) {
    uint8_t data[2];

    data[0] = 0xFF;
    data[1] = 0xFF;
    command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, FAULT_RST1_ADDR, data, 2);

    data[0] = 0x00;
    data[1] = 0x00;
    command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, FAULT_RST1_ADDR, data, 2);
}

void wake_bq79616(void) {
    // Disable UART to allow pin mode changes
    DL_UART_Main_disable(BQ79616_INST);

    // Configure pin as GPIO output
    DL_GPIO_initDigitalOutput(GPIO_BQ79616_IOMUX_TX);
    DL_GPIO_enableOutput(BQ79616_WAKE_PORT, BQ79616_WAKE_PIN);

    // Drive low for 2-2.5 ms
    DL_GPIO_clearPins(BQ79616_WAKE_PORT, BQ79616_WAKE_PIN);
    delay_us(2300);

    // Drive high again
    DL_GPIO_setPins(BQ79616_WAKE_PORT, BQ79616_WAKE_PIN);

    // Wait 10 + 0.6 * number of devices ms before any further comms
    delay_us(10600);

    // Reconfigure the pin back to UART TX function
    DL_GPIO_initPeripheralOutputFunction(
        GPIO_BQ79616_IOMUX_TX, GPIO_BQ79616_IOMUX_TX_FUNC
    );

    // Re-enable UART
    DL_UART_Main_enable(BQ79616_INST);
}

void auto_address_bq79616(void) {
    uint8_t data[1];
    uint8_t num_tries = 0;

    while (num_tries++ < 3) {
        // Dummy broadcast write OTP_ECC_TEST=0x00 to sync the DLL (delay-locked loop)
        data[0] = 0x00;
        command_bq79616(BROADCAST_WRITE, 0, OTP_ECC_TEST_ADDR, data, 1);

        // Enable auto addressing mode by broadcast writing CONTROL1=0x01
        data[0] = 0x01;
        command_bq79616(BROADCAST_WRITE, 0, CONTROL1_ADDR, data, 1);

        // Loop through the total number of boards setting the DIR0_ADDR of each board
        data[0] = BQ79616_DEVICE_ADDR;
        command_bq79616(BROADCAST_WRITE, 0, DIR0_ADDR_ADDR, data, 1);

        // Broadcast write everything as a stack device first (COMM_CTRL=0x02)
        data[0] = 0x02;
        command_bq79616(BROADCAST_WRITE, 0, COMM_CTRL_ADDR, data, 1);

        // Set device as base and top of stack (COMM_CTRL=0x01)
        data[0] = 0x01;
        command_bq79616(SINGLE_DEVICE_WRITE, BQ79616_DEVICE_ADDR, COMM_CTRL_ADDR, data, 1);
        
        // Dummy broadcast read OTP_ECC_TEST to sync the DLL
        data[0] = 0b000;
        command_bq79616(BROADCAST_READ, 0, OTP_ECC_TEST_ADDR, data, 1);
        if (response_bq79616() == 0) {
            continue; // try again
        }

        break;
    }
}

void command_bq79616(uint8_t req_type, uint8_t dev_addr, uint16_t reg_addr, uint8_t *data, uint8_t data_len) {
    uint8_t frame[14];
    uint8_t frame_len = 0;

    frame[frame_len++] = ((1 << 7) | (req_type << 4)) | (data_len - 1);

    if (req_type == SINGLE_DEVICE_READ || req_type == SINGLE_DEVICE_WRITE) {
        frame[frame_len++] = dev_addr;
    }

    frame[frame_len++] = (uint8_t)(reg_addr >> 8);
    frame[frame_len++] = (uint8_t)(reg_addr & 0xFF);

    for (uint8_t i = 0; i < data_len; i++) {
        frame[frame_len++] = data[i];
    }

    uint16_t crc = crc_bq79616(frame, frame_len);
    frame[frame_len++] = (uint8_t)(crc >> 8);
    frame[frame_len++] = (uint8_t)(crc & 0xFF);

    for (uint8_t i = 0; i < frame_len; i++) {
        DL_UART_Main_transmitDataBlocking(BQ79616_INST, frame[i]);
    }
}

// returns 0 on CRC error - try again if required
uint8_t response_bq79616(void) {
    uint8_t init_byte = DL_UART_Main_receiveDataBlocking(BQ79616_INST);
    uint8_t data_len = init_byte + 1;

    uint8_t frame[6 + data_len];
    uint8_t frame_len = 0;

    frame[frame_len++] = init_byte;

    frame[frame_len++] = DL_UART_Main_receiveDataBlocking(BQ79616_INST); // device address byte

    frame[frame_len++] = DL_UART_Main_receiveDataBlocking(BQ79616_INST); // register address byte 1
    frame[frame_len++] = DL_UART_Main_receiveDataBlocking(BQ79616_INST); // register address byte 2

    for (uint8_t i = 0; i < data_len; i++) {
        bq79616_response_data[i] = DL_UART_Main_receiveDataBlocking(BQ79616_INST);
        frame[frame_len++] = bq79616_response_data[i];
    }

    frame[frame_len++] = DL_UART_Main_receiveDataBlocking(BQ79616_INST); // CRC byte 1
    frame[frame_len++] = DL_UART_Main_receiveDataBlocking(BQ79616_INST); // CRC byte 2

    if (crc_bq79616(frame, frame_len) != 0x0000) { // error - CRC wrong
        // clear RX FIFO
        while (!DL_UART_Main_isRXFIFOEmpty(BQ79616_INST)) {
            DL_UART_Main_receiveData(BQ79616_INST);
        }
        return 0;
    }

    return data_len;
}

uint16_t crc_bq79616(const uint8_t *data, uint8_t length) {
    uint16_t crc = 0xFFFF;
    for (uint8_t i = 0; i < length; i++) {
        uint8_t byte = data[i];
        // Process each bit of the byte
        for (uint8_t bit = 0; bit < 8; bit++) {
            // XOR the LSB of CRC with the LSB of the data bit
            uint8_t lsb_crc = crc & 0x0001;
            uint8_t lsb_data = byte & 0x01;
            uint8_t xor_val = (lsb_crc ^ lsb_data);

            // Shift CRC right by 1
            crc >>= 1;

            // If XOR result is 1, XOR with polynomial
            if (xor_val) {
                crc ^= 0xA001; // Polynomial in LSB-first form
            }

            // Move to the next bit of the byte
            byte >>= 1;
        }
    }
    return (crc >> 8) | (crc << 8);
}

void delay_us(uint32_t us) {
    DL_TimerG_startCounter(MICROS_INST);
    uint32_t start = micros;
    while (micros - start < us) {}
    DL_TimerG_stopCounter(MICROS_INST);
}

/* Should not be called by our own program -- should only be called by the interrupt */
void micros_counter(void) {
    micros++;
}

  • Hello Yun,

    What exactly are you trying to do with the OTP?
    Can you show me in psudo code what you are doing and how you are detecting an error?


    Best,

         Quentin

  • I am trying to follow the instructions on pages 82 and 83 of the BQ79616 datasheet to program the OTP. For each of page 1 and page 2 of the OTP, I write data to the OTP_PROG_UNLOCK registers to unlock the OTP, and then I read OTP_PROG_STAT[UNLOCK] to see if the OTP was unlocked. For both page 1 and page 2, I detect that OTP_PROG_STAT[UNLOCK] = 0, indicating that they are both locked and that I'm not able to continue with the rest of the OTP programming procedure.

    I can give more details about the rest of the code if you think it will help.

  • Yun,

    Where are you writing all 8 of these commands?

    Be sure you are writing them in the correct order with no other writes in between. 
    If it does not work get a logic analyzer and lets look at the data going across. 


    "Each block of registers must be written in order (that is, A, B, C, then D) with no other writes or reads between. The best practice is to use the same Write command to update. Any attempt to update the registers out of sequence, or if another register is written or read between writes, the entire sequence must be redone."

    Best,

          Quentin

  • Hi Quentin,

    In one function, I use a single Write command to write to the OTP_PROG_UNLOCK2A to OTP_PROG_UNLOCK2D registers, then read OTP_PROG_STAT[UNLOCK]. 

    In a separate function, I use a single Write command to write to the OTP_PROG_UNLOCK1A to OTP_PROG_UNLOCK1D registers, then read OTP_PROG_STAT[UNLOCK]. 

    Best,

    Yun

  • Yun,

    Show me these commands and their order please.

    Best,

         Quentin

  • Hi Quentin,

    This is the order it's done in:

    1. Use a single Write command to write to the OTP_PROG_UNLOCK2A to OTP_PROG_UNLOCK2D registers.

    2. Use a Read command to read OTP_PROG_STAT[UNLOCK] -- this is where I detect that OTP is locked, so I exit before I can finish the rest of the OTP programming for page 2.

    3. Use a single Write command to write to the OTP_PROG_UNLOCK1A to OTP_PROG_UNLOCK1D registers. 

    4. Use a Read command to read OTP_PROG_STAT[UNLOCK] -- this is where I detect that OTP is locked, so I exit before I can finish the rest of the OTP programming for page 1.

    Best,

    Yun

  • Yun,

    Write to OTP_PROG_UNLOCK1A-D in one write first.

    Then write to OTP_PROG_UNLOCK2A-D in another write. 

    Only then can you read the status register.

    Best,

         Quentin

  • Hi Quentin,

    Thank you for your help. I'm now facing a different problem, if you'd like to help figure out what is causing it. I am unable to get a response from the BQ79616 when trying to read OTP_PROG_STAT[DONE] after programming. This is the order in which I am doing things:

    1. Write to OTP_PROG_UNLOCK1A-D in one write.

    2. Write to OTP_PROG_UNLOCK2A-D in one write.

    3. Read OTP_PROG_STAT[UNLOCK] -- I am now detecting that OTP is successfully unlocked.

    4. Write to OTP_PROG_CTRL[PAGESEL][PROG_GO] = 0x03 in order to select page 2.

    5. Do the OTP programming by writing to the desired NVM registers.

    6. Wait 100 ms.

    7. Try to read OTP_PROG_STAT[DONE] to make sure that OTP programming is done -- this is where I am unable to get a response from the BQ79616.

    Best,

    Yun

  • Yun,

    Is the device going into shutdown? (You can tell by reading the references)
    If it is perhaps you are running into a communication timeout. You can disable this communication timeout by writing to a register.
    It is outlined in the datasheet.

    Otherwise you should be getting a response.

    Best,

         Quentin

  • Hi Quentin,

    I don't think the device is going into shutdown mode after testing voltages. In addition, I haven't written anything to COMM_TIMEOUT_CONF, which by default disables any communication timeouts.

    If it helps, some additional information is that before following the steps outlined in my post, I first checked that OTP_CUST2_STAT[TRY] = 0. However, I re-ran my code and then saw that OTP_CUST2_STAT[LOADED], [PROGOK], [TRY], [OVOK], and [UVOK] bits are 1 and the other OTP_CUST2_STAT bits are 0, which suggests that the BQ79616 thinks page 2 was successfully programmed, even though it hasn't been.

    Best,

    Yun

  • Yun,

    Ok so this means your device should be working normally. 

    How are you determining you are not writing correctly?

    Best,

         Quentin

  • Hi Quentin,

    I am determining that the OTP was not successfully programmed by reading the value in DEV_CONF, which should have been programmed with 0b01100000. However, when I read DEV_CONF, it has a value of 0b01010100, which is just its default Reset value.

    I just read about a potential issue with CUST_CRC_HI/CUST_CRC_LO and a CRC check for the NVM registers. I did not write anything to CUST_CRC_HI/CUST_CRC_LO. Was I supposed to write to these registers to program the OTP successfully? 

    Best,

    Yun

  • Yun,

    Can you try adding a large delay before you read DEV_CONF?
    Perhaps it does not have enough time to update.

    Best,

         Quentin

  • Hi Quentin,

    I reran my code and re-woke the device before reading DEV_CONF, so I don't think the amount of time needed to update was insufficient.

    Best,

    Yun

  • Yun,

    Show me how you are programming DEV_CONF? Perhaps there is something wrong here?

    Best,

         Quentin

  • These are all the writes I do in step 5, in hex:

    90 00 00 02 60 E5 55 (DEV_CONF_ADDR = 0x60)
    
    90 00 00 03 0A 64 EA
    
    90 00 00 09 2E 62 51
    
    91 00 00 0C FF FF DD 29
    
    90 00 00 0E 12 60 70
    
    91 00 00 16 FC 1E 3C 56
    
    90 00 00 18 00 EE 1D

  • Yun,

    Did you wait the 100ms before OTP programming?
    Did you do the soft reset before reading the register to make sure it was OTP programmed correctly?

    Best,

         Quentin

  • Hi Quentin,

    I first did the OTP programming (writing to the NVM registers), and then waited 100ms. Was I instead supposed to wait 100ms before doing the OTP programming?

    Yes, I did do the soft reset before reading the register.

    Best,

    Yun

  • Yun,

    Try adding a 100ms delay after selecting the OTP page and after you finish programming. 

    Best,

         Quentin

  • Hi Quentin,

    This seems to have resolved the previous issue, but yet another issue comes up now. After the OTP programming, I check OTP_CUST2_STAT, which reads 0x00. If it was successfully programmed, it should have read 0b10001111. Yet, the NVM registers seem to hold the correct values, which makes it seem like the OTP programming was successful.

    Then, I rerun my code to see if any behavior changes. Now, OTP_CUST2_STAT = 0b10001111, indicating that the OTP was successfully programmed. However, the NVM registers don't hold the correct values anymore and instead hold the default values.

    Would you have any idea what could have caused this?

    Best,

    Yun

  • Yun,

    It seems like something is wrong in your script. For the OTP section of your code, I would implement 100ms delays to try and debug what is going on. Perhaps after you enter the PROG_UNLOCK sequence, you can add delays before and after all the subsequent commands to help debug. 

    Please go through every line item and try to isolate the problem to a single command. 

    Best,

         Quentin

  • Quentin,

    After all of my OTP programming attempts, both pages 1 and 2 on almost all of my boards already have OTP_CUST*_STAT[TRY] set. I only have one more board in which OTP programming has not been attempted yet. I'm not sure if it would be a good idea to use this final board for debugging.

    Instead, is there a script I can find somewhere for OTP programming that has been proven to work?

    Best,

    Yun

  • Yun,

    request access to this page and check this sample code:
    https://www.ti.com/secureresources/BQ7971X-Q1-DESIGN?

    Best,

         Quentin

  • Quentin,

    Is that the correct link? It says BQ7971X-Q1, but I am working with BQ79616-Q1.

    I have already gotten access to the sample code at https://www.ti.com/secureresources/BQ7X61X-Q1-DEVELOPMENT, but was not able to find any examples of OTP programming in it.

    Best,

    Yun

  • Yun,

    Yes sorry, that was for 718. But if you have the 616 sample code, that is where it would be.
    I will look and see if I can find an example for you. 

    Let's close this thread and please email me (q-silic@ti.com).

    Link this thread so we keep the conversation going there. 

    Best,

         Quentin