Forum: Mikrocontroller und Digitale Elektronik ATtiny Watchdog


von Joni (jonquil)


Lesenswert?

Guten Abend,

momentan versuche ich, einen ATtiny13 schlafen zu legen und per Watchdog 
zu wecken. Im Prinzip funktioniert das auch, nur dass der Watchdog 
doppelt so lange läuft, wie erwartet.

Der ATtiny13 läuft mit internem Oszillator (9,6MHz) und CLKDIV8, was für 
den Watchdog aber keine Rolle spielen sollte.

Der Watchdog wird wie folgt eingstellt, wie ich es in einem Tutorial 
gefunden habe:
1
MCUSR &= ~(1 << WDRF);            // remove reset flag
2
WDTCR = (1 << WDCE);              // set WDCE to access prescaler
3
WDTCR = 1 << WDP3;                // set prescaler bits to 4s
4
WDTCR |= 1 << WDTIE;              // enable WDT interrupt
5
6
interrupts ();                    // same as sei()
7
sleep_cpu ();                     // sleep

Und damit schläft der Controller ca. 9-10 Sekunden statt der erwarteten 
4 Sekunden (Seite 49 auf 
https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny13A-Data-Sheet-DS40002307A.pdf).

Wo liegt mein Fehler? Danke für alle Tipps!

Viele Grüße
Joni

von Robert B. (crolexx)


Lesenswert?

Hallo,
eventuell solltest du den WDTIE erst aktivieren bevor du ihm die 
Prescaler einstellst.
Aber bitte nicht böse sein falls es nicht stimmt, ist bei mir schon 
etwas her und habe es seit damals nicht mehr verwendet (und dann auch 
noch fremder, angepasster, Arduino-Code):


Beitrag "Re: selbstblinkende SMD-LED mit niedrigem Stromverbrauch"

von Klaus H. (hildek)


Lesenswert?

Interessant, dass der Tiny13 WDCE braucht für den Prescaler. Ist nicht 
so beim Tinyx5 - nur nebenbei.

Vielleicht stört ihn
WDTCR = (1 << WDCE);
WDTCR = 1 << WDP3;
und man sollte
WDTCR |= (1 << WDCE);
WDTCR |= (1 << WDP3);
nehmen?
Zumindest die beim Prescaler, denn ich könnte mir vorstellen, dass er 
das gesetzte WDCE noch sehen will, das überschreibt man aber bei der 
Zuweisung.

Rätselhaft bleibt aber, warum er dann die längste Zeit nimmt.

von Uwe D. (monkye)


Angehängte Dateien:

Lesenswert?

Klaus H. schrieb:
> Interessant, dass der Tiny13 WDCE braucht für den Prescaler. Ist nicht
> so beim Tinyx5 - nur nebenbei.

Wirklich?

von Keks F. (keksliebhaber)


Lesenswert?

Klaus H. schrieb:
> und man sollte

Korrekt, da (Zeile 3) gehört ein |= hin, ansonsten überschreibt man das 
Register komplett, und setzt nicht nur das eine Bit, was man setzen 
möchte.
Ebenso sind die Klammern in Zeile 2 ohne Funktion.
Das Tutorial ist doof.

: Bearbeitet durch User
von Klaus H. (hildek)


Lesenswert?

Uwe D. schrieb:
> Wirklich?

Ja! Dein Bild zeigt es doch: WDCE braucht man nur um WDE auszuschalten.
Beim Tiny13 steht zusätzlich drin, dass man es auch fürs Ändern von WDPx 
setzen muss.

von M. K. (sylaina)


Lesenswert?

@Joni
Wie stellst du fest, dass der Attiny 9-10 us schläft? Der Watchdog ist 
auf 4s eingestellt und du hast den Watchdog Interrupt auch aktiviert, 
d.h. für einen Reset des Attinys muss der Watchdog zweimal auslösen, 
sprich er läuft dann 8s nominal was mit den beobachteten 9-10s recht gut 
korreliert.
Stellst du das Aufwachen über den Watchdog-Reset in deinem Test fest? 
Dann würde es passen und dein Denkfehler liegt darin begründet, dass du 
den Watchdog-Interrupt "vergessen" hast.
Auch denkbar, dass du im Watchdog-Interrupt einen Pin toggelst. Hast du 
bedacht, dass du das Interrupt-Bit des Watchdogs nach dem Auftreten des 
Interrupts erneut setzen musst? Auch dann würden nämlich die 9-10s 
passen zu deiner Beobachtung.

