Frage zu Interrupt - Arduino

Gast #5102753
Lesenswert?

Hallo zusammen.

Ich habe eine Frage zum Abfangen eines Interrupts mit dem Arduino Uno.

An den digitalen Pins 2 und 3 soll es ja möglich sein, einen Interrupt 
auszulösen, wenn (wie in meinem Beispiel) ein Taster daran angeschlossen 
ist und betätigt wird. In meinem Fall ist der Taster an DigPin 3 
angeschlossen.

Ich möchte diesen Taster dafür nutzen, um auf meinem 16x2-LCD vom 
"LCD_Seite_0" in "LCD_Seite_1" zu springen, und bei erneutem Drücken 
wieder von "LCD_Seite_1" zurück zu "LCD_Seite_0". Das Ganze mit einem 
Taster...

Mein Arduino liest derzeit zwei Temperatursensoren (1-Wire) aus, was 
soweit auch funktioniert. Nur habe ich das Problem mit dem Auslösen des 
Interrupts und dem Hin- und Herswitchen von LCD_Seite_1 zu LCD_Seite_2.


Hier mein Programmcode:
1
int TasterPin=3;                // Taster an DigPin 3 angeschlossen
2
int LCD_Menu=0;
3

4
setup()
5
{
6
  pinMode(TasterPin, INPUT);                                          //         Taster-Pin als Eingang deklarieren
7
  attachInterrupt(digitalPinToInterrupt(TasterPin), LCD_Seite_1, CHANGE);
8
}
9

10

11
loop()
12
{
13
    if(LCD_Menu == 0)
14
    {
15
        // zeige am Display Seite_0 an
16
    }
17

18
}
19

20

21
//----------------------------- Interrupt Service Routine ---------------------------------------
22

23
void LCD_Seite_1()
24
{
25
  if(LCD_Menu == 1)    // wenn man bereits im LCD_Menu_1 ist...
26
  {
27
    LCD_Menu = 0;      // ...setze LCD_Menu_Variable auf 0..
28
    loop();            // und starte die loop()-Funktion
29
  }
30

31
  else
32
  {
33
      sensor_AMB_Temp.requestTemperatures();
34

35
      // ansonsten hole bei Tastendruck die Sensorwerte vom AmbientSensor und zeige sie am Display an (Displayausgabe aus Platzgründen weggelassen) 
36

37
      LCD_Menu = 1;
38
      
39
  }
40
}


Leider funktioniert das Ganze aber nicht so wie ich das möchte.
Der Taster soll quasi zwischen zwei Displayanzeigen (LCD_Seite_0 und 
LCD_Seite_1) zum hin- und herwechseln genutzt werden.
LCD_Seite_1 soll solange angezeigt werden, bis der Taster wieder 
betätigt wird. Dann soll die Anzeige LCD_Seite_0 folgen.

Was mache ich falsch?

Danke schonmal!
Gast #5102771
Lesenswert?

Hallo Ralf,

unabhängig von deiner Codierung, ist es sehr sehr wichtig zu wisse: 
Taster Prellen, man schließt sie nie an einen aktiven Interrupteingang 
an.
Sie müssen entprellt werden und die Funktionen liefern dann nur 
Ereignisse, wie: wurde gerückt oder wurde losgelassen, zurück.

Du findest dazu gefühlt 10.000 Beiträge hier im Forum.
Eine sehr gute Lösung ist Peter Danneggers key debounce.

Eine Auswahl:

https://www.mikrocontroller.net/articles/Entprellung
Gast #5102778
Lesenswert?

Hallo,

die Entprellung kann bei den Arduinos weggelassen werden, weil die 
bereits eine Entprellung beinhalten, wenn man einen Taster/Schalter 
abfragt.

Zumal der Taster allerdings auch eine Aktion auslöst und somit vom 
Controller erkannt wird, jedoch nicht die gewünschte Aktion:

 - Ich befinde mich in LCD_Seite_0..
 - Diese wird wunderbar angezeigt.
 - Ich drücke den Taster: Der Controller schaltet wie gewünscht auf 
