Forum: Mikrocontroller und Digitale Elektronik Sleep verhält sich auf verschiedenen ATMega328p unterschiedlich


von Mathias (ribisl)


Lesenswert?

Hallo, ich habe bei einem Projekt ein recht eigenartiges Verhalten von 
verschiedenen AtMega328P's. Bei Atmega A funktioniert der Beispiel 
Sketch von unten wie geplant und ohne Probleme. Jedoch funktioniert das 
Programm bei einem anderen nicht bzw. nur sehr sporadisch. z.B. Watchdog 
löst nach 3x loop aus (mal mehr, mal weniger),  nach dem in Power Down 
(genauer in sleep_cpu) wahrscheinlich nicht mehr in die Interrupt 
Funktion gewechselt wird und der wdt nicht mehr resetted wird.

Prinzipiell sollte es so funktionieren:
1. WDT aktivieren um nach uC zu resetten wenn er durch irgendwas 
blockiert wird
2. Jedoch sollte zwischen den Messungen immer kurz in den sleep Modus 
gewechselt werden
3. WDT wieder auf 8s setzen um zu resetten wenn nötig

Schritt 2 macht mir teilweise bei manchen AtMega328P's Probleme.
Verwende für den Sleep Modus die LowPower Library 
https://github.com/rocketscream/Low-Power
1
#include <Arduino.h>
2
#include "LowPower.h"
3
#include <avr/wdt.h>
4
5
6
void setup() {
7
    bool _WDRF = ((1 << WDRF) & MCUSR);     // Watchdog Reset
8
9
    if (_WDRF) {
10
        cli();
11
        MCUSR &= ~(1 << WDRF);
12
        WDTCSR |= (1<<WDCE) | (1<<WDE);
13
        WDTCSR = 0;
14
        sei();
15
    }
16
17
    bool _BORF = ((1 << BORF) & MCUSR);     // BrownOut Reset
18
    bool _EXTRF = ((1 << EXTRF) & MCUSR);    // External Reset
19
    bool _PORF = ((1 << PORF) & MCUSR);     // Power-Off Reset
20
    MCUSR = 0;
21
22
    
23
        Serial.begin(4800);
24
25
    if (_WDRF)
26
        Serial.println("WDT Triggered");
27
    Serial.println(F("Start Setup"));
28
29
30
    WDTCSR |= (1 << WDIE) | (1 << WDE); //Watchdog  Mode = Interrupt Mode && System Reset Mode
31
    wdt_enable(WDTO_8S);
32
    wdt_reset();
33
}
34
35
void loop() {
36
    wdt_reset();
37
38
    // eventuell blockiert
39
40
    LowPower.powerDown(ENERGY_MANAGEMENT_SLEEP_INTERVAL, adc_t::ADC_ON, bod_t::BOD_ON);
41
42
43
    WDTCSR |= (1 << WDIE) | (1 << WDE); //Watchdog  Mode = Interrupt Mode && System Reset Mode
44
    wdt_enable(WDTO_8S);
45
    wdt_reset();
46
}
Wenn der uC neugestartet wird, passiert das bei sleep_cpu(); in 
LowPower.cpp
1
#define  lowPowerBodOn(mode)  \
2
do {             \
3
      set_sleep_mode(mode); \
4
      cli();        \
5
      sleep_enable();    \
6
      sei();        \
7
      sleep_cpu();      \
8
      sleep_disable();    \
9
      sei();        \
10
} while (0);

Vielleicht hat jemand eine Idee was das Problem sein könnte.

PS:
1
    "efuse": "0xFF",
2
    "file": "atmega/ATmegaBOOT_168_atmega328_pro_8MHz.hex",
3
    "hfuse": "0xD9",
4
    "lock_bits": "0xFF",
5
    "lfuse": "0x62",
6
    "unlock_bits": "0xFF"
f_cpu = 1MHz

: Bearbeitet durch User
von Monk (roehrmond)


Lesenswert?

Mathias schrieb:
> wahrscheinlich nicht mehr in die Interrupt
> Funktion gewechselt wird

Überprüfe das. Du könntest dazu in der Interrupt-Funktion einen Ausgang 
toggeln.

Warum rufst du innerhalb der loop wdt_enable() auf? Das scheint mir 
unnötig.

Bist du sicher, dass die Brown-Out Schwelle 4,3V OK ist? Bei USB kann 
man durchaus mal darunter fallen, das müssen USB Geräte tolerieren.

Offenbar soll der ADC den Mikrocontroller aufwecken, aber ich sehe keine 
Code dazu. Die Interrupt-Funktion hast du auch nicht gezeigt. Mit so 
wenig Infos kann man dir kaum helfen.

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