Keks F. schrieb:
> Korrekt, da (Zeile 3) gehört ein |= hin, ansonsten überschreibt man das
> Register komplett, und setzt nicht nur das eine Bit, was man setzen
> möchte.

Ist in dem Fall aber völlig egal. WDCE muss lediglich gesetzt sein, wenn 
man die WDPx-Bits setzen muss und das ist da der Fall. Dass man dann 
zeitgleich WDCE löscht ist für das Setzen der WDPx-Bits irrelevant und 
es muss ja geklappt haben da, hätte es nicht geklappt, der Watchdog 
sonst alle 16ms kommen würde, d.h. der Mikrocontroller müsste alle 16ms 
aufwachen und nicht alle 9-10s ;)

: Bearbeitet durch User
von Klaus H. (hildek)


Angehängte Dateien:

Lesenswert?

Klaus H. schrieb:
> Uwe D. schrieb:
>> Wirklich?
>
> Ja! Dein Bild zeigt es doch: WDCE braucht man nur um WDE auszuschalten.
> Beim Tiny13 steht zusätzlich drin, dass man es auch fürs Ändern von WDPx
> setzen muss.

@Uwe D.: Ich habe wohl gestern Abend deinen Ausschnitt nicht voll 
erfasst 🙄.
Ja, du hast recht, es steht auch beim Tinyx5 drin, trotzdem braucht man 
WDCE für den WD-Timer nicht.

Z.B. das im Anhang läuft so wie es soll, für Tiny25. (Für den Tiny13 
muss man WDIE durch WDTIE ersetzen) ohne WDCE zu verwenden. Und selbst 
wenn man nach einigen Zyklen die WDPs auf einen neuen Prescaler-Wert 
stellt, geht das ohne WDCE - gerade nochmal ausprobiert und in anderen 
Programmen auch mehrfach so verwendet.
Es ist im Datenblatt unklar ausgedrückt; tatsächlich ist WDCE nur bei 
aktiviertem WDE notwendig - ein Sicherheitsfeature, um weder die Zeiten 
noch den WD-Reset versehentlich zu löschen. Es wird (vermutlich) beim 
Tiny13 genauso sein.
Joni benötigt die zweite Zeile nicht und auch die erste ist redundant 
und nur für den WD-Reset relevant.

M. K. schrieb:
> WDCE muss lediglich gesetzt sein, wenn
> man die WDPx-Bits setzen muss und das ist da der Fall.
Siehe oben. Das kann beim Tiny13 so sein (kann ich mangels Device nicht 
testen). Der ist aber sehr ähnlich zum Tiny25, da steht es auch drin, 
trotzdem braucht man es nicht für den reinen Timerinterruptbetrieb.
Und, er hat den WDE gar nicht in Verwendung, also nur Timer zum Wecken, 
kein Reset. Außer er hätte die Fuse gesetzt.

Da Joni nur einen Programmausschnitt zeigt: es könnte auch ein ganz 
trivialer Grund sein: Er toggelt eine LED mit dem Interrupt und dann ist 
die 4s an und 4s aus und er wollte eigentlich eine Periode von 4s?

von Thomas E. (thomase)


Lesenswert?

Klaus H. schrieb:
> und man sollte
> WDTCR |= (1 << WDCE);
> WDTCR |= (1 << WDP3);
> nehmen?

Nein. Damit versaust du die Timed Sequence.

Nach Datenblatt:
1
void WDT_Prescaler_Change(void)
2
{
3
__disable_interrupt();
4
__watchdog_reset();
5
/* Start timed sequence */
6
WDTCR |= (1<<WDCE) | (1<<WDE);
7
/* Set new prescaler(time-out) value = 64K cycles (~0.5 s) */
8
WDTCR = (1<<WDE) | (1<<WDP2) | (1<<WDP0);
9
__enable_interrupt();
10
}

von Klaus H. (hildek)


Lesenswert?

Thomas E. schrieb:
> Nein. Damit versaust du die Timed Sequence.

Beim WDE in deinem Codeschnipsel braucht man die Timed Sequence, ja.

Ist jedoch nur WDIE aktiv, wie beim TO, dann nicht. Man kann dann den 
Teil
   WDTCR |= (1 << WDCE);
einfach weglassen!

