Forum: Mikrocontroller und Digitale Elektronik Microchip/Atmel AVR ATtiny85, Pin-change interrupt (PCINT0) ) und Watchdog als Timer (WDT)


von Lasse Klingeln (Gast)


Lesenswert?

Der folgende Code läuft auf einem "Digispark" Klon mit ATtiny85 bei 5V 
extern (USB ist nicht verbunden).
Wenn ich INPUT mit NC2 low ziehe, schaltet OUTPUT für 204 ms ein - nicht 
für 8 Sekunden.
Stimmt etwas mit der Watchdog-Konfiguration nicht, oder woran kann's 
noch liegen?

1
// Microchip / Atmel AVR ATtinyx5
2
// 
3
4
//#define F_CPU 1000000 // 8MHz/8 CLKDIV8 (default, fuse sets prescaler in CLKPR, can be overwritten)
5
#define F_CPU 16500000 // 16.5 MHz: PLL overclocked by micronucleus USB bootloader (USB sync.)
6
7
8
// C types
9
#include <stdint.h>
10
11
// AVR-libc
12
#include <avr/io.h>
13
#include <avr/sleep.h>
14
#include <avr/interrupt.h>
15
#include <avr/wdt.h>
16
#include <util/delay.h>
17
18
19
// IO
20
//  #RESET PB5 1   8 VCC
21
//         PB3 2   7 PB2
22
//         PB4 3   6 PB1
23
//         GND 4   5 PB0
24
#define NC0       (1<<PB0)  // 5 (MOSI/DI/SDA/AIN0/OC0A/#OC1A/AREF/PCINT0)
25
#define LED       (1<<PB1)  // 6 (MISO/DO/AIN1/OC0B/OC1A/PCINT1)
26
#define NC2       (1<<PB2)  // 7 (SCK/USCK/SCL/ADC1/T0/INT0/PCINT2)
27
#define INPUT     (1<<PB3)  // 2 (PCINT3/XTAL1/CLKI/#OC1B/ADC3)
28
#define OUTPUT    (1<<PB4)  // 3 (PCINT4/XTAL2/CLKO/OC1B/ADC2)
29
//                          // 1 (PCINT5/#RESET/ADC0/dW)
30
//                             4 GND
31
//                             8 VCC
32
33
34
ISR(PCINT0_vect) {
35
  GIMSK = 0;          // stop PCIE
36
  PORTB |= (LED | OUTPUT);    // debug
37
  WDTCR = (1<<WDIE) | WDTO_8S;  // enable WDT
38
}
39
40
41
ISR(WDT_vect) {
42
  wdt_disable();      // stop WDT
43
  PORTB &= ~(LED | OUTPUT); // debug
44
  GIMSK = (1<<PCIE);    // enable interrupt
45
}
46
47
48
int main(void) {
49
   
50
  MCUSR = 0;  // reset status
51
  GIFR = 0xFF;  // clear interrupt flags
52
  
53
  // low power
54
  wdt_disable();    // stop WDT
55
  sleep_bod_disable();  // BOD
56
  ACSR |= (1<<ACD);    // analog comparator
57
  PRR |= (1<<PRTIM0) | (1<<PRTIM1) | (1<<PRUSI) | (1<<PRADC);
58
59
  // output(s)
60
  DDRB = (LED | OUTPUT | NC0 | NC2); //
61
  PORTB = 0;
62
  
63
  // hello world
64
  for (uint8_t n=0; n<6; n++) {
65
  PORTB ^= LED;
66
  _delay_ms(200);
67
  }
68
69
  // enable pin change interrupt
70
  GIFR |= (1<<PCIF);  // the flag can be cleared by writing a logical one to it
71
  GIMSK = (1<<PCIE);  // 
72
  PCMSK = (1<<PCINT3);  // 
73
74
  // power down
75
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
76
  sleep_enable();
77
  sei();
78
  while (1) {
79
    sleep_cpu();
80
  //sleep_disable();
81
  }
82
}

Beitrag #5907874 wurde von einem Moderator gelöscht.
von Lasse Klingeln (Gast)


Lesenswert?

Mit WDTO_4S reduziert sich die Zeit auf 85 ms.
Die Zeit zwischen Flanke und LED sind über 50 ms!

von Einer K. (Gast)


Lesenswert?

Lasse Klingeln schrieb:
> WDTCR = (1<<WDIE) | WDTO_8S;  // enable WDT

Warum nutzt du hier nicht das vorgesehene Macro.
An anderen Stellen tust du das doch auch.