Die LowPower-Bibliothek, die du verwendest, scheint auch was mit dem 
Watchdog zu machen, und hat mehrere Stellen, die den umkonfigurieren.

Hab jetzt nicht im Detail geprüft, ob das mit deiner eigenen 
WDT-Verwendung kollidiert, könnte aber sehr gut sein.

Insbesondere wird dort der Watchdog zum Aufwachen aus dem Sleep 
verwendet, was mit deiner Intention, den µC per Watchdog zu resetten, 
nicht ganz kompatibel ist.

Den Sleep mode "zu Fuß" selber zu implementieren ist vmtl. einfacher als 
zu versuchen deinen Code mit dieser Low-Power-Library in Einklang zu 
bringen.

Steve schrieb:
> Warum rufst du innerhalb der loop wdt_enable() auf? Das scheint mir
> unnötig.

Weil die Library folgendes enthält:
1
ISR (WDT_vect)
2
{
3
  // WDIE & WDIF is cleared in hardware upon entering this ISR
4
  wdt_disable();
5
}

d.H. der Watchdog schaltet sich damit immer selber aus...

: Bearbeitet durch User
von Monk (roehrmond)


Lesenswert?

Εrnst B. schrieb:
> Den Sleep mode "zu Fuß" selber zu implementieren ist vmtl. einfacher als
> zu versuchen deinen Code mit dieser Low-Power-Library in Einklang zu
> bringen.

Ja, dann wird auch transparenter, was der Code eigentlich tut.

von Peter D. (peda)


Lesenswert?

Um sleep zu benutzen, brauchst Du einen Interrupt, der die CPU wieder 
aufweckt. Der fehlt aber.

Die Reihenfolge ist falsch. MCUSR muß gelöscht werden, bevor man den 
Wachhund disabled.

wdt_enable(WDTO_8S); gehört nicht in die Loop. Einmal im Init reicht 
völlig, die CPU ist ja nicht vergeßlich.
Ebenso ist des doppelte sei() im Macro überflüssig, einmal reicht.

von Εrnst B. (ernst)


Lesenswert?

Peter D. schrieb:
> Um sleep zu benutzen, brauchst Du einen Interrupt, der die CPU wieder
> aufweckt. Der fehlt aber.

Läuft bei Arduino nicht sowieso immer ein Timer mit, um die millis() 
hochzuzählen?

Ansonsten wärs der Watchdog-Irq...

Peter D. schrieb:
> wdt_enable(WDTO_8S); gehört nicht in die Loop. Einmal im Init reicht
> völlig, die CPU ist ja nicht vergeßlich.

Dafür sorgt die Rundum-Glücklich-Lowpower-Library :)

@ribisl:

tl;dr:

Entweder du kontrollierst den Watchdog in deinem Code, oder 
rocketscream/Low-Power tut es. Dann lässt du aber auch die Finger davon.

von Peter D. (peda)


Lesenswert?

Εrnst B. schrieb:
> Dafür sorgt die Rundum-Glücklich-Lowpower-Library :)

Mit Sicherheit macht sie das Programm extrem undurchschaubar. Ich hab da 
keine Lust mich durchzukämpfen. Sie scheint mir nicht gerade foolproof 
zu sein.

Εrnst B. schrieb:
> Entweder du kontrollierst den Watchdog in deinem Code, oder
> rocketscream/Low-Power tut es. Dann lässt du aber auch die Finger davon.

So wirds sein. Ich sehe auch keinen rechten Nutzen in der Lib.
Ich würde sie komplett in die Tonne treten und alles selber ordentlich 
machen. Sleepmode ist nämlich recht tricky und enthält einige 
Race-Conditions.

Daß ich sleep_disable(); für Bullshit halte, hab ich ja schon mehrfach 
gesagt.
Ohne das sleep_disable(); reicht es völlig, das sleep_enable(); nur im 
Init zu machen und dann ist man schonmal eine Race-Condition los.

: Bearbeitet durch User
von Mathias (ribisl)


Lesenswert?

Steve schrieb:
> Warum rufst du innerhalb der loop wdt_enable() auf? Das scheint mir
> unnötig.

Nach dem Sleep Modus soll der Watchdog wieder auf 8s gesetzt werden, 
damit er nicht nach 250ms wieder resetted.

Steve schrieb:
> Bist du sicher, dass die Brown-Out Schwelle 4,3V OK ist? Bei USB kann
> man durchaus mal darunter fallen, das müssen USB Geräte tolerieren.

Ja, AtMega läuft auf 3.3V und BrownOut wird nie ausgelöst

Steve schrieb:
> Offenbar soll der ADC den Mikrocontroller aufwecken, aber ich sehe keine
> Code dazu.

