2011-09-21 12 views
6

Lo publiqué hace un par de días en el Microchip Forum (here) pero la única respuesta han sido los grillos. El siguiente código I2C funciona la mayoría de las veces, pero ocasionalmente durante el encendido hay una colisión de bus (BCLIF) y el módulo I2C no puede recuperarse después del BCLIF. Las líneas I2C se detienen 3.3K ohms. Utilizando REALICE y puntos de interrupción, puedo ver que i2c_write() restablece BCLIF y devuelve FALSE cuando se establece BCLIF. He usado un osciloscopio para verificar que el bus I2C tenga una línea plana. La reinicialización del módulo PIC18F25K20 I2C (consulte init_i2c() a continuación) cuando i2c_write() devuelve FALSE no ayuda. El PIC18F25K20 I2C está conectado a un único dispositivo esclavo (MCP4018 I2C Digital POT). He usado este mismo código en proyectos anteriores de PIC18 sin problema, así que reemplacé el MCP4018 sospechando una parte defectuosa, pero no veo ninguna diferencia. ¿Hay alguna manera de reiniciar el módulo PIC18F25K20 I2C cuando está bloqueado?¿Cómo recuperarse de la colisión del bus I2C BCLIF?

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

No puedo ver el código donde los pines SDA y SCL están configurados como entradas. –

+0

¿Puedes intentar cambiar la condición de inicio iniciando y primero verificar la colisión del bus en i2c_write() y ejecutar esta función pocas veces seguidas con algunas pequeñas demoras en el medio cuando ocurre este problema? ¿Qué pasa entonces? –

+0

Además, podría ser realmente útil si tiene un osciloscopio con disparador automático, por lo que puede monitorear cuál es el comportamiento de la línea SDA después del encendido. Estados de la hoja de datos: * Si al comienzo de la condición de Inicio, los pines SDA y SCL ya se muestrean bajos, o si durante la condición de Inicio, la línea SCL se muestrea baja antes de que la línea SDA se reduzca, se produce una colisión del bus, La bandera de interrupción de colisión del bus, BCLIF, está configurada, la condición de inicio se cancela y el módulo I2C se reinicia en estado inactivo *. –

Respuesta

7

This Errata necesita ser añadido a PIC18F25K20 erratas.

PIC18F2455/2550/4455/4550 Rev. A3 silicio erratas

17 Módulo: MSSP

Se ha observado que después de una puesta en tensión de reinicio, el modo I2C no puede inicializar correctamente por simplemente configurando los pines SCL y SDA como entradas o salidas . Esto solo se ha visto en algunos entornos únicos del sistema .

Una prueba de una muestra estadísticamente significativa de sistemas de preproducción, a través del voltaje y de corriente de la fuente de alimentación de la aplicación , debe indicar si un sistema es susceptible a este problema.

trabajo alrededor

Antes de configurar el módulo para la operación I2C :

  1. Configurar los pines SCL y SDA como salidas por la limpieza de sus bits correspondientes TRIS.
  2. Fuerce SCL y SDA bajo borrando los bits LAT correspondientes.
  3. Mientras se mantienen los bits LAT despejados, configure SCL y SDA como entradas configurando sus bits TRIS.

Una vez hecho esto, utilice los registros SSPCON1 y SSPCON2 para configurar el modo I2C adecuado como antes.

+0

Entonces, ¿esta secuencia funcionó para usted? –

+0

@AndrejsCainikovs: Sí. Funcionó. No puedo aceptar esta respuesta hasta mañana. – jacknad

2

Este mismo error parece ocurrir en PIC18F26K20/SS (Revisión B3) también, también debe agregarse a su errata.

2

No conozco sus detalles, pero me encontré con un problema en el que el microcontrolador salía del modo de reinicio temprano (mucho antes de que el Vdd se estabilizara en el bus I2C). Entonces el uController comenzó a leer/escribir datos antes de que el objetivo pudiera funcionar correctamente, causando todo tipo de problemas operativos de I2C.

Cuestiones relacionadas