7

几天前我在微芯片论坛(这里)上发布了这个,但唯一的回应是蟋蟀。下面的 I2C 代码大部分时间都在工作,但偶尔在上电时会出现总线冲突 (BCLIF),并且 I2C 模块在 BCLIF 之后无法恢复。I2C 线路上拉 3.3K 欧姆。使用 REALICE 和断点,我可以看到i2c_write()重置 BCLIF 并在设置 BCLIF 时返回 FALSE。我使用示波器来验证 I2C 总线是否为扁平线。重新初始化 PIC18F25K20 I2C 模块(见init_i2c()下文)时i2c_write()返回 FALSE 没有帮助。PIC18F25K20 I2C 连接到单个从设备(MCP4018 I2C 数字电位器)。我在以前的 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

4 回答 4

10

此勘误表需要添加到 PIC18F25K20 勘误表中。

PIC18F2455/2550/4455/4550 Rev. A3 硅勘误表

17 模块:MSSP

据观察,上电复位后,仅将 SCL 和 SDA 引脚配置为输入或输出可能无法正确初始化 I2C 模式。这仅在少数独特的系统环境中出现。

在应用电源的电压和电流范围内对具有统计意义的预生产系统样本进行测试,应表明系统是否容易受到此问题的影响。

解决方法

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

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

完成后,使用 SSPCON1 和 SSPCON2 寄存器配置正确的 I2C 模式,如前所述。

于 2011-09-21T21:03:13.863 回答
2

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

于 2011-09-27T23:08:41.803 回答
2

我不知道你的具体情况,但有一次我遇到了一个问题,即微控制器很早就退出复位(远在 Vdd 在 I2C 总线上稳定之前)。因此,在目标可以正常运行之前,uController 开始读取/写入数据,从而导致各种 I2C 操作问题。

于 2011-11-14T19:46:25.917 回答
1

此勘误表还帮助我解决了 I2C 卡在 PIC18F27J53 上的问题(但在我的情况下,还需要解锁被 SGP30 设备阻塞的 I2C 总线,此处的代码示例:https ://github.com/esp8266/Arduino/issues/1025#issuecomment- 158667929 ) 我终于决定在启动条件失败时执行执行restart-unstuck-restart 的例程(我的I2C 堆栈的代码片段):

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 现在似乎很稳定并且运行良好。

于 2019-06-07T21:33:22.890 回答