Forum: Mikrocontroller und Digitale Elektronik ATtiny84A Watchdog Problem


von Wolfgang W. (wolfgang_w563)


Lesenswert?

Hallo,

ich hatte auf einem ATtiny45 ein Programm erstellt, das in einer 
Endlosschleife eine Spannung mißt, daraufhin reagiert und weil das ganze 
stromsparend sein sollte per sleep eine Sekunde schlafen gelegt und per 
Watchdog-Interrupt aufgeweckt. Dann begann das ganze wieder von vorne.
Das funktionierte alles unauffällig. Weil ich aber 3 Pins mehr brauchte, 
wechselte ich auf den ATtiny84A. Platine und Programm angepaßt, aber es 
gibt Probleme mit dem Watchdog.
Für das Debugging habe ich das Programm entkernt und in der 
Endlosschleife den Schlafmodus auskommentiert. Das Problem beginnt 
sofort mit dem Initialisieren des WDT in init(). Am Ende wird mit dem 
Befehl WDTCSR = 0x07; irrigerweise auch das WDE-Bit gesetzt und das 
Register hat den Wert 0x0F.

Gruß    WoW

Ich hänge hier mal das entkernte Programm rein:
1
#include <xc.h>
2
#include "avr/interrupt.h"
3
#include "avr/wdt.h"
4
#include "avr/sleep.h"
5
#include "myTool.h"
6
#include "avr/cpufunc.h"
7
#include <stdbool.h>
8
9
#define ADenStartConversion ADCSRA |= 0b11000000
10
#define ADCfinished ((ADCSRA & 0b01000000) == 0) 
11
#define ADCsetMuxToUGen  (ADMUX = (ADMUX & 0b11000000) | 0b00000000)  // ADC0
12
#define ADCsetMuxToBatt2 (ADMUX = (ADMUX & 0b11000000) | 0b00000011)  // ADC3
13
#define ADCsetMuxToTheta (ADMUX = (ADMUX & 0b11000000) | 0b00000111)  // ADC7
14
#define FEToff PORTA &= 0b11111101
15
#define FETon  PORTA |= 0b00000010
16
#define UsenseTetasenseCircuitOff PORTA &= 0b11111011 // PA2
17
#define UsenseTetasenseCircuitOn  PORTA |= 0b00000100 // PA2
18
#define FanOff PORTB &= 0b11111011 // PB2
19
#define FanOn  PORTB |= 0b00000100 // PB2
20
#define AllPullupsDisable (MCUCR |= 0b01000000)
21
#define AllPullupsEnable  (MCUCR &= 0b10111111)
22
23
#define SwitchOnVoltage  220 // AD-Wert bei 13.8V: (13.8*10000)/(22000+10000)/5*255   ADC-Wert bei 13.8V und 22K/10K Spannungsteiler
24
#define SwitchOffVoltage 204 // AD-Wert bei 12.8V
25
#define ADCONST_60Deg 135
26
#define ADCONST_70Deg 112
27
#define ADCONST_80Deg  90
28
#define HVcntr_max 16 // ca4 s bis Pon
29
#define HVcntr_switch_off 4
30
#define HVcntr_switch_on 16
31
32
int HVintegral = 0;
33
int LVintegral = 0;
34
#define HVvalidTime 5   // nachdem das HV-Integral 4 erreicht hat -> Wechsel von Off -> On
35
#define LVvalidTime 300 // nachdem das LV-Integral 300 erreicht hat -> Wechsel von On -> Off
36
37
unsigned int FanYetActive = 0;
38
39
enum OnOffState {off, on} RelayState = off;
40
enum mode_t {automatic = 3, forceon = 2, forceoff = 1};  // low active
41
  
