• AVR Freaks

Helpful ReplyHot!I2C - recover from SCL clock hangs

Author
tdarlic
New Member
  • Total Posts : 22
  • Reward points : 0
  • Joined: 2015/06/15 01:55:35
  • Location: 0
  • Status: online
2016/10/14 12:21:39 (permalink)
0

I2C - recover from SCL clock hangs

Hi guys
I am debugging the board with the PIC32MX250F128D. The WDT has been resetting the product approximately 2 times per day and I was trying to figure out what is the cause. I had to keep the thing connected permanently and I was not there at all times. The board has the debug UART outputting all debug information to PC. I finally realized today that every time MC hangs the SCL line remains low. PIC is talking to MMA8653 accelerometer. This glitch happens rarely so it is really hard to debug. Sometimes a 24 hours can pass between resets. After the reset everything runs normally, MMA responds instantly to it is not the accelerometer bringing the SCL down.
MC reads the sample buffer from the MMA approximately every 2 seconds using the following function:

 
//accelerometer.c
 
BOOL I2C_ReadMMA8653FIFO(ACC_DATA * accdata, BYTE * count) {
    BOOL overflow;
    BOOL watermark;
    BYTE cnt;
    if (!(I2C_GetMMA89653FIFOStatus(&overflow, &watermark, &cnt))) {
        return FALSE;
    }
    *count = cnt;
    int i, j;
    BYTE buffer[192];
    memset(buffer, 0, 192);
    I2C_StartTransfer();
    I2C_TxOneByte(ACC_ADDRESS_WRITE);
    I2C_TxOneByte(MMA8652_REG_OUT_X_MSB);
    I2C_ReStartTransfer();
    I2C_TxOneByte(ACC_ADDRESS_READ);
    for (i = 0; i < (6 * cnt) - 1; i++) {
        I2C_RxOneByte(&buffer[i], TRUE);
    }
    i++;
    I2C_RxOneByte(&buffer[i], FALSE);
    I2C_StopTransfer();
    //@todo: reset the I2C line - it hangs the clock low?
    I2C2CONbits.ON = 0;
    I2C2CONbits.ON = 1;
    for (j = 0; j < cnt; j++) {
        accdata[j].x = (((int) buffer[6 * j + 0] << 8) | (int) buffer[6 * j + 1]) >> mma_acc_shift;
        accdata[j].y = (((int) buffer[6 * j + 2] << 8) | (int) buffer[6 * j + 3]) >> mma_acc_shift;
        accdata[j].z = (((int) buffer[6 * j + 4] << 8) | (int) buffer[6 * j + 5]) >> mma_acc_shift;
    }
}

I have attached I2C library I use for I2C communication, i didn't have much luck with the Microchip one. 
I still have to confirm that the MC hangs on one of the lines above but this is the only function that uses the I2C. I presume that the code hangs somewhere while waiting for the I2C to become idle. In the function below that would be the lines starting with "while". 
 
I was thinking adding counter in the while sections to reset the I2C interface on overflow. Another approach is to have the ISR checking the status of I2C and resetting it if needed. 
Is there a better approach?
 
I will post the status of I2C registers here as soon as I catch another hang. 
 

 
//I2C.c
 
BOOL I2C_RxOneByte(BYTE *data, BYTE ack) {
    // Clear Master Event flag
    INTClearFlag(INT_I2C2M);

    // Wait for Bus to become idle
    while (((I2C2STAT & 0x00004000) != 0) && ((I2C2CON & 0x0000001F) != 0));

    // Enable receive mode
    I2C2CONSET = 0x00000008;

    // Master Event flag will be set when all 8 bits are received, so wait for it
    while (!INTGetFlag(INT_I2C2M));

    // Clear the flag, receive is complete
    INTClearFlag(INT_I2C2M);

    // Get the data from recieve buffer
    *data = I2C2RCV;

    if (ack) {
        // If ack = 1 then transmit an Acknowledge
        I2C2CONCLR = 0x00000020; // Clear bit 5
    } else {
        // Otherwise transmit a Not Acknowledge
        I2C2CONSET = 0x00000020; // Set bit 5
    }

    // Send the Acknowledge sequence
    I2C2CONSET = 0x00000010;

    // Master Event flag will be set when Acknowledge sequence completes, so wait for it
    while (!INTGetFlag(INT_I2C2M));

    // Clear the flag, Acknowledge sequence complete
    INTClearFlag(INT_I2C2M);

    return TRUE;
}

#1
DavidBLit
Super Member
  • Total Posts : 1578
  • Reward points : 0
  • Joined: 2012/02/18 13:08:48
  • Location: The Land of Confusion
  • Status: offline
Re: I2C - recover from SCL clock hangs 2016/10/14 12:56:47 (permalink)
0
I'm not sure of the objective of this:
I2C2CONbits.ON = 0;
I2C2CONbits.ON = 1;


Yeah, "//Code and stuff".
#2
tdarlic
New Member
  • Total Posts : 22
  • Reward points : 0
  • Joined: 2015/06/15 01:55:35
  • Location: 0
  • Status: online
Re: I2C - recover from SCL clock hangs 2016/10/14 13:30:58 (permalink)
2 (2)
DavidBLit
I'm not sure of the objective of this:
I2C2CONbits.ON = 0;
I2C2CONbits.ON = 1;


