AVR-Tutorial: Watchdog

Wechseln zu: Navigation, Suche

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.

WDTCR
WDCE[1] WDE[2] WDP2[3] WDP1[3] WDP0[3]
7 6 5 4 3 2 1 0
  1. Watch Dog Change Enable; heißt bei ATmega16/32: WDTOE – Watchdog Turn Off Enable
  2. Watch Dog Enable
  3. 3,0 3,1 3,2 Watch Dog Timer Prescaler, bit x


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 AVR's 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]