Guten Tag, ich bin neu im Forum und möchte euch um Hilfe bitten. Folgendes: Ich habe für mein LCD ein kleines Programm geschrieben. Leider muss ich immer einige Male die Tasten drücken, damit ich den richtigen Moment im Programm abpasse und es ist etwas träge. Mit der ISR denke ich, dass ich schon mal auf dem richtigen Weg bin. Im Internet habe ich mir schon einiges zur ISR angeschaut, bekomme es aber nicht bei meinem Programm hin. Des weiteren würde ich gerne wissen, welche Teile meines Programms in ISR-Funktionen gesteckt werden sollten. Ich verwende einen Arduino Mega. Soll ich noch einen Schematikplan erstellen? Danke! LG Jannis #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27, 20, 4); #include <SPI.h> #include <Adafruit_Sensor.h> #include "Adafruit_BME680.h" #define BME_SCK 13 #define BME_MISO 12 #define BME_MOSI 11 #define BME_CS 10 #define SEALEVELPRESSURE_HPA (1013.25) Adafruit_BME680 bme; int upButton=18; int downButton=19; int selectButton=2; int menu=1; void setup() { // put your setup code here, to run once: lcd.createChar(0, customChar_1); lcd.home(); lcd.write(0); Serial.begin(9600); while (!Serial); Serial.println(F("BME680 test")); if (!bme.begin()) { Serial.println("Could not find a valid BME680 sensor, check wiring!"); while (1); } lcd.init(); //Im Setup wird der LCD gestartet lcd.backlight(); //Hintergrundbeleuchtung einschalten (0 schaltet die Beleuchtung aus). lcd.clear(); // Set up oversampling and filter initialization bme.setTemperatureOversampling(BME680_OS_8X); bme.setHumidityOversampling(BME680_OS_2X); bme.setPressureOversampling(BME680_OS_4X); bme.setIIRFilterSize(BME680_FILTER_SIZE_3); bme.setGasHeater(320, 150); // 320*C for 150 ms pinMode(upButton,INPUT); digitalWrite(upButton,HIGH); // pullup-widerstand hinzuschalten pinMode(downButton,INPUT); digitalWrite(downButton,HIGH); pinMode(selectButton,INPUT); digitalWrite(selectButton,HIGH); updateMenu(); } void loop() { if (! bme.performReading()) { Serial.println("Failed to perform reading :("); return; } if(!digitalRead(downButton)){ menu++; updateMenu(); delay(100); } if(!digitalRead(upButton)){ menu--; updateMenu(); delay(100); } if(!digitalRead(selectButton)){ executeAction(); updateMenu(); delay(100); //Warum delay? } } void updateMenu() { switch(menu){ case 0: menu=1 ; break; case 1: lcd.clear(); lcd.print(">Temperatur"); lcd.setCursor(0,1); lcd.print(" Luftdruck"); lcd.setCursor(0,2); lcd.print(" Luftfeuchtigkeit"); lcd.setCursor(0,3); lcd.print(" Luftqualitaet"); break; case 2: lcd.clear(); lcd.print(" Temperatur"); lcd.setCursor(0,1); lcd.print(">Luftdruck"); lcd.setCursor(0,2); lcd.print(" Luftfeuchtigkeit"); lcd.setCursor(0,3); lcd.print(" Luftqualitaet"); break; case 3: lcd.clear(); lcd.print(" Temperatur"); lcd.setCursor(0,1); lcd.print(" Luftdruck"); lcd.setCursor(0,2); lcd.print(">Luftfeuchtigkeit"); lcd.setCursor(0,3); lcd.print(" Luftqualitaet"); break; case 4: lcd.clear(); lcd.print(" Temperatur"); lcd.setCursor(0,1); lcd.print(" Luftdruck"); lcd.setCursor(0,2); lcd.print(" Luftfeuchtigkeit"); lcd.setCursor(0,3); lcd.print(">Luftqualitaet"); break; case 5: lcd.clear(); lcd.print(">Hoehe"); lcd.setCursor(0,1); lcd.print(" Helligkeit"); lcd.setCursor(0,2); lcd.print(" Uhrzeit"); lcd.setCursor(0,3); lcd.print(" Datum"); break; case 6: lcd.clear(); lcd.print(" Hoehe"); lcd.setCursor(0,1); lcd.print(">Helligkeit"); lcd.setCursor(0,2); lcd.print(" Uhrzeit"); lcd.setCursor(0,3); lcd.print(" Datum"); break; case 7: lcd.clear(); lcd.print(" Hoehe"); lcd.setCursor(0,1); lcd.print(" Helligkeit"); lcd.setCursor(0,2); lcd.print(">Uhrzeit"); lcd.setCursor(0,3); lcd.print(" Datum"); break; case 8: lcd.clear(); lcd.print(" Hoehe"); lcd.setCursor(0,1); lcd.print(" Helligkeit"); lcd.setCursor(0,2); lcd.print(" Uhrzeit"); lcd.setCursor(0,3); lcd.print(">Datum"); break; case 9: lcd.clear(); lcd.print(">Ultraschall"); lcd.setCursor(0,1); lcd.print(" Sonderzeichen"); lcd.setCursor(0,2); lcd.print(" Zaehler->unendlich"); lcd.setCursor(0,3); lcd.print(" Displayspiel"); break; case 10: lcd.clear(); lcd.print(" Ultraschall"); lcd.setCursor(0,1); lcd.print(">Sonderzeichen"); lcd.setCursor(0,2); lcd.print(" Zaehler->unendlich"); lcd.setCursor(0,3); lcd.print(" Displayspiel"); break; case 11: lcd.clear(); lcd.print(" Ultraschall"); lcd.setCursor(0,1); lcd.print(" Sonderzeichen"); lcd.setCursor(0,2); lcd.print(">Zaehler->unendlich"); lcd.setCursor(0,3); lcd.print(" Displayspiel"); break; case 12: lcd.clear(); lcd.print(" Ultraschall"); lcd.setCursor(0,1); lcd.print(" Sonderzeichen"); lcd.setCursor(0,2); lcd.print(" Zaehler->unendlich"); lcd.setCursor(0,3); lcd.print(">Displayspiel"); break; case 13: menu=13; break; } } void executeAction() { switch (menu) { case 1: action1(); break; case 2: action2(); break; case 3: action3(); break; case 4: action4(); break; case 5: action5(); break; case 6: action6(); break; case 7: action7(); break; } } void action1() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Temperatur"); lcd.setCursor(11,0); lcd.print(bme.temperature); lcd.setCursor(17,0); lcd.print("C"); delay(6000); } void action2() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Luftdruck"); lcd.setCursor(10,0); lcd.print(bme.pressure / 100.0); lcd.setCursor(17,0); lcd.print("hPa"); delay(6000); } void action3() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Luftfeucht."); lcd.setCursor(12,0); lcd.print(bme.humidity); lcd.setCursor(18,0); lcd.print("%"); delay(6000); } void action4() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Luftqu."); lcd.setCursor(8,0); lcd.print(bme.gas_resistance / 1000.0); lcd.setCursor(15,0); lcd.print("KOhms"); delay(6000); } void action5() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Hoehe..."); delay(6000); } void action6() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Helligkeit..."); delay(6000); } void action7() { lcd.clear(); lcd.createChar(0, customChar_1); delay(6000); }
:
Verschoben durch Moderator
Hallo, kein Mensch hat verstehen können was Du willst, den Code für Arduino wird sich niemand antun wenn Du nicht sagst was Du willst.
Jannis K. schrieb: > Leider muss ich > immer einige Male die Tasten drücken, damit ich den richtigen Moment im > Programm abpasse und es ist etwas träge Na ja, wer Taster als digitalen Eingang abfragt, und nicht entprellt, muss halt damit rechnen, daß er prellende Signale einliest. Das wird dasnn Glückssache. Dein Hauptptrogramm ist die loop() { } Die soll nicht zu schnell laufen, nur alle 10ms ein mal rum loop() { delay_ms(10); } Darin liest man den Taster ein, vergleicht mit dem vorherigen Zustand, und macht die Aktion nur wenn gedrückt. loop() { downButtonpressed=!digitalRead(downButton); if(downButtonPressed&&!downButtonPressedOld) { menu++; updateMenu(); } downButtonPressedOld=downButtonPressed; : delay_ms(10); } und darin nun auch die anderen Taster loop() { downButtonPressed=!digitalRead(downButton); if(downButtonPressed&&!downButtonPressedOld) { menu++; updateMenu(); } downButtonPressedOld=downButtonPressed; upButtonpressed=!digitalRead(upButton); if(upButtonPressed&&!upButtonPressedOld) { menu--; updateMenu(); } upButtonPressedOld=upButtonPressed; selectButtonpressed=!digitalRead(selectButton); if(selectButtonPressed&&!selectButtonPressedOld) { executeAction(); updateMenu(); } selectButtonPressedOld=selectButtonPressed; delay_ms(10); } Deine serial.prints können das Programm langsam machen.
Hi Jannis, Michael hat es im Prinzip schon gesagt. Eine ISR ist für Taster nur dann benutzbar, wenn Du entweder den Taster mit einem Kondensator entprellst (was aber zu spürbaren Verzögerungen führen kann). Oder in dem Du in der ISR einen Timer startest (per millis()), sobald ein Ereignis eintritt (je nach Schaltung z.B. bei steigender Flanke) und Du dann aber(!) alle nachfolgenden ISR-Ereignisse bis zu einem Timeout von z.B. 100ms sperrst. Dieser Weg ist aber viel aufwendiger als der Weg, den Dir Michael oben beschrieben hat. Nur gibt es tatsächlich Anwendungen wie z.B. ein Drehencoder, wo Du mit einer ISR arbeiten solltest, da zum Zeitpunkt der steigenden Flanke der Zustand eines anderen Pins für die Richtung notwendig ist. Ohne ISR besteht eine hohe Chance, die korrekte Richtung zu verpassen. Aber wie gesagt, das ist ein Ausnahmefall. Simple Taster pollt man einfach. Auch hier gilt übrigens, dass man nach dem Ereignis (Taste gedrückt) unbedingt solange "Zeit verbrennen" muss, bis mindestens 100ms vergangen sind. Wenn Du aber mit LCD irgendwas machst, ist das quasi automatisch so. Sonst kann es Dir eben passieren, dass Du bei nicht entprellten Tastern denkst, dass sie mehrmals gedrückt wurden, obwohl das gar nicht der Fall war...
:
Bearbeitet durch User
Ach, und was mir gerade auffällt: Um den Pullup zu aktivieren nimmt man normalerweise pinMode(xxx, INPUT_PULLUP). Guck mal, ob das bei Dir nicht auch funktioniert als Alternative dazu, den Ausgang auf HIGH zu schalten. Intern macht Arduino das selbe, aber Du hättest dann portableren Code, der auch mit einem ESP32 z.B. funktioniert. Und als Empfehlung noch: guck Dir mal an, was man mit objektorientierter Programmierung (C++) erreichen kann. Gibt dazu viele Lernvideos bei Youtube und co.. Das macht den Code extrem viel übersichtlicher, eleganter und Du vermeidest Redundanz. Also wenn Du mehrere Taster verwendest, könntest Du Dir eine Klasse "Button" oder so schreiben mit der Pinnummer als Argument. Dort programmierst Du dann das Verhalten eines Tasters (z.B. in dem Du eine Methode is_pressed() oder so einführst). Das programmierst Du einmal (z.B. in einer Button.h und Button.cpp) und dann kannst Du das x-mal in Deinem Projekt aufrufen und später dann in anderen Projekten wiederverwenden. So mache ich das und habe mir damit mittlerweile schon eine riesige Bibliothek erstellt, die ich in allen Projekten wiederverwende. Auch mit unterschiedlichen Prozessoren...
Andreas J. schrieb: > Das macht den Code extrem viel übersichtlicher, eleganter und Du > vermeidest Redundanz Das ist jetzt aber schon grobes Fanboy-Geschwätz. Anders gesagt: meistens trifft in der Praxis das behauptete nicht zu, auch wenn es seltene Ausnahmebeispiele in Lehrbüchern gibt.
MaWin schrieb: > Das ist jetzt aber schon grobes Fanboy-Geschwätz. Es war ja auch nur eine Empfehlung. "Übersichtlich" und "Elegant" ist natürlich subjektiv. Aber dass man Redundanz vermeidet ist definitiv wahr. Also hier mal konkret ein Beispiel (auf C Level) aus Jannis' Code: Wenn Du zwei Methoden einführst: void showHeader(bool selected, int line, const char* text) { lcd.setCursor(0, line); lcd.print(selected ? '<' : ' '); lcd.print(text); } void showMenu(int x) { lcd.clear(); showHeader(x == 0, 0, "Temperatur"); showHeader(x == 1, 1, "Luftdruck"); showHeader(x == 2, 2, "Luftfeuchtigkeit"); showHeader(x == 3, 3, "Luftqualitaet"); } Kannst Du später folgendes machen: showMenu(0) => Temperatur ist ausgewählt... Dann hast Du den Text an einer Stelle, die Logik an einer Stelle und musst später nur eine Stelle warten bzw. verändern. Du hast in meinen Augen - also ganz subjektiv - übersichtlichen, eleganten und leicht wartbaren Code. Und Dein Code würde sich um gefühlt die Hälfte reduzieren (und auch weniger Flash verbrauchen). Das hier ist auch schon einfachstes OO. Mit C++ kann man noch einen Schritt weiter gehen (mit C auch), dann würde man ein Header-Objekt einführen und ein Menu-Objekt. Aber jeder eben wie kann und mag. ;-)
:
Bearbeitet durch User
Danke für eure Rückmeldungen und Hinweise. Michael B. schrieb: > Na ja, wer Taster als digitalen Eingang abfragt, und nicht entprellt, > muss halt damit rechnen, daß er prellende Signale einliest. Das wird > dasnn Glückssache. Der Entprellcode ist nun eingefügt. __________ Andreas J. schrieb: > Um den Pullup zu aktivieren nimmt man normalerweise pinMode(xxx, > INPUT_PULLUP). Den Befehl zur Aktivierung des Pullup Widerstand habe ich abgeändert. ____________ Andreas J. schrieb: > Dann hast Du den Text an einer Stelle, die Logik an einer Stelle und > musst später nur eine Stelle warten bzw. verändern. Du hast in meinen > Augen - also ganz subjektiv - übersichtlichen, eleganten und leicht > wartbaren Code. Und Dein Code würde sich um gefühlt die Hälfte > reduzieren (und auch weniger Flash verbrauchen). Deinen Vorschlag werde ich mir demnächst vornehmen. ___________ Probehalber habe ich den Seriellen-Teil für die Sensor-Kommunikation entfernt und der uC reagierte sofort auf meine Tasten-Befehle. Gäbe es eine weitere Möglichkeit das Programm so zu ändern, dass der uC sofort im Menü umspringt, wenn ich die Taster betätige? Mir ist aufgefallen, dass das Springen in einen Unterpunkt sehr schnell geht, nur das Wechseln der Einträge im Hauptmenü ist manchmal ein wenig verzögert. #include <Wire.h> #include <LiquidCrystal_I2C.h> LiquidCrystal_I2C lcd(0x27, 20, 4); #include <SPI.h> #include <Adafruit_Sensor.h> #include "Adafruit_BME680.h" #define BME_SCK 13 #define BME_MISO 12 #define BME_MOSI 11 #define BME_CS 10 #define SEALEVELPRESSURE_HPA (1013.25) Adafruit_BME680 bme; int upButton=18; int downButton=19; int selectButton=2; int menu=1; int downButtonPressed=19; int downButtonPressedOld=19; int upButtonPressed=18; int upButtonPressedOld=18; int selectButtonPressed=2; int selectButtonPressedOld=2; void setup() { // put your setup code here, to run once: lcd.createChar(0, customChar_1); lcd.home(); lcd.write(0); Serial.begin(9600); while (!Serial); Serial.println(F("BME680 test")); if (!bme.begin()) { Serial.println("Could not find a valid BME680 sensor, check wiring!"); while (1); } lcd.init(); //Im Setup wird der LCD gestartet lcd.backlight(); //Hintergrundbeleuchtung einschalten (0 schaltet die Beleuchtung aus). lcd.clear(); // Set up oversampling and filter initialization bme.setTemperatureOversampling(BME680_OS_8X); bme.setHumidityOversampling(BME680_OS_2X); bme.setPressureOversampling(BME680_OS_4X); bme.setIIRFilterSize(BME680_FILTER_SIZE_3); bme.setGasHeater(320, 150); // 320*C for 150 ms pinMode(18,INPUT_PULLUP); pinMode(19,INPUT_PULLUP); pinMode(2,INPUT_PULLUP); updateMenu(); } void loop() { if (! bme.performReading()) { Serial.println("Failed to perform reading :("); return; } downButtonPressed=!digitalRead(downButton); if(downButtonPressed&&!downButtonPressedOld) { menu++; updateMenu(); } downButtonPressedOld=downButtonPressed; upButtonPressed=!digitalRead(upButton); if(upButtonPressed&&!upButtonPressedOld) { menu--; updateMenu(); } upButtonPressedOld=upButtonPressed; selectButtonPressed=!digitalRead(selectButton); if(selectButtonPressed&&!selectButtonPressedOld) { executeAction(); updateMenu(); } selectButtonPressedOld=selectButtonPressed; delay(10); } void updateMenu() { switch(menu){ case 0: menu=1 ; break; case 1: lcd.clear(); lcd.print(">Temperatur"); lcd.setCursor(0,1); lcd.print(" Luftdruck"); lcd.setCursor(0,2); lcd.print(" Luftfeuchtigkeit"); lcd.setCursor(0,3); lcd.print(" Luftqualitaet"); break; case 2: lcd.clear(); lcd.print(" Temperatur"); lcd.setCursor(0,1); lcd.print(">Luftdruck"); lcd.setCursor(0,2); lcd.print(" Luftfeuchtigkeit"); lcd.setCursor(0,3); lcd.print(" Luftqualitaet"); break; case 3: lcd.clear(); lcd.print(" Temperatur"); lcd.setCursor(0,1); lcd.print(" Luftdruck"); lcd.setCursor(0,2); lcd.print(">Luftfeuchtigkeit"); lcd.setCursor(0,3); lcd.print(" Luftqualitaet"); break; case 4: lcd.clear(); lcd.print(" Temperatur"); lcd.setCursor(0,1); lcd.print(" Luftdruck"); lcd.setCursor(0,2); lcd.print(" Luftfeuchtigkeit"); lcd.setCursor(0,3); lcd.print(">Luftqualitaet"); break; case 5: lcd.clear(); lcd.print(">Hoehe"); lcd.setCursor(0,1); lcd.print(" Helligkeit"); lcd.setCursor(0,2); lcd.print(" Uhrzeit"); lcd.setCursor(0,3); lcd.print(" Datum"); break; case 6: lcd.clear(); lcd.print(" Hoehe"); lcd.setCursor(0,1); lcd.print(">Helligkeit"); lcd.setCursor(0,2); lcd.print(" Uhrzeit"); lcd.setCursor(0,3); lcd.print(" Datum"); break; case 7: lcd.clear(); lcd.print(" Hoehe"); lcd.setCursor(0,1); lcd.print(" Helligkeit"); lcd.setCursor(0,2); lcd.print(">Uhrzeit"); lcd.setCursor(0,3); lcd.print(" Datum"); break; case 8: lcd.clear(); lcd.print(" Hoehe"); lcd.setCursor(0,1); lcd.print(" Helligkeit"); lcd.setCursor(0,2); lcd.print(" Uhrzeit"); lcd.setCursor(0,3); lcd.print(">Datum"); break; case 9: lcd.clear(); lcd.print(">Ultraschall"); lcd.setCursor(0,1); lcd.print(" Sonderzeichen"); lcd.setCursor(0,2); lcd.print(" Zaehler->unendlich"); lcd.setCursor(0,3); lcd.print(" Displayspiel"); break; case 10: lcd.clear(); lcd.print(" Ultraschall"); lcd.setCursor(0,1); lcd.print(">Sonderzeichen"); lcd.setCursor(0,2); lcd.print(" Zaehler->unendlich"); lcd.setCursor(0,3); lcd.print(" Displayspiel"); break; case 11: lcd.clear(); lcd.print(" Ultraschall"); lcd.setCursor(0,1); lcd.print(" Sonderzeichen"); lcd.setCursor(0,2); lcd.print(">Zaehler->unendlich"); lcd.setCursor(0,3); lcd.print(" Displayspiel"); break; case 12: lcd.clear(); lcd.print(" Ultraschall"); lcd.setCursor(0,1); lcd.print(" Sonderzeichen"); lcd.setCursor(0,2); lcd.print(" Zaehler->unendlich"); lcd.setCursor(0,3); lcd.print(">Displayspiel"); break; case 13: menu=13; break; } } void executeAction() { switch (menu) { case 1: action1(); break; case 2: action2(); break; case 3: action3(); break; case 4: action4(); break; case 5: action5(); break; case 6: action6(); break; case 7: action7(); break; } } void action1() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Temperatur"); lcd.setCursor(11,0); lcd.print(bme.temperature); lcd.setCursor(17,0); lcd.print("C"); delay(6000); } void action2() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Luftdruck"); lcd.setCursor(10,0); lcd.print(bme.pressure / 100.0); lcd.setCursor(17,0); lcd.print("hPa"); delay(6000); } void action3() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Luftfeucht."); lcd.setCursor(12,0); lcd.print(bme.humidity); lcd.setCursor(18,0); lcd.print("%"); delay(6000); } void action4() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Luftqu."); lcd.setCursor(8,0); lcd.print(bme.gas_resistance / 1000.0); lcd.setCursor(15,0); lcd.print("KOhms"); delay(6000); } void action5() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Hoehe..."); delay(6000); } void action6() { lcd.clear(); lcd.setCursor(0,0); lcd.print("Helligkeit..."); delay(6000); } void action7() { lcd.clear(); lcd.createChar(0, customChar_1); delay(6000); }
LCD-Befehle sind generell sehr langsam. Was Du optimieren kannst, ist z.B. nicht immer das ganze Menü neu zu zeichnen (was ja auch zum Blinken führt), sondern nur das '>' Zeichen. Du malst also erst das Menü ohne '>'. Und wenn Du ein Menüeintrag änderst, dann überschreibst Du das alte selektierte Menü an dieser Stelle mit einem Leerzeichen und das neu selektierte mit eben dem '>'. Also sowas hier: int old_selected_line = -1; void selectMenu(int line) { if (old_selected_line != -1) { lcd.setCursor(0,old_selected_line); lcd.print(' '); } lcd.setCursor(0,line); lcd.print('>'); old_selected_line = line; }
:
Bearbeitet durch User
Außerdem sehe ich gerade, dass Du da bei den actions() ein delay(6000) drin hast. Generell sollte man delays() vermeiden, es sei denn sie dienen der Kommunikation mit Geräten. Ich würde wohl per Tastendruck (selectButton) zurückgehen. Aber auch hier: alles Geschmackssache...
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.