Forum: Mikrocontroller und Digitale Elektronik AVR Sleep Modes und TWI


von Manfred L. (manni)


Angehängte Dateien:

Lesenswert?

Hallo,
ich fahre einen ATmega8 mit 2 MHz Quarz und +5V Versorgungsspannung.
Angeschlossen ist eine I2C Real Time Clock, RTC (PCF8563). Die TWI 
bitrate beträgt 20.000 bit/s. Der TWI Bus besitzt 2 x 10 KOhm Pull-up 
Widerstände. Die Kommunikation mit der RTC funktioniert tadellos. Der 
Alarm-Teil der RTC erzeugt nach jeder Minute einen low level interrupt, 
der an INT1 angeschlossen ist.

Jetzt das Problem:
Wenn die RTC "scharf" gemacht wurde, versetze ich den ATmega8 in den 
Power-Down mode. Nach einer Minute weckt die RTC via INT1 den ATmega8 
wieder auf.

--> Danach soll die RTC wieder scharf gemacht und der ATmega8 wieder in 
den Power-Down mode versetzt werden. Das funktioniert aber nicht mehr, 
denn die TWI Kommunikation ist tot, bzw hängt.

Durch debuggen habe ich folgendes heraus gefunden:
- Vor dem Schlafen legen hat das TWCR register einen Wert von 0x20 oder 
0x04, je nachdem welche TWI Operation vorher statt gefunden hat - so wie 
es sein soll.
- Unmittelbar nach dem Aufwachen hat das TWCR register einen Wert von 
0x84, d.h. das TWINT und das TWWC bit ist gesetzt.

Das TWWC (TWI Write Collision Flag) besagt: "The TWWC bit is set when 
attempting to write to the TWI Data Register – TWDR when TWINT is low.". 
Dabei hat während der Power-Down Phase keinerlei TWI write Aktion statt 
gefunden.

Es ist auch schon mal passiert, dass die CPU nach dem Aufwachen einen 
Reset durchgeführt hat.

Meine ad hoc Lösung: Bevor ich wieder das TWI nach dem Aufwachen 
verwende, setze ich das TWCR = 0.

Im Anhang die entsprechenden Code-Schnipsel als txt file.

Frage: Ist das ein Hardware Fehler des ATmega8, den bisher keiner 
bemerkt hat, oder habe ich etwas übersehen, oder in der Doku noch nicht 
verstanden ?

Grüße Manni

von Peter D. (peda)


Lesenswert?

Manfred L. schrieb:
> Im Anhang die entsprechenden Code-Schnipsel als txt file.

Was soll die Schnipselei, da sieht doch keiner durch.
Wo ist die Mainloop?
Poste echten Code und nenne ihn auch richtig (*.c), sonst funktioniert 
die Darstellung nicht.

von c-hater (Gast)


Lesenswert?

Manfred L. schrieb:

> Dabei hat während der Power-Down Phase keinerlei TWI write Aktion statt
> gefunden.

Während des PowerDown sicher nicht. Aber wohl beim Einschlafen oder beim 
Aufwachen. Mehr kann man angesichts des unvollständigen Quelltextes 
nicht sagen.

Ähem doch: die Sequenz zum Einschlafen ist schlicht falsch, sie enthält 
die Möglichkeit für eine race condition. Ob das jetzt die Ursache des 
TWI-Problems ist, kann man allerdings (wieder mangels vollständigem 
Code) nicht sagen.

> Es ist auch schon mal passiert, dass die CPU nach dem Aufwachen einen
> Reset durchgeführt hat.

Das ist auf jeden Fall ein sicherer Hinweis auf entweder einen kapitalen 
Bug oder Hardwareprobleme.

von Manfred L. (manni)


Angehängte Dateien:

Lesenswert?

Peter D. schrieb:
> Was soll die Schnipselei, da sieht doch keiner durch.

Entschuldige bitte, wenn ich gegen die Netiquette im Forum verstoßen 
haben sollte. Bin erst seit 2005 dabei. Ich wollte Euch und das Forum 
nicht mit Sinnlosem vollmüllen.

Wie von Dir gewünscht, in der zip file Anlage alle Sourcen der .c und .h 
files. Ebenso eine Schematics der verwendeten Hardware.

