Forum: Mikrocontroller und Digitale Elektronik ATTiny85 Probleme mit watchdog


von Tobias (Gast)


Lesenswert?

Hallo,
nachdem ich es dank eurer Hilfe geschafft habe, einen ATTiny85 schlafen 
zu legen und mit einem externen Interrupt aufzuwecken wollte ich das nun 
mit Hilfe des watchdog versuchen. Dabei passiert aber folgendes: die LED 
leuchtet 10 Sekunden und geht dann für einen kurzen Moment aus um gleich 
danach wieder 10 Sekunden zu leuchten. Sinn soll sein, dass der watchdog 
den sleep-Modus unterbricht, den mitgeführten Zähler inkrementell erhöht 
und prüft, ob er über einen Schwellenwert gekommen ist für einen 
kompletten reset oder ob er nochmal schlafen gehen darf.
Es würde mich freuen, wenn ich hier wieder etwas Lern-hilfe bekommen 
konnte.
Danke!
1
#include <avr/wdt.h>
2
#include <avr/sleep.h> 
3
#include <avr/power.h>
4
5
unsigned long time;
6
int LED = 0;
7
int watchdog_counter = 0;
8
9
void wdt_first(void) __attribute__((naked)) __attribute__((section(".init3")));
10
void wdt_first(void){
11
 MCUSR = 0;
12
 wdt_disable(); 
13
}
14
15
void setup(){
16
 pinMode (LED, OUTPUT);
17
 digitalWrite (LED, HIGH);
18
}
19
20
void loop(){
21
  time = millis()/1000;
22
  if (time >= 10)
23
  {     
24
   go_sleep();
25
  }
26
}
27
28
//Wenn Hund aufwacht gehts hierhin
29
ISR(WDT_vect) {
30
  watchdog_counter++;
31
  if (watchdog_counter < 10)
32
  {
33
   wdt_reset();
34
  }
35
  else
36
  {
37
   wdt_reboot(); 
38
  }
39
}
40
41
//wdt_enable() klappt bei tinys nicht
42
void wdt_reboot () {
43
 WDTCR = 0xD8 | WDTO_1S;
44
 while (true) {}
45
}
46
47
void go_sleep (){
48
   wdt_enable(WDTO_4S);
49
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);
50
   sleep_enable();
51
   sleep_cpu();  
52
}

von HildeK (Gast)


Lesenswert?

Beim Verwenden des WD-Timers muss dieser nach jedem Auslösen wieder 
aktiviert werden, z.B. so:
1
 WDTCR |=  (1<< WDIE);  // Watchdog Interrupt muss jedes mal neu aktiviert werden,   sonst mach WDT Reset
Ich habe dazu diese Codezeile in der ISR(WDT_vect) drin, sonst wird die 
eigentliche Funktion aktiv: der WD macht Reset.

von Tobias (Gast)


Lesenswert?

Also der Teil so?
1
//Wenn Hund aufwacht gehts hierhin
2
ISR(WDT_vect) {
3
  watchdog_counter++;
4
  WDTCR |=  (1<< WDIE);
5
  if (watchdog_counter < 10)
6
  {
7
   wdt_reset();
8
  }
9
  else
10
  {
11
   wdt_reboot(); 
12
  }
13
}

Jetzt geht er exakte 4 Sekunden schlafen. Kann es sein: entweder 
resettet der watchdog den tiny85 noch während er im Schlaf ist und/oder 
er springt nicht in die ISR(WDT_vect)
Weil, wenn ich die ISR(WDT_vect) mit einem wdt_reset() in der 1. Zeile 
versehe und danach in einer kleinen for-Schleife die LED blinken lassen 
will, blinkt die LED nämlich nicht.

von HildeK (Gast)


Lesenswert?

Ich bin jetzt kein Profi in Sachen µC-Programmierung, habe aber als 
Übung mal einen Treppenhausautomaten programmiert unter Verwendung des 
WD-Timers. Auch mit ein paar Ehrenrunden, speziell beim WD :-).
> oder er springt nicht in die ISR(WDT_vect)
Ich sehe z.B. kein sei() in deinem Code, somit sind die Interrupts gar 
nicht aktiv. Und, die Fuse WDTON darf nicht aktiviert sein!