In meinem Beispiel gibt es gar keine Timed Sequence; man setzt die 
Prescalerbits und aktiviert den Interupt Enable. Man kann auch 
irgendwann später die WDPx neu setzen für eine neue Zeit - alles ohne 
WDCE!

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

... nutze was da ist und funktioniert!
1
#include <avr/sleep.h>
2
#include <avr/wdt.h>
3
4
enum {
5
  WDT15MS=15, WDT30MS=30, WDT60MS=60, WDT120MS=120, WDT250MS=250,
6
  WDT500MS=500, WDT1S=1000, WDT2S=2000, WDT4S=4000, WDT8S=8000
7
};
8
9
ISR(WDT_vect) {
10
  bitSet(WDTCSR, WDIE); //re-enable isr
11
  ...
12
} 
13
14
//main
15
wdt_enable(WDTO_4S);
16
//bitSet() beliebig z.B. arduino lib 
17
bitSet(WDTCSR, WDIE); //interrupt on wdt event
18
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
19
sleep_enable();
20
while (1);
21
sleep_disable();
22
wdt_disable();
23
...
Nur Raten macht keinen Sinn, wenn man nicht lesen und verstehen kann!

von M. K. (sylaina)


Lesenswert?

Klaus H. schrieb:
> Ist jedoch nur WDIE aktiv, wie beim TO, dann nicht. Man kann dann den
> Teil
>    WDTCR |= (1 << WDCE);
> einfach weglassen!

Ich zitiere aus dem Datenblatt des ATTiny13:

>• Bit 4 - WDCE: Watchdog Change Enable
> This bit is used in timed sequences for changing WDE and prescaler
> bits. To clear the WDE bit, and/or change the prescaler bits,
> WDCE must be set.

Danach muss auch zum Ändern des Prescalers das WDCE-Bit gesetzt sein, 
vorher lässt sich WDPx nicht ändern.

Aber ohne konkrete Rückmeldung vom TE ist sowieso alles nur stochern im 
Nebel. Für mich klingt es schwer danach, dass WDE gesetzt sit oder das 
Fusebit für den Watchdog ist gesetzt. Dann macht das beobachtete 
Verhalten nämlich durchaus sinn ;)

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

...
wdt Handling erledigen die 3 Macros von wdt.h
wdt_reset(), wdt_disable(), wdt_enable(timeout)

.. und man kann es ohne oder mit wdt ISR machen.

von Klaus H. (hildek)


Lesenswert?

M. K. schrieb:
> Ich zitiere aus dem Datenblatt des ATTiny13:
Ja, kenne ich und steht beim auch Tiny25 so drin. Es ist imho einfach 
unklar im DB ausgedrückt: den Prescaler kann man trotzdem im 
WDIE-Betrieb ohne weiteres verändern. Nur nicht im WDE-Betrieb.

> Danach muss auch zum Ändern des Prescalers das WDCE-Bit gesetzt sein,
> vorher lässt sich WDPx nicht ändern.
Nach meinen Erfahrungen eben nur wenn WDE oder die Fuse aktiv sind. Da 
ist es auch sinnvoll - aus Sicherheitsgründen.

Oben habe ich ein korrekt laufendes Beispiel für den Tiny25 angehängt 
(watchdog2.c). Da wird weder WDCE noch WDE angefasst. Könnte man ja mal 
ausprobieren ... (Tiny13: WDIE durch WDTIE ersetzen)

> Aber ohne konkrete Rückmeldung vom TE ist sowieso alles nur stochern im
> Nebel.
Ja.

Apollo M. schrieb:
> wdt Handling erledigen die 3 Macros von wdt.h
> wdt_reset(), wdt_disable(), wdt_enable(timeout)
>
> .. und man kann es ohne oder mit wdt ISR machen.
Ich verstehe leider die Makros dort nicht gut genug, aber mir scheint, 
dass wdt_enable(timeout) auch WDE aktiviert. Das will ich üblicherweise 
nicht.
Auch in den Datenblattbeispielen ist immer WDE beteiligt.

von M. K. (sylaina)


Lesenswert?

Klaus H. schrieb:
> Ich verstehe leider die Makros dort nicht gut genug, aber mir scheint,
> dass wdt_enable(timeout) auch WDE aktiviert.

Ja, das ist richtig. wdt_enable() aktiviert auch WDE.

Klaus H. schrieb:
> Das will ich üblicherweise
> nicht.