"LCD_Seite_1", die Temperatur wird auch für etwa 10 Sekunden angezeigt 
(flackert allerdings etwas am Display)
 - Bei erneutem Drücken des Tasters um wieder auf Seite_0 zu kommen, 
startet der Arduino neu (sehe ich am "SplashScreen" am Display) nachdem 
er sich aufgehängt hat.
#5102783
Lesenswert?

1
//...
2
void LCD_Seite_1()
3
{
4
  if(LCD_Menu == 1)    // wenn man bereits im LCD_Menu_1 ist...
5
  {
6
    LCD_Menu = 0;      // ...setze LCD_Menu_Variable auf 0..
7
    loop();            // und starte die loop()-Funktion FEHLER!!!!
8
  }
9
//....


Du betreibst da aus einer ISR eine Rekursive Ausführung. Schreib dir ein 
Programmablaufplan auf:


Taste -> ISR ( LCD_MENU==1 -> loop()) ->???

Deine ISR wird quasi nie verlassen - also raus da mit dem Loop() der 
läuft sowieso "weiter" (Rücksprung aus der ISR ist immer der letzte 
Ausführungspunkt)
#5102784
Lesenswert?

> //----------------------------- Interrupt Service Routine
> ---------------------------------------
>
> void LCD_Seite_1()
> {
...
>     loop();            // und starte die loop()-Funktion
>   }
>
>   else
>   {
>       sensor_AMB_Temp.requestTemperatures();
...
>       LCD_Menu = 1;
>
>   }
> }
>
>

Unabhängig vom entprellen der Taste, bin ich der Meinung das deine ISR 
total daneben ist.
- Eine ISR sollte so kurz wie möglich sein
- die ISR hat aus meiner sicht niemals einen Aufruf von loop() da sie 
nach Abarbeitung von alleine wieder zu der Stelle in loop() springt, an 
der der Interrupt ausgelöst wurde.
Vorschlag: Setze in der ISR einen Marker der Loop signalisiert das die 
Taste betätigt wurde. Diesen Marker musst du dann in loop() auswerten 
und entsprechend reagieren.


Schaue dir mal das Beispiel unter
https://www.arduino.cc/en/Reference/AttachInterrupt
an.
Gast #5102807
Lesenswert?

CO2 ist ihm N. schrieb:
> Ralf schrieb:
>> die Entprellung kann bei den Arduinos weggelassen werden, weil die
>> bereits eine Entprellung beinhalten, wenn man einen Taster/Schalter
>> abfragt.
>
> Bitte um Erklärung wie das gemacht wird.
>
> LG
> old.

Ja, das würde mich jetzt auch interessieren. Bislang kannte ich das noch 
nicht. Das müßte ja irgendwas in Hardware sein.
Gast #5102815
Lesenswert?

Wenn das allgemeine Interrupt-handling von Arduino Entprellung 
beinhalten würde, dann wären die Interrupts für andere gewöhnliche 
Aufgaben ungeeignet.

Im Arduino Tutorial ist erklärt, wie das Debouncing dort realisiert 
werden kann: https://www.arduino.cc/en/Tutorial/Debounce
Wie man dort sieht, enthält das Framework zu diesem Zweck offensichtlich 
keine spezielle Funktion. Es wird dort "zu Fuß" gemacht.
#5102817
Lesenswert?

Hi

PittyJ schrieb:
> Das müßte ja irgendwas in Hardware sein.

Och, Das könnte schon in der Arduino-Software vergossen sein - sollte 
dann aber auch irgendwo erwähnt werden - sonst kann es ganz schnell 
passieren, daß das serielle Signal vom Arduino gar nicht so seriell 
erfasst wird ;)

Denke mir eher, daß die Taster-Routiene genauso oft aufgerufen wird, wie 
der Taster prellt.
Da dann jedes Mal noch die loop() aufgerufen wird, läuft der Stack voll 
und die Kiste wird irgend wann vom WDT resettet, da die 
Rücksprung-Adressen nur noch 'irgendwo hin' zeigen.

MfG
#5102829
Lesenswert?

Entprellung gibts da nicht..

