ATtiny84A Watchdog Problem

OP #7374710
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
}
#7374770
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.

#7375392
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.

#7375410
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

OP #7375579
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

#7375914
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren