AVR-Tutorial: Watchdog
Dieser Artikel ist im Entstehen: Die Diskussion wird in diesem Forumsthread geführt.
Der Watchdog im AVR (WDT) ist ein spezieller Timer, der nach Ablauf (typisch ein paar Millisekunden) automatisch einen Reset auslöst. Im Normalbetrieb wird der Watchdog in der Hauptschleife des Programms regelmäßig zurückgesetzt. Wenn durch einen Fehler dieses Zurücksetzen nicht mehr stattfindet, läuft der Watchdog-Timer ab und löst einen Reset aus, um den Mikrocontroller und damit das Programm neu zu starten.
Anwendung
(Nach Ganssle-03)
Der Watchdog ist im Prinzip zur Stelle, wenn kein Anwender da ist, um den Resetknopf zu drücken.
Der Watchdog bringt dabei das System aus einem unvorhergesehenen Fehlerzustand wieder in einen betriebsbereiten Zustand.
Dieser Zustand nach einem WDT-Reset kann je nach Implementierung im Programm sein:
- Debugzustand
- Sicherheitszustand
- Betriebszustand
Den Debugzustand kann man während der Entwicklung nutzen, um unvorhergesehene Ereignisse herauszufinden. Im fertigen System sollten diese durch das Debuggen bekannten Ereignisse korrekt, d. h. nicht über WDT behandelt werden.
Den Sicherheitszustand kann man verwenden, wenn das System aufgrund von Hardwareproblemen den WDT ausgelöst hat. Nach dem Reset durch den WDT wird die Resetquelle (normaler Reset oder WDT-Reset?) ausgewertet und das System bzw. die Hardware geprüft und ggf. in eine sichere Konfiguration statt in den normalen Betrieb gebracht.
Der normale Betriebszustand ist im Prinzip ein Sonderfall des Sicherheitszustands. Es ist zwar ein unerwartetes Ereignis eingetreten (z. B. einzelner zufälliger Speicherlesefehler), aber ein Neustart des Programms scheint nach einer Neuinitialisierung möglich. Ein Sonderfall ist die Anwendung des WDT zum bewussten Reset (siehe Tipps & Tricks).
Steuerung
Der WDT wird durch das Watchdog Timer Control Register (WDTCR) gesteuert.
— | — | — | WDCE[1] | WDE[2] | WDP2[3] | WDP1[3] | WDP0[3] |
---|---|---|---|---|---|---|---|
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
WDP2 | WDP1 | WDP0 | Time-out nach [ms] |
---|---|---|---|
0 | 0 | 0 | 16,3 |
0 | 0 | 1 | 32,5 |
0 | 1 | 0 | 65 |
0 | 1 | 1 | 130 |
1 | 0 | 0 | 260 |
1 | 0 | 1 | 520 |
1 | 1 | 0 | 1100 |
1 | 1 | 1 | 2100 |
- Achtung
- Die Zeiten sind abhängig von der Betriebsspannung und der Taktfrequenz des WDT-Oszillators. Sie sollten daher aus dem jeweiligen Datenblatt des Mikrocontrollers entnommen werden.
Beispiele
WDT durch WDTON-Fuse aktivieren
Am einfachsten läßt es sich durch ein kleines Programmbeispiel demonstrieren.
Ein ATmega8 wird mit 4 MHz des internen Taktgenerators mit einer Start-up-Zeit von 64 ms getaktet. Die WDTON-Fuse ist gesetzt (WDT aktiviert). An Port B ist eine LED angeschlossen (Pin egal).
.include "m8def.inc"
; ATmega8L mit internen 4 MHz getaktet + 64 ms Start-up-Zeit
; WDTON aktiviert!
.def Temp1 = R16
.def SubCount = R17
.org 0x0000
rjmp Reset ; Reset Handler
.org OC1Aaddr
rjmp timer1_compare ; Timer Compare Handler
Reset:
ldi Temp1, HIGH(RAMEND) ; Stackpointer initialisieren
out SPH, Temp1
ldi Temp1, LOW(RAMEND)
out SPL, Temp1
ldi Temp1, 0xFF ; Port B auf Ausgang
out DDRB, Temp1
ldi Temp1, high(40000 - 1)
out OCR1AH, temp1
ldi Temp1, low(40000 - 1)
out OCR1AL, temp1
; CTC Modus einschalten
; Vorteiler auf 1
ldi Temp1, ( 1 << WGM12 ) | ( 1 << CS10 )
out TCCR1B, temp1
ldi Temp1, 1 << OCIE1A ; OCIE1A: Interrupt bei Timer Compare
out TIMSK, temp1
; kann auch weggelassen werden, da nach einem Reset das Register
; auf 0x00 steht, WDT-Reset nach 16 ms
ldi Temp1, (0<<WDCE)|(0<<WDE)|(0<<WDP2)|(0<<WDP1)|(0<<WDP0)
out WDTCR, Temp1
sei
Mainloop:
rjmp Mainloop
timer1_compare: ; Timer 1 Output Compare Handler
;** findet 100-mal pro Sekunde statt (10 ms)
wdr ; Watch-Dog-Reset
inc SubCount ; Wenn dies nicht der 50. Interrupt
cpi SubCount, 50 ; ist, dann passiert gar nichts
brne exit_isr
;** findet 2-mal pro Sekunde statt (500 ms)
clr SubCount
;** Port B negieren
in Temp1, PinB
com Temp1
out PortB, Temp1
exit_isr:
reti ; das wars. Interrupt ist fertig
Der Timer 1 läuft im CTC-Modus mit einer Frequenz von 100 Hz (10 ms). Durch den Soft-Subcounter wird die Frequenz auf 2 Hz geteilt und jeweils nach 500 ms das Port B negiert.
Da die LED in diesem Beispiel nach 500 ms jeweils ein- und ausgeschaltet wird, blinkt sie mit einer Frequenz von 1 Hz. Der WDT wird nach 10 ms zurückgesetzt, so dass er keinen RESET auslösen kann.
Wird jetzt der Befehl wdr
auskommentiert, führt der WDT nach 16 ms
einen RESET aus. Die LED blinkt nun, bedingt durch die Start-up-Zeit
von 64 ms und einem Time-out von 16 ms, mit rund 6 Hz:
1/(64 ms + 16 ms) ≈ 12 Hz (halbe Periode)
WDT durch Software aktivieren/deaktivieren
Der WDT läßt sich auch softwaremäßig durch Setzen des WDE-Bits im WDTCR-Register aktivieren.
WDT_on:
in Temp1, WDTCR ; Write logical one to WDE
ori Temp1, (1<<WDE)
out WDTCR, Temp1
ret
Dieses hat den Vorteil, dass man den WDT auch softwaremäßig wieder deaktivieren kann.
Ein Deaktivieren des WDTs ist nicht möglich, wenn die WDTON-Fuse gesetzt ist!
Das softwaremäßige Deaktivieren verlangt allerdings eine besondere Deaktivierungssequenz (in drei Phasen):
WDT_off:
;** 1. Phase
wdr ; reset WDT
;** 2. Phase
in Temp1, WDTCR ; Write logical one to WDCE and WDE
ori Temp1, (1<<WDCE)|(1<<WDE)
in Temp2, SREG ; save I Flag
cli ; we have only 5 cycles to reset WDE
out WDTCR, Temp1
;** 3. Phase
ldi Temp1, (0<<WDE) ; Turn off WDT
out WDTCR, Temp1
out SREG, Temp2 ; restore I Flag
ret
Wenn WDCE und WDE nicht in einem Zug vor dem Deaktivieren auf 1 gesetzt werden, hat das Rücksetzen des WDE-Bits keine Wirkung und der WDT läuft munter weiter!
Dazu hat man maximal 5 Takte Zeit. Diese Sequenz darf auch nicht durch einen Interrupt unterbrochen werden.
Tipps & Tricks
Wem die Timer im AVR nicht reichen, der kann den Watchdog als einen zusätzlichen Timer verwenden. In den neueren AVRs hat der WDT einen eigenen Interruptvector. Dazu den WDT im Interruptmode einstellen. Der AVR springt dann bei abgelaufenem WD-Timer durch den Vector die ISR an. Dort kann man entscheiden, ob ein RESET durchgeführt wird oder ob man nur irgendwelche anderen Operationen ausführt. Besonders wenn lange Timerzeiten benötigt werden (beim ATtiny2313 läuft eine WDT-Periode bis zu 8 Sekunden) ist der WDT interessant. Insbesondere wird das ganze System nicht weiter belastet, da der WDT von einem eigenen Oszillator getaktet wird.
Nötige Einstellungen: WDE=0, WDIE=1, WDTON=1
WDT nach einem Reset
Der WDT bleibt bei manchen AVRs nach einem Reset (ob durch den Watchdog, extern oder aus sonstigen Gründen, also auch über das Flashen einer neuen Software hinweg!) aktiv, wenn er einmal an war. Er läuft danach mit der kürzesten Zeit weiter, da die Prescaler-Bits beim Reset gelöscht werden und somit die Watchdog-Zeit auf die kürzeste Zeit gesetzt wird. Das kann zu unerwarteten Problemen führen (siehe diesen Forumsthread).
Dies steht nicht explizit im Datenblatt, sondern man kann es nur
anhand der Defaultwerte der Bits entnehmen, was viele übersehen.
Dies ist vor allem beim Programmieren in einer Hochsprache wie C wichtig, denn da verwendet man meist Makros für den Watchdog und kommt somit nicht direkt mit den Registern aus dem Datenblatt in Berührung. Weiterhin dauert die Initialisierung der globalen Variablen vor dem Start der main
-Funktion oft länger als die Watchdog-Periode, was dazu führt, dass die main
-Funktion nie erreicht wird.
Der Watchdog-Timer muss daher vorher abgeschaltet werden, was beim gcc über Code in einer speziellen Section geschieht, die unmittelbar nach dem Reset ausgeführt wird.
Ein Beispiel findet sich dazu in
diesem Thread.
WDT gezielt zum Reset verwenden
Man kann den WDT auch verwenden, um gezielt per Software einen Reset des AVR auszulösen. Das wird z. B. im AVR Bootloader FastBoot von Peter Dannegger gemacht.
Variante 1: Ohne Unterbrechung des Programmablaufs über eine Statusvariable oder Statusflag, z. B.:
tst rFlag
brne PC+2
wdr
Um ein WDT-Reset auszulösen wird das Register rFlag an beliebiger Stelle auf einen Wert ungleich null gesetzt.
Variante 2: Programm anhalten und auf WDT-Reset warten
rjmp PC
Resetquelle auswerten
Nach einem WDT-Reset wird die gleiche Adresse (0x0000) angesprungen wie nach einem normalen Reset. Zur Unterscheidung der Reset-Quelle ist eine Auswertung des WDRF-Flags im MCU-Control- und Statusregister (MCUCSR) erforderlich.
in Temp1, MCUCSR ; MCU-Control- und Statusregister lesen
sbrs Temp1, WDRF ; Prüfen, ob Watchdog-Restart erfolgte
rjmp normalReset ; nein: Normalstart
cbr Temp1, 1<<WDRF ; Watchdog-Resetflag zurücksetzen
out MCUCSR, Temp1
rjmp watchDogReset ; WDRStart
Aufwecken aus einem Sleep Mode
Der WDT kann auch verwendet werden, um einen AVR im Rahmen der möglichen WDT-Zeiten zeitgesteuert aus einem Sleep Mode aufzuwecken. Allerdings verbraucht der eingeschaltete WDT einen gewissen Strom (siehe diesen Forumsthread). Beispiel in C: Pollin Funk-AVR-Evaluationsboard: Pennen bis der Hund bellt.
WDTON-Fuse zurücksetzen
Die Änderung der WDTON-Fuse wird erst nach einer Spannungsunterbrechung wirksam. Ein Reset ist nicht ausreichend: siehe diesen Forumsthread.
Siehe auch
- Artikel Watchdog
- Forumsthread Watchdog Reset Flag auswerten: Erklärung, wie das WDRF unter C abgefragt wird
Weblinks
- Ganssle, Jack (2003): Li'l Bow Wow, embedded.com (engl.)