Forum: Mikrocontroller und Digitale Elektronik Watchdog im Interrupt-Mode


von M. K. (sylaina)


Lesenswert?

Hallo alle zusammen,

ich möchte den Watchdog des AVRs (hier ein Mega328p) im Interrupt-Mode 
(nicht System-Reset)benutzen. Das habe ich auch schon gemacht und 
funktionierte immer prima, allerdings war der Time-out immer nur auf den 
16 ms eingestellt. Jetzt will ich allerdings eine andere Zeit 
einstellen, das klappt aber nicht. Ich bin einfach zu doof dazu ne 
andere Zeit einzustellen, kann mir mal jemand bitte das Brett vom Kopp 
nehmen? Hier mein Bleistift:
1
#include "lcd.h"
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
void setUpAvr(void){
6
    /* insert your hardware initialization here */
7
    lcd_init(LCD_DISP_ON);
8
    lcd_clrscr();
9
    lcd_puts("WDT Test");
10
    
11
    WDTCSR |= (1 << WDCE)|(1 << WDIE)|(1 << WDP2)|(1 << WDP1);
12
    // enable interrupts
13
    sei();
14
}
15
int main(void)
16
{
17
    setUpAvr();
18
    
19
    for(;;){
20
        /* insert your main loop code here */
21
        asm("wdr");
22
        _delay_ms(25);
23
    }
24
    return 0;   /* never reached */
25
}
26
ISR(WDT_vect){
27
    lcd_clrscr();
28
    lcd_puts("Watchdog");
29
    _delay_ms(1000);
30
}

von Carl D. (jcw2)


Lesenswert?

Setzen von WDCE und Änderung am Prescaler darf nicht zeitgleich 
erfolgen.
Erst WDCE setzen, dann den Rest.

Atmel:
1
void WDT_Prescaler_Change(void) { 
2
  __disable_interrupt(); 
3
  __watchdog_reset();
4
  /* Start timed  equence */ 
5
  WDTCSR |= (1<<WDCE) | (1<<WDE); 
6
  /* Set new prescaler(time-out) value = 64K cycles (~0.5 s) */ 
7
  WDTCSR  = (1<<WDE) | (1<<WDP2) | (1<<WDP0); 
8
  __enable_interrupt();
9
}

von Georg G. (df2au)


Lesenswert?

M. K. schrieb:
> ISR(WDT_vect){
>     lcd_clrscr();
>     lcd_puts("Watchdog");
>     _delay_ms(1000);

In der Service Routine solltest du keine langwierigen Ausgaben machen 
und auch das Delay ist kontraproduktiv. Setze ein Flag und verlege das 
alles in die Hauptschleife.

von Peter D. (peda)


Lesenswert?

M. K. schrieb:
> Jetzt will ich allerdings eine andere Zeit
> einstellen
1
#include <avr/wdt.h>
2
3
  wdt_enable( WDTO_2S );

von M. K. (sylaina)


Lesenswert?

Georg G. schrieb:
> In der Service Routine solltest du keine langwierigen Ausgaben machen
> und auch das Delay ist kontraproduktiv. Setze ein Flag und verlege das
> alles in die Hauptschleife.

Das ist ja nur ein Beispiel-Code der das Problem zeigt ;)

Peter D. schrieb:
> M. K. schrieb:
>> Jetzt will ich allerdings eine andere Zeit
>> einstellen
>
>
1
> #include <avr/wdt.h>
2
> 
3
>   wdt_enable( WDTO_2S );
4
>

Ich will keinen System-Reset, macht die Verwendung von der wdt.h aber 
immer soweit ich weiß. However, ich habs probiert. Die ISR wird hierbei 
anscheinend nicht angesprungen. Das Display blitzt kurz auf (mit Oszi 
hab ich noch nicht geschaut, tippe aber auf 16 ms ;)) und bleibt dann 
Dunkel.

Carl D. schrieb:
> Setzen von WDCE und Änderung am Prescaler darf nicht zeitgleich
> erfolgen.
> Erst WDCE setzen, dann den Rest.

Das ändert leider nichts an dem Problem.

von Karl M. (Gast)


Lesenswert?

Hallo M. Köhler,

M. K. schrieb:
> Das ändert leider nichts an dem Problem.

Ich mache es immer so, wie es im zugehörigen Datenblatt beschrieben 
wurde.
Damit hatte ich nie Probleme.
Man muss evtl. noch das globale Interrupt Flag abschalten, um im 
Zeitrahmen zu bleiben.
Ich schreibe deshalb diese Abschnitt in Assembler.

von S. Landolt (Gast)


Lesenswert?

Karl M. hat Recht.
Konkret sieht es bei mir in Assembler so aus:
1
  cli
2
  wdr
3
  lds    tmp0,WDTCSR
4
  ori    tmp0,(1<<WDCE)+(1<<WDE)
5
  sts    WDTCSR,tmp0
6
  ldi    tmp0,(1<<WDIE)+(1<<WDP2)+(1<<WDP1)+(1<<WDP0)  ; 2 s
7
  sts    WDTCSR,tmp0
8
  sei

von S. Landolt (Gast)


Lesenswert?

Ohne 'Vorleben' geht es auch etwas kürzer:
1
  ldi    tmp0,(1<<WDCE)+(1<<WDE)
2
  sts    WDTCSR,tmp0
3
  ldi    tmp0,(1<<WDIE)+(1<<WDP2)+(1<<WDP1)+(1<<WDP0)  ; 2 s
4
  sts    WDTCSR,tmp0
5
  sei

von M. K. (sylaina)


Lesenswert?

Ich hab das Brett gefunden. Ich schrieb:
1
...
2
WDTCSR |= (1 << WDCE)|(1 << WDE);
3
WDTCSR |= (1 << WDP2)|(1 << WDP1);
4
...

Und richtig wars/ists:
1
...
2
WDTCSR = (1 << WDCE)|(1 << WDE);
3
WDTCSR = (1 << WDP2)|(1 << WDP1);
4
...

Was für ein blödes Brett...

von Karl M. (Gast)


Lesenswert?

M. K. schrieb:
> Und richtig wars/ists:
> ...
> WDTCSR = (1 << WDCE)|(1 << WDE);
> WDTCSR = (1 << WDP2)|(1 << WDP1);

Nicht wirklich !

Was ist mit dem Bit WDIE und der zugehörigen Interruptfreigabe?

Ich schreibe mir dazu gerne alle Register und deren Bit-Namen aus dem 
Datenblatt in den Quellcode:

MCU Status Register
MCUSR
– – – – WDRF BORF EXTRF PORF
MCUSR &= ~(1<<<WDRF)

Watchdog Timer Control Register
WDTCSR
WDIF WDIE WDP3 WDCE WDE WDP2 WDP1 WDP0
const uint8_t WDR_TIMER_MODE = (0<<WDP3) | (1<<WDP2) | (1<<WDP1) 
(1<<WDP0); // time out aprox. 2 sec
WDTCSR |= (1<<WDIF) | (1<<WDCE) | (1<<WDE);
WDTCSR = (1<<WDIE) | WDR_TIMER_MODE;

von M. K. (sylaina)


Lesenswert?

Karl M. schrieb:
> Was ist mit dem Bit WDIE und der zugehörigen Interruptfreigabe?

Da hast du natürlich recht, das muss noch entsprechen gesetzt werden. 
Kernproblem war halt, dass ich die Zeit nicht umgestellt bekam und das 
oben Beschriebene war das Problem. Ich hätte da nicht verodern dürfen 
wie es auch im Datenblatt steht, das war mein Brett vorm Kopf ;)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.