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.

CC2340R5: How to recover from an I2C error

Part Number: CC2340R5


Tool/software:

Hello. My name is Mori.

I encountered an I2C error and tried the following steps to recover, but communication was not possible at step ⑩.
Resetting the CPU allowed I2C communication to resume.
I thought that I2C_close → I2C_open would be sufficient as a recovery method when an I2C error occurs, but
is there anything else I'm missing?
Thank you for your response.

① I2C_transfer
② I2C error
③ I2C_close
④ GPIO_resetConfig(SCL)
⑤ GPIO_resetConfig(SDA)
⑥ Sleep
⑦ Wakeup
⑧ I2C_open
⑩ I2C_transfer

  • Sorry.
    I forgot to mention that.
    I executed GPIO_setConfig after waking up.
    I2C communication failed in step 11.

    ① I2C_transfer
    ② I2C error
    ③ I2C_close
    ④ GPIO_resetConfig(SCL)
    ⑤ GPIO_resetConfig(SDA)
    ⑥ Sleep
    ⑦ Wakeup
    ⑧ GPIO_setConfig(SCL)
    ⑨ GPIO_setConfig(SDA)
    ⑩ I2C_open
    ⑪ I2C_transfer

  • Hello mori.oq

    I hope you are doing well. I created a similar code to what you provided above (minus the GPIO_reset/setConfig); however, my code did not display the same behavior as what you explained above (*as I2C still worked). 

    #include <ti/drivers/I2C.h>
    // Define name for an index of an I2C bus
    #define SENSORS 0
    // Define the target address of device on the SENSORS bus
    #define OPT_ADDR 0x47
    /*
     *  ======== mainThread ========
     */
    void *mainThread(void *arg0)
    {
        uint8_t writeBuffer[3];
        uint8_t readBuffer[3];
        // One-time init of I2C driver
        I2C_init();
        // initialize optional I2C bus parameters
        I2C_Params params;
        I2C_Params_init(&params);
        params.bitRate = I2C_400kHz;
        // Open I2C bus for usage
        I2C_Handle i2cHandle = I2C_open(SENSORS, &params);
        // Initialize target address of transaction
        I2C_Transaction transaction = {0};
        transaction.targetAddress = OPT_ADDR;
        // Read from I2C target device
        transaction.readBuf = readBuffer;
        transaction.readCount = sizeof(readBuffer);
        transaction.writeCount = 0;
        I2C_transfer(i2cHandle, &transaction);
        // Write to I2C target device
        transaction.writeBuf = writeBuffer;
        transaction.writeCount = sizeof(writeBuffer);
        transaction.readCount = 0;
        I2C_transferTimeout(i2cHandle, &transaction, 5000);
        // Close I2C
        I2C_close(i2cHandle);
        sleep(1);
        I2C_open(SENSORS, &params);
        // Read from I2C target device
        transaction.readBuf = readBuffer;
        transaction.readCount = sizeof(readBuffer);
        transaction.writeCount = 0;
        I2C_transfer(i2cHandle, &transaction);
        // Write to I2C target device
        transaction.writeBuf = writeBuffer;
        transaction.writeCount = sizeof(writeBuffer);
        transaction.readCount = 0;
        I2C_transferTimeout(i2cHandle, &transaction, 5000);
        // Close I2C
        I2C_close(i2cHandle);
        return 0;
    }

    Thanks,
    Alex F

  • Thank you for your reply.
    Have you implemented "② I2C error"?

  • Hello Mori.oq,

    Looks like my code did not do the same error in step 2 currently, thanks for pointing that out. Can you read the state of the status of the i2c transaction at step 11? (IE transaction.status = #)

    Also which SDK version is this in.

    Thanks,
    Alex F

  • Thank you for your reply.
    transaction.status is -5 (I2C_STATUS_ADDR_NACK).
    The SDK version is 8.40.00.61.

  • Additional information:
    I placed a metal object on pin 38 (PA5/SCL) of the CC2345R5 to cause an I2C error.
    I removed the metal object immediately after the error occurred.

  • Hello Mori.oq

    Thank you for the extra details, looking at the status you provided it does show an unsuccessful transfer (address not acknowledged), do you have a child device on the other end running another I2C project perhaps? I will also need to re-run my code on the 8.40 SDK because my results were from the 9.12 SDK. 

    Thanks,
    Alex F

  • Thank you for your reply.
    I am communicating with an LCD driver using I2C.
    However, I have determined that there is no problem with the slave (LCD driver).
    I have found that even when I2C_open is called after an I2C abnormality, communication is not possible because the CFE bit cannot be set to 1.
    I2CC_Params passes the data previously obtained by I2C_Params_init to I2C_open.
    I would appreciate your reply as to why the CFE bit cannot be set.

  • Hello Mori.oq,

    I believe I have replicated your issue, I grounded the SCL pin (like you preformed) and I see that the device becomes stuck upon an I2C_transfer; looking into the driver the specific line that is causing the issue is:

    /* Wait for the primed transfer to complete */
    if (SemaphoreP_pend(&(object->transferComplete), timeout) == SemaphoreP_TIMEOUT)

    Which is found in the I2C.c file. looks like since we grounded the I2C SCL pin the device is "always" pending on the transfer to complete (which it will never do as long as that pin is grounded). I see that once I remove the grounded SCL pin the code resumes since the transfer can now complete.

    void *mainThread(void *arg0)
    {
        uint8_t writeBuffer[3];
        uint8_t readBuffer[3];
        // One-time init of I2C driver
        I2C_init();
        while(1)
        {
        // initialize optional I2C bus parameters
        I2C_Params params;
        I2C_Params_init(&params);
        params.bitRate = I2C_400kHz;
        // Open I2C bus for usage
        I2C_Handle i2cHandle = I2C_open(SENSORS, &params);
        // Initialize target address of transaction
        I2C_Transaction transaction = {0};
        transaction.targetAddress = OPT_ADDR;
        // Read from I2C target device
        transaction.readBuf = readBuffer;
        transaction.readCount = sizeof(readBuffer);
        transaction.writeCount = 0;
        I2C_transfer(i2cHandle, &transaction);
        // Write to I2C target device
        transaction.writeBuf = writeBuffer;
        transaction.writeCount = sizeof(writeBuffer);
        transaction.readCount = 0;
        I2C_transferTimeout(i2cHandle, &transaction, 5000);
        // Close I2C
        I2C_close(i2cHandle);
        sleep(1);
        I2C_open(SENSORS, &params);
        // Read from I2C target device
        transaction.readBuf = readBuffer;
        transaction.readCount = sizeof(readBuffer);
        transaction.writeCount = 0;
        I2C_transfer(i2cHandle, &transaction);
        // Write to I2C target device
        transaction.writeBuf = writeBuffer;
        transaction.writeCount = sizeof(writeBuffer);
        transaction.readCount = 0;
        I2C_transferTimeout(i2cHandle, &transaction, 5000);
        // Close I2C
        I2C_close(i2cHandle);
        GPIO_toggle(14);
        }
        return 0;
    }

    Thanks,
    Alex F

  • Thank you for your reply.
    So the CFE cannot be set because the transfer is not completed.
    Is a software reset the only way to recover?
    If there are any other methods, please let me know.

  • Sorry.
    I didn't read your answer to the end.
    As you said, when I removed the ground, the I2C transfer completed,
    but even after I went through I2C_close → I2C_Init → I2C_open, I was unable to set CFE. It may not be related to the error, but I'd like to know what you think could be the reason why CFE cannot be set.

  • I apologize for the lack of information.
    In this case, I processed I2C_open, I2C_transfer, and I2C_close in that order,
    and then executed I2C_close once more.
    This seems to have caused CFE to not be set to 1 on the next I2C_open.
    Is this a specification issue?

  • Hello Mori.oq,

    Is a software reset the only way to recover?

    If we do not modify the driver code a software reset would be the only way to recover currently. 

    This seems to have caused CFE to not be set to 1 on the next I2C_open.

    Just to clarify here so I can try to replicate, in this case you did initially ground the I2C for the device to be stuck, then removed the grounded pin which then the code does I2C_open, i2C_transfer, and I2C_close correct? 

    Thanks,
    Alex F

  • Thank you for your reply.
    I think I didn't execute it correctly.
    I executed I2C_close one extra time in step ③.
    As a result, CFE did not become 1 in step ④.

    ① I2C error
    ② I2C_close
    ③ I2C_close
    ④ I2C_open
    ⑤ i2C_transfer

  • Hello Mori.oq,

    You could add another line of code after the first I2C_close that checks if the I2C is really closed (checking the handle i2cHandle), if not get stuck in a while loop or some other error case. 

    Thanks,
    Alex F