Ich hoffe, das hilft weiter :-)

von Manfred L. (manni)


Lesenswert?

c-hater schrieb:
> Das ist auf jeden Fall ein sicherer Hinweis auf entweder einen kapitalen
> Bug oder Hardwareprobleme.

Das Reset Problem hat sich als Wackelkontakt erledigt.

Der vollständige Source Code ist im oberen Post an Peter beigelegt.

Aber ich danke Dir für den Hinweis zum Thema "race condition". Dabei bin 
ich auf die Seite:

http://www.gammon.com.au/interrupts

gestoßen. Unter dem Heading "How are interrupts queued?" findet man hier 
noch weitere Lösungen für den Sleep mode und das Aufwachen durch 
interrupts. Habe dies aber noch nicht im Detail getestet.

von S. Landolt (Gast)


Lesenswert?

> Unmittelbar nach dem Aufwachen hat das TWCR register
> einen Wert von 0x84, d.h. das TWINT und das TWWC bit
> ist gesetzt

TWWC? Ich lese im Datenblatt TWINT und TWEN.

von Oliver S. (oliverso)


Lesenswert?

Manfred L. schrieb:
> Unter dem Heading "How are interrupts queued?" findet man hier
> noch weitere Lösungen für den Sleep mode und das Aufwachen durch
> interrupts. Habe dies aber noch nicht im Detail getestet.

Kannst du machen, ist sicher auch interessant, hat aber nichts mit 
deinem Problem zu tun.

Das wichtigste Rat steht übrigends ganz unten in dem Beitrag:

"Read the data sheet!"

Oliver

von c-hater (Gast)


Lesenswert?

Manfred L. schrieb:

> Habe dies aber noch nicht im Detail getestet.

Neben diesem Problem ist auch das Handling des Level-Interrupts stark 
verbessungswürdig. Auf der sicheren Seite bist du nur, wenn du ihn 
bereits in der ISR verbietetest.

Ansonsten wird diese bei einem Aufwachen deutlich mehr als nur einmal 
ausgeführt...

Außerdem ist mir aufgefallen, dass du vor dem Einschlafen weder testest, 
ob deine TWI-Operation vollständig abgeschlossen ist, noch ob deine 
UART-Ausgabe vollständig abgeschlossen ist. Beides muß gegeben sein, 
sonst versteht der jeweilige Peer nur noch "gnülpft", denn beides hält 
beim Powerdown erstmal stumpf an.

Ganz prekär ist das beim TWI, den das enthält auch asynchrone Logik, die 
zwar als Master nicht relevant ist, aber nichtsdestotrotz sicherlich in 
irgendeiner Form beim Einschlafen "angefasst" wird.

von Jack V. (jackv)


Lesenswert?

Manfred L. schrieb:
> Bin erst seit 2005 dabei. Ich wollte Euch und das Forum
> nicht mit Sinnlosem vollmüllen.

Sorry für OT: besser wäre, die Quelltexte und Header mit korrekter 
Endung anzuhängen. Die Forensoftware kann’s dann hübsch mit 
Syntax-Highlighting und so darstellen, so dass es für potentielle Helfer 
erheblich angenehmer und schneller ist, sich das anzuschauen. Das 
ganze Geraffel in ein Archiv zu packen, das man sich dann erstmal laden 
und auspacken muss, war ein Schritt in genau die falsche Richtung.

von Einer K. (Gast)


Lesenswert?

Jack V. schrieb:
> das man sich dann erstmal laden
> und auspacken muss, war ein Schritt in genau die falsche Richtung.

Ich finde beides gut!
OK, es ist eher so, dass es schon etwas nervt, wenn das gerade bequeme 
fehlt.

Zum anschauen ist es schön alle Dateien im Foren Erleuchter zu 
erreichen.

Will ich das Projekt testen, macht eine *.zip mehr Spaß.
Je mehr Dateien drin sind, desto mehr Spaß.
Viele Einzeldateien im Erleuchter sind dann eher Unspaß.

von c-hater (Gast)


Lesenswert?

Jack V. schrieb:

> Das
> ganze Geraffel in ein Archiv zu packen, das man sich dann erstmal laden
> und auspacken muss, war ein Schritt in genau die falsche Richtung.

