Forum: Compiler & IDEs Arduino schlafen legen und aufwachen mit interrupt


von Tobias H. (tobivanhernobi)


Lesenswert?

Hallo,

ich habe leider Probleme damit, einen Arduino schlafen zu legen.
1
#include <avr/sleep.h>
2
3
const int Taster = 2;
4
int buttonState = 0;
5
int lastButtonState = 0;
6
int buttonPushCounter = 0;
7
8
void setup()
9
{
10
  Serial.begin(115200);
11
  pinMode(2, INPUT_PULLUP);
12
}
13
14
void loop()
15
{
16
  buttonState = digitalRead(Taster);
17
  //Überprüfen auf Veränderung
18
  if (buttonState != lastButtonState) {
19
    // Wenn letzter Wert nicht gleich:
20
    if (buttonState == HIGH) {
21
      buttonPushCounter += 1;
22
      ausgabe();
23
      Serial.print("number of button pushes: ");
24
      Serial.println(buttonPushCounter);
25
    }
26
  }
27
  delay(50); //Entprellen quick-and-dirty
28
  lastButtonState = buttonState; //für nächsten loop-Durchgang speichern
29
}
30
31
void ausgabe()
32
{
33
  if (buttonPushCounter == 4)
34
  {
35
    enterSleepMode();
36
  }
37
}
38
39
void isrAwake()
40
{
41
}
42
43
void enterSleepMode()
44
{
45
  Serial.println("in enterSleepMode");
46
  attachInterrupt(digitalPinToInterrupt(Taster), isrAwake, FALLING);
47
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
48
  sleep_mode();
49
}

Konkret, er legt sich nicht schlafen...Der Zähler geht hoch, 1, 2, 3 und 
dann geht er beim nächsten Tastendruck in "void enterSleepMode()" und 
sofort wieder in loop().

Woran kann das liegen?
Vielen Dank für Tipps.

von Stefan F. (Gast)


Lesenswert?

Welcher Mikrocontroller ist das konkret?

Ich frage, weil der mit dem ich das mal ausprobiert habe für diesen 
Zweck keine Flanken (FALLING) benutzen kann.

von Tobias H. (tobivanhernobi)


Lesenswert?

m328p auf dem Arduino
CHANGE? RISING?

von Stefan F. (Gast)


Lesenswert?

sleep_enable(); fehlt

von Stefan F. (Gast)


Lesenswert?

Tobias H. schrieb:
> RISING

Das ist auch eine Flanke.

Es geht nur LOW oder HIGH. Im Sleep Modus kann der Atmega328 keine 
Flanken erkennen, nur Pegel.

von Tobias H. (tobivanhernobi)


Lesenswert?

Probiere ich, danke.

von Gerald B. (gerald_b)


Lesenswert?

Du hast mit dem Delay zwar sowas Ähnliches, wie Entprellung für Arme 
gemacht. Das greift aber nicht beim Interrupt. Wenn da das Kabel länger 
ist und der Eingang gar noch offen ist, dann ist das Ding eine bessere 
Wünschelrute und wacht bei jedem bisschen Ladung auf, die 
rumvagabundiert. Also relativ niederohmigen Pullup/down (Richtwert 10K, 
erhöhen zum Stromsparen kann man immernoch) und 100n über den Taster.
Somit hast du elektrisch entprellt und der Controller kann sich auch mal 
unbehelligt schlafen legen ;-)

von Stefan F. (Gast)


Angehängte Dateien:

Lesenswert?

Damit dein AVR einschlafen kann musst du sleep_enable() aufrufen.

Stefan F. schrieb:
> Es geht nur LOW oder HIGH

Ich glaube das war falsch. Es geht wohl nur mit LOW, denn Arduino 
unterstützt hier nur die Pins 2 (INT0) und 3 (INT1).