Verstehe nicht ganz wie du drauf kommst, dass der ADC den uC aufweckt?

Steve schrieb:
> Die Interrupt-Funktion hast du auch nicht gezeigt.
1
ISR (WDT_vect)
2
{
3
  // WDIE & WDIF is cleared in hardware upon entering this ISR
4
  wdt_disable();
5
}

Das eigenartige ist halt, dass bei einigen ATMegas alles ohne Probleme 
funktioniert, und bei manchen nach wenigen loops resetted wird.

Werde jetzt mal versuchen den sleep Teil selbst zu machen.

Peter D. schrieb:
> Daß ich sleep_disable(); für Bullshit halte, hab ich ja schon mehrfach
> gesagt.

Könntest du es kurz nocheinmal erklären? :)

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Mathias schrieb:
> Könntest du es kurz nocheinmal erklären? :)
1
sleep_enable(); 
2
sleep_cpu();    
3
sleep_disable();
In dieser Sequenz ist sleep_disable(); vollkommen überflüssig, da ja vor 
jedem Sleep das sleep_enable(); ausgeführt wird.
Daher reicht sleep_enable(); einmal im Init und sleep_disable(); fällt 
ersatzlos weg.
Übrig bleibt in der Loop nur das:
sleep_cpu();

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Eine Race-Condition wäre möglich wenn im Interrupt das SMCR angefaßt 
wird.

Der Interrupt ändert aber das WDTCSR. Ob dadurch auch eine 
Race-Condition möglich ist, müßte man bei jeder Funktion prüfen, die im 
Main das WDTCSR ändert. Am besten vorsorglich alle diese Funktionen 
atomar kapseln.

von Monk (roehrmond)


Lesenswert?

Mathias schrieb:
> Verstehe nicht ganz wie du drauf kommst, dass der ADC den uC aufweckt?

Weil du geschrieben hast, dass der µC zwischen den Messungen schlafen 
soll. Habe ich wohl missverstanden.

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

Mathias schrieb:
> Das eigenartige ist halt, dass bei einigen ATMegas alles ohne Probleme
> funktioniert, und bei manchen nach wenigen loops resetted wird.

Das ist das Fiese an so Race-Conditions, besonders wenn sie von zwei 
voneinander unabhängigen Clock-Sources abhängen...
Die Watchdog-Clock ist sehr ungenau, temperaturabhängig, und streut 
stark mit den Exemplaren. Da kann es schon sein, dass ein Chip bei 
Raumtemperatur lange problemlos läuft, und der nächste praktisch ständig 
ins Fettnäpfchen tritt.
Kannst ja mal probieren, deine Crash-Kandidaten etwas zu erwärmen oder 
zu kühlen, oder anderweitig das Timing beeinflussen, z.B. durch ein paar 
extra delay() in der loop().
Aber: das ist natürlich keine Lösung, nur ein evtl. interessantes 
Experiment, falls du der Sache auf den Grund gehen möchtest.

von Mathias (ribisl)


Lesenswert?

Εrnst B. schrieb:
> Das ist das Fiese an so Race-Conditions, besonders wenn sie von zwei
> voneinander unabhängigen Clock-Sources abhängen...

Hmm... dachte Race Condidtions können nur bei mehreren Threads 
auftreten. Und wie kann ich diese in meinem Fall am besten verhindern?

von Mathias (ribisl)


Lesenswert?

Habs jetzt mal so umgeschrieben
1
#include <Arduino.h>
2
#include <avr/sleep.h>
3
#include <avr/wdt.h>
4
5
inline void sleep()
6
{
7
    cli();
8
    wdt_reset();
9
    WDTCSR |= (1 << WDCE) | (1 << WDE);
10
    WDTCSR = (1 << WDIE) | (1 << WDP2) | (1 << WDP0); // Interrupt and 0.5s
11
    sei();
12
    
13
    SMCR = (1 << SE) | (1 << SM1);  // Sleep Enable and Power-Down Sleep Mode
14
    sleep_cpu();                    // goto sleep
15
}
16
17
ISR(WDT_vect)
18
{
19
    SMCR = 0;
20
}
21
22
void setup()
23
{
24
    bool _WDRF = ((1 << WDRF) & MCUSR); // Watchdog Reset
25
    if (_WDRF)
26
    {
27
        cli();
28
        MCUSR &= ~(1 << WDRF);
29
        WDTCSR |= (1 << WDCE) | (1 << WDE);
30
        WDTCSR = 0;
31
        sei();
32
    }
33
    bool _BORF = ((1 << BORF) & MCUSR);   // BrownOut Reset
34
    bool _EXTRF = ((1 << EXTRF) & MCUSR); // External Reset
35
    bool _PORF = ((1 << PORF) & MCUSR);   // Power-Off Reset
36
    MCUSR = 0;
37
38
    pinMode(6, OUTPUT);
39
40
    Serial.begin(4800);
41
    if (_WDRF)
42
        Serial.println("WDT Triggered");
43
    Serial.println(F("Start Setup"));
44
    delay(3000);
45
46
    cli();
47
    WDTCSR |= (1 << WDIE) | (1 << WDE); // Watchdog  Mode = Interrupt Mode && System Reset Mode
48
    wdt_enable(WDTO_8S);
49
    wdt_reset();
50
    sei();
51
}
52
void loop()
53
{
54
    static int x = 0;
55
    wdt_reset();
56
    digitalWrite(6,x++%2==0);
57
    delay(500);
58
    sleep();
59
60
    cli();
61
    wdt_reset();
62
    WDTCSR = (1 << WDCE) | (1 << WDIE) | (1 << WDE);   // Watchdog  Mode = Interrupt Mode && System Reset Mode // do not put this in ISR
63
    WDTCSR |= (1 << WDIE) | (1 << WDP3) | (1 << WDP0); // Interrupt and 0.5s
64
    wdt_reset();
65
    sei();
66
}