Nö. Das ist genau richtig. So kann man sehr schnell ein Projekt daraus 
machen und dieses (hoffentlich) kompilieren und (hoffentlich) laufen 
lassen.

Ich habe keine zwei Minuten gebraucht, um aus dem Archiv ein solches 
Projekt zu machen. Syntax-Highlighting habe ich dann auch in der lokalen 
Version.

Und ich weiß, dass es ohne Fehler und Warnungen kompiliert, der Schöpfer 
des Projektes also zumindest mit einiger Wahrscheinlichkeit die 
verwendete Sprache hinreichend beherrscht. Ich kann mich also bezüglich 
der Fehleranalyse voll auf den sachlichen Gehalt der Software 
konzentrieren.

Ich habe aber noch sehr viel mehr, z.B. ein *.lss-File, was mir zeigt, 
was wirklich passiert, wenn an irgendeiner Stelle Zweifel bezüglich 
irgendeiner Sache bestehen, die einfach nicht im C-Quelltext nachgelesen 
werden kann. Was u.a. bei der Pennerei nicht ganz unwichtig ist...

von Manfred L. (manni)


Lesenswert?

c-hater schrieb:
> Neben diesem Problem ist auch das Handling des Level-Interrupts stark
> verbessungswürdig. Auf der sicheren Seite bist du nur, wenn du ihn
> bereits in der ISR verbietetest.

Ich danke Dir auch hier für den wertvollen Hinweis. Die ISR habe ich 
daraufhin wie folgt geändert:
1
ISR (INT1_vect)
2
{
3
cli ();
4
CLEAR_BIT (GICR, INT1);
5
}

c-hater schrieb:
> Außerdem ist mir aufgefallen, dass du vor dem Einschlafen weder testest,
> ob deine TWI-Operation vollständig abgeschlossen ist, noch ob deine
> UART-Ausgabe vollständig abgeschlossen ist.

Auch dieser Hinweis ist vollkommen richtig! Meines Erachtens habe ich 
dies in allen TWIM_xxxx Funktionen (file TWI_Master.c) so implementiert 
habe, z.B. in TWI_Write()
1
/*
2
** Wait until transmission completed
3
*/
4
while (!(TWCR & (1<<TWINT)));
Somit sollte doch sicher gestellt sein, dass beim Verlassen der Funktion 
die TWI Aktion für den Master abgeschlossen ist.

Bei der UART Ausgabe prüfe ich jetzt im UCSRA Register, ob das TXC Bit 
gesetzt ist. Somit sollten allen Aktiviäten der CPU abgeschlossen sein, 
bevor es in den sleep mode geht.

Leider steht im TWCR Register nach dem wake-up immer noch der Wert 0x84 
(TWINT=1, TWEN=1), aber vor dem Sleep mode war es 0x04 (TWEN=1), was es 
auch sein sollte.

Mit meiner Krücke --> TWCR=0 läuft die CPU jetzt schon seit mehr als 
zwei Tagen kontinuierlich durch, ohne sonstige Fehler oder Mucken. Aber 
trotzdem wurmt es mich, denn es darf nicht sein, was nicht sein kann !

von c-hater (Gast)


Lesenswert?

Manfred L. schrieb:

> while (!(TWCR & (1<<TWINT)));[/c]
> Somit sollte doch sicher gestellt sein, dass beim Verlassen der Funktion
> die TWI Aktion für den Master abgeschlossen ist.

Nein, das ist leider nicht der Fall. Das letzte, was du bei einer 
Transaktion normalerweise tust, ist das Absetzen der STOP condition. 
Deren Erscheinen auf dem Bus geschieht aber einerseits nicht instantan, 
wird aber andererseits auch nicht durch TWINT zurückgemeldet.

Du mußt also vor dem Einschlafen zusätzlich auf das "Verlöschen" von 
TWSTO warten.

von Manfred L. (manni)


Lesenswert?

Hallo,

Problem detektiert und behoben !

Problem war:
Wenn die CPU (ATmega8) in den sleep mode geschickt und durch einen INT1 
interrupt wieder aufgeweckt wurde, funktionierte der TWI (I2C) Link 
nicht mehr. Vorher funktionierte der Link noch. Hier arbeitet die CPU 
als Master.


