Forum: Mikrocontroller und Digitale Elektronik ATMEGA328 - Problem mit I2C Startbedingung


von M. G. (ixil96)


Lesenswert?

Hallo!

Ich möchte einen I2C Sensor mit einem ATMega328 auslesen.
Leider bleibt mein Programm bei der Startbedingung in der Zeile

while(!(TWSR&0xF8) != START);    // START = 0x08

hängen. Kann mir jemand einen Tip geben?


Hier meine Funktion für die Startbedingung:
1
/*** Function to send a START Condition ***/
2
void TWI_START(void)
3
{
4
  // Send a START condition
5
  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
6
  // Wait for TWINT Flag set. This indicates that the START condition has been transmitted
7
  while (!(TWCR & (1<<TWINT)));
8
  // Check value of TWI statusregister. Mask prescaler bits. If status different from START go to ERROR 
9
  if ((TWSR & 0xF8) != START)
10
    ERROR();
11
}

von Stefan F. (Gast)


Lesenswert?

Die TWI Schnittstelle ist recht kompliziert zu benutzen. Ich denke, es 
kann sehr hilfreich sein, zumindest bei den ersten Versuchen das I²C 
Protokoll "manuell" zu implementieren, indem du zwei IO Pins direkt 
ansteuerst (Bit-Banging).

Bei mir hat das bisher immer so gut geklappt, dass ich den Code danach 
gar nicht mehr auf TWI ändern wollte. Ich habe ca. 15 Projekte mit 
Bit-Banging gemacht, aber nur 2 mit TWI.

von noch ein anderer (Gast)


Lesenswert?

Stefan U. schrieb:
> Die TWI Schnittstelle ist recht kompliziert zu benutzen. Ich denke, es
> kann sehr hilfreich sein, zumindest bei den ersten Versuchen das I²C
> Protokoll "manuell" zu implementieren, indem du zwei IO Pins direkt
> ansteuerst (Bit-Banging).
>
> Bei mir hat das bisher immer so gut geklappt, dass ich den Code danach
> gar nicht mehr auf TWI ändern wollte. Ich habe ca. 15 Projekte mit
> Bit-Banging gemacht, aber nur 2 mit TWI.

Natürlich geht das "manuell" besser, weil entscheidende Aktionen auf dem 
TWI damit übergangen werden können und eine Fehlermeldung dafür dann 
nicht existiert. Das funktioniert zwar bei den meisten I2C-Chips, jedoch 
nicht immer. Ist also nicht wirklich eine Lösung.

Der TWI ist eigentlich gar nicht so kompliziert wie er aussieht. Die TWI 
Schnittstelle im ATmega ist sehr brauchbar. Alle Aktionen die auf dem 
TWI Bus stattfinden werden durch das TWINT Flag gemeldet. Am besten ist, 
den Interrupt TWIE in TWCR setzen. Jedes mal wenn auf dem TWI eine 
Aktion stattfindet, wird dann ein Interrupt ausgelöst. In der Interrupt 
Routine lädst du das Statusregister TWSR und blendest die ersten 3 Bit´s 
aus. Weil diese ersten 3 Bit´s andere Aufgeben haben! Also Bit0, Bit1 
und Bit2 ausblenden! Dann bleibt ein Wert in den restlich verbliebenen 
Bit´s übrig, der dann eine Art "Hausnummer" für die gerade erkannte 
Aktion auf dem TWI darstellt. Ein Beispiel:
Bleibt nach Ausblenden der unteren 3 Bit´s hexadezimal 60 (also 0x60) im 
TWSR Register übrig, dann wurde die Adresse für Schreiben empfangen und 
wenn die empfangene Adresse mit dem Wert im Adressregister TWAR 
übereinstimmt, wird ein ACK zur Bestätigung vom TWI automatisch 
generiert. Würde aber z.B. im Statusregister TWSR der Wert 0x80 übrig 
bleiben, so wären Daten empfangen wurden, die nun aus dem TWDR Register 
ausgelesen werden können und es wurde ein ACK gesendet, was bedeutet, es 
waren noch nicht die letzten Daten. Bei dem Wert 0x88 wären dann Daten 
empfangen wurden die aber kein ACK hatten. Das wäre der Fall wenn der 
Master damit das Ende des Datensendens kenntlich macht.