von Lasse Klingeln (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> Warum nutzt du hier nicht das vorgesehene Macro.

Es gibt ein Makro, um den WDT als Timer zu verwenden? Welches?

https://www.nongnu.org/avr-libc/user-manual/group__avr__watchdog.html

von Lasse Klingeln (Gast)


Lesenswert?

Gleiches Verhalten mit
1
  wdt_enable(WDTO_4S);      // enable WDT
2
  WDTCR = (1<<WDIE);      // WDT for interrupt

von Lasse Klingeln (Gast)


Lesenswert?

Oops.. Moment.

von Lasse Klingeln (Gast)


Lesenswert?

1
  wdt_enable(WDTO_4S);      // enable WDT
2
  WDTCR |= (1<<WDIE);      // WDT for interrupt

8.8 s
1
  wdt_enable(WDTO_8S);      // enable WDT
2
  WDTCR |= (1<<WDIE);      // WDT for interrupt

17.5 s

Läuft er doppelt?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Lasse Klingeln schrieb:
> #define F_CPU 16500000

Wahrscheinlich läuft Dein µC nur mit 8 MHz. Mit obiger Zeile kannst Du 
nicht die Taktfrequenz vorgeben.

Umgekehrt wird ein Schuh daraus: Du musst hier angeben, mit welchem Takt 
Dein AVR tatsächlich läuft.

: Bearbeitet durch Moderator
von Lasse Klingeln (Gast)


Lesenswert?

Vor dem Wiedereinschalten des PCINT noch
1
  GIFR |= (1<<PCIF);

oder über PCMSK ein- & ausschalten.

von Lasse Klingeln (Gast)


Lesenswert?

Frank M. schrieb:
> Wahrscheinlich läuft Dein µC nur mit 8 MHz.

Die PLL ist gefust, der Bootloader ("micronucleus") setzt den Takt wohl 
synchron zur USB.
Hat aber eigentlich nichts mit dem WDT zu tun (128kHz RC)?

von Thomas E. (thomase)


Lesenswert?

Lasse Klingeln schrieb:
> WDTCR = (1<<WDIE) | WDTO_8S;  // enable WDT

Das geht nicht.

Auf das WDTCR kann nur in einer Timed Sequence zugegriffen werden. Mach 
das wie im Datenblatt beschrieben oder benutze die Makros.

: Bearbeitet durch User
von Lasse Klingeln (Gast)


Lesenswert?

Also:
Fehler 1: WDT nicht korrekt konfiguriert (Schreibsequenz per Makro)
Fehler 2: WDT lief doppelt, weil der PCINT nach Ablauf von WDT sofort 
wieder ausgeführt wurde.
50..60 ms "Latenz" zwischen Flanke und Schalten des Ausgangs: PLL 
start-up time

von Lasse Klingeln (Gast)


Lesenswert?

Thomas E. schrieb:
> Auf das WDTCR kann nur in einer Timed Sequence zugegriffen werden. Mach
> das wie im Datenblatt beschrieben oder benutze die Makros.

Im Datenblatt steht dazu "keine Einschränkung", wenn WDTON nicht 
aktiviert ist.
Zum Löschen von WDE ist dagegen die Sequenz erforderlich.

Neuer Versuch, anderer Wert:
1
WDTCR |= (1<<WDIE) | WDTO_2S;

Geht. 8-D

von Lasse Klingeln (Gast)


Lesenswert?

Lasse Klingeln schrieb:
> Geht. 8-D

Geht nicht. Irgendwas stimmt hier wohl mit Atmel Studio nicht.

von Einer K. (Gast)


Lesenswert?

Lasse Klingeln schrieb:
> Irgendwas stimmt hier wohl mit Atmel Studio nicht.

Vielleicht auch mit der Badehose :-)

Hier eine Variante mit der Arduino IDE.
Ein paar include hinzufügen, dann müsste es auch mit dem Atmel Studio im 
8 Sekunden Takt blinken.

Getestet mit einem 16,5MHz Digistump Klon.
Die Eingebaute LED hängt an PB1
1
#include <avr/wdt.h>
2
3
4
int main(void) 
5
{
6
  wdt_enable(WDTO_8S);  
7
  DDRB |= _BV(PB1);   // led output
8
  WDTCR |= 1<<WDIE;    
9
  sei();
10
  for(;;){}
11
}
12
13
14
ISR(WDT_vect) 
15
{
16
  WDTCR |= 1<<WDIE;  
17
  PINB = _BV(PB1); // toggle led
18
}

von Thomas E. (thomase)


Lesenswert?

Lasse Klingeln schrieb:
> Geht nicht.

Habe dir doch gesagt, daß das nicht geht.

Aber du hast ja trotzdem eine Lösung gefunden:

> Irgendwas stimmt hier wohl mit Atmel Studio nicht.

: Bearbeitet durch User
von Lasse Klingeln (Gast)


Lesenswert?

Thomas E. schrieb:
> Habe dir doch gesagt, daß das nicht geht.

Wie im vagen Datenblatt beschrieben, geht es leider auch nicht:

    wdt_reset();
    WDTCR |= (1<<WDCE) | (1<<WDE);
    WDTCR = (1<<WDIE) | WDTO_4S;


Es gibt kein Makro für den WDT als Timer.
Bislang funktioniert es bei mir nur so, wie um 11:55 geschrieben, also 
WDT enable Makro und dann umschalten auf Interrupt:

    wdt_enable(WDTO_4S);
    WDTCR |= (1<<WDIE);

Arduino Fanboy D. schrieb:
> Hier eine Variante mit der Arduino IDE.

Danke, so läuft's auch bei mir.

von Einer K. (Gast)


Lesenswert?

Lasse Klingeln schrieb:
> Danke, so läuft's auch bei mir.
Ich weiß.

Und: Gerne geschehen.

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.