https://www.arduino.cc/en/Tutorial/Debounce
Arduino-IDE->Beispiele->Digital->Debounce
Arduino-IDE->Beispiele->Digital->StateChangeDetection (Einfacher)


https://www.arduino.cc/en/Reference/AttachInterrupt
Dafür gibt es kein Beispiel weil es einfach ist.

Die beiden verbinden Viola.. wenn de rInterrupt nciht so wichtig ist 
müsste das StateChangeDetection für das Menü genügen.. den Interrupt 
benötigt man für Taster nur zum aufwecken des Arduino aus dem tiefen 
Sleep.. ansonsten ist das nutzlos.
Gast #5102867
Lesenswert?

> Loop() ist eine Funktion, die alleine vom "Betriebssystem" des Arduino
> aufgerufen wird.

Das gesuchte Wort ist Laufzeitumgebung für anstelle von "BS".

Loop() ist eine Funktion, die ausschliesslich von der Laufzeitumgebung 
des Arduino aufgerufen wird.

Man weiss zwar nicht wann genau loop() aufgerufen wird, jedoch sobald 
möglich und immer wieder wird die Funktion loop() automagisch 
aufgerufen.
Im Code den man selber schreibt, darf nie ein Aufruf loop() vorkommen.

(eigentlich erstaunlich dass in der "Wattekiste" Arduino ein Aufruf 
loop() an falscher Stelle nicht von der Toolchain überprüft und als 
Fehler zurückgewiesen wird...)
Gast #5103088
Lesenswert?

Guten Morgen!

Ok, das mit der nicht von Haus aus in den DigitalRead()-verankerten 
Debouncing-Dinge habe ich nicht gewusst, man lernt nie aus.
Bisher habe ich die Debounce-Methoden jedoch nicht genutzt, 
fälschlicherweise, da ich sie schlichtweg bei meinen Projektchen nicht 
"benötigt" habe und es trotzdem wunderbar funktioniert hat.
Ich werde aber diese in meinem hier gezeigten Code einfügen.

Zum Interrupt:
 - Ich versuche es mit der StateChangeDetection, da -wie ihr schon 
sagtet- mit Kanonen auf Spatzen geschossen wird, wenn man einen 
"wertvollen" Interrupt-Pin dazu nutzt, lediglich ein Menü eines Displays 
umzublättern.

>Setze in der ISR einen Marker der Loop signalisiert das die
>Taste betätigt wurde. Diesen Marker musst du dann in loop() auswerten
>und entsprechend reagieren.
Das hatte ich ja eigentlich mit der globalen Variable "LCD_Menu" vor, 
die in der ISR auf 1 gesetzt wird. In der loop() wird der Wert der 
Variablen dann in der If-Anweisung geprüft...

Ich berichte, wenn ich heute Abend das Ganze mit StateChangeDetection 
probiert habe.

Gruß
Gast #5103093
Lesenswert?

Eine Ergänzung hätte ich da noch, bezüglich StateChangeDetection:
Im folgenden Link ist ein Beispiel angehängt:
https://www.arduino.cc/en/Tutorial/StateChangeDetection

Diese Art des Abfragens (was ja lediglich ein digitalRead() innerhalb 
der loop() ist) habe ich gemacht, bevor ich den Taster per Interrupt 
abgefragt habe.
Da hat das Menü aber nur dann umgeblättert, wenn -so vermute ich- 
während der Laufzeit der Taster exakt in dem Moment gedrückt wurde, wo 
digitalRead() ausgeführt wurde.
Oder gibt es intern eine Art Puffer, der den State so lange "speichert", 
bis er gelesen wird (so wie beim Empfangspuffer des UARTs)?


Gruß
#5103282
Lesenswert?

Ralf schrieb:
> Da hat das Menü aber nur dann umgeblättert, wenn -so vermute ich-
> während der Laufzeit der Taster exakt in dem Moment gedrückt wurde, wo
> digitalRead() ausgeführt wurde.

Nur solange Du Delay (Systembremse) im Loop benutzt.. das sollte man 
ohnehin vermeiden, leider konnte man das ja an den Codeschnippseln nicht 
erkennen.