Funktioniert schon etwas besser aber immer noch nicht prefekt

von Mathias (ribisl)


Lesenswert?

Peter D. schrieb:
> Eine Race-Condition wäre möglich wenn im Interrupt das SMCR angefaßt
> wird.

Sollte das nicht gemacht werden? Wenn der WDT Triggered soll ja der 
Sleep Modus deaktiviert werden, ich dachte das macht man jetzt mit
1
ISR(WDT_vect)
2
{
3
    SMCR = 0; // stop sleeping
4
}

von Peter D. (peda)


Lesenswert?

Mathias schrieb:
> Funktioniert schon etwas besser aber immer noch nicht prefekt

Kannst Du das mal übersetzen.
Was soll das ganze Konstrukt eigentlich bewirken.

von Mathias (ribisl)


Lesenswert?

Peter D. schrieb:
> Was soll das ganze Konstrukt eigentlich bewirken.

Es sollte eine Messung durchgeführt werden und periodisch auch etwas auf 
eine SD Karte geschrieben werden. Nach der Messung sollte für 500ms 
geschlafen werden. Nun kam es manchmal dazu, dass der uC komplett 
blockiert wurde und nichts mehr ging. Deshalb wollte ich den WDT so 
umkonfigurieren, dass wenn der blockierte Fall eintritt nach 8s resetted 
wird.
1
void loop(){
2
  wdt_reset();
3
  
4
  // Messung oder SD Karte beschreiben, könnte blockieren
5
  
6
  sleepFor500MS();
7
8
  // setze WDT auf 8s und System Reset Modus
9
}

Ich hoffe das ganze ist jetzt etwas klarer.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Mathias schrieb:
> ich dachte das macht man jetzt mit
> ISR(WDT_vect)
> {
>     SMCR = 0; // stop sleeping
> }

Nö, das stoppt nichts.
Es ändert nur die Funktion eines folgenden sleep_cpu();.


