Forum: Mikrocontroller und Digitale Elektronik Attiny85 aus dem schlaf holen


von Torsten W. (toto1975)


Angehängte Dateien:

Lesenswert?

Hallo in die Runde,

ich möchte euch gleich warnen.... ich bin noch absoluter Anfänger!! Ich 
hoffe Ihr habt dafür Verständnis ;-)

Mein Ziel ist es mit einem PIR-Sensor Funksteckdosen zu schalten, dies 
funktioniert auch ohne Probleme allerdings ist der Stromverbrauch doch 
recht hoch ohne den Sleep-Modus und genau hier ist auch mein Problem, 
ich bekomme den Attiny85 leider nicht mehr aus dem Schlaf-Modus.

Hier mal mein bisheriger Code:
1
include <avr/sleep.h>
2
#include <avr/power.h>
3
#include <RCSwitch.h>
4
RCSwitch mySwitch = RCSwitch();
5
6
void setup()
7
{
8
  mySwitch.enableTransmit(3);
9
  mySwitch.setPulseLength(320);
10
  pinMode(bewegung, INPUT);
11
  attachInterrupt(0, interrupt, RISING);
12
}
13
14
void loop()
15
{
16
  bewegungsstatus = digitalRead(bewegung);
17
  
18
  if (status != bewegungsstatus)
19
  {
20
21
    if (bewegungsstatus == HIGH)
22
    {
23
        mySwitch.send(4433, 24);
24
        delay(500);
25
        status = bewegungsstatus;
26
        enterSleep();
27
    }
28
29
    else
30
    {
31
        mySwitch.send(4436, 24);
32
        delay(500);
33
        status = bewegungsstatus;
34
        enterSleep();
35
    }
36
  }
37
}
38
39
void interrupt() {
40
  //leere Interruptfunktion
41
}  
42
43
void enterSleep(){
44
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  //festlegen des Schlafmoduses
45
  sleep_enable();  //ermoeglichen der angegebenen Schlaffunktion
46
  sleep_mode();  //starten der Schlaffunktion
47
  //hier geht es nach dem Interrupt weiter
48
  sleep_disable();  //deaktivieren der Schlaffunktion
49
  power_all_enable();  //reaktivieren aller Chips/Funktionen
50
}

Der Bewegungssensor gibt einen HIGH ab sobald er eine Bewegung 
feststellt. Dieser sollte doch eigentlich den Attiny85 wieder aus dem 
schlaf holen (Dachte ich jedenfalls).

Der Data Pin des Bewegungs- Sensor liegt am Pin2 des Attinys. Ein 
"Schaltplan" habe ich hier im Anhang


Hat jemand noch mal einen Tipp oder eine Idee woran es liegen kann?

Viele Grüße
Torsten

von S. Landolt (Gast)


Lesenswert?

Ich verstehe nicht viel von alledem, aber:
>  //hier geht es nach dem Interrupt weiter
Wo wird dieser zugelassen?

von Jan H. (jan_m_h)


Lesenswert?

Soweit ich das sehe, musst du noch Interrupts Global aktivieren mit 
sei();

Ich weiß aber nicht, ob eine deiner Libraries das nicht schon macht, 
dann hättest einen andren Fehler noch drin.

von Torsten W. (toto1975)


Lesenswert?

Danke für die Antworten

S. Landolt schrieb:
> Ich verstehe nicht viel von alledem, aber:
>>  //hier geht es nach dem Interrupt weiter
> Wo wird dieser zugelassen?

