CC3235SF: Application CPU not entering LPDS

Part Number: CC3235SF

Facing an issue in SDK 4.30, the device does not enter LPDS after an error condition on I2C.

Using CC3235 as an I2C master and in I2C_MODE_CALLBACK mode.

Sometimes following errors occur on the I2C bus. I am able to recover from these conditions through a reattempt. I2C_STATUS_DATA_NACK, I2C_STATUS_ADDR_NACK, I2C_STATUS_ARB_LOST.

The issue is after this condition occurs, MCU does not enter LPDS. I2C continues to function as normal.

How to debug the issue. I have verified that I am calling I2C_CloseConnection(). in both failures and success.

Below is the code flow:

void I2C_Callback(I2C_Handle Handle, I2C_Transaction *I2CTransaction_struct, Bool Result)
{
lastI2CTransStatus = I2CTransaction_struct->status;
sem_post(&App_CB.i2cTransfer);
}

I2C_init();

I2C_Params_init(&i2cParams);
i2cParams.bitRate = I2C_400kHz;
i2cParams.transferMode = I2C_MODE_CALLBACK;
i2cParams.transferCallbackFxn = (I2C_CallbackFxn)&I2C_Callback;

i2cHandle = I2C_open(CONFIG_I2C_0, &i2cParams);

I2C_transfer(i2cHandle, &i2cTransaction);

error = sem_timedwait(&App_CB.i2cTransfer, &ts);

The semaphore returns -1 in an error condition

I2C_close(i2cHandle);