Ohne Delay müsstest Du den Taster als Beispiel weniger lang als einen 
Loop-Durchgang drücken.. das passiert glaube ich eher seltener.

Wenn das doch probleme macht müsste man wirklich auf den Interrupt 
zurückgreifen, damit sollte man dann aber mit den ganzen Beispielen 
zurechtkommen.. Es gibt auch noch richtige Interrupt Entprellung aber 
Dein Bild wird ja auch nicht früher aktualisiert wie der Loop 
angesprungen wird ;)
Gast #5103313
Lesenswert?

Ich habe in der LCD_Menü_0-Ansicht ja bereits das Problem, dass das LCD 
etwas "ruckelt", weil vermutlich in der 1Wire-Library einige 
zeitkritische Dinge passieren, die die Loop ziemlich verlangsamt.
Deshalb vermute ich auch, dass das langsame Verhalten daher rührt.
Ich verwende jedenfalls keinerlei "Brems"-Funktionen wie delay() und Co.

Gruß
#5103518
Lesenswert?

Erich T. schrieb:
> Wie ist das zu verstehen?

Stimmt, hatte nix damit zutun..  ich hatte das eben mal auf dem Handy 
geschaut.. nobody is perfect ;)

Ist ja ein uralter Fred, also nicht vom TO.. habe gerade gesehen das es 
da wohl um die 12bit ADC Berechnug des DS18B20 geht.. bis zu 750ms 
verzug.

Der mögliche Workaround wäre in dem Arduino Thread.

Man kann halt nur ins blaue erraten.. da darf auf jedenfall nix stocken 
bei den paar Bits.

Womöglich könnte eine lange Interruptabarbeitung um die Tasterfunktion 
"Durchzudrängeln" schon den Wire Interrupt stören.. habe da jetzt aber 
ohne mich selbst wieder einzulesen (Selbst schonmal gemacht) keine 
tiefere Ahnung.

Man kann zwar den Taster auch so einbinden.. aber dann wäre der Taster 
auf jedenfall mal erkannt und im Ruckler debounced. Quick&Dirty..

da fehlt dann noch die Entprellung..