Probably remained in the code as I copy-pasted the test code here... Please disregard those two lines...
#3
DavidBLit
Super Member
  • Total Posts : 1578
  • Reward points : 0
  • Joined: 2012/02/18 13:08:48
  • Location: The Land of Confusion
  • Status: offline
Re: I2C - recover from SCL clock hangs 2016/10/15 06:33:18 (permalink)
5 (2)
Try posting a minimal, complete program that demonstrates the problem.  I'm pretty sure nobody wants to waste their time on "oh, just ignore that, it's not really there" code.

Yeah, "//Code and stuff".
#4
stephaneC
Starting Member
  • Total Posts : 46
  • Reward points : 0
  • Joined: 2007/06/22 04:20:29
  • Location: 0
  • Status: offline
Re: I2C - recover from SCL clock hangs 2016/10/17 01:21:44 (permalink)
4 (1)
Hi,
With I2C I advise to never let a while(xxx) without a timeout ! some I2C hangs are blocking.
 
I don't think it's your problem but in case of slave blocking the bus by holding SDA low the the solution is to take control of the I2C pin and with SDA as input clocking the SCL while SDA is LOW.
 
 
#5
tdarlic
New Member
  • Total Posts : 22
  • Reward points : 0
  • Joined: 2015/06/15 01:55:35
  • Location: 0
  • Status: online
Re: I2C - recover from SCL clock hangs 2016/11/25 15:52:26 (permalink)
0
Thank you guys for input.
I'm back to debugging the same board again and have decided to set the timeout on the while loop with the function below. Will let the board run this weekend and see will that fix the issue...
BOOL I2C_WaitMasterEventFlag() {
int i2c_to = 0;
// Master Event flag will be set when stop condition completes, so wait for it
while ((!INTGetFlag(INT_I2C2M)) && (i2c_to < I2C_TIMEOUT)) i2c_to++;
    if (i2c_to >= I2C_TIMEOUT) {
        debug_log_to_uart2("I2C port timeout", __LINE__);
        I2C_RestartPort();
        return FALSE;
   }
return TRUE;
}

 
#6
tdarlic
New Member
  • Total Posts : 22
  • Reward points : 0
  • Joined: 2015/06/15 01:55:35
  • Location: 0
  • Status: online
Re: I2C - recover from SCL clock hangs 2016/12/08 12:44:11 (permalink) ☄ Helpfulby Jose Garzón 2019/02/03 00:01:04
1 (1)
Hi guys, just to close the issue here if this would help anyone. I managed to find the culprit after implementing the function in my last post. Apparently the UAR1 interrupt code messed up the timing of the I2C and it hanged. 
Finally I disabled the UART1 interrupt while collecting samples from the accelerometer buffer and after I get the samples I trigger the interrupt manually just to be sure i did not miss any characters.
This is the code where I call the function which gets the accelerometer buffer:
// DEBUG: disable UART interrupt during I2C operation
        INTEnable(INT_U1RX, INT_DISABLED);
        I2C_ReadMMA8653FIFO(accdata, &count);
        INTEnable(INT_U1RX, INT_ENABLED);
        // trigger uart interrupt to pick up any lost bytes in the buffer
        INTSetFlag(INT_U1RX);
        if (count == 0) {
            return;
        }

 And this is actual UART1 interrupt routine
/**
* UART 1 interrupt routine
*/
void __ISR(_UART1_VECTOR, IPL7AUTO) UART1Handler(void) {
// do not read into buffer if interrupt is disabled
char b;
if (INTGetFlag(INT_U1RX)) {
if (!UART_DISABLE_RX_INT) {
//code for Rx
// while the buffer is not empty read into buffer
while (U1STAbits.URXDA) {
if (uart_rx_pointer < UART_RX_BUFFER_SIZE - 2) {
uart_rx_buffer[uart_rx_pointer] = (char) U1RXREG;
// do not put null characters into buffer
if (uart_rx_buffer[uart_rx_pointer] != 0) {
#if defined(OPEN_UART2_TX) && defined(DEBUG_OUTPUT_UART)
if (UARTTransmitterIsReady(UART2)) {
UARTSendDataByte(UART2, uart_rx_buffer[uart_rx_pointer]);
}
#endif
uart_rx_pointer++;
}
}
}
} else {
// just clear the buffer
while (U1STAbits.URXDA) {
b = (char) U1RXREG;
}
}
// clear interrupt flag
INTClearFlag(INT_U1RX);
}
if (INTGetFlag(INT_U1TX)) {
INTClearFlag(INT_U1TX);
//code for Tx
}
}

I have attached updated I2C library I use...
post edited by tdarlic - 2016/12/08 13:10:04
#7
Jose Garzón
New Member
  • Total Posts : 1
  • Reward points : 0
  • Joined: 2018/09/22 10:16:59
  • Location: 0
  • Status: offline
Re: I2C - recover from SCL clock hangs 2019/02/03 00:10:15 (permalink)
0
tdarlic
Apparently the UAR1 interrupt code messed up the timing of the I2C and it hanged. 

Hi!
I just wanted to thank you for sharinng the solution to your problem. I was struggling with a simmilar behavior and finally, after reading this post, I was able to fix it.

What I did was a disable of the UART Interrupts that were triggering while the I2C transaction was running, and setting up the maximum interrupt priority (7/0 and 7/1) to the I2C module.
I am working with a PIC32MK1024GPD064.
 
Thanks again for sharing.
#8
Jump to:
© 2019 APG vNext Commercial Version 4.5