Hallo,
Ich versuche gerade einen ATTINY85 per Watchdog interrupt (nicht
watchdog reset!) aus dem sleep mode aufzuwecken.
Mein Testprogramm sieht folgendermassen aus:
1
#define F_CPU 1000000UL
2
3
#include<avr/interrupt.h>
4
#include<avr/io.h>
5
#include<avr/sleep.h>
6
#include<avr/wdt.h>
7
8
intmain(void)
9
{
10
DDRB=(1<<DDB4);
11
PORTB=(0<<PB4);
12
13
MCUSR&=~(1<<WDRF);
14
15
WDTCR=(1<<WDCE)|(1<<WDE);
16
WDTCR=(1<<WDIE)|(1<<WDP2)|(1<<WDP1);
17
18
sei();
19
while(1){
20
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
21
sleep_enable();
22
sleep_cpu();
23
sleep_disable();
24
}
25
26
return0;
27
}
28
29
ISR(WDT_vect)
30
{
31
PORTB^=(1<<PB4);
32
WDTCR|=(1<<WDIE);
33
}
Wenn ich den Inhalt der Hauptschleife entferne dann blinkt meine LED.
Sobald ich jedoch z.B. nur set_sleep_mode(SLEEP_MODE_PWR_DOWN);
ausfuehre (egal ob in der loop oder davor) wird die ISR nicht mehr
ausgefuehrt.
Hat jemand eine Idee woran das liegen kann?
Danke schon mal...
tkl schrieb:> Hat jemand eine Idee woran das liegen kann?
Ich zitiere mal kurz aus dem Datenblatt:
"When the SM[1:0] bits are written to 10, the SLEEP instruction makes
the MCU enter Powerdown mode. In this mode, the Oscillator is stopped,
while the external interrupts, the USI start condition detection and the
Watchdog continue operating (if enabled). Only an External Reset, a
Watchdog Reset, a Brown-out Reset, USI start condition interupt, an
external level interrupt on INT0 or a pin change interrupt can wake up
the MCU. This sleep mode halts all generated clocks, allowing operation
of asynchronous modules only."
Zitat Ende
Ich lese da nichts von einem WD Interrupt, sondern nur vom WD Reset. Der
Interrupt reicht nicht aus, um den MC zu wecken.
es steht so viel im Datenblatt ...
The Wathdog Timer can also be configured to generate an interrupt
instead of a reset. This can be very helpfulwhen using the Watchdog to
wake-up from Power-down.
tkl schrieb:> set_sleep_mode(SLEEP_MODE_PWR_DOWN);> sleep_enable();> sleep_cpu();> sleep_disable()
Ich bin jetzt zu faul, denn Sinn dieser Befehle zu ergründen.
Vielleicht hilft Dir ein Beispiel was direkter programmiert ist und
funktioniert. http://mino-elektronik.de/progs/avr/PBW-wdt/PBW-wdt.c
tkl schrieb:
> Hat jemand eine Idee woran das liegen kann?
Nein - ich fürchte, ich habe die Fragestellung nicht verstanden: wenn
ich das gezeigte Programm unverändert laufen lasse, so blinkt die LED an
B4.
(Fuses: 62 DF)
tkl schrieb:> WDTCR = (1 << WDCE) | (1 << WDE);> WDTCR = (1 << WDIE) | (1 << WDP2) | (1 << WDP1);
Ich frage mich, warum du das Register beschreibst und dann direkt danach
mit was anderem überschreibst.
Sieht komisch aus...
Arduino Fanboy D. schrieb:> tkl schrieb:>> WDTCR = (1 << WDCE) | (1 << WDE);>> WDTCR = (1 << WDIE) | (1 << WDP2) | (1 << WDP1);> Ich frage mich, warum du das Register beschreibst und dann direkt danach> mit was anderem überschreibst.>> Sieht komisch aus...
Ist aber in diesem Fall nötig. WDTCR ist durch eine Zeitbegrenzung gegen
versehentliches Überschreiben geschützt. Erst muss WDE gesetzt werden,
dann muss innerhalb von vier CPU-Takten das geschrieben werden, was man
eigentlich in das Register reinschreiben will, ansonsten passiert
garnix.
Das Problem ist: In C kannst du nicht garantieren, dass die Aufgabe
innerhalb von vier Takten erledigt ist. C kennt keinerlei Garantien
bezüglich des Timing. Das wäre also entweder im Asm-Listing zu
überprüfen oder die Sache gleich in der natürlichen Sprache des
Controller hinzuschreiben (spart die Iteration über die Analyse des
Listings). Diese Sache wird hier aber vermutlich nicht das Problem
sein...
Das Problem könnte eher sein, dass selbst hinreichend getimeter Code
durch Interrupts unterbrochen werden kann und dann definitiv nicht mehr
in vier Takten durchläuft, weil schon der minimale Interruptframe 8
Takte beträgt. Dieses Problem ist sprachunabhängig, kann also in Asm
genauso zuschlagen...
Das könnte hier tatsächlich zum Problem werden, denn spätestens nach dem
ersten Aufwachen sind die Interrupts enabled, wenn der kritische Code
durchlaufen wird...
Ein weiteres Problem (hier vermutlich das Entscheidende) ist aber:
WDT-Interrupt kann zu WDT-Reset werden, wenn man nicht im richtigen
Moment das Richtige dagegen tut. DB-Lesen hilft, denn da steht das
ALLES natürlich ganz genau drin, man muss es eben nur lesen wollen
(und auch können)...
c-hater schrieb:> Ist aber in diesem Fall nötig. WDTCR ist durch eine Zeitbegrenzung gegen> versehentliches Überschreiben geschützt. Erst muss WDE gesetzt werden,> dann muss innerhalb von vier CPU-Takten das geschrieben werden, was man> eigentlich in das Register reinschreiben will, ansonsten passiert> garnix.
Ja.
So spricht das Datenblatt!
c-hater schrieb:
eine gute Erklärung
Ich bin positiv überrascht, du hast ja mal eine hilfreiche Erklärung
abgegeben, ohne das sonst übliche C bashing. Das gefällt mir!
c-hater schrieb:> Das könnte hier tatsächlich zum Problem werden, denn spätestens nach dem> ersten Aufwachen sind die Interrupts enabled, wenn der kritische Code> durchlaufen wird...
Das verstehe ich nicht, welcher "kritische Code"? In der while-Schleife
steht nur Unkritisches, und bei dem WDTCR-Setzen davor ist der globale
Interrupt noch nicht freigegeben.
S. Landolt schrieb:> Das verstehe ich nicht, welcher "kritische Code"? In der while-Schleife> steht nur Unkritisches, und bei dem WDTCR-Setzen davor ist der globale> Interrupt noch nicht freigegeben.
Oops, stimmt. Der Code steht ja garnicht in der Schleife, wird also auch
nach dem Aufwachen nicht erneut durchlaufen.
Also: Punkt3 ;o)
Arduino Fanboy D. schrieb:> Ja.> So spricht das Datenblatt!
Das stimmt so nicht! Um den WDT zu aktivieren ist kein spezielles Timing
einzuhalten. Das ist dann erforderlich, wenn ein aktiver WDT gestoppt
oder verändert werden soll.
WDTCR = (1 << WDIE) | (1 << WDP2) | (1 << WDP1);
reicht zur Aktivierung aus.
Korrektur:
Es liegt an dem sleep-Zeugs, das wird durch den Interrupt gestört.
Pack das ganze sleep-Init for das sei(). Das muß man nur einmal machen,
die Sleep-Bits ändern sich nicht magisch.
an c-hater und Peter Dannegger:
Ich kann nur wiederholen, dass das ursprüngliche Programm bei mir läuft,
warum auch immer. Vielleicht kann das noch jemand anderer verifizieren,
oder auch nicht.
Watchdog schrieb:> es steht so viel im Datenblatt ...>> The Wathdog Timer can also be configured to generate an interrupt> instead of a reset. This can be very helpfulwhen using the Watchdog to> wake-up from Power-down.
Die Datenblätter enthalten viel copy and Paste, da ist Atmel öfter mal
was von einem anderen Prozessor verrutscht.
In dem Fall allerdings dürfte das schlicht falsch sein, das
Interruptsystem ist bei allen sleep-modes unterhalb idle deaktiviert.
Oliver
S. Landolt schrieb:> Ich kann nur wiederholen, dass das ursprüngliche Programm bei mir läuft,
Stimmt, es läuft.
Das sleep-init ist zwar nicht interruptfest, aber der einzige Interrupt
wird ja direkt nach dem Sleep ausgeführt, ist also keine Gefahr.
Man sollte es aber trotzdem aus der Schleife herausnehmen.
Peter D. schrieb:> Man sollte es aber trotzdem aus der Schleife herausnehmen.
War wohl von Anfang an garnicht drinne. Mir war aber auch so, als wäre
es so gewesen. So kann man sich täuschen...
Eine Möglichkeit könnte der fehlende Stützkondensator hinter dem
Spannungsregler sein. Durch den Stromanstieg beim Aufwachen bricht der
kurz ein und der MC macht einen Reset.
Hallo,
Vielen Dank fuer all eure Antworten. Am Ende ist es so wie S. Landolt
schrieb:
> Nein - ich fürchte, ich habe die Fragestellung nicht verstanden: wenn> ich das gezeigte Programm unverändert laufen lasse, so blinkt die LED an> B4.
Die LED blinkt. Mein Problem war nicht der Sourcecode sondern die
vorcompilierte avr Toolchain aus den Debian Buster Paketquellen (benutzt
die jemand?). "Linearer" code liess sich damit erzeugen, jedoch sobald
Interrupts ins Spiel kamen war das verhalten komplett komisch...
Ich hab' mir jetzt 'ne Toolchain selbst compiliert und mit der laeuft's.
Ja. Das ist reproduzierbar (Ist aber dich nicht Debian Buster sondern
Raspbian auf 'nem Raspberry pi).
Ich hab' das aber auf 2 verschiedenen pi's probiert (3B+ und4). Ist das
Selbe verhalten...
Hallo,
Ich habe ein ähnliches Problem(auch Tiny85), dass beim Sleepmodus
"PowerDown" der Controller vom Watchdog resettet wird.
Wenn ich den Code im Sleepmodus "Idle" ausführe, dann wird der
Controller vom WD-Interrupt korrekt wieder aufgeweckt.
Ich nutze Atmel Studio unter Windows10. Das mit der Toolchain habe ich
nicht ganz verstanden.
Initialisiert habe ich den mit
1
wdt_enable(WDTO_15MS);//init Watchdog
2
WDTCR|=(1<<WDIE);//enable interrupt instead of reset
Deine Frage hätte durchaus einen eigenen Thread verdient gehabt.
Johannes H. schrieb:> Ich habe ein ähnliches Problem(auch Tiny85), dass beim Sleepmodus> "PowerDown" der Controller vom Watchdog resettet wird.
Beim WD vom Tinyx5 funktioniert das Wecken jedenfalls auch im
PowerDown-Sleep. Habe ich X-fach so implementier und es steht auch so im
Datenblatt im Abschnitt 'Power Management and Sleep Modes'.
Etwas komplizierter wird es, wenn man sowohl den Watchdogtimer zum
Wecken und auch als Watchdogreset zum Schutz gegen Hänger nutzen will.
Jedenfalls solltest du prüfen, ob in den Fuses der WDTON auch
deaktiviert ist. Ich verwende den WD-Reset zwar selten, aber wenn, dann
aktiviere ich in nicht in mit der Fuse sondern im Programm.
Im Anhang ein Demo-Beispiel, bei dem beides, Watchdogreset und
Watchdoginterrupt, verwendet wird.
Klaus H. schrieb:> ... noch ein möglicher Grund: du hast keine ISR für den WD.
Nee, die habe ich ja drin. Nach dem "Idle"-Sleep macht der µC ja auch
korrekt weiter, wo er aufgehört hat.
Anders als in Deinem Code setze ich das
1
WDTCR|=(1<<WDIE);
direkt in der ISR.
In den Fuses ist ist die WDTON nicht aktiviert.
Johannes H. schrieb:> Anders als in Deinem Code setze ich das WDTCR |= (1<<WDIE);> direkt in der ISR.
Das braucht man nur mehrfach, wenn man den WD-Reset auch mit nutzt.
Welche Variante willst du den haben? Das geht aus deinen zwei Zeilen
nicht hervor. Sonst reicht es WDIE im Setup einmal zu setzen und WDE in
Ruhe zu lassen. Das aktiviert den IRQ und so kann man den WD als Timer
nutzen.
Nur wenn man jedoch den WD-Reset auch haben will, dann macht der µC
folgendes:
Mit gesetztem WDIE macht er erst die ISR. Wird WDIE nicht erneut wieder
gesetzt, dann macht der nächste WD-Interupt einen Reset. Datenblatt: "If
WDE is set, WDIE is automatically cleared by hardware when a time-out
occurs. "
Das müsste zwar auch in der ISR gehen, aber ich denke, dass du dann nie
in den WD-Reset kommst! Denn dann wird WDIE immer neu gesetzt, auch wenn
das Programm in einer Endlosschleife festhängen würde. Wenn das Programm
hängt, wird WDIE, falls es außerhalb der ISR bedient wird, nicht mehr
gesetzt und es erfolgt dann erst der Reset.
Im Anhang noch eine Demo, wie man den WD nur als Timer nutzen kann. Hier
wird WDIE nur einmal aktiviert.
Funktionieren denn meine beiden Beispiele bei dir?
Klaus H. schrieb:> Das braucht man nur mehrfach, wenn man den WD-Reset auch mit nutzt.> Welche Variante willst du den haben? Das geht aus deinen zwei Zeilen> nicht hervor. Sonst reicht es WDIE im Setup einmal zu setzen und WDE in> Ruhe zu lassen. Das aktiviert den IRQ und so kann man den WD als Timer> nutzen.
Hi Klaus, Danke erstmal, dass Du dir hier soviel Zeit für mein Problem
nimmst.
Also Dein Text verwirrt mich jetzt. Denn im Datenblatt steht ja etwas
gegenteiliges. Da ich den Reset verhindern will, setze ich eben extra
bei jedem Interrupt das WDIE-Bit wieder.
Ich habe es auch mit und ohne gesetztem WDE-Bit probiert.
Deine beiden Beispiele kann ich morgen erst ausprobieren.
Johannes H. schrieb:> Also Dein Text verwirrt mich jetzt. Denn im Datenblatt steht ja etwas> gegenteiliges.
Nein, tut es nicht. Aber ich weiß noch immer nicht, was du bezweckst:
nur Timer, nur Watchdog oder beides?
Nur Timerinterrupt: Zeile 2 in deinem Bild. Einmal setzen, dann läuft
der. ISR ist notwendig. Mein watchdog2.c Beispiel.
Nur Watchdog: Zeile 3 in deinem Bild. Muss mit rechtzeitigem,
wiederholten wdt_reset() das Leben des Programms bestätigen, sonst
erfolgt ein Reset.
Beides: Zeile 4 in deinem Bild, dann wird zuerst ein Interrupt
ausgeführt (als Timer nutzbar) und dann der Reset. Um den Timer zu
nutzen, muss man jedes Mal WDIE neu setzen. wdt_reset() geht nicht, denn
dann erfolgt kein Interrupt und ist als Timer dann nicht nutzbar.
Wenn das Programm hängen bleibt, dann wird noch einmal der Timer-IRQ
ausgelöst und dann beim nächsten Ablauf dann der Reset.
(Ich hoffe, ich habe alles richtig in Erinnerung.
[bin jetzt weg für heute 😀])
Ich brauche nur den Interrupt, da der µC nicht resettet werden soll.
Und jetzt funktioniert es mit Deinem Beispiel #2.
Aber leider war der Aufwand umsonst, da der Powerdownmodus eine andere
Funktionalität behindert und die 16ms des Watchdogs auch zu lange sind 😅
Aber Danke dir auf jeden Fall für die Hilfe 🙂
Johannes H. schrieb:> Aber Danke dir auf jeden Fall für die Hilfe 🙂
Gerne!
Natürlich kannte ich deine 'andere Funktionalität' nicht. Da aber
Ähnliches immer mal wieder gebraucht wird, hast du jetzt einen
Anhaltspunkt. Irgendwann musste ich mir das auch erarbeiten und dabei
sind diese Beispiel entstanden.
Der WD-Timer zusammen mit sleep ist m.E. schön geeignet, um zumindest
grob gestufte lange Zeiten recht stromsparend zu generieren - mit sehr
einfacher Software.
Ein Monoflop kann auch viel, aber wenn es den Sekundenbereich
überschreitet (Beispielfunktionalität von einem Treppenlichtautomat),
dann löse ich das lieber mit dem WD-Timer. Zudem hat man noch viel
Reserve für weitere Aufgaben - schon eine einfache Periodizität (z.B.
LED einmal pro Minute blinken lassen) erfordert beim MF Zusatzaufwand.
Johannes H. schrieb:> Aber leider war der Aufwand umsonst, da der Powerdownmodus eine andere> Funktionalität behindert und die 16ms des Watchdogs auch zu lange sind
Da gibt es doch ne hübsche Tabelle, was in welchem Mode noch geht.
Peter D. schrieb:> Da gibt es doch ne hübsche Tabelle, was in welchem Mode noch geht.
Ja, die kenne ich auch schon.
Es funktioniert mit Watchdog im Powerdown alles, aber leider ist das für
meine Anwendung mit 16ms zu langsam. Meine maximal mögliche
Periodendauer liegt bei 5ms.