Estoy intentando implementar I2C utilizando un controlador AVR sin los registros TWI internos designados.
El protocolo I2C requiere que el dispositivo maestro maneje las líneas SDA y SCL para comenzar con el fin de abordar y comenzar una transacción de comunicación. El problema que tengo es que después de que se envía una dirección a través de la línea serie, debe dejar que la línea de datos "flote" (es decir, que el resistor de pull-up la coloque hacia arriba) para que el esclavo pueda bajar la línea para una señal ACK.
Soy capaz de abordar con éxito el dispositivo I2C, pero cuando intento que la línea "flote" tengo un poco de problema. Tenía la impresión de que en un estado de entrada ALTO-Z (tri-estado que es - DDRX 0, PORTX 1 para un pin en particular) esencialmente se modelaría como:
Sinembargo,elestadodelalíneaSDAsiguesiendoalto,apesardequeeldispositivoI2CestárespondiendoconunbitACKalreducirlalínea.Séqueestoesciertoporqueestablezcounretrasoconsiderabledespuésdeconfigurarelpincomoentrada.Duranteesteretraso,lalíneasemantienealta.Sinembargo,siquitoelcablequeatalaMCUalalíneaSDA,caeráaBAJOdebidoaldispositivoesclavo.
Enresumen,¿cómomanejael"soltar" de una línea para asegurarse de que su potencial esté controlado por los dispositivos en su línea de datos I2C en lugar de ser influenciado por la MCU?
/* Pin and direction register manipulations */
#define I2C_CLKDEL 10 //10uS
#define I2C_PORT PORTB
#define SDA PB0
#define SCL PB1
#define set_high(port, pin) (port |= (1<<pin))
#define set_low(port, pin) (port &= ~(1<<pin))
#define set_in(portDDR, pin) (portDDR &= ~(1<<pin))
#define set_out(portDDR, pin) (portDDR |= (1<<pin))
void clkStrobe(void){
set_high(I2C_PORT, SCL);
_delay_us(I2C_CLKDEL);
set_low(I2C_PORT, SCL);
_delay_us(I2C_CLKDEL);
}
uint8_t sendByte(uint8_t byte){ //MSB first
uint8_t count = 8, ack;
set_low(PORTB, SCL);
while( count-- ){
if( byte & 0x80 )
set_high(PORTB, SDA);
else
set_low(PORTB, SDA);
byte <<= 1;
_delay_ms(I2C_CLKDEL);
clkStrobe();
}
//set as input and read in the I2C port data
set_in(I2C_PORT, SDA);
_delay_ms(1000);
ack = PINB;
set_out(I2C_PORT, SDA);
clkStrobe(); //clock in the ACK bit
return (ack & (1<<SDA)); //return ACK bit
}
Se insertó un gran retraso después de que se estableció SDA en la MCU como entrada para la prueba. La línea sigue siendo alta. Si se quita la línea SDA de la MCU (el cable ya no está conectado), la línea SDA es controlada por el dispositivo I2C al que se dirige (el dispositivo está enviando ACK). Claramente estoy haciendo algo mal aquí.