Forum: Mikrocontroller und Digitale Elektronik Attiny85 aus Schlaf aufwecken


von Tobias (Gast)


Lesenswert?

Hallo,
ich habe einen Attiny85-20PU mit dem Arduino Uno als ISP geflasht und 
dabei für den Tiefschlaf folgenden Code verwendet:
1
void goToSleep(void) {
2
    // GIMSK |= _BV(INT0);                    //enable INT0
3
    // MCUCR &= ~(_BV(ISC01) | _BV(ISC00));   //INT0 on low level
4
    ACSR |= _BV(ACD);                         //disable the analog comparator
5
    ADCSRA &= ~_BV(ADEN);                     //disable ADC
6
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
7
    sleep_enable();
8
    //turn off the brown-out detector.
9
    //must have an ATtiny45 or ATtiny85 rev C or later for software to be able to disable the BOD.
10
    //current while sleeping will be <0.5uA if BOD is disabled, <25uA if not.
11
    cli();
12
    mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE);  //turn off the brown-out detector
13
    mcucr2 = mcucr1 & ~_BV(BODSE);
14
    MCUCR = mcucr1;
15
    MCUCR = mcucr2;
16
    sei();                         //ensure interrupts enabled so we can wake up again
17
    sleep_cpu();                   //go to sleep
18
    cli();                         //wake up here, disable interrupts
19
    // GIMSK = 0x00;               //disable INT0
20
    sleep_disable();               
21
    sei();                         //enable interrupts again (but INT0 is disabled from above)
22
}

"Außenrum" ist dieser Code:
1
const int buttonPin = 2;
2
const int ledPin =  3;
3
4
void setup() {
5
  pinMode(ledPin, OUTPUT);      
6
  pinMode(buttonPin, INPUT_PULLUP);     
7
}
8
 
9
void loop(){
10
  buttonState = digitalRead(buttonPin);
11
12
  if (buttonState == HIGH) {     
13
    // turn LED on:    
14
    digitalWrite(ledPin, HIGH);  
15
  } 
16
  else {
17
    // turn LED off:
18
    digitalWrite(ledPin, LOW); 
19
  }
20
}
An PIN3 des Attiny hängt die LED, an PIN2 wird zum Ausschalten auf GND 
gezogen.
Das Ausschalten funktioniert auch wunderbar, nur: wie schalte ich wieder 
ein? Sobald PIN2 nicht mehr auf GND ist, so der Wunsch, soll die LED 
leuchten.
Danke für Hilfe!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Da du den low level interrupt (INT0) nicht nutzen kannst, denn du
willst dich ja während eines Low-Pegels im Schlaf befinden, musst
du einen Pinchange-Interrupt (PCINT2) konfigurieren.

Dran gedacht – gut gelungen:

. direkt vor dem Aktivieren erstmal anhängige alte Interrupts aus dem 
Status löschen

. direkt nach dem Aufwachen (in der ISR) den PCINT2 wieder abschalten

von m.n. (Gast)


Lesenswert?

Ein Beispiel mit PCINT-Aufweckfunktion "toggle_25.c". Vielleicht kannst 
Du Dir da etwas abkucken. 
Beitrag "Re: EIN-AUS mit Taster per Interrupt, ATtiny25 o.ä."

von Tobias (Gast)


Lesenswert?

Danke für deine Hilfe, aber dafür bin ich noch lange nicht fit genug. 
Soll heißen: verstehe ich (noch) nicht… Den sleep-Bereich habe ich 
relativ unreflektiert von Frau Google übernommen hüstl und versuche, 
das Prinzip zu verstehen: schlafen-aufwachen.

von Tobias (Gast)


Lesenswert?

Das war für dl8dtl, den Beitrag von m.n. habe ich erst nachher gesehen 
und schaue ihn mir gleich mal an.

von Tobias (Gast)


Lesenswert?

Okay, hier komme ich nicht weiter, ich muss glaube ich zurück an den 
Anfang und mit den einfachen Schlafmodi anfangen… Trotzdem danke für den 
input.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tobias schrieb:
> Okay, hier komme ich nicht weiter, ich muss glaube ich zurück an den
> Anfang und mit den einfachen Schlafmodi anfangen…

Ist eigentlich egal: wie du den Pin für einen passenden Interrupt
aufsetzt, musst du so oder so lernen, denn ein Interrupt ist die
einzige Möglichkeit, wie du den Dornröschenschlaf des Controllers
beenden kannst.