Regards

  • Hi,

    How do you handle the errors? by just retrying the transfer?

    Do you know what is the reason for the I2C failures? How frequent is this happening? is this expected?

    What happens if you use the (default) I2C_MODE_BLOCKING? Do you still see errors? issues entering LPDS?

    Can you try closing I2C (with I2C_Close, i'm not sure what is I2C_CloseConnection) and using sem_destroy after the failure and then reopen both?

    Br,

    Kobi

  • Hi Kobi,

    Yes, we have implemented a retry for error conditions. A retry works as expected.

    After any error condition:

    I2C_cancel(i2cHandle);

    If the I2C bus SDA is held low by slave, reset the I2C bus by toggling SCL line till slave release SDA.

    I2C_close(i2cHandle);

    Then Retry the whole opertation.

    The failure reason is because of long wires(about 23cm) between master and slave. We can not avoid this. The error occurs very rarely.

    I2C_MODE_BLOCKING was causing an indefinite hang condition to avoid it we implemented nonblocking.

    I2C_CloseConnection is just I2C_close(i2cHandle); and return 0.

    I can try sem destroy and reopen. 

    Can we look at some register values of variables to check why it is not entering LPDS?

  • I think I am able to figure out the reason. In one of the failure case, I am not calling sem_timedwait() after I2C_transfer(). 

    This is expected behavior right?

  • It can be. We will need to verify this (in the code and TRM) but it won't be simple. 

    You can also try to follow the Power_sleep() to see where it fails.

    Please try to fix the missing sem_timedwait()  and let us know.

  • Hi Kobi,

    I tested more rigorously. It seems the issue arises not because of hardware design but has something to do with the drivers.

    To simplify the implementation I modified the i2ctemp example from SDK and tried to recreate the behaviour in blocking with timeout mode. I write 176 bytes and read back 7 bytes repeatedly. I ran the code for hours without a single glitch in reading or writing. 

    But I faced some issues when I intentionally added glitches on I2C bus. I expected the code to recover from these glitches. But there is a situation(I2C_STATUS_BUS_BUSY) in which I had to reboot CC3235 to recover it. Can you suggest the right way to recover from this situation?

    I am attaching the modified code and the serial terminal logs. I am using TDO and TDI pins for I2C.

    I am adding I2C register status in the error condition and SDA and SCL logic analyzer.

    Regards

    i2ctemp_logs.txt
    In case of SUCCESS:
    
    [2021-06-10 23:00:24.695] I2C Initialized!
    [2021-06-10 23:00:24.699] 
    [2021-06-10 23:00:24.699] A
    [2021-06-10 23:00:24.703] B
    [2021-06-10 23:00:24.703] SUCCESS Write
    [2021-06-10 23:00:24.804] C
    [2021-06-10 23:00:24.806] D
    [2021-06-10 23:00:24.806] SUCCESS Read
    [2021-06-10 23:00:24.907] I2C closed!
    [2021-06-10 23:00:24.910] I2C Initialized!
    [2021-06-10 23:00:24.912] 
    [2021-06-10 23:00:24.912] A
    [2021-06-10 23:00:24.918] B
    [2021-06-10 23:00:24.918] SUCCESS Write
    [2021-06-10 23:00:25.018] C
    [2021-06-10 23:00:25.022] D
    [2021-06-10 23:00:25.022] SUCCESS Read
    [2021-06-10 23:00:25.122] I2C closed!
    [2021-06-10 23:00:25.125] I2C Initialized!
    
    
    In Case of FAILURE:
    
    [2021-06-10 23:18:24.501] I2C Initialized!
    [2021-06-10 23:18:24.501] 
    [2021-06-10 23:18:24.501] A
    [2021-06-10 23:18:24.501] B
    [2021-06-10 23:18:24.501] I2C bus is already in use!
    [2021-06-10 23:18:24.501] C
    [2021-06-10 23:18:24.501] D
    [2021-06-10 23:18:24.501] I2C bus is already in use!
    [2021-06-10 23:18:24.501] I2C closed!
    [2021-06-10 23:18:24.501] I2C Initialized!
    [2021-06-10 23:18:24.501] 
    [2021-06-10 23:18:24.502] A
    [2021-06-10 23:18:24.502] B
    [2021-06-10 23:18:24.502] I2C bus is already in use!
    [2021-06-10 23:18:24.609] C
    [2021-06-10 23:18:24.609] D
    [2021-06-10 23:18:24.618] I2C bus is already in use!
    [2021-06-10 23:18:24.683] I2C closed!
    [2021-06-10 23:18:24.683] I2C Initialized!
    [2021-06-10 23:18:24.689] 
    [2021-06-10 23:18:24.689] A
    [2021-06-10 23:18:24.689] B
    [2021-06-10 23:18:24.689] I2C bus is already in use!
    [2021-06-10 23:18:24.801] C
    [2021-06-10 23:18:24.801] D
    [2021-06-10 23:18:24.810] I2C bus is already in use!
    modified_i2ctemp.c
    #include <stdint.h>
    #include <stddef.h>
    #include <unistd.h>
    
    /* Driver Header files */
    #include <ti/drivers/GPIO.h>
    #include <ti/drivers/I2C.h>
    #include <ti/display/Display.h>
    #include <ti/sysbios/knl/Clock.h>
    /* Driver configuration */
    #include "ti_drivers_config.h"
    
    #define TASKSTACKSIZE       640
    
    /* Temperature result registers */
    #define TMP006_RESULT_REG          0x0001
    #define TMP11X_RESULT_REG          0x0000
    
    /* I2C slave addresses */
    #define TMP006_LAUNCHPAD_ADDR      0x41
    #define TMP11X_BASSENSORS_ADDR     0x48
    #define TMP116_LAUNCHPAD_ADDR      0x49
    
    /* Number of supported sensor iterations */
    #define TMP_COUNT           3
    
    /*
     * Data structure containing currently supported I2C TMP sensors.
     * Sensors are ordered by descending preference.
     */
    static const struct {
        uint8_t address;
        uint8_t resultReg;
        char *id;
    } sensors[TMP_COUNT] = {
        { TMP11X_BASSENSORS_ADDR, TMP11X_RESULT_REG, "11X" },
        { TMP116_LAUNCHPAD_ADDR,  TMP11X_RESULT_REG, "116" },
        { TMP006_LAUNCHPAD_ADDR,  TMP006_RESULT_REG, "006" }
    };
    
    static uint8_t slaveAddress;
    static Display_Handle display;
    
    static void i2cErrorHandler(I2C_Transaction *transaction,
        Display_Handle display);
    
    /*
     *  ======== mainThread ========
     */
    
    #define MSEC_TO_TICKS(x) (x * 1000 / Clock_tickPeriod)
    unsigned char writebuff[] = {0x01,0xA7,0xA9,0x00,0xB8,0xA4,0x00,0x52,0x77,0x00,0x07,0x50,0x5B,0x49,0x32,0x43,0x49,0x5D,0x20,0x52,0x20,0x30,0x20,0x31,0x32,0x20,0x30,0x38,0x00,0x10,0x48,0x5B,0x49,0x32,0x43,0x49,0x5D,0x20,0x4F,0x00,0x02,0x51,0x5B,0x49,0x32,0x43,0x49,0x5D,0x20,0x57,0x20,0x31,0x38,0x32,0x20,0x30,0x20,0x30,0x38,0x00,0x0B,0x4F,0x5B,0x49,0x32,0x43,0x49,0x5D,0x20,0x52,0x20,0x30,0x20,0x37,0x20,0x30,0x38,0x00,0x05,0x45,0x5B,0x50,0x5D,0x20,0x57,0x00,0x01,0x4A,0x5B,0x50,0x5D,0x20,0x45,0x20,0x30,0x20,0x35,0x36,0x00,0x06,0x48,0x5B,0x49,0x32,0x43,0x49,0x5D,0x20,0x4F,0x00,0x02,0x4F,0x5B,0x49,0x32,0x43,0x49,0x5D,0x20,0x57,0x20,0x37,0x20,0x30,0x20,0x30,0x38,0x00,0x04,0x4F,0x5B,0x49,0x32,0x43,0x49,0x5D,0x20,0x52,0x20,0x30,0x20,0x39,0x20,0x30,0x38,0x00,0x04,0x4C,0x5B,0x50,0x5D,0x20,0x47,0x49,0x20,0x30,0x35,0x20,0x30,0x35,0x00,0x01,0x48,0x5B,0x49,0x32,0x43,0x49,0x5D,0x20,0x4F,0x5E,0x3F,0xE2,0xD9,0x17};
    
    
    void *mainThread(void *arg0)
    {
        uint16_t        sample;
        int16_t         temperature;
        uint8_t         txBuffer[1];
        uint8_t         rxBuffer[200];
        int8_t          i;
        I2C_Handle      i2c;
        I2C_Params      i2cParams;
        I2C_Transaction i2cTransaction;
    
        /* Call driver init functions */
        Display_init();
    //    GPIO_init();
    
        /* Open the UART display for output */
        display = Display_open(Display_Type_UART, NULL);
        if (display == NULL) {
            while (1);
        }
    
        while(1)
        {
        I2C_init();
    
        /* Create I2C for usage */
        I2C_Params_init(&i2cParams);
        i2cParams.bitRate = I2C_400kHz;
        i2c = I2C_open(CONFIG_I2C_TMP, &i2cParams);
        if (i2c == NULL) {
            Display_printf(display, 0, 0, "Error Initializing I2C\n");
            while (1);
        }
        else {
            Display_printf(display, 0, 0, "I2C Initialized!\n");
        }
    
        /* Common I2C transaction setup */
        i2cTransaction.writeBuf   = writebuff;
        i2cTransaction.writeCount = sizeof(writebuff);
        i2cTransaction.readBuf    = rxBuffer;
        i2cTransaction.readCount  = 7;
        i2cTransaction.slaveAddress = 8;
    
        int error;
        Display_printf(display, 0, 0, "A");
        error = I2C_transferTimeout(i2c, &i2cTransaction,10000);
        Display_printf(display, 0, 0, "B");
        if (error == I2C_STATUS_SUCCESS) {
    
                Display_printf(display, 0, 0, "SUCCESS Write");
        }
        else {
            i2cErrorHandler(&i2cTransaction, display);
        }
    
        usleep(100000);
    
        /* Common I2C transaction setup */
        i2cTransaction.writeBuf   = 0;
        i2cTransaction.writeCount = 0;
        i2cTransaction.readBuf    = rxBuffer;
        i2cTransaction.readCount  = 7;
        i2cTransaction.slaveAddress = 8;
    
        Display_printf(display, 0, 0, "C");
        error = I2C_transferTimeout(i2c, &i2cTransaction,10000);
        Display_printf(display, 0, 0, "D");
        if (error == I2C_STATUS_SUCCESS) {
    
            char success_resp[]={0x01,0x00,0x00,0x00,0xFF,0xFF,0x17};
            if(!memcmp(success_resp, rxBuffer, 7))
                Display_printf(display, 0, 0, "SUCCESS Read");
            else
                Display_printf(display, 0, 0, "Invalid Resp");
        }
        else {
            i2cErrorHandler(&i2cTransaction, display);
        }
    
    
        /* Sleep for 1 second */
        usleep(100000);
    
        I2C_close(i2c);
        Display_printf(display, 0, 0, "I2C closed!");
    
        }
        return (NULL);
    }
    
    /*
     *  ======== i2cErrorHandler ========
     */
    static void i2cErrorHandler(I2C_Transaction *transaction,
        Display_Handle display)
    {
        switch (transaction->status) {
        case I2C_STATUS_TIMEOUT:
            Display_printf(display, 0, 0, "I2C transaction timed out!");
            break;
        case I2C_STATUS_CLOCK_TIMEOUT:
            Display_printf(display, 0, 0, "I2C serial clock line timed out!");
            break;
        case I2C_STATUS_ADDR_NACK:
            Display_printf(display, 0, 0, "I2C slave address 0x%x not"
                " acknowledged!", transaction->slaveAddress);
            break;
        case I2C_STATUS_DATA_NACK:
            Display_printf(display, 0, 0, "I2C data byte not acknowledged!");
            break;
        case I2C_STATUS_ARB_LOST:
            Display_printf(display, 0, 0, "I2C arbitration to another master!");
            break;
        case I2C_STATUS_INCOMPLETE:
            Display_printf(display, 0, 0, "I2C transaction returned before completion!");
            break;
        case I2C_STATUS_BUS_BUSY:
            Display_printf(display, 0, 0, "I2C bus is already in use!");
            break;
        case I2C_STATUS_CANCEL:
            Display_printf(display, 0, 0, "I2C transaction cancelled!");
            break;
        case I2C_STATUS_INVALID_TRANS:
            Display_printf(display, 0, 0, "I2C transaction invalid!");
            break;
        case I2C_STATUS_ERROR:
            Display_printf(display, 0, 0, "I2C generic error!");
            break;
        default:
            Display_printf(display, 0, 0, "I2C undefined error case!");
            break;
        }
    }
    

  • Hi Kobi,

    Few more observations. The below condition arises when the slave is busy sometimes and not able to acknowledge data. CC3235 never recovers from this condition. The only way to recover from this condition is to power cycle.

    Please check the attachment.

  • Hi Kobi,

    There is one more condition in which the master does not recover.

    If there is a delay in acknowledging the Master. I2C_transferTimeout() returned with I2C_STATUS_ARB_LOST and then I2C_STATUS_BUS_BUSY forever.

    I this condition a reload of debugging session resolves the issue. No need to power cycle.

    Regards

  • Did you try to reset the I2C (i.e. calling I2CCC32XX_reset) following the error?

    I'm not sure if the module can handle the errors on the the interface without being reset.

     

    Br,

    Kobi

  • I will try and update you.

    Any hints you can get from the I2C registers. Why it does not even attempt anything on the bus and return I2C_STATUS_BUS_BUSY. In this state, the bus looks completely Idle. It looks like some internal state machine of I2C module is stuck in some condition.

  • I'm not sure what your glitch causing, but it looks like the module missed the stop signal and it is kept in bus busy state (in the MCS register).

    I've briefly checked the TRM ( https://www.ti.com/lit/pdf/swru543 ), but I couldn't find anything else that will reset the bus busy.

  • Hi Kobi,

    Can you check with the device driver team? They must be having a clue.

  • Please try to reset the module.

  • Hi Kobi,

    you advised to use I2CCC32XX_reset function. But it is neither documented in code nor used in any example.
    I have found that if was used in SDK 3.30.01.02 but was not used in 3.20.0.6 and is not used in 4.10.00.07, 4.20.00.07 and 4.30.00.06.

    What are the use cases when this function shall be call? Shall I call it in device boot phase? Or is similar covered by I2C_open (we are using only this function in boot phase; I2C_init does currently nothing even it is mandatory to be called)?

    Our error is I2C_STATUS_INCOMPLETE when calling I2C_transferTimeout.
    We have 2 slaves on the bus, but only LED driver ADP8866 is the active one when this problem happens (other slave is EEPROM 24LC64).

    Regards
    Jiří

  • I understand that this is not provided as a public (TI Driver) API, but you can check the implementation in  I2CCC32XX.c.

    It uses the PRCM to reset the I2C module.

    /*
     *  ======== I2CC32XX_reset ========
     */
    void I2CCC32XX_reset()
    {
        PRCMPeripheralReset(PRCM_I2CA0);
        while(!PRCMPeripheralStatusGet(PRCM_I2CA0)) {}
    }
    

  • Shouldn't be then this used internally when some problem is detected? I mean it should not be responsibility of application.

  • Not all the problems requires a module reset. The app may decide to reset the entire system if it works better,

    This specific issue could have been handled by the driver but currently it doesn't.

  • Is it valid for I2C_STATUS_INCOMPLETE returned by I2C_transferTimeout?
    If not, what shall be the recovery procedure in this case?

  • It indicates that not all the bytes were written/read. 

    You need to check the I2C lines to get better understanding of what went wrong.

  • Thank you for the hint.