Mathias schrieb:
> void loop()
> {
>     ...
>     delay(500);

Da krempeln sich einem die Fußnägel hoch. Laß den Unsinn.

von Εrnst B. (ernst)


Lesenswert?

Mathias schrieb:
> Deshalb wollte ich den WDT so
> umkonfigurieren, dass wenn der blockierte Fall eintritt nach 8s resetted
> wird.

Was nicht funktioniert hat, weil das "LowPower-Sleep" den Watchdog zum 
Aufwachen nach den 500ms umkonfiguriert hat, also auf kürzeren Timeout 
und auf Interrupt statt Reset.

=> wenn der Watchdog zuverlässig immer zuschlagen können soll, darf 
dein sleep den nicht mal zwischendurch zum Wiederaufwachen verwenden.
Also: andere Aufwach-Zeitquelle verwenden, auch wenn dann der 
Schlafmodus nicht ganz so stromsparend ausfallen kann.

Timer2 im Async-Mode mit 32kHz Uhrenquarz wäre eine Möglichkeit, der 
läuft im Power-Save Modus weiter, und kann die CPU wieder aufwecken.
Stomverbrauch müsstest du nachmessen.

von Mathias (ribisl)


Lesenswert?

Peter D. schrieb:
> Da krempeln sich einem die Fußnägel hoch. Laß den Unsinn.

Das war nur um es Testweise etwas weiter zu verzögern und hat auch 
nichts mit meinem Problem zu tun...

von Mathias (ribisl)


Lesenswert?

Εrnst B. schrieb:
> Was nicht funktioniert hat, weil das "LowPower-Sleep" den Watchdog zum
> Aufwachen nach den 500ms umkonfiguriert hat, also auf kürzeren Timeout
> und auf Interrupt statt Reset.

Hab jetzt die LowPower Library rausgeschmissen und was anderes probiert.

Εrnst B. schrieb:
> wenn der Watchdog zuverlässig immer zuschlagen können soll, darf
> dein sleep den nicht mal zwischendurch zum Wiederaufwachen verwenden.

Verstehe nicht ganz warum das nicht geht soll? WDT umkonfigurieren um 
nach 500ms sleep aufzuwachen, nach dem Aufwachen wieder so 
umkonfigurieren damit er nach 8s keinem wdt_reset() den uC resetted. 
Klingt für mich nicht ganz unlogisch aber ich kann mich auch irren.

von Εrnst B. (ernst)


Lesenswert?

Mathias schrieb:
> Verstehe nicht ganz warum das nicht geht soll?

Es geht schon wenn es korrekt umgesetzt ist, und nicht wenn, wie in 
deinem Original-Code, der "Absturz" genau an der Stelle passiert an der 
der Watchdog gerade Pause macht.

von Mathias (ribisl)


Lesenswert?

Εrnst B. schrieb:
> Es geht schon wenn es korrekt umgesetzt ist, und nicht wenn, wie in
> deinem Original-Code, der "Absturz" genau an der Stelle passiert an der
> der Watchdog gerade Pause macht.

Und wie würde es richtig gehen? :) Bzw warum passiert der Absturz genau 
an der Stelle?

von Monk (roehrmond)


Lesenswert?

Mathias schrieb:
> dachte Race Condidtions können nur bei mehreren Threads
> auftreten

main() und alle Interrupt-Routinen sind Threads. Also hast du alleine 
deswegen schon mindestens zwei Threads. Alles worauf diese gemeinsam 
zugreifen bedarf besonderer Vorsicht.

von Εrnst B. (ernst)


Angehängte Dateien:

Lesenswert?

Mathias schrieb:
> Bzw warum passiert der Absturz genau
> an der Stelle?

ich hab keine große Freude daran, diese LowPower-Bibliothek zu debuggen. 
Das darf gerne jemand anderes machen.
Auf den ersten Blick: an den beiden markierten Stellen könnte die 
Watchdog-ISR ausgeführt werden, und ein "wdt_disable()" reinmogeln.

Führt das zum Absturz? Zum sleep ohne wakeup? Keine Ahnung. Möglich.

von Mathias (ribisl)


Lesenswert?

Εrnst B. schrieb:
> ich hab keine große Freude daran, diese LowPower-Bibliothek zu debuggen

Ich hab weiter oben eine Version ohne LowPower gemacht;)

von Εrnst B. (ernst)


Lesenswert?

Mathias schrieb:
> Ich hab weiter oben eine Version ohne LowPower gemacht;)

die hat schonmal den Vorteil, dass du den Watchdog-counter zurücksetzt, 
bevor du den interrupt-mode aktivierst.

der LowPower Code hat das nicht gemacht, was durchaus der Grund dafür 
sein könnte, dass die ISR unerwartet früh ausgeführt wird.

von Monk (roehrmond)


Lesenswert?

Ich finde es nicht so schlau, den Watchdog Timer wechselweise als 
Watchdog und zum Aufwachen zu benutzen. Immer wenn der Mikrocontroller 
schläft, ist der Watchdog deaktiviert. Wenn er sich dabei aufhängt, 
bleibt er für immer hängen.

Εrnst B. schrieb:
> Also: andere Aufwach-Zeitquelle verwenden, auch wenn dann der
> Schlafmodus nicht ganz so stromsparend ausfallen kann.

von Mathias (ribisl)


Lesenswert?

Εrnst B. schrieb:
> Watchdog-ISR ausgeführt werden, und ein "wdt_disable()" reinmogeln.

wenn ich hier ein wdt_disable() einbauen würde, dann würde ja nie mehr 
aufgewacht werden oder?

Steve schrieb:
> Immer wenn der Mikrocontroller
> schläft, ist der Watchdog deaktiviert.

sollte er eigentlich nicht sein, wenn der uC schläft ist der WDT auf 
500ms und Interrupt eingestellt?

von Εrnst B. (ernst)


Lesenswert?

Mathias schrieb:
> Εrnst B. schrieb:
>> Watchdog-ISR ausgeführt werden, und ein "wdt_disable()" reinmogeln.
>
> wenn ich hier ein wdt_disable() einbauen würde, dann würde ja nie mehr
> aufgewacht werden oder?

Genau das könnte bei deinen Abstürzen passiert sein. Nur dass das 
"wdt_disable()" natürlich nicht direkt an der Stelle steht, sondern in 
der ISR (WDT_vect)

Das ist exakt eine der Sachen, die man beachten muss, und die die 
Autoren der LowPower-Lib vermasselt haben:
ISRs können den Programmfluss immer unterbrechen, solange nicht per CLI 
verboten, und dann an den ungünstigsten Stellen ausgeführt werden.

von Monk (roehrmond)


Lesenswert?

Mathias schrieb:
>> Immer wenn der Mikrocontroller
>> schläft, ist der Watchdog deaktiviert.

> sollte er eigentlich nicht sein,

Aber im Sleep macht der Watchdog nur einen Interrupt, keinen Reset. Wenn 
der µC hängt, führt er den Interrupt mit einer gewissen 
Wahrscheinlichkeit auch nicht mehr aus.

von Mathias (ribisl)


Lesenswert?

Εrnst B. schrieb:
> Genau das könnte bei deinen Abstürzen passiert sein. Nur dass das
> "wdt_disable()" natürlich nicht direkt an der Stelle steht, sondern in
> der ISR (WDT_vect)

hab jetzt mal versucht, das ist mein Ergebnis:
1
#include <Arduino.h>
2
#include <avr/sleep.h>
3
#include <avr/wdt.h>
4
5
inline void sleep()
6
{
7
    cli();
8
    wdt_reset();
9
    WDTCSR |= (1 << WDCE) | (1 << WDE);
10
    WDTCSR = (1 << WDIE) | (1 << WDP2) | (1 << WDP0) | (0 << WDE); // Interrupt and 0.5s
11
    wdt_reset();
12
    
13
    Serial.println("1");
14
    Serial.flush();
15
16
    SMCR &= ~0x0e;
17
    SMCR |= 0x01 | 0x04; // SE + SM1 (power-down)
18
    sei();
19
    sleep_cpu();
20
21
    Serial.println("2");
22
    Serial.flush();
23
24
    cli();
25
    wdt_reset();
26
    WDTCSR |= (1 << WDCE) | (1 << WDIE) | (1 << WDE); // Watchdog  Mode = Interrupt Mode && System Reset Mode // do not put this in ISR
27
    WDTCSR = (1 << WDE) | (1 << WDP3) | (1 << WDP0);     // Interrupt and 8s
28
    sei();
29
}
30
31
ISR(WDT_vect)
32
{
33
    wdt_disable();
34
    Serial.println("ISR");
35
    Serial.flush();
36
}
37
38
void setup()
39
{
40
    bool _WDRF = ((1 << WDRF) & MCUSR); // Watchdog Reset
41
    if (_WDRF)
42
    {
43
        cli();
44
        MCUSR &= ~(1 << WDRF);
45
        WDTCSR |= (1 << WDCE) | (1 << WDE);
46
        WDTCSR = 0;
47
        sei();
48
    }
49
    bool _BORF = ((1 << BORF) & MCUSR);   // BrownOut Reset
50
    bool _EXTRF = ((1 << EXTRF) & MCUSR); // External Reset
51
    bool _PORF = ((1 << PORF) & MCUSR);   // Power-Off Reset
52
    MCUSR = 0;
53
54
    pinMode(6, OUTPUT);
55
56
    Serial.begin(4800);
57
    if (_WDRF)
58
        Serial.println("WDT Triggered");
59
    Serial.println(F("Start Setup"));
60
    delay(3000);
61
62
    cli();
63
    WDTCSR |= (1 << WDIE) | (1 << WDE); // Watchdog  Mode = Interrupt Mode && System Reset Mode
64
    wdt_enable(WDTO_8S);
65
    wdt_reset();
66
    sei();
67
}
68
void loop()
69
{
70
    static int x = 0;
71
    wdt_reset();
72
    digitalWrite(6, x++ % 2 == 0);
73
    sleep();
74
}

Mit der seriellen Ausgabe hab ich herausgefunden, dass er nach "1" 
manchmal in den Setup wechselt.
Was auch relativ eigenartig ist, dass er fast sofort in nach "1" 
wechselt, also ohne die 8s abzuwarten
1
15:27:47.970 > ISR
2
15:27:47.980 > 2
3
15:27:47.988 > 1
4
15:27:48.527 > Start Setup
5
15:27:49.018 > ISR

PS das selbe Verhalten ist auch ohne Seriellen Monitor sichtbar. Und es 
kommt auch die WDT Triggered Nachricht nicht!

: Bearbeitet durch User
von Monk (roehrmond)


Lesenswert?

> Serial.println("ISR");