Hier mal einen Extrakt aus meinem Code für u.U. sehr lange Zeiten, den 
ich aber so nicht geprüft habe. Vielleicht kannst du damit was anfangen:
1
/*
2
* Langzeittimer mit Watchdog-Interrupt
3
*
4
* Monoflop, wie Treppenlichautomat mit Langzeittimer, nachtriggerbar
5
6
*/
7
// Hinweis: WDTON unprogrammiert!
8
9
#define F_CPU 1e6
10
11
#include <avr/io.h>
12
#include <avr/interrupt.h> 
13
#include <avr/sleep.h>
14
#include <util/delay.h>
15
#include <avr/wdt.h>
16
17
#define WD_PRESCALE   9     // Prescaler-Einstellung (Einheit) von 0 (16ms) bis 9 (8s) [6: ca 1s]
18
#define TIME_MULTIPLY   5    // Faktor für Zeiteinheit
19
20
// Setup Funktion
21
#define HIGH_ACTIVE  
22
#define NOT_RETRIGGERABLE  
23
24
volatile uint32_t l_wcount = 1; // einfach ungleich Null setzen, dann sleep nach Reset 
25
26
27
ISR(WDT_vect)
28
{
29
 l_wcount--;
30
 WDTCR |=  (1<< WDIE);  // Watchdog Interrupt muss jedes mal neu aktiviert werden,   sonst mach WDT Reset
31
}
32
33
ISR (INT0_vect)        // Über ADC die Zahl der WD-Schleifen lesen l_wcount
34
  { 
35
    l_wcount = TIME_MULTIPLY;  // Zeit festlegen
36
  GIMSK=0;           // INT0-IRQ abschalten
37
  }  
38
39
int main()
40
{
41
  uint8_t wd_timer_prescale;
42
  uint8_t wdt_flags;
43
  
44
 // Ports definieren
45
 /*
46
 PB1 (Pin 6) Output, MISO, Schaltsignal
47
 PB2 (Pin 7) Input,  Starttaste, SCK, INT0
48
 PB5 (Pin 1) Reset
49
50
 Alle Ausgänge sind LOW-aktiv 
51
 INT0 und Taste sind LOW-aktiv
52
 */
53
54
// Ports definieren
55
  PORTB  = 0x00;
56
  DDRB  = (1<<PB1);
57
  PORTB |= (1<<PB2) ;  //Pullup
58
 
59
// zum Test, ob Reset passiert
60
  PORTB |=  (1 << PB1);
61
  _delay_ms (100);
62
  PORTB &= ~(1 << PB1);
63
64
// Power-Down Sleep Mode einstellen
65
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // stromsparend, aufwecken über WD oder INT0
66
  sleep_enable();             // Schlafmodus vorbereiten
67
68
 sei();
69
// Externen Pin Level IRQ auf Low Level aktivieren
70
 MCUCR &= ~((1 << ISC00) | (1 << ISC01)); 
71
  
72
// Watchdog IRQ vorbereiten
73
 wd_timer_prescale = WD_PRESCALE;
74
 if (wd_timer_prescale > 9 ) wd_timer_prescale=9;
75
 wdt_flags=wd_timer_prescale & 7;
76
 if (wd_timer_prescale > 7) wdt_flags|= (1<<5);  // wdt_flags enthält den Prescalerwert (0 .. 9)
77
 wdt_flags |= (1<<WDCE);
78
 
79
// Hauptschleife
80
 while (1)
81
  { 
82
    if(l_wcount==0)
83
  {
84
     // WD-Timer ist 'l_wcount'-mal abgelaufen
85
       // Watchdog ausschalten, dann Dauerschlaf bis INT0
86
    PORTB &= ~(1 << PB1);
87
    MCUSR = 0;
88
    wdt_disable();   // Sequenz abgelaufen, WD deaktivieren
89
    // falls  bis zum Ende der Sequenz INT0 gedrückt wird
90
      while((PINB & (1<<PINB2)) ==0 ) {_delay_ms(50);} // einfache Entprellung
91
  }
92
    GIMSK |= (1 << INT0);       // im Nicht-nachtriggerbaren Mode muss IRQ zum Aufwachen wieder aktiviert werden
93
    sleep_mode();         // hier schlafen bis zum INT0-IRQ, auch nach Reset
94
    // nach INT0 geht es hier weiter
95
96
    #ifdef NOT_RETRIGGERABLE
97
     GIMSK=0;
98
    #else
99
     GIMSK |= (1 << INT0);
100
  #endif
101
  PORTB |=  (1 << PB1);
102
    // Watchdog wieder aktivieren
103
    WDTCR |= (1<<WDCE) | (1<<WDE);
104
    // set watchdog timeout value, start WD-IRQ
105
    WDTCR = wdt_flags | (1<<WDIE);
106
107
  }
108
109
}
Sorry, die Einrückungen sind leider nicht konsistent. Scheint ein 
Tab/Blank-Problem zu sein beim Kopieren des Codes.

von S. Landolt (Gast)


Lesenswert?

In Assembler sieht es so aus, vielleicht hilft es weiter:
1
.org WDTaddr
2
  inc   wd_cnt
3
  cpi   wd_cnt,5         ; nach 5*4 s: reset
4
  breq  pc+4
5
   in   tmp0,WDTCR
6
   ori  tmp0,(1<<WDIE)   ; sonst beim naechsten Mal: reset
7
   out  WDTCR,tmp0
8
 reti
9
;*******************************************************
10
11
main:
12
  ldi   tmp0,(1<<SE)+(1<<SM1)                        ; Power-down
13
  out   MCUCR,tmp0
14
  ldi   tmp0,(1<<WDE)+(1<<WDCE)+(1<<WDIE)+(1<<WDP3)  ; 4 s
15
  out   WDTCR,tmp0
16
  sei
17
main_loop:
18
  sleep
19
 rjmp  main_loop

von Tobias (Gast)


Lesenswert?

Vielen Dank für die Hilfe, ich habe den Code jetzt verändert, leider 
ohne Erfolg:
1
#include <avr/wdt.h>
2
#include <avr/sleep.h> 
3
#include <avr/power.h>
4
5
unsigned long time;
6
int LED = 0;
7
int watchdog_counter = 0;
8
9
void wdt_first(void) __attribute__((naked)) __attribute__((section(".init3")));
10
void wdt_first(void){
11
 MCUSR = 0;
12
 wdtDisable(); 
13
}
14
15
void setup(){
16
 pinMode (LED, OUTPUT);
17
 digitalWrite (LED, HIGH);
18
}
19
20
void loop(){
21
  time = millis()/1000;
22
  if (time >= 10)
23
  {     
24
   go_sleep();
25
  }
26
}
27
28
ISR(WDT_vect) {
29
  WDTCR |=  (1<< WDIE);
30
  watchdog_counter++;
31
  if (watchdog_counter < 5)
32
  {
33
   go_sleep(); 
34
  }
35
  else
36
  {
37
    wdt_reboot();
38
  }
39
}//Wenn Hund aufwacht sollte es hierhin gehen, tut es aber nicht...
40
41
void go_sleep (){
42
   digitalWrite (LED, LOW);
43
   set_sleep_mode(SLEEP_MODE_IDLE);//Im IDLE geht tiny nicht mehr an, PWR_DOWN macht nach WDTO_xS reboot
44
   ADCSRA = 0;
45
   wdtEnable();
46
   sleep_enable();
47
   sleep_cpu();
48
   sleep_disable();
49
   wdtDisable();
50
}//schlafen gehen
51
52
void wdtEnable()
53
{
54
 cli();
55
 wdt_enable(WDTO_4S);
56
 sei();
57
}//watchdog aktivieren
58
59
void wdtDisable()
60
{
61
 cli();
62
 wdt_disable();
63
 sei();
64
}//watchdog deaktivieren
65
66
void wdt_reboot () {
67
 wdt_enable(WDTO_500MS);
68
 while (true) {}
69
}//watchdog löst nach 500ms reboot aus

Was interessant ist: im IDLE geht der Tiny überhaupt nicht mehr an, in 
PWR_DOWN nach der Zeit, die in WDTO_xS steht.
Die fuses sind gebrannt mit:
  low_fuses=0xe2
  high_fuses=0xdf (für WDTON cf)
  extended_fuses=0xff

Vom Prinzip her glaube ich reißt der watchdog den Tiny aus dem Schlaf 
und macht einen reset statt einen interrupt auszulösen und in die 
ISR(wdt_vect) zu springen. Wenn das stimmt, weiß ich aber leider nicht 
wie ich den Code ändern müsste, damit ein interrupt draus wird.
bascom und assembler kann ich leider nicht…
Vielen Dank für weitere Starthilfe!

von S. Landolt (Gast)


Lesenswert?