42
43
44
void init(void)
45
{
46
  _NOP();
47
  _NOP();
48
  _NOP();
49
  cli();  // eigentlich unnötig nach Power-on-Reset....
50
  // WDT init; Global IE-Flag disabled => Atomic OP zw. WDCE und WDE=0 gewährleistet
51
  // Nach der Sequenz wird nur mit WDIE der WDT angehalten (WDIE = 0) bzw. laufen gelassen (WDIE = 1) 
52
  wdt_reset();
53
  /* Clear WDRF in MCUSR */
54
  MCUSR = 0x00;
55
  /* Write logical one to WDCE and WDE */
56
  WDTCSR |= (1<<WDCE) | (1<<WDE);
57
  //WDTCSR |= 0b00011000;
58
  /* WDEnable = 0 und WDIE = 0 => Watchdog angehalten*/
59
  WDTCSR = 0x07;
60
  
61
  // Global Interrupt enable
62
  sei();
63
  _NOP();
64
  _NOP();
65
  _NOP();
66
  
67
}
68
69
70
void sleep(void)
71
{
72
  wdt_reset();
73
  WDTCSR = 0b01000111; // WDIE = 1 => WDT läuft; Overflow nach 2s
74
    
75
  set_sleep_mode(0b00010000);  // SM1:SM0 = 10 => Power down mode 
76
  sleep_mode();         // setzt SE-Bit, ruft sleep-Befehl auf und setzt danach das SE-Bit zurück (siehe AVR-LIBC
77
  _NOP();
78
  _NOP();
79
  _NOP();
80
  
81
}
82
83
ISR(WDT_vect)   // Watchdog ISR
84
{
85
  WDTCSR &= 0b10111111;  // WDIE = 0 => WDT steht
86
  _NOP();
87
  _NOP();
88
  _NOP();
89
}
90
91
92
int main(void)
93
{
94
  unsigned char ADUbatt, ADTheta;
95
  _NOP();
96
  _NOP();
97
  init();
98
  _NOP();
99
  _NOP();
100
  
101
    while(1)
102
    {  //AllPullupsEnable;
103
    //senseUbattandTheta(&ADUbatt,&ADTheta);
104
    //reactOnUbattAndTheta(ADUbatt,ADTheta, readMode());
105
    //AllPullupsDisable;
106
    _NOP();
107
    _NOP();
108
    _NOP();
109
  //  sleep();
110
    _NOP();
111
    _NOP();
112
    _NOP();
113
        // Timebase für Kühlung
114
    if (FanYetActive)
115
        {
116
      --FanYetActive;
117
        }
118
    }
119
}

von M. K. (sylaina)


Lesenswert?

Öhm, warum benutzt du nicht alles aus der wdt.h? wdt_reset() nutzt du 
doch auch, warum also nicht mit wdt_enable(WDTO_1S) den Watchdog auf 1 
Sekunde einstellen anstelle zu Fuß via WDTCSR? Die ganzen NOPs verstehe 
ich ebensowenig und die Magic Numbers...naja, sind heute zutage 
eigentlich nicht mehr nötig.

von Wolfgang W. (wolfgang_w563)


Lesenswert?

Danke für die Ausführungen - diese haben aber nichts mit dem Kern des 
Problems zu tun....

von Εrnst B. (ernst)


Lesenswert?

Wolfgang W. schrieb:
> diese haben aber nichts mit dem Kern des
> Problems zu tun

naja, doch. Dann sieht man direkt, was der Code machen soll, ohne 
erstmal 0b10111111, 0x07 usw. auf die einzelnen Bits im Datenblatt 
runterbrechen zu müssen.

Und falls sich die Lage der Flags im Register zwischen den beiden AVRs 
geändert haben sollte, würde der Compiler das selber in Ordnung 
bringen.

von Robert G. (robert_g311)


Lesenswert?

Servus,


Wolfgang W. schrieb:
> Für das Debugging habe ich das Programm entkernt und in der
> Endlosschleife den Schlafmodus auskommentiert. Das Problem beginnt
> sofort mit dem Initialisieren des WDT in init(). Am Ende wird mit dem
> Befehl WDTCSR = 0x07; irrigerweise auch das WDE-Bit gesetzt und das
> Register hat den Wert 0x0F.

ohne jetzt zu 100% durch das Problem genau analysiert zu haben, hier ein 
Tipp von mir:
1
  /* Write logical one to WDCE and WDE */
2
3
  WDTCSR |= (1<<WDCE) | (1<<WDE);
4
5
  //WDTCSR |= 0b00011000;
6
7
  /* WDEnable = 0 und WDIE = 0 => Watchdog angehalten*/
8
9
  WDTCSR = 0x07;

Hast Du im Assembler nachgesehen, daß der 2. Befehl auch wirklich 4 
Taktzyklen nach dem 1. ausgeführt wird?

Wie stehen denn die Fuse-Bits? Ist dort der Watchdog "daueraktiviert"?

Gruß Robert

von Wolfgang W. (wolfgang_w563)


Lesenswert?