Das funktioniert nur mit Glück. Wenn der Sendepuffer gerade voll ist, 
hängt sich der µC auf. Denn dann wartet println() für immer, dass der 
Puffer frei wird, wird er aber nie.

Und zwar deswegen nicht, weil der AVR nicht gleichzeitig die ISR vom 
seriellen port ausführen kann, welche den Puffer senden und somit Platz 
schaffen würde.

von Mathias (ribisl)


Lesenswert?

Steve schrieb:
> Wenn der Sendepuffer gerade voll ist,
> hängt sich der µC auf.

sollte hier aber nicht der Fall sein oder? Ohne serielle Schnittstelle 
passiert ja dasselbe.

Bin jetzt gerade auch drauf gekommen, dass alle Reset Reasons 0 sind, 
vielleicht hilft das ja auch was.

von Εrnst B. (ernst)


Lesenswert?

Mathias schrieb:
> Und es
> kommt auch die WDT Triggered Nachricht nicht!

Das Auslesen von MCUSR in der setup() zum herausfinden der Reset-Quelle 
muss nicht klappen. Da läuft vorher noch der Bootloader und 
Arduino-code, die das Register u.U. schon gelöscht haben.
Hab ich nicht geprüft --> selber nachschauen.

Warum verwendest du nicht die sleep_enable() und 
set_sleep_mode(SLEEP_MODE_PWR_DOWN)
aus der avr/sleep.h?

und welchen Sinn siehst du im wdt_disable() in deiner ISR?

von Peter D. (peda)


Lesenswert?

Steve schrieb:
> Ich finde es nicht so schlau, den Watchdog Timer wechselweise als
> Watchdog und zum Aufwachen zu benutzen.

Es sollte schon gehen.
Einfach vor dem sleep_cpu(); den Watchdog auf 500ms, Interrupt setzen.
Und danach, also wenn er aufgewacht ist, den Watchdog auf 8s, Reset 
setzen. Der Interrupt bleibt leer:
EMPTY_INTERRUPT(WDT_vect);

Zum Testen kann man per Pinabfrage die Loop irgendwo anhalten d.h. in 
eine Endlosschleife gehen. Nur dann darf ein Reset erfolgen (nach 8,5s).

Ein wdt_disable(); darf nirgends stehen!

Und Sleepmode einstellen erfolgt nur einmalig im Setup, d.h. da dreht 
man nicht mehr ständig dran rum. Bzw. nur, wenn man definitiv nicht mehr 
schlafen will.

von Mathias (ribisl)


Lesenswert?

Εrnst B. schrieb:
> Das Auslesen von MCUSR in der setup() zum herausfinden der Reset-Quelle
> muss nicht klappen. Da läuft vorher noch der Bootloader und
> Arduino-code, die das Register u.U. schon gelöscht haben.
> Hab ich nicht geprüft --> selber nachschauen.

ist nicht der Fall, wenn ich im LOOP ein +8s delay hineingebe, erscheint 
die wdt triggered Nachricht und der Reset Modus wird auch angezeigt.

Εrnst B. schrieb:
> Warum verwendest du nicht die sleep_enable() und
> set_sleep_mode(SLEEP_MODE_PWR_DOWN)
> aus der avr/sleep.h?

Weil es keinen Unterschied macht.

Εrnst B. schrieb:
> und welchen Sinn siehst du im wdt_disable() in deiner ISR?

Ich dachte mir da ich den WDT eh danach wieder direkt aktiviere kann ich 
ihn hier deaktivieren. Sollte die ISR leer bleiben?

von Peter D. (peda)


Lesenswert?

Mathias schrieb:
> Und es
> kommt auch die WDT Triggered Nachricht nicht!

Im Power-Down macht die UART ja auch nichts.
Alle Hardwaren außer Watchdog, Pin-Change und async T2 schlafen tief und 
fest.
Willst Du noch was machen, darfst Du maximal in Idle gehen.

von Mathias (ribisl)


Lesenswert?

Peter D. schrieb:
> Es sollte schon gehen.
> Einfach vor dem sleep_cpu(); den Watchdog auf 500ms, Interrupt setzen.
> Und danach, also wenn er aufgewacht ist, den Watchdog auf 8s, Reset
> setzen. Der Interrupt bleibt leer:
> EMPTY_INTERRUPT(WDT_vect);

