Attiny85 aus Schlaf aufwecken

Gast #4198334
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!
Moderator Persönliche Seite #4198340
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
Gast #4198350
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.
Moderator Persönliche Seite #4199737
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.
Moderator Persönliche Seite #4200120
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.
Gast #4202687
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.
Gast #4202720
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.
Gast #4202754
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?
Gast #4202788
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.
#4203212
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.

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