Einen weiteren Fehler  - der aber eigentlich nichts mit dem Problem zu 
tun haben dürfte - habe ich gefunden:

Beim ursrünglichen ATtiny45 heißt der Interruptvector der AVR-LIBC 
"WDT_ISR".
Beim jetzigen verwendeten ATtiny84 heißt dieser "WATCHDOG_vect".

von Wolfgang W. (wolfgang_w563)


Lesenswert?

Robert G. schrieb:
> Hast Du im Assembler nachgesehen, daß der 2. Befehl auch wirklich 4
> Taktzyklen nach dem 1. ausgeführt wird?
>
> Wie stehen denn die Fuse-Bits? Ist dort der Watchdog "daueraktiviert"?
>
> Gruß Robert

Hallo,

die FUSE-Bits lauten high: DF low: E2 -> Die WDT-allways-on-Fuse ist 
also inaktiv.

AAAAberrrr - schau mal her das Disassembly:
1
  WDTCSR |= (1<<WDCE) | (1<<WDE);
2
00000074  LDI R24,0x41    Load immediate 
3
00000075  LDI R25,0x00    Load immediate 
4
00000076  LDI R18,0x41    Load immediate 
5
00000077  LDI R19,0x00    Load immediate 
6
00000078  MOVW R30,R18    Copy register pair 
7
00000079  LDD R18,Z+0    Load indirect with displacement 
8
0000007A  ORI R18,0x18    Logical OR with immediate 
9
0000007B  MOVW R30,R24    Copy register pair 
10
0000007C  STD Z+0,R18    Store indirect with displacement 
11
  WDTCSR = 0x07;
12
0000007D  LDI R24,0x41    Load immediate 
13
0000007E  LDI R25,0x00    Load immediate 
14
0000007F  LDI R18,0x07    Load immediate 
15
00000080  MOVW R30,R24    Copy register pair 
16
00000081  STD Z+0,R18    Store indirect with displacement 
17
  sei();

Bei Adresse $7C ist der Zugriff auf das WDTCSR. Danach kommen drei LDI 
und ein MOVW - jetzt sind die 4 Zyklen vorbei!!! Nach der 5. clk kommt 
das Beschreiben des WDTCSR um ein Takt zu spät.

Diese C-Code-Sequenz habe ich aus dem ATtiny84A-Manual herauskopiert. 
Damit hätte ich nicht gerechnet und kann es kaum glauben. Deswegen habe 
ich das auch nicht geprüft.

Supi Robert - eine Eins mit Stern...

Gruß   WoW

von Wolfgang W. (wolfgang_w563)


Lesenswert?

Also Robert - es funktioniert jetzt. Die Compileroptimierung war 
ausgeschaltet. Deswegen war der Code zu lang. Ich habe die Optimierung 
hochgedreht - jetzt wird im 3. Takt das Register geschrieben und es 
funktioniert. Danke nochmal.

Gruß    WoW

von M. K. (sylaina)


Lesenswert?

Wolfgang W. schrieb:
> Also Robert - es funktioniert jetzt. Die Compileroptimierung war
> ausgeschaltet. Deswegen war der Code zu lang. Ich habe die Optimierung
> hochgedreht - jetzt wird im 3. Takt das Register geschrieben und es
> funktioniert. Danke nochmal.
>
> Gruß    WoW

Siehst du, und genau sowas wäre dir mit Verwendung den wdt.h-Funktionen 
erst gar nicht passiert. Daher rate ich dir das WDTCSR-Regierter nur 
anzufassen, wenn es wirklich sein muss (z.B. im den Watchdog-Interrupt 
wieder einzuschalten nachdem er einmal ausgelöst wurde) und ansonsten 
mit den Funktionen aus wdt.h zu arbeiten. Mach weniger Kopfzerbrechen ;)

Schön, dass es jetzt funktioniert und danke für die Rückmeldung bzgl. 
der Lösung

: Bearbeitet durch User
von Robert G. (robert_g311)


Lesenswert?

Servus,

freut mich.

Wenn Du während der Initialisierung Interrupts aktiv hast, mußt Du 
aufpassen, daß zwischen den beiden Befehlen kein Interrupt zuschlagen 
kann, weil sonst die 4 Takte schnell gerissen werden.

Denk auch daran, daß einige Befehle z.B. movw 2 Takte benötigen.

Gruß Robert

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.