Comunicación I2C con AVR: ¿cómo dejar que las líneas "floten"?

Estoy tratando de implementar I2C usando un controlador AVR sin los registros TWI internos designados.

El protocolo I2C requiere que el dispositivo maestro controle 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 enviar una dirección a través de la línea serial, debe dejar que la línea de datos "flote" (es decir, que la resistencia pull-up la levante) para que el esclavo baje la línea. para una señal ACK.

Puedo abordar con éxito el dispositivo I2C, pero cuando intento dejar que la línea "flote", tengo algunos problemas. Tenía la impresión de que en un estado de entrada HIGH-Z (tres estados, es decir, DDRX 0, PORTX 1 para un pin en particular) se modelaría esencialmente como:

ingrese la descripción de la imagen aquí

Sin embargo, el estado de la línea SDA permanece alto a pesar de que el dispositivo I2C está respondiendo con un bit ACK al hacer que la línea sea baja. Sé que esto es cierto porque configuré un retraso considerable después de configurar el pin como entrada. Durante este retraso, la línea permanece alta. Sin embargo, si quito el cable que une la MCU a la línea SDA, caerá a BAJO debido al dispositivo esclavo.

En resumen, ¿cómo maneja el "soltar" una línea para garantizar que su potencial sea impulsado 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 configurar SDA en la MCU como entrada para la prueba. La línea sigue siendo alta. Si la línea SDA se elimina de la MCU (el cable ya no está conectado), la línea SDA se reduce debido al dispositivo I2C que se está direccionando (el dispositivo está enviando ACK). Claramente estoy haciendo algo mal aquí.

Establezca el bit DDR para el pin en 0 cuando esté esperando un ACK.
Eso es exactamente lo que hago: establecer el pin como ENTRADA (que es una configuración tri-estado de alta Z). Sin embargo, la línea sigue siendo alta independientemente de esto. Publiqué el código.
¿Dónde? No hay ninguna referencia al registro DDRB en ese código.
Actualizado con declaraciones #define.
¿Posiblemente un problema de reloj? ¿Estás seguro de que estás enviando un noveno ciclo de reloj a SCL?
@Dzarda, puse el cronometraje en una subrutina separada y, de hecho, vinculé las líneas SDA y SCL a través de transistores para impulsar los LED, así que sé lo que está sucediendo. Parece que hay un reloj, pero la línea SDA permanece alta a pesar de poner el pin SDA en la MCU como entrada. Y vale la pena repetir que si corto la conexión entre la línea SDA y la MCU durante el retraso (que se muestra en el código anterior, quizás ampliado a 5000 mS), entonces la línea se vuelve baja. Por lo tanto, el dispositivo I2C estaba bajando la línea todo el tiempo, pero aparentemente la MCU todavía tenía alguna influencia en la línea a pesar de haber sido ingresada.
Interesante... Podría intentar medir el consumo total del sistema para confirmar que sus dos dispositivos se mueven en direcciones opuestas. El código me parece bien...

Respuestas (1)

Para set_in, está pasando I2C_PORT, que es PORTB, pero necesita pasar DDRB.