Warum eigentlich nicht? Das ist doch eigentlich der übliche 
Einsatzzweck: Interrupt und System-Reset nutzen. Ich frage nur aus 
reinem Interesse, soll kein Vorwurf sein. Ich nutze auch idR den 
Interrupt + System-Reset. Sollte ich mal hängen bleiben und das WDIE-Bit 
nicht rechtzeitig wieder setzen gibts halt den Systemreset. In der ISR 
des WDT mache ich üblicher Weise übrigens nichts.

von Klaus H. (hildek)


Lesenswert?

M. K. schrieb:
> Ich frage nur aus reinem Interesse, soll kein Vorwurf sein.
Ich fasse das auch nicht so auf.

> Warum eigentlich nicht?
Nun, ich habe mehrere Programme 24/7 am laufen, z.T. seit Jahren, die 
eher weniger komplex sind. Welche, die mit 3 AAA oder einer LiIon und 
welche, die mit 5V Wandwarze bzw. mit eigenem Trafonetzteil betrieben 
werden.
Bisher hatte ich bei keinem einen Hänger gesehen, der den WD-Reset 
notwendig gemacht hätte. Und wenn dem so wäre, würde ich das auf einen 
HW- oder SW-Fehler zurückführen und würde die Ursache suchen.
Hardware ohne µC hat ja auch selten einen Watchdog und ist nicht 
zwangsweise unempfindlicher auf externe Störungen.
Ich verwende den WD-Interrupt meist als einfachen stromsparenden Timer, 
bei dem die Zeittoleranz des 128kHz Oszillators nicht stört, meist im 
Zusammenhang mit sleep. Zugegeben, diese Toleranz ist manchmal der 
Hauptnachteil.

Außerdem habe ich die Befürchtung, dass ich mit der Kombination aus 
Interrupt und Reset mehr Chancen für Programmierfehler habe, 😀 z.B. den, 
nicht rechtzeitig den WD zu quittieren oder in der ISR den WDIE neu zu 
setzen (wie ich hier schon gesehen habe).

> In der ISR des WDT mache ich üblicher Weise übrigens nichts.
Ich schon, meist habe ich da eine Variable, die den Mulitplikator für 
die Basiszeit darstellt und in der ISR dekrementiert wird. Wie auch in 
dem obigen Beispiel. Damit kann man 'endlos' lange Zeiten (grob 
natürlich) erreichen - endlos bez. der Lebenserwartung eines Menschen 😉.

von Peter D. (peda)


Lesenswert?

Klaus H. schrieb:
> Bisher hatte ich bei keinem einen Hänger gesehen, der den WD-Reset
> notwendig gemacht hätte.

Ist auch meine Erfahrung.
Ich habe es lieber, wenn mir der MC ordentlich auf die Finger haut, wenn 
ich einen SW-Fehler gemacht habe, damit ich ihn suchen und beheben kann.
Der Watchdog würde den Fehler nur verschleiern und der Kunde ärgert sich 
über den Schluckauf des Gerätes.

Gegen EMI hilft der Watchdog eh nicht, da kriegt er die hängende CPU 
nicht mehr raus, bzw. wenn durch EMI der Flash korrumpiert wurde.

von M. K. (sylaina)


Lesenswert?

Klaus H. schrieb:
> Bisher hatte ich bei keinem einen Hänger gesehen, der den WD-Reset
> notwendig gemacht hätte. Und wenn dem so wäre, würde ich das auf einen
> HW- oder SW-Fehler zurückführen und würde die Ursache suchen.

So ähnlich gehe ich dabei vor: Ich schau erst nach wenn ich merke, dass 
das Watchdog ständig das System resetet denn wenn der Watchdog mir den 
Mikrocontroller resetet habe ich sicher einen Zustand erreicht, den ich 
nicht bedacht hatte. Wir sind ja alle nicht fehlerfrei. Der Systemreset 
ist bei mir also mehr Angst als sonst was ;)

Den Interrupt des Watchdogs nutze ich idR auch nur um den 
Mikrocontroller aufzuwecken. Daher brauch ich im Interrupt auch nichts 
machen. Uhren und ähnliches, dafür hat der Mikrocontroller Timer die 
IMHO besser geeignet sind als der Watchdog. Klar, man kann dafür auch 
den Watchdog nutzen, keine Frage. Mir wäre der Watchdog dafür aber zu 
ungenau bzw. das wäre mir zu viel Gezumpel um das in adequate 
Zeitintervalle zu schubsen.