Die Fuses stimmen. Das Programm sieht sehr kompliziert aus, können Sie 
nicht versuchen, meinen Vorschlag in c umzusetzen, so irgendwie (ich 
kann kein c):
1
volatile int watchdog_counter = 0;
2
ISR(WDT_vect){
3
watchdog_counter++;
4
if (watchdog_counter < 5) {
5
  WDTCR |= (1<<WDIE) }
6
}
7
8
int main()
9
{
10
MCUCR = (1<<SE)+(1<<SM1) ;   Power-down
11
WDTCR = (1<<WDE)+(1<<WDCE)+(1<<WDIE)+(1<<WDP3) ; 4 s
12
while (true) { sleep }
13
}
Jetzt noch das LED-Schalten einfügen, fertig.

von S. Landolt (Gast)


Lesenswert?

Im main habe ich ein
sei();
vergessen.

von MWS (Gast)


Lesenswert?

Tobias schrieb:
> leider ohne Erfolg:

Kann ja auch nicht gehen, wenn WDIE erst im Interrupt gesetzt wird, der 
seinerseits erst aufgerufen wird, wenn WDIE vorher gesetzt war.
Und WDE darf für den WDT-Interrupt nicht gesetzt sein, auf die 
notwendige Sequenz dafür achten. Bzw. wdtDisable(); sollte es auch tun.

von Tobias (Gast)


Lesenswert?

Jetzt verstehe ich besser und der Code funktioniert nun auch richtig, 
vielen Dank für eure Hilfe.

von Thomas E. (thomase)


Lesenswert?

MWS schrieb:
> Und WDE darf für den WDT-Interrupt nicht gesetzt sein, auf die
> notwendige Sequenz dafür achten. Bzw. wdtDisable(); sollte es auch tun.

Aber sicher darf der gesetzt sein. Muss er sogar, wenn nach dem 
Interrupt auch der Reset ausgeführt werden soll. Um diesen Reset dann 
nicht auszulösen, muss, sinnvoller Weise in der ISR, WDIE erneut gesetzt 
werden.

Ist WDE nicht gesetzt, läuft der WD als reiner Timer. Dann kann auch das 
erneute Setzen von WDIE entfallen.

mfg.

: Bearbeitet durch User
von MWS (Gast)


Lesenswert?

Thomas E. schrieb:
> MWS schrieb:
>> Und WDE darf für den WDT-Interrupt nicht gesetzt sein, auf die
>> notwendige Sequenz dafür achten. Bzw. wdtDisable(); sollte es auch tun.
>
> Aber sicher darf der gesetzt sein. Muss er sogar, wenn nach dem
> Interrupt auch der Reset ausgeführt werden soll. Um diesen Reset dann
> nicht auszulösen, muss, sinnvoller Weise in der ISR, WDIE erneut gesetzt
> werden.

Ich weiß nicht, zu welchem Thread Du diskutierst, aber ich beziehe mich 
auf den Thread hier und da geht's ausschließlich um's Aufwecken aus dem 
Sleep per WD-Interrupt, der WD-Reset war nicht Thema.

Und "sinnvollerweise in der ISR"?
Das Datenblatt ist dazu nicht Deiner Meinung:

> WDIE must be set after each interrupt. This should however not be done
> within the interrupt service routine itself, as this might compromise
> the safety-function of the Watchdog System Reset mode.

Thomas E. schrieb:
> Ist WDE nicht gesetzt, läuft der WD als reiner Timer. Dann kann auch das
> erneute Setzen von WDIE entfallen.

Das erneute Setzen kann entfallen, nicht das erstmalige. Ich hab' auch 
nicht geschrieben, dass WDIE erneut gesetzt werden muss, lies meinen 
Satz nochmal genau, da steht dass das WDIE so nicht erreicht werden 
kann.

von Thomas E. (thomase)


Lesenswert?

MWS schrieb:
> der WD-Reset war nicht Thema.

Nein?

Tobias schrieb:
> ob er über einen Schwellenwert gekommen ist für einen
> kompletten reset oder ob er nochmal schlafen gehen darf.

MWS schrieb:
>> WDIE must be set after each interrupt. This should however not be done
>> within the interrupt service routine itself, as this might compromise
>> the safety-function of the Watchdog System Reset mode.

OK. Mein Fehler.


mfg