Also du siehst, es ist gar nicht so schwer, wenn man es verstanden hat. 
Sobald eine Aktion auf dem TWI stattfindet, dann wird ein IRQ ausgelöst, 
nachgesehen welcher Wert im TWSR Register steht und dann anhand dieses 
Wertes in die entsprechend benötigte Routine gesprungen, die dann z.B. 
dem Datenregister TWDR das benötigte Byte übergibt, oder in einer 
anderen Routine den empfangenen Wert aus dem TWDR Register holt.
Wichtig bei dem TWI ist es aber immer, nach dem setzen des TWINT Flags 
durch eine Aktion auf dem TWI Bus, dieses auch wieder per "Hand" zu 
löschen. Also eine "1" darauf schreiben! Denn das TWINT wird nicht 
automatisch zurück gesetzt.

Wenn du den TWI nutzt, dann kannst du auch alles was sich I2C nennt auch 
anschließen! Mehr Informationen über die Werte in dem TWSR Register und 
deren Bedeutung findest du in den Datenblättern und auch hier im Forum.

Noch ein Tipp. Wenn man den TWI soweit programmiert hat und ein 
"Testprogramm" erstellt, wo gleich nach dem "Stop" sofort wieder ein 
neues "Start" folgt, kann es sein, das der TWI das "Stop" noch gar nicht 
fertig abgearbeitet hat, weil der Clock des TWI nur 100kHz oder 400kHz 
beträt, der Takt für den ATmega aber 8MHz oder mehr und der neue "Start" 
noch nicht generiert werden kann. Deshalb sollte man bei einem solchen 
"Testprogramm" immer das Timing des TWI nicht aus den Augen verlieren! 
Auch gibt es den "Restart" der nicht umsonst existiert und in manchen 
Protokollen auch dringend benötigt wird!

von c-hater (Gast)


Lesenswert?

noch ein anderer schrieb:

> Der TWI ist eigentlich gar nicht so kompliziert wie er aussieht. Die TWI
> Schnittstelle im ATmega ist sehr brauchbar. Alle Aktionen die auf dem
> TWI Bus stattfinden werden durch das TWINT Flag gemeldet.

Das ist leider nicht richtig (ich wünschte, es wäre tatsächlich so, wie 
du schreibst, das würde vieles vereinfachen).

> Noch ein Tipp. Wenn man den TWI soweit programmiert hat und ein
> "Testprogramm" erstellt, wo gleich nach dem "Stop" sofort wieder ein
> neues "Start" folgt, kann es sein, das der TWI das "Stop" noch gar nicht
> fertig abgearbeitet hat

Genau das ist der Knackpunkt: das erfolgreiche Erreichen von 
Stop-Zustands wird entgegen der sonstigen durchdachten Logik nicht per 
Interrupt gemeldet, sondern ist nur per Polling von TWSTO zu ermitteln.

Unterläßt man aber die Verifizierung von Stop, knallt es im Problemfall 
beim nächsten Start, also genau dann, wenn man üblicherweise den Bus 
dringend benötigt. Vorher hätte man aber vielleicht viele Stunden Zeit 
gehabt, den Bus "wiederzubeleben", wenn man denn nur gewusst hätte, dass 
er tot ist...

Also: man braucht immer einen Timer-Interrupt zusätzlich, um die Sache 
komplett interruptgesteuert abwickeln zu können. Nunja, der hilft (mit 
der entsprechenden Behandlung in der state machine) auch gegen viele 
andere denkbare Probleme auf dem I2C-Bus, ist also sowieso dringend 
empfehlenswert. Aber das fehlende Interrupt-Event für "Stop erreicht" 
macht die state machine deutlich komplexer, als sie sein könnte, wenn es 
das Event gäbe.

Weil man halt im Timer-Interrupt TWSTO pollen muß, das aber wiederum nur 
dann, wenn ein Stop überhaupt intendiert war, was wiederum eine 
Hilfsvariable erfordert, damit die state machine das zuverlässig wissen 
kann, während alles andere problemlos allein aus den Registern der 
I2C-Hardware zu entnehmen wäre...

Irgendwie Mist. Aber eine bessere Lösung ist mir bisher nicht 
eingefallen.

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.