Как оправиться от столкновения шины I2C BCLIF? - PullRequest
7 голосов
/ 21 сентября 2011

Я опубликовал это пару дней назад на форуме по микрочипам ( здесь ), но единственным ответом были сверчки.Приведенный ниже код I2C работает большую часть времени, но иногда при включении питания возникает коллизия шины (BCLIF), и модуль I2C не может восстановиться после BCLIF.Линии I2C подняты на 3,3 кОм.Используя REALICE и точки останова, я вижу, что i2c_write() сбрасывает BCLIF и возвращает FALSE, если установлен BCLIF.Я использовал прицел, чтобы убедиться, что шина I2C имеет плоскую линию.Повторная инициализация модуля I2C PIC18F25K20 (см. init_i2c() ниже), когда i2c_write() возвращает FALSE, не помогает.PIC18F25K20 I2C подключен к одному подчиненному устройству (MCP4018 I2C Digital POT).Я использовал этот же код в предыдущих проектах PIC18 без проблем, поэтому я заменил MCP4018, подозревая плохую часть, но не вижу разницы.Есть ли способ сбросить модуль I2C PIC18F25K20, когда он заблокирован?

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 ]

9 голосов
/ 22 сентября 2011

Эта ошибка должна быть добавлена ​​к ошибкам PIC18F25K20.

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

17 Модуль: MSSP

Было отмечено, что после сброса при включении питания режим I2C может не правильно инициализируйте, просто настроив контакты SCL и SDA как входы или выходы. Это было замечено только в нескольких уникальных системах сред.

Тест статистически значимого образца системы подготовки производства по всему диапазону напряжения и тока источник питания приложения, должен указывать, восприимчива ли система к этому вопросу.

Работа вокруг

Перед настройкой модуля для I2C операции:

  1. Настройка выводов SCL и SDA в качестве выходов путем очистки их соответствующие биты TRIS.
  2. Принудительно установить низкий уровень SCL и SDA, очистив соответствующие биты LAT.
  3. Удерживая биты LAT в чистоте, настройте SCL и SDA в качестве входов установив их биты TRIS.

Как только это будет сделано, используйте SSPCON1 и SSPCON2 регистрируется для настройки правильного режима I2C, как и раньше.

2 голосов
/ 14 ноября 2011

Я не знаю ваших особенностей, но однажды я столкнулся с проблемой, когда микроконтроллер выходил из режима сброса рано (задолго до стабилизации Vdd на шине I2C).Таким образом, uController начал читать / записывать данные до того, как цель могла функционировать должным образом, вызывая все виды операционных проблем I2C.

2 голосов
/ 28 сентября 2011

Эта же ошибка, по-видимому, возникает и на PIC18F26K20 / SS (Revision B3), также необходимо добавить к ней ошибки.

1 голос
/ 08 июня 2019

Эта ошибка также помогла мне с зависанием I2C на PIC18F27J53 (но в моем случае также была необходима разблокировка шины I2C, заблокированной устройством SGP30, пример кода здесь: 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 сейчас стабильно работает и хорошо работает.

...