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!
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
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.
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.
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.
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.
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.
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.
Und dieser Taster liegt im Ruhezustand auf ...? Vielleicht mal auf
Verdacht den internen Pullup einschalten.
Sind diese 143 uA stabil oder schwanken sie?
> 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.
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.