Forum: Mikrocontroller und Digitale Elektronik Watchdog interrupt & sleep mode ATTINY85


von tkl (Gast)


Lesenswert?

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
int main(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
    return 0;
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...

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

: Bearbeitet durch User
von Watchdog (Gast)


Lesenswert?

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.

von m.n. (Gast)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

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)

von Einer K. (Gast)


Lesenswert?

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...

von c-hater (Gast)


Lesenswert?

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)...

von Einer K. (Gast)


Lesenswert?

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!

von Stefan F. (Gast)


Lesenswert?

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!

von S. Landolt (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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)

von Peter D. (peda)


Lesenswert?

Du must initial das WDIE setzen.

Das hier läuft bei mir:
1
#include <avr/interrupt.h>
2
#include <avr/io.h>
3
#include <avr/sleep.h>
4
#include <avr/wdt.h>
5
6
int main(void)
7
{
8
  MCUSR = 0;
9
  PORTB = 0b1010;
10
  DDRB = 0b1111;
11
  wdt_enable(WDTO_500MS);
12
  WDTCR |= 1<<WDIE;                     // <-
13
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
14
  sleep_enable();
15
  sei();
16
  while(1)
17
  {
18
    PORTB ^= 0b11;
19
    sleep_cpu();
20
  }
21
}
22
23
ISR(WDT_vect)
24
{
25
  PORTB ^= 0b100;
26
  WDTCR |= 1<<WDIE;
27
}

von m.n. (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

>  das Interruptsystem ist bei allen sleep-modes unterhalb idle deaktiviert.

? - ??

von Peter D. (peda)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> Stimmt, es läuft.

Prima, danke!
 Und damit liegt der Ball wieder im Feld von tkl.

von c-hater (Gast)


Lesenswert?

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...

von Peter D. (peda)


Lesenswert?

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.

von tkl (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

Ich hatte mit der Toolchain von Debian noch nie Probleme. Insofern 
überrascht es mich. Ist das Problem reproduzierbar?

von tkl (Gast)


Lesenswert?

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...

von Stefan F. (Gast)


Lesenswert?

Krass. Jetzt bin ich vorgewarnt. Danke

von Johannes (menschenskind)


Lesenswert?

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

von Klaus H. (hildek)


Angehängte Dateien:

Lesenswert?

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.

: Bearbeitet durch User
von Klaus H. (hildek)


Lesenswert?

Johannes H. schrieb:
> der Controller vom Watchdog resettet wird

... noch ein möglicher Grund: du hast keine ISR für den WD.

von Johannes (menschenskind)


Lesenswert?

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.

von Klaus H. (hildek)


Angehängte Dateien:

Lesenswert?

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?

: Bearbeitet durch User
von Johannes (menschenskind)


Angehängte Dateien:

Lesenswert?

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.

: Bearbeitet durch User
von Klaus H. (hildek)


Lesenswert?

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 😀])

: Bearbeitet durch User
von Johannes (menschenskind)


Lesenswert?

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 🙂

von Klaus H. (hildek)


Lesenswert?

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.

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

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.

von Johannes (menschenskind)


Lesenswert?

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.

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.