Hallo allemiteinander! Mein erstes Post hier, bitte seid nachsichtig :)
(ich hab auch brav gesucht und keine Lösung/Erklärung im Netz gefunden)
Ich habe ein recht triviales Problem: Ich möchte einen AtTiny85 in einen
Low-Energy-Schlaf legen und über den Watchdog alle paar Sekunden
aufwecken.
Zusätzlich soll der µC über einen externen Interrupt (ein Buttom)
aufgeweckt werden können.
Beide Teile (Schlaf und Interrupt) laufen separat völlig tadellos, aber
setzt man beides zusammen, läuft alles nicht so wie es soll.
Ich habe jetzt lange herumprobiert und nun auch eine Lösung gefunden,
mir gefällt das aber nicht, weil ich nicht verstehe, WARUM der
Workaround geht (bei sowas bin ich immer skeptisch, weil Pfusch-Lösungen
oft böse Nebenwirkungen haben. Würde es gern verstehen)
Am Anfang vom Code ist ein define "UseWatchdog", damit kann man die zwei
Verhalten umschalten.
Ich habe im Code an zwei Stellen meine Fragen reingeschrieben.
Hier nochmal kurz:
1.) Wenn ich den µC via Watchdog schlafen lege, reagiert er plötzlich
nicht mehr auf den INT0 Interrupt. Mach ich die Pause via "delay"
(Define am Anfang vom Code auskommentieren), dann funktioniert das.
Ich muss explizit über MCUCR und GIMSK den INT0 aktivieren. Warum warum
warum??
2.) Und jetzt wird's völlig rätselhaft für mich:
In der Interrupt-Routine vom Button muss ich - wieder via GIMSK - den
externen Interrupt ausschalten.
Mache ich das nicht, dann hängt der µC.
Was zum Geier ist da denn nun los?? Wieder: Ohne Watchdog ist das nicht
nötig.
Könnt ihr mir helfen zu verstehen, was da los ist?
Hier der Code:
1
// Blink every 2 seconds, send proc to sleep in between
2
// If button is pressed -> wake up and blink 4 times quickly
Ralf S. schrieb:> MCUCR &= ~(_BV(ISC01) | _BV(ISC00)); // INT0 on low level
Damit definierst du INT0 pegelgetriggert ...
> attachInterrupt(IntNr, IntHandlerButton, FALLING);
... und damit vermutlich flankengetriggert (kenne den Arduino-Kram
nicht). Was im Powerdown mangels Takt nicht auslöst. Nur
pegelgetriggerte Interrupts und Pin Change Interrupts kommen ohne Takt
aus.
> In der Interrupt-Routine vom Button muss ich - wieder via GIMSK - den> externen Interrupt ausschalten.
Wenn pegelgetriggert, dann löst der Interrupt so lange erneut aus, bis
er abgeschaltet wird oder der Pin nicht mehr low ist.
A. K. schrieb:> ... und damit vermutlich flankengetriggert (kenne den Arduino-Kram> nicht). Was im Powerdown mangels Takt nicht auslöst. Nur> pegelgetriggerte Interrupts und Pin Change Interrupts kommen ohne Takt> aus.
Das stimmt nicht ganz.
INT2 vieler ATMegas ist nicht PCINT, kann die Teile oft aber durchaus
flankengetrieben aus den tiefsten (IO-taktlosen) Schlafzuständen
aufwecken. Ist offensichtlich so eine Art Vorfahr der PCINT-Geschichte,
nur besser, weil die Flanke konfigurierbar ist und der Interrupt
exklusiv für einen Pin ist.
Beim Mega328(P) allerdings tatsächlich nicht, weil es da INT2 schlicht
nicht gibt...
c-hater schrieb:> Das stimmt nicht ganz.
Beim hier betrachteten ATtiny85 schon.
Wobei man bei solchen Details stets das Datasheet des verwendeten
Controllers berücksichtigen sollte. Auch wenn Atmel einige Eigenschaften
quer durch die AVR Reihe gleich zu implementieren scheint, sollte man
sich nicht blind drauf verlassen.
A. K. schrieb:> Ralf S. schrieb:>> MCUCR &= ~(_BV(ISC01) | _BV(ISC00)); // INT0 on low level>> Damit definierst du INT0 pegelgetriggert ...>>> attachInterrupt(IntNr, IntHandlerButton, FALLING);>> ... und damit vermutlich flankengetriggert
Ok, stimmt. Das ist tatsächlich unterschiedlich.
> (kenne den Arduino-Kram> nicht). Was im Powerdown mangels Takt nicht auslöst. Nur> pegelgetriggerte Interrupts und Pin Change Interrupts kommen ohne Takt> aus.
Hm. Zu den Pegel-Interrupts hab ich gleich zwei Fragen:
1.) Verstehe ich das richtig: Der Interrupt unterbricht die Ausführung
in loop(), springt in die ISR, macht dort sein Ding, uns setzt dann die
Ausführung in loop() an genau der Stelle fort, wo er unterbrochen wurde,
oder?
2.) Kann in der ISR sofort noch mal der gleiche Interrupt ausgelöst
werden? (und wenn ja: wie schnell). Sprich: Wenn ich den Pegel-Interrupt
nehme, dann geht der Pin auf Low, die ISR wird angesprungen, dort das
erste Kommando ausgeführt ... der Pegel ist ja noch immer LOW, wird da
dann die Ausführung in der ISR unterbrochen und gleich noch mal der ISR
angesprungen? (dann müsste doch recht flott der Stack volllaufen, oder?)
Dann wär' der Pegel-Interrupt ja für nix.
Danke!
Ralf S. schrieb:> Kann in der ISR sofort noch mal der gleiche Interrupt ausgelöst> werden?
Nur wenn es der Programmierer erzwingt.
Ralf S. schrieb:> Dann wär' der Pegel-Interrupt ja für nix.
Stell dir mal vor: Der Programmierer kann den Pegelinterrupt auch sogar
deaktivieren.
Sollte er auch tun, zumindest dann solange, bis der Interrupt wieder
gebraucht wird.
Ralf S. schrieb:> Der Interrupt unterbricht die Ausführung> in loop(),
Nicht nur in loop(), sondern überall, wo, oder besser, wenn es erlaubt
ist.
Auch wenn deine Antwort auf mich sehr patzig wirkt (warum schreibst du
eigentlich so von-oben-herab?), hat sie doch geholfen.
Ich hab mal das Datasheet durchforstet und poste mal schnell meine
Erkenntnisse, nur für den Fall dass jemand mal das selbe Problem hat:
Arduino F. schrieb:> Ralf S. schrieb:>> Kann in der ISR sofort noch mal der gleiche Interrupt ausgelöst>> werden?> Nur wenn es der Programmierer erzwingt.
Scheinbar wird ein Interrupt-Flag (ich glaub in GIFR, weiss nimmer
genau) gelöscht wenn der Interrupt-Handler beendet wird. Vorher muss man
also nicht vor einem erneuten Auslösen Angst haben.
> Ralf S. schrieb:>> Dann wär' der Pegel-Interrupt ja für nix.> Stell dir mal vor: Der Programmierer kann den Pegelinterrupt auch sogar> deaktivieren.> Sollte er auch tun, zumindest dann solange, bis der Interrupt wieder> gebraucht wird.
Für den ArduinoUNO (hab grad keinen Tiny zum Testen) hiess das für mich
beim Auslösen vom Interrupt
EIMSK &= ~_BV(INT0);
und dann halt das Bit wieder setzen wenn man den Interrupt potentiell
wieder braucht.
Und zu meiner alten Frage:
Bei INT0 geht scheinbar nur der Level-Interrupt (und nicht FALLING)
weil, siehe Seite 49 vom Datasheet:
Note that recognition of falling or rising edge
interrupts on INT0 requires the presence of an I/O clock, described in
“Clock Systems and their Distribution” on
page 23.
Lg,
Ralf
Ralf S. schrieb:> (warum schreibst du eigentlich so von-oben-herab?)
Patzig, ok, aber von oben?
Ich bin nicht "oben"!
Als Arduino Jünger werde ich hier oft von oben herab behandelt.
Das ist der ganz normale Ton hier.
Und "Patzig", weil das doch alles in den Datenblättern und in gefühlten
1000 Tutorials zu finden ist.
Mir behagt das "Erst fragen und danach Datenblatt lesen" nicht so.
Ralf S. schrieb:> Scheinbar wird ein Interrupt-Flag (ich glaub in GIFR, weiss nimmer> genau) gelöscht wenn der Interrupt-Handler beendet wird. Vorher muss man> also nicht vor einem erneuten Auslösen Angst haben.
Nicht ganz richtig!
Damit keine anderen Interrupts dazwischen funken, wird in ISRs
jeglicher Interrupt gesperrt.
Suche mal nach: "avr sreg register"
> INT0 ... Level-Interrupt ...
Auf demselben Pin wie INT0 liegt PCINT2, wie wäre es damit? Zwar
triggert Letzterer, wenn ich mich recht erinnere, auf beide Flanken,
aber das sollte man in Griff bekommen können.
Ralf S. schrieb:> (ein Buttom)
Ist das eine Krankheit :-)
Ralf S. schrieb:> setzt dann die> Ausführung in loop() an genau der Stelle fort, wo er unterbrochen wurde,> oder?
Ja, wenn kein weiterer Interrupt (z. B. "Millis" beim Arduino)
dazwischen funkt.
Dieter F. schrieb:> Ralf S. schrieb:>> setzt dann die>> Ausführung in loop() an genau der Stelle fort, wo er unterbrochen wurde,>> oder?>> Ja, wenn kein weiterer Interrupt (z. B. "Millis" beim Arduino)> dazwischen funkt.
Zwischen 2 Interrupts, wird immer min. ein Maschinenbefehl des
Hauptprogramms ausgeführt.
Es ist also gewährleistet, dass selbst bei Interrupt Dauerfeuer, die
Hauptschleife weiter läuft. Und sei es auch nur im Schneckentempo.