Ich habe versucht, meine ATTINY85 zu bit-bang I2C (lesen/schreiben). Ich habe die folgende Konfiguration:I2C bizarre Verzögerung Problem beim Lesen
PB0 = SDA
PB1 = LED
PB2 = SCL
ich in der Lage bin ohne Probleme zu schreiben, aber das Lesen funktioniert nur, wenn ich meine Verzögerung() 'Funktion innerhalb der Leseschleife habe, so weit, so gut:
char i2c_read(void)
{
uint8_t B = 0;
DDRB &= 0b11111110; // switch PB0 to input
for (int bit = 0; bit < 0x08; bit++)
{
delay(); // <--!!!!!!!!! the root of all evil
SIGNAL_HIGH(PORT_SCL);
B <<= 1;
if(PINB & (1 << PB0))
{
B |= 1;
}
else
{
B |= 0;
}
SIGNAL_LOW(PORT_SCL);
}
DDRB |= 0b00000001; // switch PB0 as output
i2c_nack();
return B;
}
Wenn ich die Verzögerung() entferne, funktioniert I2C nicht mehr und ich kann nicht vom Gerät lesen (das Gerät antwortet nicht). Scheint logisch, aber der Grund, warum ich die Verzögerung() entfernen möchte, ist, dass es keine echte Verzögerung ist, es schaltet einfach eine LED ein und aus, die sich an einem anderen Pin befindet (PB1), I2C-Leitungen sind an PB0 und PB2 .
Die _delay_ms war zu langsam, also habe ich PB1 Pin ein- und ausgeschaltet, um eine kleine Verzögerung zu machen und das ist die einzige Möglichkeit. Hier sind die Inhalte meiner Verzögerungsfunktion, alles funktioniert gut, wenn ich es so lassen:
void delay()
{
LED_ON();
LED_OFF();
}
void LED_ON(void)
{
PORTB |= 0b00000010; // PB1
}
void LED_OFF(void)
{
PORTB &= 0b11111101; // PB1
}
Ich vermuten, dass ich wahrscheinlich ‚genagelt‘ eine perfekte Verzögerung, die die entsprechende Signallänge von dem anderen Gerät erwartet erzeugt, so ich habe versucht, die gleiche Verzögerung mit der for-Schleife und Oszilloskop zu machen:
void delay()
{
for(int i=0; i<20; i++){ }
}
Kein Glück, I2C Lesung nicht mehr funktioniert ..
Dann habe ich beschlossen, die LED auf einen anderen PIN zu wechseln und das PB1 verlassen ganz allein um zu sehen, ob es sich um eine Verzögerung handelt oder Pin/Schaltung bezogen:
void delay()
{
LED_ON();
LED_OFF();
}
void LED_ON(void)
{
PORTB |= 0b00001000; // PB3
}
void LED_OFF(void)
{
PORTB &= 0b11110111; // PB3
}
Und seltsamerweise hörte I2C auf, wieder zu arbeiten! Es funktioniert nur, wenn ich PB1 hoch/niedrig setze. Ich kann immer noch nicht verstehen, ob ich zufällig die perfekte Verzögerung gefunden habe, und es ist einfach so, dass das Einschalten von PB1 weniger Zeit in Anspruch nimmt als das Drehen des PB3 oder etwas mit der Schaltung selbst und LED, die eine Art Pull- Up/Pull-Down-Funktionalität (vergib meine Ignoranz, ich bin ein Anfänger) auf dem I2C, aber dann wieder PB1 ist überhaupt nicht mit den I2C-Linien verbunden.
Kann jemand bitte ein wenig Licht darauf werfen, warum es nur funktioniert, wenn ich PB1 an/aus einschalte, anstatt eine wirkliche Verzögerung zu machen? Vielen Dank!
Die vollständige Quelle:
#define PORT_SDA PB0
#define PORT_SCL PB2
#define SIGNAL_HIGH(PORT) PORTB |= (1 << PORT)
#define SIGNAL_LOW(PORT) PORTB &= ~(1 << PORT)
void delay();
void LED_ON(void);
void LED_OFF(void);
void i2c_init(void);
void i2c_start(void);
char i2c_read(void);
void i2c_stop(void);
void i2c_nack(void);
void i2c_ack(void);
void i2c_ack_slave(void);
void i2c_write(uint8_t byte);
void i2c_init()
{
DDRB = 0b00000010; // TODO: should be removed once the weird delay issue is solved
DDRB |= (1 << PORT_SDA);
DDRB |= (1 << PORT_SCL);
}
void i2c_start(void)
{
SIGNAL_LOW( PORT_SCL);
SIGNAL_HIGH(PORT_SDA);
SIGNAL_HIGH(PORT_SCL);
SIGNAL_LOW( PORT_SDA);
SIGNAL_LOW( PORT_SCL);
}
void i2c_stop(void)
{
SIGNAL_LOW( PORT_SCL);
SIGNAL_LOW( PORT_SDA);
SIGNAL_HIGH(PORT_SCL);
SIGNAL_HIGH(PORT_SDA);
}
void i2c_ack(void)
{
SIGNAL_LOW( PORT_SDA);
SIGNAL_HIGH(PORT_SCL);
SIGNAL_LOW( PORT_SCL);
SIGNAL_HIGH(PORT_SDA);
}
void i2c_nack(void)
{
SIGNAL_HIGH(PORT_SDA);
SIGNAL_HIGH(PORT_SCL);
SIGNAL_LOW( PORT_SCL);
}
void i2c_ack_slave(void)
{
SIGNAL_HIGH(PORT_SCL);
SIGNAL_LOW(PORT_SCL);
}
void i2c_write(uint8_t byte)
{
uint8_t bit;
for (bit = 0; bit < 0x08; bit++)
{
if((byte << bit) & 0x80)
SIGNAL_HIGH(PORT_SDA);
else
SIGNAL_LOW(PORT_SDA);
SIGNAL_HIGH(PORT_SCL);
SIGNAL_LOW(PORT_SCL);
}
// Clear both lines (needed?)
SIGNAL_LOW(PORT_SCL);
SIGNAL_LOW(PORT_SDA);
i2c_ack();
}
char i2c_read(void)
{
uint8_t B = 0;
DDRB &= 0b11111110; // switch PB0 to input
for (int bit = 0; bit < 0x08; bit++)
{
delay(); // <-- the root of all evil
SIGNAL_HIGH(PORT_SCL);
B <<= 1;
if(PINB & (1 << PB0))
{
B |= 1;
}
else
{
B |= 0;
}
SIGNAL_LOW(PORT_SCL);
}
DDRB |= 0b00000001; // switch PB0 as output
i2c_nack();
return B;
}
void delay()
{
LED_ON();
LED_OFF();
}
void LED_ON(void)
{
PORTB |= 0b00000010;
}
void LED_OFF(void)
{
PORTB &= 0b11111101;
}
Sie lesen im Wesentlichen die SDA-Linie an der steigenden Flanke der SCL-Linie; Vielleicht ist Ihr Gerät dann noch nicht bereit? Haben Sie versucht SDA näher an der fallenden Kante zu lesen? Das heißt, SIGNAL_HIGH (PORT_SCL); _delay_us (1); B = (B << 1) | !! (PINB & (1 << PB0)); SIGNAL_LOW (PORT_SCL); _delay_us (1); '(achtmal wiederholt)? –