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