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.

TM4C1294NCPDT: I2C Bus stuck. Need switch from I2C to GPIO then back to I2C during runtime

Part Number: TM4C1294NCPDT
Other Parts Discussed in Thread: TCA9535

Dear Support Team,

We are using TM4C1294NCPDT as I2C Master to communicate with TCA9535(I2C IO Expander) Salve.

We are getting into situation where I2C Bus gets stuck.

To getting the bus "unstuck" We are toggling the clock line multiple (18) times before doing any I2C operation with the slave device to convince it that the last transaction is complete.

We are following the sequence below

  1. disable I2C peripheral (SysCtlPeripheralDisable(I2C)),
  2. enable GPIO Peripheral (SysCtlPeripheralEnable(GPIO)),
  3. use SCL and SD as digital output,
  4. enable I2C peripheral (SysCtlPeripheralEnable(I2C)

But still unable to establish the connection with I2C Slave TCA9535. What could be the reason?

Can we switch from I2C to GPIO mode then back to I2C mode during run time ? What is the correct way of dong this?
or Is there any other way to tackle this situation ?

Thanks & Best Regards,
Prashant Naik

  • Hello Prashant,

    Yes you can switch between them your general flow seems to be okay but I'd have to see your configuration code for both to be certain. Feel free to post it.

    Try and bit bang a start and then a stop condition to the I2C slave. This worked for customers in the past: https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/954162/tm4c1294ncpdt-problems-resetting-i2c-communication/3541214#3541214

    Beyond that, also let me know how the bus is hanging (is slave holding a pin low etc.) so I can see what other ideas to offer.

    Best Regards,

    Ralph Jacobi

  • Does this method of toggling SCL to unblock the I2C bus works always ?

  • Hi Ralph

    Thanks for the feedback.

    Actually we are trying this on healthy I2C bus with SCL\SDA high since the error (due to noise) occurred rarely.
    Is this the right way to test? or should we reproduce the error and then apply the dummy clock to I2C slave ?

    Thanks & Best Regards,
    Prashant Naik

  • Hi Prashant,

    I would reproduce the error to test it, I don't see a correlation to trying it on a healthy bus and then assuming it can recover from a situation where a slave holds SCL low. The idea with the toggle is to force the signal high while the slave it trying to pull it low so that it sees the start / stop and that clears the hang up and the slave releases SCL. Without the slave in that state, you wouldn't be able to validate that it works exactly for your device.

    Best Regards,

    Ralph Jacobi

  • Hi Ralph,

    I am Prashant's colleague. Our intention to test on healthy bus is to ensure our logic works. We need to fit bit-banging logic in our complex multithreaded environment.

    As Prashant mentioned, error scenario is very rare and it can't be reproduced frequently. Once we know our logic works, we will spend sufficient time for validation it to ensure it is working every time during actual error scenario.

    Here is the I2C config code that we use on power up

    /* Enable the peripheral */
    MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C2);
    MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_I2C2);

    halReturn = CheckI2cPeripheralReadyStatus(SYSCTL_PERIPH_I2C2);
    if(HAL_EOK == halReturn)
    {
    MAP_GPIOPinConfigure(GPIO_PN5_I2C2SCL);
    MAP_GPIOPinConfigure(GPIO_PN4_I2C2SDA);
    MAP_GPIOPinTypeI2CSCL(GPIO_PORTN_BASE, GPIO_PIN_5);
    MAP_GPIOPinTypeI2C(GPIO_PORTN_BASE, GPIO_PIN_4);

    MAP_I2CMasterGlitchFilterConfigSet(I2C2_BASE, I2C_MASTER_GLITCH_FILTER_8);

    UINT32 numOfI2cClocks = 0x7D; // This calculates to 5ms with I2C operating at 400KHZ
    MAP_I2CMasterTimeoutSet(I2C2_BASE, numOfI2cClocks);

    I2C_init();

    ------------------------------------------------------------------------------------------------------

    HalErr
    CheckI2cPeripheralReadyStatus(const UINT32 peripheral)
    {
    HalErr halReturn = HAL_ENOK;
    const UINT32 SLEEP_TIME_IN_TICKS = 1;
    const UINT8 MAX_RETRY_COUNT = 5;
    UINT8 retryCountIndex = 1;
    bool readyStatus = MAP_SysCtlPeripheralReady(peripheral);

    while((!readyStatus) &&
    (MAX_RETRY_COUNT > retryCountIndex))
    {
    Task_sleep(SLEEP_TIME_IN_TICKS);
    retryCountIndex++;
    readyStatus = MAP_SysCtlPeripheralReady(peripheral);
    }

    if(!readyStatus)
    {
    GPIO_LOG_BASIC(GPIO_PACK_ID, LOG_CLASS_WARNING,
    ("%s : I2C peripheral is not ready.", __FUNCTION__));
    }
    else
    {
    halReturn = HAL_EOK;
    }
    return halReturn;
    }

    -----------------------------------------------------------------------------------------------------

    HalErr
    HalI2cOpen(const HalI2CBoardIndex index,
    const HalI2CParams param,
    HalI2CHandle* handle)
    {
    OSA_ASSERT(NULL != handle);

    HalErr halReturn = IsValidOpenParameters(index,
    param);
    if(HAL_EOK == halReturn)
    {
    I2C_Params halI2CActualParams;
    I2C_Params_init(&halI2CActualParams);
    halI2CActualParams.transferMode = static_cast<I2C_TransferMode>(param.transferMode);
    halI2CActualParams.bitRate = static_cast<I2C_BitRate>(param.bitRate);
    halI2CActualParams.transferCallbackFxn = I2cTivaCallbackFxn;

    I2C_Handle actualHandle = I2C_open(index,
    &halI2CActualParams);

    ------------------------------------------------------------------------------------------------

    HalI2CParams i2cParams;
    i2cParams.bitRate = HAL_I2C_400kHz;
    i2cParams.transferMode = HAL_I2C_MODE_CALLBACK;
    HalErr halResult = HalI2cOpen(HAL_BOARD_I2C2,
    i2cParams,
    &m_I2CHandle);

    -----------------------------------------------------------------------------------------------

    Thank you for your support.

    Thanks & Regards

    Abhijit

  • Hello Abhijit,

    Once we know our logic works, we will spend sufficient time for validation it to ensure it is working every time during actual error scenario.

    Understood. I would implement the logic to disable the I2C peripheral, enable the GPIO as digital outputs, toggle the IO to bit bang a start & stop condition, turn the GPIO back to I2C configuration, and restart the I2C peripheral.

    I just want to be clear that I just can't say for certain that this technique works for every I2C slave device. I would strongly expect it will work, but I can't guarantee how the I2C IO expander will respond to that manner of trying to clear the error. But it is definitely what I would recommend trying as in my experiences that is the best way to clear an issue like this.

    Best Regards,

    Ralph Jacobi

  • Hello Ralph,

    Thank you for your response.

    We use I2C driver in call back mode; after performing slave reset using bit-bang method we observed our write operation is failing and Boolean parameters in call back function returns FALSE. How do we get to know the failure reasons? Any API can get us the actual reason?

    Thanks & Regards

    Abhijit

  • Hello Abhijit,

    I'm not entirely clear what APIs you are using. The I2C transfer API should always return successful. Can you post the specific segment of code that is having the issue and report the parameters returning with the FALSE? I can then check through our TI-RTOS driver documentation / source code to help guide further.

    Best Regards,

    Ralph Jacobi

  • Hello Ralph,

    We are using TI-RTOS I2C drivers in call back mode. I am talking about I2C_CallbackFxn.

    Thanks & Regards

    Abhijit

  • Hello Abhijit,

    I am aware you are using TI-RTOS API's, please post the full segment of code so I can see the exact sequence and what parameters are used etc.

    I2C_CallbackFxn is not an API we ship with TI-RTOS for Tiva-C so that is specific to your system. 

    Best Regards,

    Ralph Jacobi

  • Hello Ralph,

    Please take a look at code snippet that I shared earlier. We provide our call back function (I2cTivaCallbackFxn) during I2C_open call. After invoking I2C_transfer(); result value received with callback function was false.

    Thanks & Regards

    Abhijit 

  • Hello Abhijit,

    What is the return for I2C_open then? If its NULL then it did not succeed which would help narrow down the issue.

    If it does succeed, I don't see the actual code for I2cTivaCallbackFxn posted or anything using I2C_transfer so I can't review any deeper.

    Best Regards,

    Ralph

  • Hello Ralph,

    Here is out I2C call back code; we have multiple software abstraction in place; so there is some complexity. The result assignment in the bold line is what we check at one layer above this where we got FALSE value.

    /*
    * Will be called by Tiva library when each I2C transaction ends
    */
    void
    I2cTivaCallbackFxn(I2C_Handle handle, /*TICS !ORG#011 allow C-function at global scope */
    I2C_Transaction* i2cTransaction,
    bool result)
    {
    OSA_ASSERT(NULL != handle);
    OSA_ASSERT(NULL != i2cTransaction);
    OSA_ASSERT(NULL != i2cTransaction->arg);

    HalI2CBoardIndex i2cIndex;
    HalI2CHandle currHandle = reinterpret_cast<HalI2CHandle>(handle);
    HalErr halReturn = GetPeripheralIndex(currHandle,
    i2cIndex);
    if(HAL_EOK == halReturn)
    {
    UINT32 currTransactionIndex = *(static_cast<UINT32*>(i2cTransaction->arg));
    OSA_ASSERT(DetailedTransactionInfo[i2cIndex].maxTransactionCount > currTransactionIndex);
    DetailedTransactionInfo[i2cIndex].resultPtr[currTransactionIndex] = static_cast<BOOL>(result);
    if(!result)
    {
    GPIO_LOG_BASIC(GPIO_PACK_ID, LOG_CLASS_WARNING,
    ("%s :I2C transaction failed for I2C peripheral index %u in transaction %u.", __FUNCTION__, i2cIndex, currTransactionIndex));
    }

    /* If last transaction is done, then invoke application callback */
    if(DetailedTransactionInfo[i2cIndex].maxTransactionCount == (currTransactionIndex + 1))
    {
    OSA_ASSERT(NULL != DetailedTransactionInfo[i2cIndex].appCallbackFn);
    /* Call application callback */
    DetailedTransactionInfo[i2cIndex].appCallbackFn(DetailedTransactionInfo[i2cIndex].argument);

    /* Clearing the transaction details other than handles, which are not required for next transactions if any */
    DetailedTransactionInfo[i2cIndex].maxTransactionCount = 0;
    DetailedTransactionInfo[i2cIndex].argument = NULL;
    DetailedTransactionInfo[i2cIndex].appCallbackFn = NULL;

    /* Release the locking semaphore */
    OSA_ASSERT(NULL != DetailedTransactionInfo[i2cIndex].semHandle);
    Semaphore_post(DetailedTransactionInfo[i2cIndex].semHandle);
    }
    }
    else
    {
    GPIO_LOG_BASIC(GPIO_PACK_ID, LOG_CLASS_WARNING,
    ("%s : Failed to get I2C peripheral index.", __FUNCTION__));
    }
    }

  • Hello Abhijit,

    I see regarding the complexity with the software abstraction layers. I'm not really an RTOS expert so that sort of complexity makes it harder for me to offer anything of note here.

    Maybe we need to take a step in a different direction. On a software side, the I2C_transfer is what is key to understand the error which I still haven't seen how its used, but instead of trying to assess that we may be able to trace this in a simpler manner. Is it possible to use an o-scope or a logic analyzer to view the I2C lines?

    Then we'd be able to see the actual data transmission attempts to try and understand what is not being sent correctly. For example if nothing is sent, then it would indicate the I2C bus is not re-configured properly.

    Given the software complexity, I'm thinking maybe working back from what the device is actually doing hardware wise will help guide you to the correct segments of code to revisit.

    Best Regards,

    Ralph Jacobi