Arduino schlafen legen und aufwachen mit interrupt

#7522062
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.

#7522073
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 ;-)

Gast #7522083
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.

Angehängte Dateien:
(Firma: 1984now) #7522102
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.

Gast #7522104
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?

(Firma: 1984now) #7522111
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.

#7522591
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.

#7535592
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() ;)

#7539612
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.

#7539633
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.

#7542322
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"

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