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);
}
No puedo ver el código donde los pines SDA y SCL están configurados como entradas. –
¿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? –
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 *. –