Hallo liebe Community,
bin mir jetzt nicht sicher, ob das hier das richtige Forum ist. Ich
versuche es einfach mal und bitte um Nachsicht.
Ich würde das Problem gerne verstehen und beheben. Antworten, dass
Arduino nicht ernst zu nehmen sei oder man das doch lieber komplett in C
macht, sind ein anderes Thema und nicht zielführend.
Zum Verständnis habe ich mein Programm auf das wesentliche abgespeckt.
Das Programm schläft meistens, wird von einem Tastendruck (externer
Interrupt INT0) oder einem TMR2-Interrupt aufgeweckt. Die jeweilige
Aufweckoption wird im Programm gewählt. Grundsätzlich funktioniert das
auch, aber...
Wenn der externe Interrupt zum ersten Mal ausgeführt wird, funktionert
der Timer Interrupt das erste Mal danach nicht mehr. Dann muss das
Aufwachen durch einen Tastendruck geschehen. Das nächste Mal
funktioniert der Timer Interrupt dann.
Als quick and dirty Lösung habe ich deshalb in Setup() den
Timer-Interrupt aktiviert. Der wird auch ausgeführt.
Nach dem nächsten externen Interrupt (Taster) geht der Timer Interrupt
aber nicht mehr und der Controller muss durch einen Tastendruck geweckt
werden.
Hier der komplette Code:
1
#include<avr/sleep.h>
2
3
// **************** Definitions ****************
4
5
// Push Button (wired to interrupt pin and regular digital pin)
6
#define BUTTON_INT 2
7
#define BUTTON 7
8
9
// Timer
10
#define TIMER_INTERVAL 3
11
12
// **************** Global Variables ****************
13
volatileboolbuttonPress=false;
14
volatileboolbuttonInt=false;
15
volatilebooltimerInt=false;
16
volatileunsignedlongtimerCnt=0;
17
18
unsignedinttimeoutSeconds=0;
19
volatilebooltimeoutFlag=false;
20
21
enumwakeupSources
22
{
23
EXTERNAL_INTERRUPT,
24
TIMEOUT_INTERRUPT
25
};
26
27
28
// **************** Sleep and Wakeup, Interrupts and Timer2 ****************
Hier sieht man, dass der Timer-Interrupt in Setup() funktioniert, nach
dem ersten externen Interrupt nicht mehr. Das zeigt der dritte Absatz,
in dem auf einen Timer-Interrupt gewartet wird, der aber nicht kommt und
deshalb der Taster gedrückt werden muss.
*enter sleep, wait for TMR INT... iB-Wakeup*
button
Die folgenden Timer-Interrupts funktionieren dann alle, das ist hier
aber nicht gezeigt. Der Beitrag ist auch so schon lang genug...
Ich hoffe, das ist trotz der Länge einigermaßen verständlich und hier
kann mir jemand helfen, ganz vielen Dank schon mal!
LG
Arlenne
Irgendwie scheint es so, als ob der erste Aufruf des externen Interrupts
den Timer Interrupt so beeinflusst, dass letzterer dann nicht mehr
funktioniert. Aber eben nur das erste Mal!?
bei "enterSleep" musst du sicher den vorherigen Spleemode deaktivieren,
um den jeweils anderen aktivieren zu können. Ausm Deep-Sleep geht ja nur
der Low-Level INT am Button.
Denke, dass es dort "hakt", bei der Umschaltung der beiden Sleep-Modi.
Viel Erfolg!
Danke für den guten Hinweis, ich schaue mir das später am Nachmittag an
und gebe dann Rückmeldung. Das hört sich nämlich wirklich nach einer
vielversprechenden Spur an.
Arlenne schrieb:> Die folgenden Timer-Interrupts funktionieren dann alle, das ist hier> aber nicht gezeigt. Der Beitrag ist auch so schon lang genug...
Richtig - deshalb gibt es hier die Möglichkeit, Quellcode als
Dateianhang hochzuladen.
Im vorletzten Abschnitt haben wir den Fehler-Fall, dass der
Timer-Interrupt zwar gesetzt, aber nicht ausgelöst wird. Im letzten
Abschnitt klappt das dann wieder wie erwartet.
Der Unterschied ist, dass im Fehler-Fall das Timer-Interrupt Mask
Register TIMSK2 = 0 ist, wenn es dann funktioniert ist in diesem
Register das Bit OCIE2A gesetzt, wie es auch sein soll.
Nun die Frage, wie das sein kann! In beiden Fällen wird in der genannten
Funktion das Timer 2 compare interrupt flag gesetzt:
*TIMSK2 |= (1 << OCIE2A);*
Einmal klappt das nicht, dann schon.
Ich hoffe, es hat jemand eine Idee, woran das liegt.
Da ist jetzt wieder der vorletzte Abschnitt interessant (vor der Zeile
mit den .....):
Da wird also TIMSK2 beschrieben, das Ergebnis ist, dass es trotzdem noch
0x00 ist (Ausgabe TIMSK2-C). Dann wird es gleich darauf nochmal
beschrieben, jetzt erfolgreich (Ausgabe TIMSK2-D).
Dann muss die Frage jetzt also lauten, warum der Schreibzugriff auf
dieses Register nicht in jedem Fall funktioniert.
Ich bin ratlos...
> ... TIMSK2 ... dass es trotzdem noch 0x00 ist ...
Wohl eher 'wieder' - dazwischen erfolgt ja eine (längere) serielle
Ausgabe. Vielleicht mal in cli /sei/ kapseln.
Danke für den Tipp, habe das gleich probiert und die erste Zuweisung,
die ja schief geht, in cli() und sei() eingebettet.
Am Ergebnis ändert sich leider nichts, die Zuweisung ist nicht
erfolgreich, die Ausgabe die gleiche wie zuvor.
> die erste Zuweisung, die ja schief geht, in cli() und sei() eingebettet
?
Die Zuweisung geht doch nicht schief - die serielle Ausgabe sollte
eingebettet werden.
Das verstehe ich jetzt aber nicht:
TIMSK2 wird beschrieben und dann im Zug der seriellen Ausgabe zurück
gelesen.
Warum sollte das Serial.print() einen falschen Wert ausgeben?
Aber natürlich probiere ich das gleich noch aus.
Das mit dem Assembler-Listung muss ich erst noch sehen, wo ich das
finde...
Aber das cli() und sei() habe ich ausgedehnt auf das Serial.print() UND
den zweiten Schreibzugriff auskommentiert:
1
elseif(source==TIMEOUT_INTERRUPT)
2
{
3
set_sleep_mode(SLEEP_MODE_PWR_SAVE);// also wakeup by TMR2
Demnach ist das Schreiben des Registers tatsächlich erfolgreich (Ausgabe
TIMSK2-C), das zweite Schreiben entfällt, und am Ende ist das Register
doch wieder 0x00 und kein Interrupt wird ausgelöst.
Wer oder was setzt dieses Register zurück?
> Wer oder was setzt dieses Register zurück?
Na - eine Interrupt-Routine natürlich.
Aber mich in diese Interrupt-Struktur mal so eben nebenher
einzuarbeiten, das schaffe ich nicht; da fehlt mir die Zeit.
Das Problem scheint tatsächlich zu sein, dass ein Timer-Interrupt
auftritt und das Enable-Flag gelöscht wird, bevor der Controller in den
Sleep-Mode geht.
Die Lösung:
Der Timer muss initialisiert werden, wenn alle Interrupts abgeschaltet
sind. Außerdem muss das Timer-Interrupt-Flag gelöscht werden:
1
cli();
2
TCCR2B=0;// stop the timer and reset WGM22
3
TCCR2A|=(1<<WGM21);// enable timer2 CTC mode with top in OCR2A
4
OCR2A=194;// set compare match register of timer 2
5
TCNT2=0;// initialize counter value to 0
6
TCCR2B|=(1<<CS22)|(1<<CS21)|(1<<CS20);// 1:1024 prescaling for timer 2
7
TIFR2|=(1<<OCF2A);
8
TIMSK2|=(1<<OCIE2A);
9
sei();
Damit läuft das dann endlich und der Timer-Interrupt wird zuverlässig
ausgeführt!
Vielen Dank für alle Tipps, die mich in die richtige Richtung geführt
haben!
LG Arlenne
Arlenne schrieb:> Dann muss die Frage jetzt also lauten, warum der Schreibzugriff auf> dieses Register nicht in jedem Fall funktioniert.>> Ich bin ratlos...
Wärest du nicht, wenn du anfangen würdest, Datenblätter zu lesen, statt
mit Arduino zu spielen...
Der Timer2 wird doch sehr wahrscheinlich im asynchronen Modus betrieben
(um als RTC zu dienen und/oder um das Teil aus dem Tiefschlaf wecken zu
können).
Im asnchronen Modus ist es mit dem Schreiben eines SFIO-Registers nicht
getan. Man muss warten, bis dieser Schreibvorgang bei der asynchronen
Zielperipherie auch angekommen ist.
Das kann durchaus recht lange dauern. Das hängt nämlich vom Takt in
deren Taktdomain ab, bei Timer2 typisch halt nur 32kHz.