Allerdings könntest du erstmal mit den umgekehrten Pegeln anfangen:
high level => Schlaf, low level => Aufwachen.  Dann kannst du den
pegelgesteuerten Externinterrupt benutzen, der etwas einfacher zu
konfigurieren ist als der Pin-Change-Interrupt.  Bei letzterem hast
du zwei verschiedene Dinge, die du aktivieren musst: den
Pin-Change-Interrupt erstmal global (bei einem Tiny dieser Größe
gibt's da nur einen Vektor, ansonsten ein Vektor pro Port), und dann
noch die Portpins, die tatsächlich den Interrupt triggern sollen.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Jörg W. schrieb:
> Bei letzterem hast
> du zwei verschiedene Dinge, die du aktivieren musst

Da der MC den Interrupt auch ausführen möchte, muss dafür eine ISR 
vorgehalten werden, die zwar leer sein kann, aber vorhanden sein muß. 
Sonst hopst der MC in den Reset.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Matthias S. schrieb:
> Da der MC den Interrupt auch ausführen möchte, muss dafür eine ISR
> vorgehalten werden, die zwar leer sein kann, aber vorhanden sein muß.

Sie sollte aber nicht leer sein, sondern in jedem Falle zumindest
den Code enthalten, der den jeweiligen Interrupt wieder abschaltet.

Insbesondere beim pegelgesteuerten Interrupt hat man sonst eine
Endlosschleife (ISR wird verlassen, aber Interruptbedingung ist nach
wie vor erfüllt), aber auch beim Pin-Change-Interrupt sorgt das
Prellen einer Taste dafür, dass dieser mit jedem Tastendruck viele
Male ausgeführt werden würde.

von Tobias (Gast)


Lesenswert?

So, nach ein paar Tagen lesen bin ich weiter und kann (halbwegs) mit 
einem Interrupt arbeiten. Jetzt funktioniert die Schaltung wunderbar, 
aber der ATtiny85 zieht im sleep-modus noch 143 µA und das müsste doch 
eigtl. noch tiefer gehen?

Code-Schnippsel aus void schlafen() ist:
1
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
2
  ADCSRA = 0;            
3
  power_all_disable ();  
4
  sleep_enable ();
5
  sleep_cpu ();

Damit komme ich auf besagte 143 µA.

Ergänze ich den Schnippsel mit dem hier:
1
//init
2
#define BODS 7                   //BOD Sleep bit in MCUCR
3
#define BODSE 2                  //BOD Sleep enable bit in MCUCR
4
uint8_t mcucr1, mcucr2;
5
6
//brownout
7
    cli ();
8
    mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE);  //turn off the brown-out detector
9
    mcucr2 = mcucr1 & ~_BV(BODSE);
10
    MCUCR = mcucr1;
11
    MCUCR = mcucr2;
...so ändert sich rein gar nichts am Stromverbrauch. Bei gammon.com.au 
sind listings für ATtiny85 drin die damit in den nA Bereich kommen, also 
geht's scheinbar.
Würde mich freuen, nochmals einen Denkanstoß von euch zu bekommen.

von S. Landolt (Gast)


Lesenswert?

Nichts mehr angeschlossen, also auch nicht der Programmieradapter?
Liegen alle Eingänge auf GND oder Vcc?

von Tobias (Gast)


Lesenswert?

Angeschlossen sind die LED+Vorwiderstand, +5V Spannungsquelle und der 
Taster für on/off.
Die pin 0, 1, 2, 5 sind unbenutzt und nicht angeschlossen.
Wenn ich die unbenutzten pin als INPUT definiere und auf GND oder Vcc 
setze, ändert sich 0 auf dem Amperemeter.

von S. Landolt (Gast)


Lesenswert?

Und dieser Taster liegt im Ruhezustand auf ...? Vielleicht mal auf 
Verdacht den internen Pullup einschalten.
Sind diese 143 uA stabil oder schwanken sie?

von Tobias (Gast)


Lesenswert?

Absolut konstant ohne Wackler. Zum Taster: geschlossen zieht er PIN 4 
auf GND und dann heißt's void sleep()

von S. Landolt (Gast)


Lesenswert?

Dann befindet sich wohl der uC nicht im Power-down-Modus. Da ich den 
Code nicht interpretieren kann, muss ein anderer weiterhelfen.

von S. Landolt (Gast)


Lesenswert?

Darf man davon ausgehen, dass dieses
> power_all_disable ();
dem hier
1
  ldi   tmp0,(1<<PRTIM1)+(1<<PRTIM0)+(1<<PRUSI)+(1<<PRADC)
2
  out   PRR,tmp0
3
  ldi   tmp0,(1<<ACD) ; off
4
  out   ACSR,tmp0
entspricht?

von m.n. (Gast)


Lesenswert?

Mach doch mal nur dieses hier:
#define BIT(x)  (1<<x)
  MCUCR = BIT(SE) + BIT(SM1);  // Power-Down wählen
  sleep_cpu();

von S. Landolt (Gast)


Lesenswert?

> Zum Taster: geschlossen zieht er PIN 4 auf GND
> und dann heißt's void sleep()

Wie wird erreicht, dass der uC wieder aufwacht? Ist an PIN 4 etwa der 
interne Pullup eingeschaltet, das wären nach Datenblatt bei 5.0 V so um 
die 140 uA.

von Tobias (Gast)


Lesenswert?

@S. Landolt: Das war's, PIN 4 war noch auf HIGH…
Jetzt messe ich 0,5 µA
Kleiner geht mein 08/15 Messgerät leider nicht :_(

von Tobias (Gast)


Lesenswert?

Vielen Dank an euch alle für den regen Input!

von Peter D. (peda)


Lesenswert?

Jörg W. schrieb:
> Sie sollte aber nicht leer sein, sondern in jedem Falle zumindest
> den Code enthalten, der den jeweiligen Interrupt wieder abschaltet.

Sowas könnte gefährlich werden, z.B. wenn der Interrupt kurz vor dem 
Sleep auftritt.
Besser ist ein CLI direkt nach dem Sleep.
Nach jedem Interrupt wird immer erst ein Main-Befehl ausgeführt.

Interrupts, die sich selbst modifizieren, sind auch nicht gern gesehen, 
sie erschweren das Verstehen des Programmablaufs.

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.