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
intTasterPin=3;// Taster an DigPin 3 angeschlossen
2
intLCD_Menu=0;
3
4
setup()
5
{
6
pinMode(TasterPin,INPUT);// Taster-Pin als Eingang deklarieren
//----------------------------- Interrupt Service Routine ---------------------------------------
22
23
voidLCD_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!
Hi
So, wie der Code da steht, macht Der .... nicht viel.
Das LCD-Menü 1 wird gar nicht ausgegeben - vll. liegt's bereits daran?
Oha - den Teil hast Du - der Übersichtlichkeit wegen - entfernt?
A...ha...
Was soll Er machen, was macht Er?
MfG
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
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.
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)
> //----------------------------- 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.
Ralf schrieb:> loop(); // und starte die loop()-Funktion
Was soll der Aufruf?
Loop() ist eine Funktion, die alleine vom "Betriebssystem" des Arduino
aufgerufen wird.
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.
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.
Hi,
Er hat bisher den Begriff "Prellen" und "Entprellen" nicht verstanden
und denkt mit seinem Vorgehen ergibt sich eine schöne Programmstuktur
und ein funktionierendes Programm.
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.
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
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.
Ralf schrieb:> pinMode(TasterPin, INPUT);
Ich hatte jetzt auf so etwas gehofft:
pinMode(TasterPin, INPUT_DEBOUNCED);
oder
pinMode(TasterPin, INPUT_PULLUP_DEBOUNCED);
Kommt vielleicht noch ...
LG
old.
> 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...)
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ß
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ß
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 ;)
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ß
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
intMenulength=2;
2
voidLCD_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?
Philipp K. schrieb:> if(LCD_Menu==1) // wenn man bereits im LCD_Menu_1 ist...> {> LCD_Menu = 0; // ...setze LCD_Menu_Variable auf 0..> loop(); // und starte die loop()-Funktion> }
Völliger Unfug!
loop() darf sich nicht selbst aufrufen!
Harry L. schrieb:> Völliger Unfug!> loop() darf sich nicht selbst aufrufen!
Ja sorry, das ist ein Beispiel und das wurde dem TO von dem ich den Code
im Eröffnungspost wegkopiert habe schon dreimal erklärt ^^ das wird der
schon raffen.
Hallo,
Ich habe übrigens zwei DS18B20-Temperaturfühler am Arduino
angeschlossen.
So...nun habe ich es nun mit folgendem Code versucht:
1
intmenulength=2;
2
intLCD_Menu=0;
3
boolbuttonpressed=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
voidLCD_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?
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
benutzt Du noch Change, dann nehm FALLING?
bei CHANGE wird true 2 mal gesetzt.
vielleicht noch mit Pullup wenn es keinen Pullwiderstand extern gibt
pinMode(TasterPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(TasterPin), LCD_Seite_1, FALLING);
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
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