Arduino erlaubt laut Doku 
(https://www.arduino.cc/reference/de/language/functions/external-interrupts/attachinterrupt/) 
noch eine Erkennung von HIGH, aber davon weder etwas im Datenblatt des 
AVR, noch im Quelltext 
(https://github.com/arduino/ArduinoCore-avr/blob/63092126a406402022f943ac048fa195ed7e944b/cores/arduino/WInterrupts.c#L73), 
Vielleicht gilt das nur für andere Mikrocontroller.

Dann gibt es beim ATmega328 noch den Pin-Change Interrupt auf so 
ziemlich allen Pins, aber den unterstützt Arduino scheinbar nicht auf 
dem Uno/Nano Board. Ein Blick in den Quellcode erhärtet den Verdacht:
https://github.com/arduino/ArduinoCore-avr/blob/63092126a406402022f943ac048fa195ed7e944b/cores/arduino/WInterrupts.c#L372

So viel zum Thema: Arduino ist ganz einfach, weil man die Details in der 
Doku weg lässt.

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Stefan F. schrieb:

> Es geht nur LOW oder HIGH. Im Sleep Modus kann der Atmega328 keine
> Flanken erkennen, nur Pegel.

Nein, das ist natürlich Unsinn. Man darf halt dafür nur nicht INTx 
verwenden, sondern PCINTx. Letzteres kann überhaupt nur Flanken erkennen 
und kann immer auch als WakeUp-Quelle aus dem PowerDown dienen.

Sämtliche 328 (ob ohne was dran oder mit P oder mit PB dran) können das. 
Seit Jahrzehnten schon, möchte man hinzufügen.

von Stefan F. (Gast)


Lesenswert?

Ob S. schrieb:
> Nein, das ist natürlich Unsinn. Man darf halt dafür nur nicht INTx
> verwenden, sondern PCINTx.

Dann zeige mal, wie du das mit Arduino erreichen willst. Hast du meine 
zweiter Erklärung (die mit den Screenshots und Links zur Doku und 
Quelltext) nicht gelesen?

von Stefan F. (Gast)


Lesenswert?

Hier ist das "Problem" mit den Interrupts ausführlich beschrieben. Auch 
wie man am Arduino Framework vorbei die Pin Change Interrupts verwendet: 
https://dronebotworkshop.com/interrupts/

von Ob S. (Firma: 1984now) (observer)


Lesenswert?

Stefan F. schrieb:

> Dann zeige mal, wie du das mit Arduino erreichen willst.

An Arduino vorbei programmieren natürlich. Arduino ist zwar ziemlicher 
Mist, geht aber nicht so weit, einem Direktzugriffe auf Register oder 
eigene Interrupthandler zu verbieten. Wobei letztere, wenn sie nur dem 
Zweck des Aufwachens dienen, ja obendrein leer sind, also 
programmiertechnisch keine nennenswerte Herausforderung darstellen 
dürften.

von Peter D. (peda)


Lesenswert?

Stefan F. schrieb:
> Dann zeige mal, wie du das mit Arduino erreichen willst.

Nur weil eine IDE nicht für alles und jedes einen Befehl haben kann, ist 
es nicht verboten, es selber zu programmieren.

In den Urzeiten der Computerei gab es z.B. PEEK und POKE, um am Basic 
vorbei die dollsten Sachen zu programmieren.

von Tobias H. (tobivanhernobi)


Lesenswert?

Vielen Dank, Gerald B., das ist die Lösung. Mit einem Kondensator 
klappt's wunderbar. Hätte ich mir eigentlich denken müssen, Taster 
wollen entprellt werden...

sleep_enable() ist nicht notwendig, mit sleep_mode() aus der sleep.h 
wird das se-bit automatisch gesetzt/wieder gelöscht.
FALLING/RISING/CHANGE geht übrigens auch alles.

von M. K. (sylaina)


Lesenswert?

Tobias H. schrieb:
> sleep_enable() ist nicht notwendig, mit sleep_mode() aus der sleep.h
> wird das se-bit automatisch gesetzt/wieder gelöscht.
> FALLING/RISING/CHANGE geht übrigens auch alles.

Das ist richtig, was ich mich aber auch frage: Warum setzt du den 
Sleepmode immer wieder neu? Würde reichen, den in setup() einmal zu 
setzen ;)
Ähnliches gilt für attachInterrupt() ;)

von Peter D. (peda)


Lesenswert?

Tobias H. schrieb:
> mit sleep_mode() aus der sleep.h
> wird das se-bit automatisch gesetzt/wieder gelöscht.

Wobei dieses Macro nicht interruptfest ist! Im günstigsten Fall geht nur 
ein Interrupt verloren (Uhr geht nach), im worst case schläft der MC für 
immer (bis zum nächsten CPU-Reset).
Und das Löschen von SE nach dem Sleep ist eh überflüssig, es reicht 
völlig das einmalige Setzen im Initcode.

von Rolf (rolf22)


Lesenswert?

Ob S. schrieb:

>> Dann zeige mal, wie du das mit Arduino erreichen willst.
>
> An Arduino vorbei programmieren natürlich.

Wieso "vorbei"? In der Arduino IDE und auch in der Alternative 
"PlatformIO" von Visual Studio Code kann man in C++ alle Register 
ansprechen, und zwar mit Symbolen, die in diesen IDEs vordefiniert sind 
und genau denen entsprechen, die in der Prozessor-Doku benutzt werden.

@Stefan F.
Über die Register-Programmierung kann man man den 328P auch aus dem 
Deep-Sleep wecken, sowohl über die INT-Pins als auch über die 
PCINT-Pins. Bei den PCINT-Pins gibt es "Pin Change" als Event, bei den 
INT-PINS gibt es zusätzlich die Möglichkeiten "Low level", "Rising edge" 
und "Falling edge". Prozessor-Doku S. 80/82.

Steht alles in der Prozessor-Doku. Wer sich auf die Arduino-Doku und 
-Lib verlässt, muss eben "unten" bleiben und erfährt von vielen 
Möglichkeiten niemals etwas.

von Jörg (lixtop)


Lesenswert?

1
#include <avr/sleep.h>
2
3
const byte LEDLOOP = 8;
4
const byte LEDWAKE = 9;
5
6
ISR (PCINT1_vect)
7
 {
8
 // handle pin change interrupt for A0 to A5 here
9
 
10
 // toggle LED
11
 digitalWrite (LEDWAKE, !digitalRead (LEDWAKE));
12
 }  // end of PCINT1_vect
13
14
void setup () 
15
  {
16
  pinMode (LEDWAKE, OUTPUT);
17
  pinMode (LEDLOOP, OUTPUT);
18
  digitalWrite (A0, HIGH);  // enable pull-up
19
  
20
  // pin change interrupt
21
  PCMSK1 |= bit (PCINT8);  // want pin A0
22
  PCIFR  |= bit (PCIF1);   // clear any outstanding interrupts
23
  PCICR  |= bit (PCIE1);   // enable pin change interrupts for A0 to A5
24
  
25
  }  // end of setup
26
27
void loop () 
28
{
29
  
30
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
31
  sleep_mode ();  
32
33
  // flash to indicate we got out of sleep
34
  digitalWrite (LEDLOOP, HIGH);
35
  delay (100);
36
  digitalWrite (LEDLOOP, LOW);
37
  delay (100);
38
  
39
  } // end of loop

Quelle: http://gammon.com.au/interrupts

"Example of waking from sleep with a Pin Change Interrupt"

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.