zurück

Watchdog-Timer am Arduino (ATmega)

21.12.2017 (Update: 29.05.2020)
Watchdog

Es kann immer mal vorkommen, dass es durch Programmierfehler oder externe Einflüsse (z.B. Sensoren) zu unvorhergesehenen Fehlfunktionen oder Verzögerungen im Programmablauf kommt. Damit das Programm dann nicht vollständig hängen bleibt (z.B. in einer Endlosschleife), kann der sogenannte Watchdog-Timer des ATmega 328P aushelfen.
Wichtig: Der Watchdog-Timer verhindert nicht das Einfrieren, wenn der Code selbst unsauber programmiert ist!

Alle ATmega-Mikrocontroller verfügen über einen Watchdog-Timer, welcher als zusätzlicher Zähler (Timer) auf dem Chip mit einem eigenem 128kHz-Taktoszillator implementiert ist. Wird dieser einmal aktiviert, so wird der Zähler unabhängig vom laufenden Programm regelmäßig erhöht und löst beim Überlauf einen Reset des Mikrocontrollers aus, falls er nicht rechtzeitig von der Software zurückgesetzt wird.

Funktionsweise

Zunächst wird der Watchdog-Timer im setup()-Teil des Sketches mit dem Befehl wdt_enable() aktiviert, wobei auch gleichzeitig der Zähler auf 0 gesetzt wird. Am Ende der loop()-Schleife wird mit dem Befehl wdt_reset() der Zähler wieder zurück auf 0 gesetzt, damit der Watchdog nicht auslöst. Daher sollte auch die Intervall-Zeit des Watchdog-Timers so hoch gesetzt werden, dass sie in einem regulären Programmdurchlauf keinen Reset auslöst, sondern nur, falls das Programm aus einem Grund stehen bleibt oder zu lange mit der Verarbeitung benötigt.

Sketch

Für den folgenden Sketch wird nur ein Arduino UNO (oder kompatibles Board) benötigt sowie eine serielle Konsole für die Ausgabe.

#include <avr/wdt.h>

void setup()
{
    Serial.begin(9600);
    Serial.println("Begin/reset program");

    // set the watchdog timer to 2 seconds
    wdt_enable(WDTO_2S);
}

void loop()
{
    Serial.println("Begin of loop");

    /* simulate a long runtime of the sketch
     * so the watchdog timer is never been resetted.
     */
    for(byte i=0; i<10; i++) {
        Serial.println(i);
        delay(500);
    }

    Serial.println("End of loop");
    wdt_reset();
}

Zum deaktivieren des Watchdog-Timers wird der Befehl wdt_disable() verwendet.

Zeiten

Es können verschiedene Reset-Zeiten in wdt_enable() für den Watchdog-Timer gesetzt werden: (die Prescaler-Bits werden nur benötigt, wenn man die Register des Watchdog-Timers direkt manipulieren möchte)

Zeit Konstante Prescaler-Bits
WDP0 WDP1 WDP2 WDP3
16 ms WDTO_15MS 0 0 0 0
32 ms WDTO_30MS 1 0 0 0
64 ms WDTO_60MS 0 1 0 0
125 ms WDTO_120MS 1 1 0 0
250 ms WDTO_250MS 0 0 1 0
500 ms WDTO_500MS 1 0 1 0
1 s WDTO_1S 0 1 1 0
2 s WDTO_2S 1 1 1 0
4 s WDTO_4S 0 0 0 1
8 s WDTO_8S 1 0 0 1

Software-Reset

Manchmal ist jedoch kein Hardware-Reset erwünscht, wie oben durch den Watchdog ausgelöst. Um einen Software-Reset auszuführen muss nur der Programmablauf an Adresse 0 fortgesetzt werden.
Man kann dazu sowohl eine Funktion in Inline-Assembler schreiben:
void softReset(void) { asm volatile ("jmp 0 \n"); }
als auch direkt in C-Code:
void (*softReset)(void) = 0;
Durch das Aufrufen eines dieser beiden Funktionen wird der Mikrocontroller softwareseitig zurückgesetzt.

Sketch

void (*softReset)(void) = 0;

void setup()
{
    Serial.begin(9600);
    Serial.println("Start Arduino");
}

void loop()
{
    for(byte i=0; i<10; i++) {
        Serial.println(i);
        delay(500);
    }

    Serial.println("Initiate soft reset...");
    softReset();
}