Ursache ist:
Bei allen TWI Operationen (Conditions) wird das TWEN Bit im TWCR 
Register auf TRUE gesetzt, z.B.:
1
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTA);  // START Condition
2
TWCR = (1<<TWINT)|(1<<TWEN);  // WRITE Operation
3
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);  // READ Operation hier mit ACK
4
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);  // STOP Condition

Wenn diese Operationen durch eine STOP Condition beendet wurden, bleibt 
das TWEN Bit stets als einziges Bit im TWCR gesetzt. Die CPU nimmt 
vermutlich an, dass noch weitere TWI Operationen folgen, weil halt das 
TWI I/F noch ENabled ist. In diesem Zustand darf die CPU anscheinend 
nicht in den sleep mode geschickt werden, denn nach dem Aufwachen ist im 
TWCR Register neben dem TWEN Bit auch noch das TWINT Bit gesetzt worden, 
von wem auch immer.

Die Lösung:
Da am Ende einer jeden TWI Operation die STOP Condition durchgeführt 
wird, ist danach das TWCR Register auf 0 zu setzen, d.h. das TWI I/F 
wird disabled. Danach kann in den sleep mode gegangen werden und nach 
dem Aufwachen steht im TWCR Register immer noch eine 0 und die nachste 
TWI Operation läuft ohne Fehler durch.

Die überarbeitete STOP Condition Prozedur sieht dann wie folgt aus:
1
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
2
while (TWCR & (1<<TWINT));
3
while (TWCR & (1<<TWSTO));
4
TWCR = 0;

Ich bedanke mich bei allen für die hilfreichen Kommentare, speziell bei 
c-hater !

Grüße Manni

Beitrag #6325915 wurde von einem Moderator gelöscht.
von Oliver S. (oliverso)


Lesenswert?

Manfred L. schrieb:
> Wenn diese Operationen durch eine STOP Condition beendet wurden, bleibt
> das TWEN Bit stets als einziges Bit im TWCR gesetzt. Die CPU nimmt
> vermutlich an, dass noch weitere TWI Operationen folgen, weil halt das
> TWI I/F noch ENabled ist. In diesem Zustand darf die CPU anscheinend
> nicht in den sleep mode geschickt werden, denn nach dem Aufwachen ist im
> TWCR Register neben dem TWEN Bit auch noch das TWINT Bit gesetzt worden,
> von wem auch immer.

Das sind sehr viele angenommene Vermutungen. So lange im Datenblatt 
nichts zu den Themen steht, ist das aber alles eher weniger oder auch 
ganz bestimmt nicht zutreffend.

Ein leicht zu übersehendes Problem des AVR-TWI ist, daß der Zyklus für 
das Modul nach Senden eines STOP am Ende der Kommunikation erst nach 
Ablauf der für ein Stop erforderlichen Haltezeit beendet ist. Die Bits 
im Konfigurationsregister werden aber sofort gesetzt.

Oliver

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Nachtrag:

Hatte ich übersehen:

c-hater schrieb:
> Du mußt also vor dem Einschlafen zusätzlich auf das "Verlöschen" von
> TWSTO warten.

So isses.

Oliver

Beitrag #6330417 wurde von einem Moderator gelöscht.
von Manfred L. (manni)


Lesenswert?

Oliver S. schrieb:
> Das sind sehr viele angenommene Vermutungen. So lange im Datenblatt
> nichts zu den Themen steht, ist das aber alles eher weniger oder auch
> ganz bestimmt nicht zutreffend.

Das ist richtig !
Da im Datenblatt dazu nichts steht, sind praktikable Lösungen gesucht, 
die ich im Post vom 03.07.2020 18:48 dargestellt habe. Diese Lösung 
läuft jetzt seit 4 Tagen durch und zeigt keine Mucken (4 x pro Sekunde)

Oliver S. schrieb:
> So isses.
Dazu warte ich 0.1 Sekunden, bevor ich den Sleep Modus einschalte. In 
dieser Zeit ist die STOP Condition durch die CPU mit Sicherheit 
abgearbeitet.

Danke für die Kommentare
Manni

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.