Forum: Mikrocontroller und Digitale Elektronik I2C - warten bis Bus frei


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,
ich mache gerade meine ersten Gehversuche mit dem I2C-Bus an einem 
ATmega32 und bin bis jetzt schon sehr zufrieden. Im Moment habe ich aber 
eine Phase, wo ich etwas ratlos bin.

Die folgende Routine soll nach allen Adressen am I2C scannen:
1
/* In main: */
2
  i2c_init();
3
  
4
  /* Check for I2C devices */
5
  for(uint16_t i=0x00; i<0xFF; i++) {
6
    uint8_t check = i2c_checkAddress((uint8_t) i);
7
8
    if(check==0) {
9
      snprintf(strOut,40,"I2C=%2x ",i);
10
      glcd_putstr(strOut);
11
    }
12
    _delay_ms(20); // <-- hier ist das Problem
13
  }
Wie man sieht, werden auch ungültige Adressen gescannt- und jede Adresse 
zweimal (einmal zum Lesen und einmal zum Schreiben) - aber erst einmal 
liegt mein Problem neben meinem nervigen 
Deutsch-Englisch-Mischkommentaren woanders.

Hier sind die I2C-Routinen:
1
/* I2C-Bus inititalisieren (ATmega32) */
2
void i2c_init(void) {
3
    /*set bus speed (ca. 100kHz bei 16MHz) */
4
    TWSR |= (0<<TWPS1)|(0<<TWPS0);
5
    TWBR = 0x50;
6
    
7
    /* Prepare Pins */
8
    DDRC &= ~((1<<DD0)|(1<<DD1));
9
    PORTC |= (1<<PC0) | (1<<PC1); // Pull-Ups extern, aber schadet auch nicht
10
}
11
12
13
/* Check if I2C Adress is present */
14
uint8_t i2c_checkAddress(uint8_t address) {
15
16
  uint8_t retVal = 0;
17
  
18
  // Test I2C Interface with PCF8574
19
  TWCR = ((1<<TWINT)|(1<<TWSTA)|(1<<TWEN)); // TWI aktivieren und Start-Condition ausloesen
20
  while(!(TWCR & (1<<TWINT) ));             // warten auf START-Condition
21
  if((TWSR & 0xF8) != TW_START)  return (TWSR & 0xF8); // Wenn keine Start-Conditions gesendet wurde abbrechen
22
  
23
  TWDR = address & (0xFE);                  // Adresse + Schreibbit ins Datenregister
24
  TWCR = ((1<<TWINT)|(1<<TWEN));            // senden
25
  while(!(TWCR & (1<<TWINT)));              // warten auf ACK oder NACK
26
  if((TWSR & 0xF8) != TW_MT_SLA_ACK) retVal =  2; // Wenn kein Slave reagiert abbrechen
27
  TWCR = ((1<<TWINT)|(1<<TWSTO)|(1<<TWEN)); // STOP condition
28
  return retVal;
29
}
Der Delay in der Scan-Schleife ist notwendig, ich weiß nur noch nicht so 
genau warum. Vermutlich ist der I2C noch nicht wieder im Ruhezustand, 
wobei mir noch nicht klar ist, warum- schließlich wird die 
STOP-Kondition schon gesendet worden sein.

Gibt es eine Möglichkeit festzustellen, daß der ATmega noch sendet, z.B. 
ein Statusbit? Leider finde ich gerade weder im Datenblatt noch im 
AVR-TWI-Artikel den richtigen Hinweis.

Viele Grüße
Nicolas

von Walter T. (nicolas)


Lesenswert?

P.S.: Ich weiß, daß das mit dem Hochzählen von i mit einem uint16_t eine 
Verschwendung ist; die 127 Adressen könnte ich auch mit
1
for(uint8_t i = 0; i<= 127; i++) {
2
   uint8_t check = i2c_checkAddress(i<<1);
3
    ...
4
}
erzeugen, aber darum geht es mir im Moment nicht.