Ich dachte mit "attachInterrupt(0, interrupt, RISING);" wird dies 
zugelassen.... bin mir da aber auch nicht sicher. :-(

Jan H. schrieb:
> Soweit ich das sehe, musst du noch Interrupts Global aktivieren mit
> sei();
>
> Ich weiß aber nicht, ob eine deiner Libraries das nicht schon macht,
> dann hättest einen andren Fehler noch drin.

Muss ich das im Setup Bereich machen? Sorry für die Fragen.

Viele Grüße
Torsten

von CBF (Gast)


Lesenswert?

Torsten W. schrieb:
> pinMode(bewegung, INPUT);

Wer oder was ist "bewegung"? Hier fehlt eine Deklaration.

von CBF (Gast)


Lesenswert?

Noch eine dumme Frage: der Code läuft tatsächlich auf dem 85er und nicht 
auf dem im Schaltbild gezeigten Arduino-board?

von CBF (Gast)


Lesenswert?

...weitere Fragestellungen deren Antworten du dir selber sehr gut über 
das WWW beantworten kannst sind:

- du verwendest du Arduine IDE: unsterstützt diese den ATtiny korrekt? 
(im Sinne von Pinzuordnung)
- das Datenblatt vom ATtiny hast du studiert? INT-ports sind PB0 bis 
PB2, das sind die Pinne 5, 6 und 7 physikalisch.
- weißt du, was deine Zeile "attachInterrupt(0, interrupt, RISING);" 
macht? Und viel wichtiger, welcher pyhsikalische Pin sich hinter der "0" 
verbirgt?

von Torsten W. (toto1975)


Lesenswert?

Hallo,




CBF schrieb:
> Noch eine dumme Frage: der Code läuft tatsächlich auf dem 85er und nicht
> auf dem im Schaltbild gezeigten Arduino-board?

Der Adruino dient hier nur als Stromversorgng

CBF schrieb:
> Torsten W. schrieb:
>> pinMode(bewegung, INPUT);
>
> Wer oder was ist "bewegung"? Hier fehlt eine Deklaration.

Stimmt, ich habe leider die Deklaration vergessen. Sorry!

int bewegung = 2;
int bewegungsstatus = 0;
int status = 0;

"pinMode(bewegung, INPUT);" soll auf Pin zwei

CBF schrieb:
> - das Datenblatt vom ATtiny hast du studiert? INT-ports sind PB0 bis
> PB2, das sind die Pinne 5, 6 und 7 physikalisch

Das ist gar nicht so einfach... leider gab es zur meiner Schulzeit kein 
Englisch und somit ist dies mehr schlecht als recht... Ich werde aber da 
noch mal einen tieferen Blick drauf werfen müssen.

Wenn ich es richtig verstanden habe müsste
*PB0 = PCINT0 = physikalisch 5
*PB1 = PCINT1 = physikalisch 6
*PB" = PCINT2 = physikalisch 7

sein, oder?

CBF schrieb:
> - weißt du, was deine Zeile "attachInterrupt(0, interrupt, RISING);"
> macht?

Wenn ich es richtig verstanden habe geht es hier um den Pin PB0 = PCINT0 
= physikalisch 5 dieser springt zum void interrupt () wenn der Pin 5 
Volt Eingang bekommt?

Bitte korrigiere mich wenn was nicht stimmt

CBF schrieb:
> Und viel wichtiger, welcher pyhsikalische Pin sich hinter der "0"
> verbirgt?

Das müsste ja Pin 5 sein wenn ich es oben richtig verstanden habe.

CBF schrieb:
> du verwendest du Arduine IDE: unsterstützt diese den ATtiny korrekt?
> (im Sinne von Pinzuordnung)

Das muss ich jetzt erst mal weiter testen und suchen.

Vielen Dank schon mal für eure Hilfe.... das bringt mich doch schon ein 
Stück weiter.

VG
Torsten

PS: Gibt es ein gute Anleitung auf Deutsch? Ich habe bis jetzt leider 
noch nicht wirklich was gefunden.

von c-hater (Gast)


Lesenswert?

CBF schrieb:

> - das Datenblatt vom ATtiny hast du studiert?

Und du?

> INT-ports sind PB0 bis
> PB2, das sind die Pinne 5, 6 und 7 physikalisch.

Beim Tiny85 ist jeder Pin interruptfähig. PCINT0..5, wobei PCINT5 
natürlich erst nutzbar wird, wenn man den Reset-Pin zu einem IO-Pin 
umgefused hat.

Und was die "klassischen" INTx betrifft: davon hat der Tiny85 nur genau 
einen, INT0. Und der liegt auf dem gleichen physischen Pin wie 
PCINT2/PB2, also auf Pin7.

von wendelsberg (Gast)


Lesenswert?

Torsten W. schrieb:
> allerdings ist der Stromverbrauch doch
> recht hoch ohne den Sleep-Modus

Was braucht denn der Sensor?

von Freakazoid (Gast)


Lesenswert?

Interrupts sind bei Arduino generell immer global aktiviert(sei()). Das 
wird da für die millis() und micros() Funktionen gebraucht.

von TM F. (p_richner)


Lesenswert?

Hast du irgendwo deklariert, welche Pins als Interrupt gebraucht werden? 
Im Register GIMSK musst du das PCIE-Bit setzen. Sonst sind die 
Interrupts ausgeschaltet.
Kämpf dich mal durch all die Register(ab Seite 51 des Datenblattes) und 
siehe, welche du alles benötigst.
1
Datenblatt:
2
http://www.atmel.com/Images/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf

Kannst du mal posten, wie deine Interruptroutine aussieht? (Wegen 
richtigen Deklaration der Routine, etc.)

von Michael U. (amiga)


Lesenswert?

Hallo,

die normalen Interrupts  beziehen sich meines Wissens nur auf die 
Hardwareinterruts, PinChange muß man selber einbinden (mal nach googlen, 
falls sich das geändert hat).
Beim Tiny85 wäre das also nur PB2 (Pin7), in der ArduinoIDE gemappt auf 
Pinnummer 2.


// ATMEL ATTINY45 / ARDUINO
//
//                  +-\/-+
// Ain0 (D 5) PB5  1|    |8  Vcc
// Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1
// Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1
//            GND  4|    |5  PB0 (D 0) pwm0
//                  +----+

Hänge also Deinen Sensor an 2 (PB2, Pin 7), dann müßte es klappen.

Gruß aus Berlin
Michael

: Bearbeitet durch User
von TM F. (p_richner)


Lesenswert?

Hier wie es aus meiner Sicht funktionieren sollte:
1
void init_interrupt(void)
2
{
3
  // Aktiviert die globalen Interrupts;wenn es nicht geht I mit 7 ersetzen
4
  SREG = (1<<I);
5
  // aktiviert die Interrupts vom Port
6
  GIMSK = (1<<PCIE);
7
  // Aktiviert den Interrupt auf deinem Pin
8
  PCMSK = (1<<PB2);
9
}
10
11
ISR(PCINT0_vect)
12
{
13
  // Deine Interruptroutine
14
}

von Mark S. (voltwide)


Lesenswert?

Deine gezeigte Irq-Routine ist leer. Bei mir wird der Attiny85 
periodisch geweckt über den watchdog-timer. Die zugehörige Irq-routine 
sieht dann so aus:
ISR(WDT_vect){                // watchdog interrupt service routine
    sleep_disable();          // wakeup!
    WDTCR |= (1<<WDIE);       // wdt irq re-enable = avoid reset
}

von avr (Gast)


Lesenswert?

Avrs kann man im Powerdown nur durch lowlevel Interrupts wecken. Dein 
Sensor kann den avr also gar nicht wecken.

von TM F. (p_richner)


Lesenswert?

avr schrieb:
> Avrs kann man im Powerdown nur durch lowlevel Interrupts wecken. Dein
> Sensor kann den avr also gar nicht wecken.

Der INT0 und Pin Change Interrupt gehört dazu. Kannst du unter dem 
Kapitel "Power Management and Sleep modes" im Datenblatt nachlesen.

Aus dem Datenblatt:
1
Only an External Reset, a Watchdog Reset, a Brown-out Reset, USI start condition
2
interupt, an external level interrupt on INT0 or a pin change interrupt can wake up the MCU.

Besser zuerst das Datenblatt anschauen bevor nicht recherchierte 
Aussagen machen....

: Bearbeitet durch User
von günni (Gast)


Lesenswert?

TM F. schrieb:
> avr schrieb:
>> Avrs kann man im Powerdown nur durch lowlevel Interrupts wecken. Dein
>> Sensor kann den avr also gar nicht wecken.
>
> Der INT0 und Pin Change Interrupt gehört dazu. Kannst du unter dem
> Kapitel "Power Management and Sleep modes" im Datenblatt nachlesen.

Ich dachte avr hätte von sich selbst gesprochen.

von avr (Gast)


Lesenswert?

TM F. schrieb:
> avr schrieb:
> Avrs kann man im Powerdown nur durch lowlevel Interrupts wecken. Dein
> Sensor kann den avr also gar nicht wecken.
>
> Der INT0 und Pin Change Interrupt gehört dazu.
Der int0 Interrupt muss trotzdem ein Level Interrupt sein. Wenn 
AttachInterrupt diesen nutzt kann es mit rising nicht funktionieren.
> Kannst du unter dem
> Kapitel "Power Management and Sleep modes" im Datenblatt nachlesen.
Ja Schlaumeier, das mit dem levelinterrupt solltest du vielleicht auch 
noch mal nachlesen. Das ist wahrscheinlich hier das Problem.
> Aus dem Datenblatt:Only an External Reset, a Watchdog Reset, a Brown-out
> Reset, USI start condition
> interupt, an external level interrupt on INT0 or a pin change interrupt
> can wake up the MCU.

Naja meine Zeit mit den avrs ist schon etwas her. Die tinys haben wohl 
mehr Möglichkeiten als der atmega48. Auf diesen trifft meine Aussage zu.

von TM F. (p_richner)


Lesenswert?

Der Levelinterrupt wird nur gebraucht, wenn PB2 explizit als INT0 
verwendet wird. Wird PB2 als "normale" I/O gebraucht, fährt der uC aus 
dem Sleep-Mode hoch, wenn ein Interrupt kommt.

Oder sehe ich etwas falsch?

von Clemens M. (panko)


Lesenswert?

Wenn in dem Sleep-Mode kein Clock mehr läuft brauchst du den level 
Interrupt. Der wird asynchron eingelesen und braucht keinen Clock.

von Torsten W. (toto1975)


Lesenswert?

Hallo,

erst mal vielen Dank für die vielen Antworten. Das hat mir schon mal 
sehr viel weiter geholfen.

Ich habe meinen Code ein wenig "Vereinfacht"
1
#include <avr/sleep.h>
2
#include <avr/power.h>
3
#include <avr/interrupt.h> 
4
5
int bewegung = 0;
6
int interrupta = 2;
7
8
// the setup function runs once when you press reset or power the board
9
void setup() {
10
  
11
   pinMode(3, OUTPUT); //für die LED
12
   pinMode(bewegung, INPUT); 
13
   
14
15
// the loop function runs over and over again forever
16
void loop() {
17
  for (int i=0; i<5; i++){
18
    digitalWrite(3, HIGH);   // turn the LED on (HIGH is the voltage level)
19
    delay(1000);              // wait for a second
20
    digitalWrite(3, LOW);    // turn the LED off by making the voltage LOW
21
    delay(1000);              // wait for a second
22
  }
23
  enterSleep();
24
}
25
26
void enterSleep(){
27
  attachInterrupt(0,interrupt, LOW); //PIR Sensor an PIN2
28
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  //festlegen des Schlafmoduses
29
  sleep_enable();  //ermoeglichen der angegebenen Schlaffunktion
30
  sleep_mode();  //starten der Schlaffunktion
31
  //hier geht es nach dem Interrupt weiter
32
  sleep_disable();  //deaktivieren der Schlaffunktion
33
  power_all_enable();  //reaktivieren aller Chips/Funktionen
34
}  
35
36
void interrupt() {
37
  detachInterrupt(0);
38
}

Michael U. schrieb:
> Hänge also Deinen Sensor an 2 (PB2, Pin 7), dann müßte es klappen.

Ja, endlich ein Licht am Ende des Tunnels :-) Sobald der Bewegungsmelder 
keine Bewegung mehr erkennt (also von HIGH auf LOW geht) wacht mein 
Attiny85 wieder auf und die LED blinkt 5 mal. Danach legt er sich wieder 
schlafen... Jetzt wacht er allerdings nicht wieder auf :-(
1
attachInterrupt(0,interrupt, CHANGE);
funktioniert leider nicht.

Wie bekomme ich jetzt den Attiny85 wieder wach wenn er eine Bewegung 
erkennt (also von LOW auf HIGH wechselt)

avr schrieb:
> Avrs kann man im Powerdown nur durch lowlevel Interrupts wecken.

Sei mir bitte nicht böse aber ich hoffe du hast nicht recht auch wenn es 
im Moment nicht danach aussieht. Dann wären einige Stunden in den Sand 
gesetzt.

TM F. schrieb:
> Der Levelinterrupt wird nur gebraucht, wenn PB2 explizit als INT0
> verwendet wird. Wird PB2 als "normale" I/O gebraucht, fährt der uC aus
> dem Sleep-Mode hoch, wenn ein Interrupt kommt.

Sorry, das verstehe ich (noch) nicht. Was meinst du damit?

Ich hoffe Ihr habt noch ein paar Ideen für mich und könnt mich noch ein 
Stück weiterbringen

Viele Grüße
Torsten

von S. Landolt (Gast)


Lesenswert?

> ...aber ich hoffe...
Hoffen ist nicht nötig, im Datenblatt '7.1.3 Power-down Mode' lesen:
"... or a pin change interrupt can wake up the MCU."

von S. Landolt (Gast)


Lesenswert?

... wie TM FM um 10:30 bereits schrieb.

von Torsten W. (toto1975)


Lesenswert?

Hallo in die Runde,

vielen lieben Dank an alle, es funktioniert jetzt so wie es soll! :-)

Mein Code ist jetzt folgender:
1
#include <avr/sleep.h>
2
#include <avr/power.h>
3
#include <avr/interrupt.h> 
4
5
 ISR(PCINT0_vect){
6
  
7
 }
8
9
10
int bewegung = 0;
11
int interrupta = 2;
12
13
// the setup function runs once when you press reset or power the board
14
void setup() {
15
 
16
   pinMode(3, OUTPUT);
17
   pinMode(bewegung, INPUT); 
18
   GIMSK  |= (1<<PCIE);  // enable Pin Change interrupts
19
   PCMSK |= (1<<PB2); // enable PB2 which PCINT2 is on
20
   //sei();    // set Enable Interrupts
21
  }
22
23
// the loop function runs over and over again forever
24
void loop() {
25
  for (int i=0; i<5; i++){
26
    digitalWrite(3, HIGH);   // turn the LED on (HIGH is the voltage level)
27
    delay(1000);              // wait for a second
28
    digitalWrite(3, LOW);    // turn the LED off by making the voltage LOW
29
    delay(1000);              // wait for a second
30
  }
31
  enterSleep();
32
}
33
34
35
void enterSleep(){
36
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  //festlegen des Schlafmoduses
37
  sleep_enable();  //ermoeglichen der angegebenen Schlaffunktion
38
  sleep_mode();  //starten der Schlaffunktion
39
  //hier geht es nach dem Interrupt weiter
40
  sleep_disable();  //deaktivieren der Schlaffunktion
41
  power_all_enable();  //reaktivieren aller Chips/Funktionen
42
}  
43
44
void interrupt() {
45
  //nichts
46
}

TM F. schrieb:
> ISR(PCINT0_vect)
> {
>   // Deine Interruptroutine
> }

Bei mir funktioniert es erstaunlicherweise ohne. Könnt ihr mir bitte 
noch erklären was hier üblicherweise rein kommt bzw. Ist die die Routine 
die direkt nach dem Interrupt ausgeführt wird?

Vielen Dank an alle für die Geduld und die super Hilfe

Gruß
Torsten

von MWS (Gast)


Lesenswert?

Torsten W. schrieb:
> vielen lieben Dank an alle, es funktioniert jetzt so wie es soll! :-)