Very Dirty:
1
int Menulength=2;
2
void LCD_Seite_1()
3
{
4
buttonPressed=true;
5
}
und im Loop dann
1
loop(){
2

3
if(buttonpressed){
4
LCD_Menu++;
5
if(LCD_Menu>=Menulength)
6
LCD_Menu=0
7
buttonpressed=false;
8
}
9

10
  if(LCD_Menu==1)    // wenn man bereits im LCD_Menu_1 ist...
11
  {
12
    LCD_Menu = 0;      // ...setze LCD_Menu_Variable auf 0..
13
    loop();            // und starte die loop()-Funktion
14
  }
15

16
  else
17
  {
18
      sensor_AMB_Temp.requestTemperatures();
19
     LCD_Menu = 1;
20
      
21
}



Der To müsste mal den ganzen Sketch und wieviele Sensoren dranhängen 
äußern.. dazu kommt noch Parasitär oder mit VCC?
Gast #5103643
Lesenswert?

Hallo,

Ich habe übrigens zwei DS18B20-Temperaturfühler am Arduino 
angeschlossen.

So...nun habe ich es nun mit folgendem Code versucht:
1
int menulength=2;
2
int LCD_Menu=0;
3
bool buttonpressed=false;
4

5
setup()
6
{ ... }
7

8
loop()
9
{
10
   if(buttonpressed)
11
   {
12
      LCD_Menu++;
13
      if(LCD_Menu>=menulength)
14
      {
15
         sensor_AMB_Temp.requestTemperatures();
16
         lcd.clear();
17
         lcd.print("Zeige Innentemp: ");
18
          LCD_Menu=0;
19
      }
20
   }
21

22
   buttonpressed=false;
23

24
   if(LCD_Menu == 0)
25
   {
26
         
27
         lcd.print("Zeige Außentemp: ");       
28
   }
29
}
30

31
/////////////////// ISR /////////////////////////
32
void LCD_Seite_1()
33
{
34
    buttonpressed=true;
35
}

Wenn ich nun im Hauptmenü bin und den Taster betätige, dann reagiert das 
Hauptmenü (wenn es mal reagiert -> siehe: "Abfragezyklus durch 
digitalRead()") verzögert und es erscheint ca. eine Sekunde "Zeige 
Innentemp:" am Display. Danach kehrt der Arduino automatisch wieder ins 
Hauptmenü zurück, ohne im "Innentemp.:"-Menü zu bleiben bis ein weiterer 
Tastendruck erfolgt..

Ich verstehs nicht..Mit der Entprellung habe ich nun im selben Code 
herumprobiert, es hat kein anderes Ergebnis geliefert als oben 
genanntes.

Wisst ihr noch weiter?
Gast #5103651
Lesenswert?

Ralf schrieb:
> Danach kehrt der Arduino automatisch wieder ins
> Hauptmenü zurück, ohne im "Innentemp.:"-Menü zu bleiben bis ein weiterer
> Tastendruck erfolgt..

Warum sollte er dort bleiben? Du setzt doch direkt nach dem
1
lcd.print("Zeige Innentemp: ");
die Variable LCD_menu wieder auf Null.
Beitrag #5103688 wurde von einem Moderator gelöscht.
Gast #5103872
Lesenswert?

Hallo zusammen,

habe nun den Rat befolgt mit dem "FALLING" State im Interrupt.
Siehe da, er wechselt nun auch wie im folgenden Code sichtbar in das 
zweite Menü, die Temperatur wird auch angezeigt.

Allerdings gibt es noch zwei Probleme:
 - Beim zweiten Mal Drücken auf den Taster wechselt das Display NICHT 
zurück auf den LCD_Menu "0" (also auf den Ausgangsbildschirm) zurück, 
was es eigentlich sollte
 - Die angezeigte Temperatur im LCD_Menu "1" wird nicht fortlaufend 
aktualisiert, nur bei Tastendruck...

Hier der Code:
1
if(buttonpressed)
2
   {
3
      LCD_Menu++;
4
      if(LCD_Menu>=menulength)
5
      {
6
         lcd.clear();
7
         lcd.print("RaumTemp.: ");
8
         lcd.print(sensor_AMB_Temp.getTempCByIndex(0),1);           // eine Nachkommastelle
9
         lcd.print((char)223);                              // Gradzeichen
10
         lcd.print("C");
11
        
12
      }
13
   }
14

15
   buttonpressed=false;
16

17

18

19
   if(LCD_Menu == 0)
20
   {
21
        ... Ausgangsbildschirm für Außentemperatur 
22
   }

Any ideas?

Ich werd' noch verrückt..
#5103922
Lesenswert?

Hi

Du erhöhst zwar den Zähler für Dein Menü:
Ralf schrieb:
> LCD_Menu++;
>       if(LCD_Menu>=menulength)

Aber Du gehst nicht wieder auf Null zurück.
Bei jedem weiteren Tastendruck wird der Menüzähler erhöht (je nach Größe 
der Variabel kannst Du einige tausend Mal drücken, bis der Zähler 
überläuft).
Du prüfst auf > Maximum, zeigst aber nur die Raumtemperatur an.

Was erwartest Du?
1. Tastendruck Menü 0 -> 1
2. Tastendruck Menü 1 -> 2
3. Tastendruck Menü 2 -> 3
4. Tastendruck Menü 3 -> 0

Dann schreib Das doch auch so, Pseudo-Code:
1
LCD_Menu++
2
If LCD_Menu==1 then {
3
   Zeige Menü 1 an
4
}elseif LCD_Menu==2 then {
5
   Zeige Menü 2 an
6
}elseif LCD_Menu==3 then {
7
   Zeige Menü 3 an
8
}else{
9
// Menü ist 0 oder 4
10
   Zeige Menü 0 an
11
   LCD_Menu=0      <-- Hier stellst Du den Zähler wieder 'auf Anfang'
12
}

Weiter schickst Du nur Daten ans Display in der 'buttonpressed'-Abfrage.
Wenn der 'Button nicht mehr gepresst' ist, wird der ganze Schwung nicht 
abgearbeitet - somit keine Anzeige.
Genau, was Du programmiert hast.

MfG

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