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?
ElCapí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
}
}