Das tut's aber auch nur, weil Arduino einen Timer mit ISR für millis() 
uind delay() aufsetzt und deswegen Interrupts global einschaltet. Sonst 
würde ein sei() am Anfang fehlen.

> Bei mir funktioniert es erstaunlicherweise ohne.

Üblicherweise zeigen in C die Interruptvektoren von nicht deklarierten 
ISRs auf einen bad vector, der, falls nicht anders bestimmt, einen Reset 
auslöst. Kann einfach sein, dass Du den Reset nicht bemerkst.

Für eine leere ISR gibt's sowas:
EMPTY_INTERRUPT (PCINT0_vect);

Da dürfte dann ein RETI in der IVT stehen.

von c-hater (Gast)


Lesenswert?

Torsten W. schrieb:

> TM F. schrieb:
>> ISR(PCINT0_vect)
>> {
>>   // Deine Interruptroutine
>> }
>
> Bei mir funktioniert es erstaunlicherweise ohne. Könnt ihr mir bitte
> noch erklären was hier üblicherweise rein kommt bzw. Ist die die Routine
> die direkt nach dem Interrupt ausgeführt wird?

Üblicherweise kommt da rein die Deaktivierung des Interrupts, der zum 
Erwachen führen soll (und in der Folge erstmal zu nichts mehr). Nötig 
wird das i.A. bei prellenden mechanischen Kontakten oder bei 
Level-getriggertem Interrupt.