MWS schrieb:
> Das erneute Setzen kann entfallen, nicht das erstmalige. Ich hab' auch
> nicht geschrieben, dass WDIE erneut gesetzt werden muss, lies meinen
> Satz nochmal genau, da steht dass das WDIE so nicht erreicht werden
> kann.

Lies meinen Satz nochmal genau. Da wirst auch du vielleicht erkennen, 
dass ich mich gar nicht auf das erste Setzen bezogen habe.

mfg.

von HildeK (Gast)


Lesenswert?

MWS schrieb:
> Und "sinnvollerweise in der ISR"?
> Das Datenblatt ist dazu nicht Deiner Meinung:
>
>> WDIE must be set after each interrupt. This should however not be done
>> within the interrupt service routine itself, as this might compromise
>> the safety-function of the Watchdog System Reset mode.

Ich weiß nicht welches Datenblatt du hast, in meinem vom Tiny25/45/85 
von 8/2013 steht: "To avoid the Watchdog Reset, WDIE must be set after 
each interrupt." Und nicht mehr. Damit kann das auch in der WD-IRQ sein, 
denn da ist der Interrupt schon erfolgt.

Thomas E. schrieb:
> Ist WDE nicht gesetzt, läuft der WD als reiner Timer. Dann kann auch das
> erneute Setzen von WDIE entfallen.

Danke - das hatte ich bisher übersehen.

von MWS (Gast)


Lesenswert?

HildeK schrieb:
> Ich weiß nicht welches Datenblatt du hast, in meinem vom Tiny25/45/85
> von 8/2013 steht: "To avoid the Watchdog Reset, WDIE must be set after
> each interrupt." Und nicht mehr. Damit kann das auch in der WD-IRQ sein,
> denn da ist der Interrupt schon erfolgt.

Ich hab das momentan neueste Rev. 8265D–AVR–01/2014, frisch 
runtergeladene Datenblatt und Du offensichtlich ein altes. Du must kein 
altes nehmen, sondern kannst auch ein neues von Atmel runterladen.

Abgesehen davon, was verstehst Du denn an dem Zitat im neuen DB nicht? 
Es ist der Hinweis, dass es für die Sicherheit des WD-Resets schädlich 
ist, wenn im kombinierten Mode der WD-Interrupt in zugehöriger ISR 
wieder erlaubt wird.

Was klar wird, wenn man annimmt, das sich das Hauptprogramm so gefressen 
hat, dass die Interrupts noch laufen. Wenn der WDT aufgrund des 
Absturzes nicht zurückgesetzt wird, werden die WD-Interrupts dennoch 
weiter ausgeführt, was einen Systemreset verhindert. Diese Tatsache ist 
unabhängig vom DB, war auch zum Zeitpunkt des alten schon so, nur 
stand's da eben noch anders.

Technisch funktioniert das erneute Setzen des WDIE in der ISR, nur eben 
mit dem beschriebenen Nachteil. Das kannst Du für Dich dann halten, wie 
Du willst.

von HildeK (Gast)


Lesenswert?

MWS schrieb:
> Du must kein
> altes nehmen, sondern kannst auch ein neues von Atmel runterladen.

Das wundert mich, denn ich hatte mir das Tiny25/45/85 direkt von der 
Atmel Webseite geholt ... und hier ging es um den Tiny85 und nicht um 
den 87/167.
Da ist die Version Rev. 2586Q–AVR–08/2013 abgelegt:
http://www.atmel.com/Images/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf
Trotzdem ist dein Einwand korrekt, aber irgendwann vor Ablauf der 
WD-Timerzeit muss der WDIE wieder gesetzt werden. Und danach ist es dann 
für die Sicherheit wieder schädlich.

Aber, wie du schon richtig bemerkt hast:
> Das kannst Du für Dich dann halten, wie Du willst.

von MWS (Gast)


Lesenswert?

HildeK schrieb:
> Trotzdem ist dein Einwand korrekt, aber irgendwann vor Ablauf der
> WD-Timerzeit muss der WDIE wieder gesetzt werden. Und danach ist es dann
> für die Sicherheit wieder schädlich.

Damit das System wie vorgesehen sicher ist, erfolgt an einer oder 
mehreren Stellen in normal zu durchlaufendem Code entweder der Reset des 
WDT oder es wird das WDIE gesetzt.

> Aber, wie du schon richtig bemerkt hast:
>> Das kannst Du für Dich dann halten, wie Du willst.

Übers.: Du kannst es auch falsch machen.

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.