Der Watchdog

Und hier kommt das ultimative Mittel gegen die Unvollkommenheit von uns Programmierern, der Watchdog.

So sehr wir uns auch anstrengen, es wird uns kaum je gelingen, das absolut perfekte und fehlerfreie Programm zu entwickeln.
Der Watchdog kann uns zwar auch nicht zu besseren Programmen verhelfen aber er kann dafür sorgen, dass unser Programm, wenn es sich wieder mal in's Nirwana verabschiedet hat, neu gestartet wird, indem ein Reset des Controllers ausgelöst wird.

Betrachten wir doch einmal folgende Codesequenz:

unsigned char x;

x = 10;

while (x >= 0) {
    // tu was
    x--;
}

Wenn wir die Schleife mal genau anschauen sollte uns auffallen, dass dieselbe niemals beendet wird. Warum nicht? Ganz einfach, weil eine als unsigned deklarierte Variable niemals kleiner als Null werden kann.
Das Programm würde sich also hier aufhängen und auf ewig in der Schleife drehen.
Und hier genau kommt der Watchdog zum Zug.

Wie funktioniert nun der Watchdog

Der Watchdog enthält einen separaten Timer/Counter, welcher mit einem intern erzeugten Takt von 1 MHz bei 5V Vcc getaktet wird. Nachdem der Watchdog aktiviert und der gewünschte Vorteiler eingestellt wurde beginnt der Counter von 0 an hochzuzählen. Wenn nun die je nach Vorteiler eingestellte Anzahl Zyklen erreicht wurde löst der Watchdog einen Reset aus. Um nun also im Normalbetrieb den Reset zu verhindern müssen wir den Watchdog regelmässig wieder neu starten bzw. Rücksetzen (Watchdog Reset). Dies sollte innerehalb unserer Hauptschleife passieren.

Um ein unbeabsichtigtes Ausschalten des Watchdogs zu verhindern muss ein spezielles Prozedere verwendet werden um den WD auszuschalten und zwar müssen zuerst die beiden Bits WDTOE und WDE in einer einzelnen Operation (also nicht mit sbi) auf 1 gesetzt werden. Dann muss innerhalb der nächsten 4 Taktzyklen das Bit WDE auf 0 gesetzt werden.

Das Watchdog Control Register:

WDTCR

Watchog Timer  Control Register
In diesem Register stellen wir ein, wie wir den Watchdog verwenden möchten.
Das Register ist wie folgt aufgebaut:

Bit 7 6 5 4 3 2 1 0
Name - - - WDTOE WDE WDP2 WDP1 WDP0
R/W R R R R/W R/W R/W R/W R/W
Initialwert 0 0 0 0 0 0 0 0
WDTOE Watchdog Turn Off Enable
Dieses Bit muss gesetzt sein, wenn das Bit WDE gelöscht wird, andernfalls wird der Watchdog nicht ausgeschaltet.
Wenn das Bit einmal gesetzt ist wird es von der Hardware nach 4 Taktzyklen automatisch wieder gelöscht.
WDE Watchdog Enable
Wenn dieses Bit gesetzt wird so wird der Watchdog aktiviert.
Das Bit kann nur gelöscht werden solange das Bit WDTOE auf 1 steht.
WDP2
WDP1
WDP0
Watchdog Timer Prescaler Bits
Diese 3 Bits bestimmen die Anzahl Oszillatorzyklen für den Watchdog, also, wie lange es dauert, bis ein Reset ausgelöst wird:
WDP2 WDP1 WDP0 Anzahl Zyklen Typ. Timeoutzeit bei Vcc = 3V Typ. Timeoutzeit bei Vcc = 5V
0 0 0 16K 47ms 15ms
0 0 1 32K 94ms 30ms
0 1 0 64K 0.19s 60ms
0 1 1 128K 0.38s 0.12s
1 0 0 256K 0.75s 0.24s
1 0 1 512K 1.5s 0.49s
1 1 0 1024K 3s 0.97s
1 1 1 2048K 6s 1.9s

Um den Watchdog mit dem AVR-GCC Compiler zu verwenden muss die Headerdatei wdt.h in die Quelldatei eingebunden werden. Dadurch wird auch der Startup-Code entsprechend angepasst, so dass der Watchdog nach einem Reset automatisch gestartet wird. Das WDTCR-Register wird dabei mit dem Wert 0 beschrieben. Falls ein anderer Wert gewünscht ist so kann dies im Makfile in den Linker-Optionen eingetragen werden. Dazu muss in der Zeile LDFLAGS folgende Option angefügt werden:
    --defsym __init_wdtcr__=0x1f
wenn beispielsweise der Wert des Registers auf 0x1f gestellt werden soll.
Danach können die folgenden Funktionen verwendet werden:

wdt_disable();
Mit dieser Funktion kann der Watchdog ausgeschaltet werden. Dabei wird das notwendige Prozedere, wie oben beschrieben, automatisch ausgeführt.

wdt_enable(uint8_t timeout);
Aktiviert den Watchdog und stellt den Vorteiler auf den gewünschten Wert ein bzw. der in timeout übergebene Wert wird in das WDTCR-Register eingetragen.

wdt_reset();
Dies ist wohl die wichtigste der Watchdog-Funktionen. Sie erzeugt einen Watchdog-Reset, welcher periodisch, und zwar vor Ablauf der Timeoutzeit, ausgeführt werden muss, damit der Watchdog nicht etwa unbeabsichtigt den AVR zurücksetzt.

Selbstverständlich kann das WDTCR-Register auch mit den uns bereits bekannten Funktionen für den Zugriff auf Register programmiert werden.

Ein paar grundsätzliche Gedanken

Ob nun so ein Watchdog überhaupt verwendet werden soll ist wohl eher eine philosophische Frage und hängt vom Geschmack jedes einzelnen Entwicklers ab.

Ich persönlich habe es lieber, wenn ich auch merke, dass in meine Programm etwas noch nicht in Ordnung ist, als dass mir ein Watchdog einfach die CPU immer wieder zurücksetzt, denn immerhin kann es so passieren, dass ein Programm über Jahre hinweg so einigermassen läuft, obwohl noch ein voll krasser Bock drin liegt.