Configuración de un ATtiny45 como maestro I2C - SDA bloqueado

2

Estoy tratando de controlar un DAC desde un ATtiny45, pero de alguna manera la línea SDA está atascada. Para encontrar el problema en el código hice lo siguiente:

  • Simplifiqué el circuito solo al controlador con las resistencias pull up, no hay un esclavo I2C conectado;
  • Simplifiqué la base del código C al mínimo para reproducir el problema.

El siguiente es el resultado hasta ahora:

  • La línea SDA está atascada bajo;
  • Cuando quito el controlador, la resistencia de extracción levanta la línea como se esperaba;
  • Probé PB0 con un programa diferente y su hardware parece estar bien;
  • Revisé el desmontaje y ese código aparentemente hace exactamente lo que pretendía. Entonces, creo que estoy malinterpretando cómo funciona el mecanismo del controlador, en lugar de haber cometido un error de programación.

Lo que esperaría:

  • Cuando programo USIDR (Registro de Datos de Interfaz de Serie Universal) con 0x00 y 0xFF, la línea SDA debería alternar con él.
  

El pin de salida (DO o SDA, dependiendo del modo de cable) está conectado   a través del pestillo de salida al bit más significativo (bit 7) de la USI   Registro de datos. El pestillo de salida garantiza que la entrada de datos se muestree y   la salida de datos se cambia en los bordes opuestos del reloj. El pestillo está abierto   (transparente) durante la primera mitad de un ciclo de reloj serie cuando un   La fuente de reloj externa está seleccionada (USICS1 = 1) y está constantemente abierta   cuando se utiliza una fuente de reloj interna (USICS1 = 0). La salida será   cambió inmediatamente cuando se escribe un nuevo MSB siempre que el pestillo esté   abierto.

Lo que hace:

  • SDA estancado bajo.
  • Tanto SCL como mi pin de latido funcionan como se esperaba. Heartbeat solo está ahí para probar que el programa regresa al bucle principal de vez en cuando.

Mi pregunta es: ¿Qué estoy haciendo mal, cómo puedo hacer que SDA siga a USIDR?

El

Capítulo 15 de la hoja de datos trata sobre la interfaz de serie universal y estoy intentando configurar I2C / TWI como se describe en 15.3.4 y más adelante.

Aquí está el código del programa minimizado:

#include <avr/io.h>
#include <util/delay.h>

#define _BS(bit) ( 1 << ( bit ) )
#define _BC(bit) ( 0 << ( bit ) )

const uint8_t heartbeatPin      = PB3;
const uint8_t sdaPin            = PB0;
const uint8_t sclPin            = PB2;

const uint8_t maskUSICR =                                       // Universal Serial Interface Control Register
                                _BC( USISIE ) |                 // (rw) disable USI Start Condition Interrupt Enable
                                _BC( USIOIE ) |                 // (rw) disable USI Counter Overflow Interrupt Enable
                                _BS( USIWM1 ) | _BC( USIWM0 ) | // (rw) Two wire mode, not using counter overflow
                                _BC( USICS1 ) | _BC( USICS0 ) | // (rw) Clock select => software clock strobe (USICLK)
                                _BC( USICLK ) |                 // (-w) Clock strobe
                                _BC( USITC );                   // (-w) Toggle Port Pin

/*
        ATtiny45

        pin     1       2       3       4       5       6       7       8
        name    PB5     PB3     PB4     GND     PB0     PB1     PB2     VCC
        func    !RESET  h/beat          GND     SDA             SCL     VCC
*/

void i2cBegin( void ) {
        // 1. Start condition
        PORTB |= _BS( sclPin );                                 // Set SCL high

        USIDR = 0xff;                                           // Set SDA high
        // Shift out next bit
        USICR = maskUSICR |                                     // Universal Serial Interface Control Register
                _BS( USICLK );                                  // Clock strobe, shift bit out
        _delay_us( 10 );                                        // Maximum bitrate is 100kbps, 10 microseconds

        // Toggle SCL
        USICR = maskUSICR |                                     // Universal Serial Interface Control Register
                _BS( USITC );                                   // Set SCL low, Toggle Clock Port Pin
        _delay_us( 10 );                                        // Maximum bitrate is 100kbps, 10 microseconds

        USIDR = 0;                                              // Set SDA low
        // Shift out next bit
        USICR = maskUSICR |                                     // Universal Serial Interface Control Register
                _BS( USICLK );                                  // Clock strobe, shift bit out
        _delay_us( 10 );                                        // Maximum bitrate is 100kbps, 10 microseconds

        // Toggle SCL
        USICR = maskUSICR |                                     // Universal Serial Interface Control Register
                _BS( USITC );                                   // Release SCL, Toggle Clock Port Pin
}

int main( void ) {
        //
        // Setting up output drivers
        //
        DDRB =                                                  // Port B Data Direction Register.
                _BS( heartbeatPin ) |                           // PB3 output driver enable
                _BS( sdaPin ) |                                 // SDA output driver enable
                _BS( sclPin );                                  // SCL output driver enable

        //
        // Endless loop
        //
        while ( 1 ) {
                i2cBegin();                                     // Device addressing
                PORTB ^= _BS( heartbeatPin );                   // Toggle heartbeat pin
        }
}
    
pregunta jippie

1 respuesta

3

Consulte la página 120 de la hoja de datos:

  

Los controladores de salida se habilitan configurando el bit correspondiente para   SDA y SCL en el registro DDRB.   Cuando el controlador de salida está habilitado para el pin SDA, forzará la línea SDA baja si la salida   del registro de datos USI o el bit correspondiente en el registro PORTB es cero.   De lo contrario, la línea SDA no se activará (es decir, se liberará). Cuando la salida del pin SCL   el controlador está habilitado, la línea SCL se forzará a bajo si el bit correspondiente en el PORTB   El registro es cero, o por el detector de arranque. De lo contrario, la línea SCL no se activará.

Si su controlador está bajando el nivel de SDA, no tiene ninguna posibilidad de que TWI funcione. Es posible que deba colocar un '1' ficticio en el registro correspondiente de PORTB para forzar su alta.     

respondido por el Adam Lawrence

Lea otras preguntas en las etiquetas