• AVR Freaks

Regarding I2C hang issue

Author
iamnaveen685
Starting Member
  • Total Posts : 59
  • Reward points : 0
  • Joined: 2018/09/18 07:44:02
  • Location: 0
  • Status: offline
2019/12/03 05:24:14 (permalink)
0

Regarding I2C hang issue

Hi ,
 
i am working on a i2c driver where sometimes my code hangs in the driver waiting for the bus to get idle or for waiting for transfer buffer to get empty or receive buffer to get full,
 
i have attached my code here
#1

1 Reply Related Threads

    Mysil
    Super Member
    • Total Posts : 3482
    • Reward points : 0
    • Joined: 2012/07/01 04:19:50
    • Location: Norway
    • Status: online
    Re: Regarding I2C hang issue 2019/12/03 06:11:24 (permalink)
    5 (2)
    Hi,
    Apart from possible bugs in your code, ...
     
    If a program is stopped in the middle of a Read transfer, 
    by debugging,  or by watchdog timer, or by program crash, or for programming modified code into the Flash. 
    And the slave I2C device was sending a zero bit, then slave will Not know that Master have been Reset,
    so slave device will continue to hold SDA line Low, waiting for more SCL clock pulses from Master.
    One way to get out of this, is to switch off and on power to the slave.
     
    A better way, is to disable the I2C peripheral, make 9 clock pulses on SCL line by setting LAT bit for the pin to zero,
    and toggle the TRIS bit for the pin, 9 times on and off, then make a Stop signal sequence on SCL and SDA,
    the same way. Then enable I2C hardware.
    Something like:
    /*******************************************************************************
     *    Check state of I2C bus, Try a Unjam operation if SDA is stuck.
     */
    static inline void
    I2C_Check(void)
    {    static    int    Collision = 0;
        uint32_t    TimeOut;
        uint16_t pin;
        int c,    i,    n;                        // Check State of the I2C bus
        if (I2CxCONbits.ON == 1)            // Enabled
        {    if (I2CxSTATbits.BCL)            // Bus collision has occurred
            {    I2CxSTATbits.BCL = 0;        // Clear
                Collision += 1;
                if (Collision == 100)
                    I2CxCONbits.ON = 0;        // Disable I2C
        }    }
        if (I2CxCONbits.ON == 0)            // Not enabled
        {    SCL_SetDigitalInput();
            if ((pin = SCL_GetValue()) == 0)// Clock line busy
            {    n = 10000;
                while (SCL_GetValue() == 0)    // Monitor clock line in case other devices are already signaling. */
                {    n--;
                    if ( n == 0)            // Clock line timeout, Bus may be faulty or long Clock stretch.
    #ifdef    __DEBUG
                        __builtin_software_breakpoint();
    #else
                        while(1);
    #endif
                    TimeOut =  ReadCoreTimer();
                                            // 1 microsecond between probes
                    while (ReadCoreTimer() - TimeOut < MS_TICK);
                }
                if (n > 0)                    // Falling out here, means other device is already signaling.
                    Multimaster = 1;
            }
            n = 10000;
            SDA_SetDigitalInput();            // Ensure Digital Input mode.
            while ((pin = SDA_GetValue()) == 0)        // Data line busy
            {    n--;
                if ( n == 0)                // Data line timeout
                {                            // Data line jammed
                    c = 0;                        // Try to clock the bus
                    SCL_SetLow();                    // Low
                    for ( i = 0; i < 9; i++)
                    {    TimeOut =  ReadCoreTimer();
                        SCL_SetDigitalOutput();        // Output
                                                    // 1 millisecond
                        while (ReadCoreTimer() - TimeOut < MS_TICK);
                        if (SDA_GetValue() == 0)    // Data line busy
                            c += 1;
                        TimeOut =  ReadCoreTimer();
                        SCL_SetDigitalInput();        // Let Clock line go High
                        while (ReadCoreTimer() - TimeOut < MS_TICK);
                                                    // 1 millisecond
                    }
                    if (SDA_GetValue())                // Data line is Released.
                    {                                // Make Stop signal sequence
                        SCL_SetDigitalOutput();        // Drive Clock line Low
                        TimeOut = ReadCoreTimer();
                        SDA_SetLow();
                        SDA_SetDigitalOutput();        // Drive Data line Low
                        while (ReadCoreTimer() - TimeOut < MS_TICK);
                        SCL_SetDigitalInput();        // Let Clock line go High
                        TimeOut = ReadCoreTimer();
                        while (ReadCoreTimer() - TimeOut < MS_TICK);
                        SDA_SetDigitalInput();        // Let Data line go High
                        TimeOut = ReadCoreTimer();
                        while (ReadCoreTimer() - TimeOut < MS_TICK);
                        if (SDA_GetValue())            // If SDA and SCL are High, then bus is free.
                            break;
                    }
                    if (c < 8)
                        n = 10;
                    else
                        break;
            }    }
    }    }  



    There are some macros  used above,   for ReadCoreTimer();   you may use:   _CP0_GET_COUNT()  as you already do.
    You will need to know  TRIS   LAT and PORT  register bits of SCL and SDA lines.
     
        Mysil
    post edited by Mysil - 2019/12/03 07:00:55
    #2
    Jump to:
    © 2019 APG vNext Commercial Version 4.5