von Klaus H. (hildek)


Lesenswert?

@ M. K.
Ja, für Uhren ist das nicht geeignet, da braucht man Quarz und die 
Timer. Selbst das stellt nicht immer zufrieden; meist ist ein RTC besser 
...

Timer brauchen meist etwas mehr Strom (speziell bei Batteriebetrieb 
relevant) und Funktionen wie die eines Monoflops wie z.B. 
Treppenlichtautomat, limitierte Einschaltzeit für ein Spielzeug, ein 
sehr langsamer astabiler MV (keep alive für eine Powerbank z.B.) u.ä., 
selbst für eine langsam blinkende Melde-LED nutze ich das, all das hat 
selten hohe Anforderung an die zeitliche Präzision. Denn dann würde man 
es anders lösen, klar - immer schön abhängig von den Anforderungen.

Hier ging es aber um einen reinen Interruptbetrieb (so aus dem Code des 
TO zu entnehmen) und meiner Behauptung, dass man dann den WDCE nicht 
bemühen muss um den Prescaler zu setzen oder zu ändern.

> Wir sind ja alle nicht fehlerfrei.
Ja leider 😉.
> Der Systemreset ist bei mir also mehr Angst als sonst was ;)
Gut, jeder hat eine andere Vorgehensweise. Und wenn es nur selten mal 
rumpelt, dann sind unsere beiden Varianten schwer zu debuggen.

> das wäre mir zu viel Gezumpel um das in adequate
> Zeitintervalle zu schubsen.
Ich finde die Timer anstrengender zu programmieren. Aber auch wieder: 
jeder hat eine andere Vorgehensweise oder auch nur Gewohnheit ...

von Peter D. (peda)


Lesenswert?

Klaus H. schrieb:
> meist ist ein RTC besser

Ich hab auch gemerkt, daß ein Uhrenquarz am MC nicht das gelbe vom Ei 
ist. Frequenzen an anderen Pins (PWM, Multiplex) lassen den Quarz 
schnell ungenau werden.

Eine schöne externe RTC ist der RX8010. Quarz ist mit eingebaut, also 
kaum zu stören. Im SO-8 auch für ältere Leute noch gut zu löten und bei 
160nA mit nem Goldcap auch Monate zu stützen. Reichelt hat ihn lagernd.

von Joni (jonquil)


Lesenswert?

Danke für die vielen und hilfreichen Antworten. Jetzt funktioniert das 
auch. Ich habe jetzt diesen Code, der die erwarteten Timeouts liefert:
1
ISR(WDT_vect)
2
{
3
    MCUSR &= ~(1 << WDRF);        // remove reset flag
4
    WDTCR = (1 << WDTIF);         // clear flag, disable interrupt
5
    return;
6
}

und in loop():
1
    noInterrupts ();                    // cli()
2
    power_all_disable ();
3
    sleep_enable ();                    // ready to sleep
4
5
    wdt_reset();
6
    MCUSR &= ~(1 << WDRF);              // remove reset flag
7
    WDTCR = (1 << WDTIF) | (1 << WDCE); // enable change of prescaler,
8
                                        // clear flag
9
    WDTCR = (1 << WDTIE) | (1 << WDP3); // enable interrupt, set prescaler 
10
                                        // to 4s, disable reset
11
12
    interrupts ();                      // sei()
13
    sleep_cpu ();                       // sleep
14
15
    // wakeup here
16
    noInterrupts ();
17
    sleep_disable ();                   // precaution

von M. K. (sylaina)


Lesenswert?

return in einer ISR ist überflüssig. Ebenso das Flag löschen, das 
passiert alles automatisch wenn die ISR implementiert ist. Es genügt ein
1
ISR(WDT_vect){
2
}

zu schreiben. Tritt nun ein Interrupt auf wird die ISR angesprungen und 
die Automatismen werden die entsprechenden Flags löschen. Von Hand ist 
da nichts zu machen.

Das Löschen des Watchdogs Interupt Flag in loop() ist daher auch 
überflüssig und den Prescaler muss man auch nicht ständig neu setzen, 
das genügt ihn einmal zu setzen.

von Klaus H. (hildek)


Lesenswert?

Alles richtig, was du schreibst.
Auch die cli() und sei() Makros braucht man nicht, mit Ausnahmen eines 
sei() beim Setup. Naja, kann sein, dass er ein lange laufendes Programm 
hat und nur einmal für 4s schlafen lassen will. Das kann man aus dem 
Programmschnipsel nicht entnehmen.
Und das WDCE, wie oben ausführlich diskutiert, auch nicht.

