如何从I2C总线冲突中恢复BCLIF?

时间:2011-09-21 19:04:07

标签: c pic i2c

我几天前在Microchip论坛上发布了这篇文章(here),但唯一的反应是蟋蟀。 下面的I2C代码大部分时间都在工作,但偶尔在上电时会发生总线冲突(BCLIF),并且在BCLIF之后I2C模块无法恢复。 I2C线路上拉3.3K欧姆。 使用REALICE和断点我可以看到i2c_write()重置BCLIF并在设置BCLIF时返回FALSE。 我使用示波器来验证I2C总线是否具有扁平线。 当init_i2c()返回FALSE时,重新初始化PIC18F25K20 I2C模块(参见下面的i2c_write())没有帮助。 PIC18F25K20 I2C连接到单个从器件(MCP4018 I2C数字POT)。 我在之前的PIC18项目中使用了相同的代码而没有出现问题,所以我更换了怀疑是坏部件的MCP4018,但没有看到任何区别。 有没有办法在锁定时复位PIC18F25K20 I2C模块?

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); 
} 

4 个答案:

答案 0 :(得分:8)

This Errata需要添加到PIC18F25K20勘误表中。

  

PIC18F2455 / 2550/4455/4550 Rev. A3 Silicon Errata

     

17模块:MSSP

     

据观察,上电复位后,I2C模式可能没有   只需将SCL和SDA引脚配置为正确初始化即可   输入或输出。这只是在一些独特的系统中看到的   环境。

     

对具有统计学意义的样本的测试   预生产系统,跨越电压和电流范围   应用程序的电源应指示系统是否易受影响   这个问题。

     

解决方法

     

在为I2C配置模块之前   操作:

     
      
  1. 通过清除SCL和SDA引脚将其配置为输出   相应的TRIS位。
  2.   
  3. 通过清除相应的LAT位强制SCL和SDA为低电平。
  4.   
  5. 在保持LAT位清零的同时,将SCL和SDA配置为输入   通过设置他们的TRIS位。
  6.         

    完成后,使用SSPCON1和   SSPCON2注册以配置正确的I2C模式。

答案 1 :(得分:2)

同样的错误似乎也发生在PIC18F26K20 / SS(版本B3)上,也需要添加到它的勘误表中。

答案 2 :(得分:2)

我不知道你的具体细节,但是一旦微控制器早早出现复位方式(在I2C总线上Vdd稳定之前),我就遇到了问题。因此,uController在目标正常运行之前开始读/写数据,从而导致各种I2C操作问题。

答案 3 :(得分:1)

此勘误表还帮助我将I2C卡在了PIC18F27J53上(但我也需要解锁被SGP30器件阻止的I2C总线,此处的代码示例:https://github.com/esp8266/Arduino/issues/1025#issuecomment-158667929) 我最终决定实现例程,执行在启动条件失败(我的I2C堆栈的代码片段)时调用的restart-unstuck-restart:

eI2Cerr i2c_do_RUR(void) {
    //performs restart-unstuck-restart
    eI2Cerr   eRetVal;

    eRetVal = i2c_do_restart();
    if (eRetVal == I2C_ERR_TIMEOUT_RSEN_COLLISION) {
        i2c_unstuck(true); //true=performs also I2C bus unlock
        eRetVal = i2c_do_restart();
    }
    return(eRetVal);
}

I2C现在似乎稳定并且运行良好。