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.

I2C initialisation technique when SDA is stuck low

Other Parts Discussed in Thread: TM4C1294NCPDT, TSC2003

Hi,

I recently experienced a "well known" I2C bus hang issue which happens when you reset an I2C Master in the middle of a transaction, specifically when the slave device is ACKnowledging. In my case I was resetting a running TM4C1294NCPDT.

What happens is, on reset, the clock from the master stops leaving the slave device holding SDA low (ACK state), so when the I2C module starts up again the bus is stuck BUSY with SDA lo and no way of recovering.

All you have to do is toggle the SCL line a few times until the slave releases the SDA line. (I'm not sure if this is guaranteed to work with all slave devices, but it worked for me!).

Here is my I2C initialisation code incase it helps someone with the same problem...

uint32_t i, count;

//initialise I2C3..................................................................................
    ROM_GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_0);        //ensure bus is idle
    ROM_GPIOPinTypeGPIOInput(GPIO_PORTD_BASE, GPIO_PIN_1);

    count = 0;
    while(ROM_GPIOPinRead(GPIO_PORTD_BASE, GPIO_PIN_1) == 0) {
        ROM_GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_0, 0x00);
        for(i=0;i<1000;i++) ;
        ROM_GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_0, GPIO_PIN_0);
        for(i=0;i<1000;i++) ;

        count++;
        if(count > 100)    //max 100 toggles before giving up to ensure we don't get stuck here
            break;
    }

    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C3);    //now can initialise the I2C peripheral as normal
    while(!(ROM_SysCtlPeripheralReady(SYSCTL_PERIPH_I2C3))); //wait for peripheral ready

    ROM_SysCtlPeripheralReset(SYSCTL_PERIPH_I2C3);
    ROM_GPIOPinConfigure(GPIO_PD0_I2C3SCL);
    ROM_GPIOPinConfigure(GPIO_PD1_I2C3SDA);
    ROM_GPIOPinTypeI2CSCL(GPIO_PORTD_BASE, GPIO_PIN_0);
    ROM_GPIOPinTypeI2C(GPIO_PORTD_BASE, GPIO_PIN_1);

    //ROM_I2CMasterInitExpClk(I2C3_BASE, ROM_SysCtlClockGet(), true);    //400K
    ROM_I2CMasterInitExpClk(I2C3_BASE, ROM_SysCtlClockGet(), false);    //100K

    HWREG(I2C3_BASE + I2C_O_FIFOCTL) = 80008000;    //clear I2C FIFOs

  • Richard,
    Doesn't that depend on the external device?
    Both SDA and SCL are pulled up by external resistors, so from the TM4C side, on reset, these pins become high impedance - hence the lines would become high. If the line is stuck low, it is the external device that's doing it.
    But the ACK or NACK response should be informed only when the clock is low. If it comes back high (due to the reset and resistors), the device should released that signal.
    Now, there may be badly implemented I2C products that made all the way to the market... And then a whole bunch of clocks should in fact fool the device and make it sync again...
    But if the slave device is user's own implementation, then there's room for improvement there - not on the master MCU's end.
    Just my two cents... correct me if I misunderstood your post.
    Regards
    Bruno
  • Bruno,
    Yes indeed it is the external device that has got stuck. The TM4C is behaving perfectly.
    I believe some I2C slave devices have a watchdog timer which resets the internal circuitry in these circumstances.
    I am using a TSC2003 touch screen chip from TI on the I2C bus and it definitely can end up pulling SDA low after a TM4C reset.
    My code clocks it out of this state, releases the bus back to IDLE and allows the I2C to start up correctly.
    Just thought I'd post it incase someone else found it useful.
    Regards
    Richard
  • Hello Richard

    The toggling of the SCL is a known method to recover the Slave if it supports one. Unfortunately, almost all slave device specifications never mention anything like this and it is a derived feature from the I2C specification.