How to recover from I2C bus collision BCLIF?

Author
jnadelman
Starting Member
  • Total Posts : 54
  • Reward points : 0
  • Joined: 2010/11/04 08:46:25
  • Location: 0
  • Status: offline
2011/09/19 12:23:38 (permalink)
0

How to recover from I2C bus collision BCLIF?

The I2C code below works most of the time but possible noise on the line causes bus collision BCLIF and the I2C is unable to recover after the BCLIF. 3.3K pullups are used. Using REALICE and breakpoints I can see that i2c_write() resets BCLIF and returns FALSE when BCLIF is set. Re-initializing the PIC18F25K20 I2C module (see init_i2c below) when i2c_write() returns FALSE does not seem to help. Is there a way to reset the PIC18F25K20 I2C module when it is locked up? 

Edit:   The PIC18F25K20 I2C is connected to a single slave device (MCP4018 I2C Digital POT) and I have used a scope to verify that the I2C bus has flat-lined.



void init_i2c(I2C_BAUD_RATE baud_rate, float freq_mhz)
{
    UINT32 freq_cycle;
    /* Reset i2c */
    SSPCON1 = 0;
    SSPCON2 = 0;
    PIR2bits.BCLIF = 0;
    /* Set baud rate */
    /* SSPADD = ((Fosc/4) / Fscl) - 1 */
    freq_cycle = (UINT32) ((freq_mhz * 1e6) / 4.0);
    if (baud_rate == I2C_1_MHZ)
    {
        SSPADD = (UINT8) ((freq_cycle / 1000000L) - 1);
        SSPSTATbits.SMP = 1;        /* disable slew rate for 1MHz operation */
    }
    else if (baud_rate == I2C_400_KHZ)
    {
        SSPADD = (UINT8) ((freq_cycle / 400000L) - 1);
        SSPSTATbits.SMP = 0;        /* enable slew rate for 400kHz operation */
    }
    else /* default to 100 kHz case */
    {
        SSPADD = (UINT8) ((freq_cycle / 100000L) - 1);
        SSPSTATbits.SMP = 1;        /* disable slew rate for 1MHz operation */
    }
    /* Set to Master Mode */
    SSPCON1bits.SSPM3 = 1;
    SSPCON1bits.SSPM2 = 0;
    SSPCON1bits.SSPM1 = 0;
    SSPCON1bits.SSPM0 = 0;
    /* Enable i2c */
    SSPCON1bits.SSPEN = 1;
}
BOOL i2c_write(UINT8 addr, const void *reg, UINT16 reg_size, const void *data, UINT16 data_size)
{
    UINT16 i;
    const UINT8  *data_ptr, *reg_ptr;

    /* convert void ptr to UINT8 ptr */
    reg_ptr  = (const UINT8 *) reg;
    data_ptr = (const UINT8 *) data;
    /* check to make sure i2c bus is idle */
    while ( ( SSPCON2 & 0x1F ) | ( SSPSTATbits.R_W ) )
        ;
    /* initiate Start condition and wait until it's done */
    SSPCON2bits.SEN = 1;
    while (SSPCON2bits.SEN)
        ;
    /* check for bus collision */
    if (PIR2bits.BCLIF)
    {
        PIR2bits.BCLIF = 0;
        return(FALSE);
    }
    /* format address with write bit (clear last bit to indicate write) */
    addr <<= 1;
    addr &= 0xFE;
    /* send out address */
    if (!write_byte(addr))
        return(FALSE);
    /* send out register/cmd bytes */
    for (i = 0; i < reg_size; i++)
    {
        if (!write_byte(reg_ptr))
            return(FALSE);
    }
    /* send out data bytes */
    for (i = 0; i < data_size; i++)
    {
        if (!write_byte(data_ptr))
            return(FALSE);
    }
    /* initiate Stop condition and wait until it's done */
    SSPCON2bits.PEN = 1;
    while(SSPCON2bits.PEN)
        ;
    /* check for bus collision */
    if (PIR2bits.BCLIF)
    {
        PIR2bits.BCLIF = 0;
        return(FALSE);
    }
    return(TRUE);
}
BOOL write_byte(UINT8 byte)
{
    /* send out byte */
    SSPBUF = byte;
    if (SSPCON1bits.WCOL)       /* check for collision */
    {
        return(FALSE);
    }
    else
    {
        while(SSPSTATbits.BF)   /* wait for byte to be shifted out */
            ;
    }
    /* check to make sure i2c bus is idle before continuing */
    while ( ( SSPCON2 & 0x1F ) | ( SSPSTATbits.R_W ) )
        ;
    /* check to make sure received ACK */
    if (SSPCON2bits.ACKSTAT)
        return(FALSE);
    return(TRUE);
}



post edited by jnadelman - 2011/09/19 12:44:20
#1

1 Reply Related Threads

    jnadelman
    Starting Member
    • Total Posts : 54
    • Reward points : 0
    • Joined: 2010/11/04 08:46:25
    • Location: 0
    • Status: offline
    Re:How to recover from I2C bus collision BCLIF? 2011/09/21 14:11:51 (permalink)
    0

    [This Errata][1] needs to be added to PIC18F25K20 Errata.




    PIC18F2455/2550/4455/4550 Rev. A3 Silicon Errata
    17 Module: MSSP
    It has been observed that following a Power-on Reset, I2C mode may not initialize properly by just configuring the SCL and SDA pins as either inputs or outputs. This has only been seen in a few unique system environments.
    A test of a statistically significant sample of preproduction systems, across the voltage and current range of the application's power supply, should indicate if a system is susceptible to this issue.
    Work around
    Before configuring the module for I2C operation:
    Configure the SCL and SDA pins as outputs by clearing their corresponding TRIS bits. Force SCL and SDA low by clearing the corresponding LAT bits. While keeping the LAT bits clear, configure SCL and SDA as inputs by setting their TRIS bits. Once this is done, use the SSPCON1 and SSPCON2 registers to configure the proper I2C mode as before.




      [1]: http://ww1.microchip.com/...n/DeviceDoc/80220j.pdf


    #2
    Jump to:
    © 2017 APG vNext Commercial Version 4.5