AVR-Tutorial: Watchdog
Dieser Artikel ist im Entstehen: Die Diskussion wird in [1] geführt.
Der Watchdog im AVR (WDT) ist ein spezieller Timer, der nach Ablauf (typisch ein paar ms) 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[Bearbeiten]
(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/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 (s. Tipps & Tricks).
Steuerung[Bearbeiten]
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 Betriebspannung und der Taktfrequenz des WDT-Oszillators. Sie sollten daher aus dem jeweiligen Datenblatt des µCs entnommen werden.
Beispiel[Bearbeiten]
WDT durch WDTON-Fuse aktivieren[Bearbeiten]
Am Einfachsten läßt es sich durch ein kleines Programmbeispiel demonstrieren.
Ein ATmega8 wird mit 4 MHz des internen Taktgenerators mit einer Startup-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 Startuptime
; 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)
out SPH, Temp1
ldi Temp1, LOW(RAMEND) ; Stackpointer initialisieren
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 weggelasen werden, da nach einem Reset das Register
; auf 0x00 steht, WDT Reset nach 16ms
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 x 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 x pro Sekunde statt (500ms)
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 Startup-Zeit von 64 ms und einem Time-out von 16ms, mit rund 6 Hz. 1/(64ms + 16ms) ~ 12 Hz (halbe Periode)
WDT durch Software aktivieren/deaktivieren[Bearbeiten]
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[Bearbeiten]
Wem die Timer im AVR nicht reichen kann den WD 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 (ATtiny2313 eine WDT-Periode bis zu 8sek.) 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[Bearbeiten]
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 ([2])
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 folgendem Thread: [3]
WDT gezielt zum Reset verwenden[Bearbeiten]
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.
Variante1: 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 ein Wert ungleich Null gesetzt.
Variante 2: Programm anhalten und auf WDT-Reset warten
rjmp PC
Resetquelle auswerten[Bearbeiten]
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 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[Bearbeiten]
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 [4]. Beispiel in C: Pollin Funk-AVR-Evaluationsboard: Pennen bis der Hund bellt.
WDTON Fuse zurücksetzen[Bearbeiten]
Die Änderung der WDTON Fuse wird erst nach einer Spannungsunterbrechung wirksam. Ein Reset ist nicht ausreichend. [5]
Weblinks[Bearbeiten]
- Li'l Bow Wow By Jack Ganssle, Embedded Systems Design (engl.)
- Watchdog Reset Flag auswerten Erklärung, wie das WDRF unter C abgefragt wird