Die ISR selber ist aber natürlich unbedingt nötig, denn sie wird 
tatsächlich als erstes nach dem Aufwachen durchlaufen.

Fehlt sie, kommt's auf die Laufzeitumgebung an, was passiert. Beim gcc 
wird also letztlich Adresse 0 angesprungen, das ganze Programm startet 
also neu, fast wie nach PowerOn.

Aaaaber: es hat kein echter Reset stattgefunden, d.h.: im ersten Lauf 
initialisierte Hardware bleibt initialisiert und könnte u.U. über daran 
gekoppelte ISRs "erstaunliche" Folgeschäden nach dem Aufwachen 
verursachen.

Wenn du also noch alle Latten am Zaun hast, gönnst du deinem Programm 
wenigstens die leere ISR. Zumindest bis du das Ganze wirklich im Schlaf 
in Assembler beherrschst (was voraussichtlich niemals passieren wird...)

von TM F. (p_richner)


Lesenswert?

Torsten W. schrieb:
>> Der Levelinterrupt wird nur gebraucht, wenn PB2 explizit als INT0
>> verwendet wird. Wird PB2 als "normale" I/O gebraucht, fährt der uC aus
>> dem Sleep-Mode hoch, wenn ein Interrupt kommt.
>
> Sorry, das verstehe ich (noch) nicht. Was meinst du damit?

Der Levelinterrupt bedeutet, dass wenn deine Eingangsspannung einen 
festen Referenzwert(ca. 1,1V, siehe Datenblatt) überschreitet wird ein 
Interrupt ausgelöst.
Ein "normaler" I/O löst einen Interrupt aus, wenn das Eingangssignal das 
Level eines logischen 1 (oder 0, je nach Konfiguration) erreicht hat.

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.