Viele Grüße
Nicolas

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Es gibt eine definierte Prozedur, um einen 'ausser Tritt' geratenen I2C 
Bus zurückzusetzen, evtl. ist das etwas, das du nach einem reinen 
Address Scan machen solltest. Ich könnte mir vorstellen, das ein 
erfolgreich angesprochener I2C Baustein sich nicht so einfach mit einer 
Stop Condition "abspeisen" lässt, EEPROMs z.B. haben da eine recht 
merkwürdige Prozedur, und es halten sich trotz gegenteiliger Beteuerung 
nicht alle Hersteller exakt an die Vorgaben, vor allem, was abgebrochene 
Datensequenzen angeht.

Die Reset Routine hab ich nicht genau im Kopf, es werden allerdings 
mindestens 9 'leere' Clockpulse gesendet, soweit ich mich erinnere, 
eingeleitet von einer Stop Condition. Philips hat da m.W. was in ihrem 
Grundlagen Dokument zum I2C Bus.

von Walter T. (nicolas)


Lesenswert?

Hallo Matthias,
ich habe mir die I2C-Specs 
(http://www.nxp.com/documents/user_manual/UM10204.pdf) mal angesehen. 
Sehe ich das also richtig, daß ich nach einem Scanversuch noch einmal 
warten sollte, bis die STOP-Condition richtig gesendet wurde noch einmal 
eine RESET-Bedingung senden sollte?

Also so:
1
/* Check if I2C Adress is present */
2
uint8_t i2c_checkAddress(uint8_t address) {
3
4
  uint8_t retVal = 0;
5
  
6
  TWCR = ((1<<TWINT)|(1<<TWSTA)|(1<<TWEN)); // TWI aktivieren und Start-Condition ausloesen
7
  while(!(TWCR & (1<<TWINT) ));             // warten auf START-Condition
8
  if((TWSR & 0xF8) != TW_START)  return (TWSR & 0xF8); // Wenn keine Start-Conditions gesendet wurde abbrechen
9
  
10
  TWDR = address & (0xFE);                  // Adresse + Schreibbit ins Datenregister
11
  TWCR = ((1<<TWINT)|(1<<TWEN));            // senden
12
  while(!(TWCR & (1<<TWINT)));              // warten auf ACK oder NACK
13
  if((TWSR & 0xF8) != TW_MT_SLA_ACK) retVal =  2; // Wenn kein Slave reagiert abbrechen
14
  TWCR = ((1<<TWINT)|(1<<TWSTO)|(1<<TWEN)); // STOP condition
15
  while(!(TWCR & (1<<TWINT)));              // warten bis STOP gesendet
16
17
  return retVal;
18
}
und anschließend nach dem Durchprobieren nochmal ein 0x06 an alle (also 
0x00) senden? Oder eher den Soft-Reset nach jeder gescannten Adresse?

Viele Grüße
Nicolas

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Nicolas S. schrieb:
> Sehe ich das also richtig, daß ich nach einem Scanversuch noch einmal
> warten sollte, bis die STOP-Condition richtig gesendet wurde noch einmal
> eine RESET-Bedingung senden sollte?

Öhm, da fehlt irgendwie ein Wort? Aber ja, wenn du nur die I2C Adresse 
sendest und aufs ACK testest, solltest du nach einer STOP Bedingung 
nochmal die RESET Sequenz durchorgeln, denn der angesprochene Chip 
könnte evtl. SDA weiter auf low ziehen (ACK noch aktiv), wenn er die 
STOP Bedingung ignoriert. Wie bereits erwähnt sollten alle I2C Chips 
ein STOP jederzeit akzeptieren und den Bus freigeben, aber gerade bei 
EEPROMs und Software-implementierten I2Cs (Slave-MCs) passiert das nicht 
immer.
Bei erfolglosen Addressscans ist das natürlich nicht nötig, da besetzt 
ja niemand den Bus.

In meinen I2C Implementationen programmiere ich übrigens in
1
 while(!(TWCR & (1<<TWINT) ));
gerne ein Timeout mit rein, damit die Routine auf jeden Fall zu einem 
Ende kommt, dann mit Fehlerbehandlung, z.B. besagter RESET Prozedur, 
d.h. sie steckt in einem eigenen Unterprogramm.
.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.