Sollte dann so ja prinzipiell stimmen oder?
1
inline void sleep()
2
{
3
    cli();
4
    wdt_reset();
5
    WDTCSR |= (1 << WDCE) | (1 << WDE);
6
    WDTCSR = (1 << WDIE) | (1 << WDP2) | (1 << WDP0) | (0 << WDE); // Interrupt and 0.5s
7
    wdt_reset();
8
9
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
10
    sleep_enable();
11
    sei();
12
    sleep_cpu();
13
14
    cli();
15
    wdt_reset();
16
    WDTCSR |= (1 << WDCE) | (1 << WDIE) | (1 << WDE); // Watchdog  Mode = Interrupt Mode && System Reset Mode // do not put this in ISR
17
    WDTCSR = (1 << WDE) | (1 << WDP3) | (1 << WDP0);     // Interrupt and 8s
18
    sei();
19
}
20
21
EMPTY_INTERRUPT(WDT_vect);

    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
*sollten nur im setup sein()

Peter D. schrieb:
> Mathias schrieb:
>> Und es
>> kommt auch die WDT Triggered Nachricht nicht!
>
> Im Power-Down macht die UART ja auch nichts.

Die WDT Triggered Nachricht kommt ja auch erst wieder im Setup nach dem 
das WDRF Bit gesetzt wurde

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

an Mathias:

Ist bekannt, dass der ATmega328P im Modus Power-save mit einem 32 
KiHz-Quarz so um ein uA herum, je nach Ucc, benötigt?

von Mathias (ribisl)


Lesenswert?

S. Landolt schrieb:
> Ist bekannt, dass der ATmega328P im Modus Power-save mit einem 32
> KiHz-Quarz so um ein uA herum, je nach Ucc, benötigt?

Ja ist bekannt aber nicht möglich da 32kHz zu langsam sind und ich es 
gerne auf die Weise schaffen möchte.

von S. Landolt (Gast)


Lesenswert?

> da 32kHz zu langsam

Der Controller läuft dann natürlich mit dem internen RC-Oszillator, also 
z.B. mit 8 MHz. Die 32 KiHz sind nur für den asynchronen Timer2.

> gerne auf die Weise schaffen möchte

Okay - ich lese interessiert mit.

von Peter D. (peda)


Lesenswert?

Ist denn überhaupt Batteriebetrieb vorgesehen?
Bei Netzberieb laß den MC einfach durchlaufen, der wird nicht heiß.
Schon der 7805 im Netzteil zieht allein bis zu 8mA, da ist es Unsinn, um 
µA zu feilschen.

von Peter D. (peda)


Lesenswert?

Mathias schrieb:
> Sollte dann so ja prinzipiell stimmen oder?

Sieht erstmal nicht schlecht aus.

Nun ist auch klar, wo das Watchdog-Reset herkommt. Dein UART-Puffer 
läuft voll und die CPU wartet, bis sie senden darf, darf sie aber im 
Power-Down nie. Setz mal auf Idle, dann sollte es klappen.

von Εrnst B. (ernst)


Lesenswert?

S. Landolt schrieb:
> Ist bekannt, dass der ATmega328P im Modus Power-save mit einem 32
> KiHz-Quarz so um ein uA herum, je nach Ucc, benötigt?

Hast du das Nachgemessen? Im Datenblatt hab ich nur Angaben zum WDT 
gefunden, der den Stromverbrauch je nach VCC um so 6µA hochtreibt.

Εrnst B. schrieb:
> Timer2 im Async-Mode mit 32kHz Uhrenquarz wäre eine Möglichkeit, der
> läuft im Power-Save Modus weiter, und kann die CPU wieder aufwecken.
> Stomverbrauch müsstest du nachmessen.

Damit wäre ja für Low-Power/Batterie-Anwendungen ein Betrieb ohne WDT 
aber mit Uhrenquarz besser.

von S. Landolt (Gast)


Lesenswert?

> Hast du das Nachgemessen?

Ist schon eine Weile her:
1
power-save
2
RTC per 32 KiHz-Quarz
3
4
ATmega328P-PU (1426):
5
1.38 uA 5.0 V
6
1.04 uA 3.3 V
7
0.99 uA 3.0 V
8
0.76 uA 1.8 V

von Εrnst B. (ernst)


Lesenswert?

Danke!

S. Landolt schrieb:
> 0.99 uA 3.0 V

Reicht also für gefühlt "ewig" an einer CR2032.

von Mathias (ribisl)


Lesenswert?

Peter D. schrieb:
> Ist denn überhaupt Batteriebetrieb vorgesehen?

Ja Batteriebetrieb ist vorgesehen!

Habe jetzt auch herausgefunden, dass wenn ich den Mikrocontroller etwas 
erhitze das ganze sehr stabil läuft. Läuft jetzt seit einer Stunde ohne 
einen einzigen Reset.
Natürlich könnte es jetzt ein anderes Hardware Problem sein, deshalb 
habe ich mal einen "problematischen" uC mit einem neuen augetauscht und 
es läuft mit dem neuen auch ohne Probleme.

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.