M. K. schrieb:
> return in einer ISR ist überflüssig.
Ich bin mir nicht sicher, ob das nicht sogar schädlich ist. 
Normalerweise (in ASM) steht ein 'RETI' - Interrupt Return - am Ende der 
Routine.
Die macht der Compiler aber von sich aus.
-- Gerade mal getestet: nein, ist nicht schädlich; es wird vom Compiler 
entfernt. Er setzt nur ein 'RETI' unabhängig ob ein "return;" da ist 
oder nicht.

Der Vollständigkeit halber: Es gibt jedoch noch die Spezialvariante 
"ISR(XXXX_vect, ISR_NAKED);", die braucht (in C) aber ein "reti();" am 
Ende, kein "return;".

von Oliver S. (oliverso)


Lesenswert?

Klaus H. schrieb:
> die braucht (in C) aber ein "reti();" am
> Ende, kein "return;".

Kommt halt darauf an, ob man das I-Flag dabei wieder setzten will, oder 
nicht.

Oliver

von C-hater (c-hater)


Lesenswert?

Oliver S. schrieb:

> Kommt halt darauf an, ob man das I-Flag dabei wieder setzten will, oder
> nicht.

Wo sollte es sinnvoll sein, das nicht zu wollen? Einen "deferred 
handler" würde man sinnvollerweise aus der ISR aufrufen und vorher 
händisch sei() aufrufen.

Und alles andere konstruiert effektiv eine Art Trap. Der unterbroche 
Code (der unvorhersagbar ist) läuft weiter, aber mit gesperrten 
Interrupts. Wozu sollte sowas gut sein?

von Peter D. (peda)


Lesenswert?

Klaus H. schrieb:
> Die macht der Compiler aber von sich aus.
> -- Gerade mal getestet: nein, ist nicht schädlich; es wird vom Compiler
> entfernt. Er setzt nur ein 'RETI' unabhängig ob ein "return;" da ist
> oder nicht.

Ein return ist ja auch kein RETI. Es ist die Anweisung an den Compiler, 
springe zur letzten schließenden Klammer. Und da steht dann bei einem 
Interrupthandler der Epilog, der erstmal benutzte Register rettet und 
den Stack aufräumt.

von Klaus H. (hildek)


Lesenswert?

So tief bin ich in den Details nicht drin.
Der TO hat in seinem letzten Beispiel ein return in der ISR, was ich 
bisher noch in keinem Beispiel gesehen hatte. Mir war nur nicht klar, ob 
das ggf. schädlich sein könnte.

von Peter D. (peda)


Lesenswert?

Klaus H. schrieb:
> Der TO hat in seinem letzten Beispiel ein return in der ISR, was ich
> bisher noch in keinem Beispiel gesehen hatte.

Es kommt durchaus vor, daß man an mehreren Stellen den Interrupt 
verlassen möchte, z.B. beim I2C-Handler.
return ist ein Sprungbefehl in C, wie auch goto, break, continue.
In einer ISR geht return natürlich nur ohne Argument.

von Robert G. (robert_g311)


Lesenswert?

Peter D. schrieb:
> Ein return ist ja auch kein RETI. Es ist die Anweisung an den Compiler,
> springe zur letzten schließenden Klammer. Und da steht dann bei einem
> Interrupthandler der Epilog, der erstmal benutzte Register rettet und
> den Stack aufräumt.

Wie ist bitte die Anweisung "Springe zur letzten schließenden Klammer" 
zu verstehen? Ein RETI wie auch ein RET (return) springt zu der letzten 
Adresse die auf dem Stack steht. RETI aktiviert das globale 
"Interrupt-Enable" Flag wieder, RET nicht.

Zu genauer Erklärung empfehle ich das AVR Instruction Set Manual:
https://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf

Gruß

Robert

von Peter D. (peda)


Lesenswert?

Robert G. schrieb:
> Wie ist bitte die Anweisung "Springe zur letzten schließenden Klammer"
> zu verstehen?

Eine Funktion in C ist durch {} gekennzeichet:
1
void foo (void)
2
{
3
}

C Sprunganweisungen sind goto, break, continue und return.
RET, RETI sind aber Assemblersyntax, die versteht ein Compiler nicht.

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.