Hilfe bei LCD-Menu

#include <LiquidCrystal.h>
LiquidCrystal lcd(13,12,11,10,8,7);
#define LCDcol  16
#define LCDrow  2

#include <Rotary.h>
#define RotSW 4
Rotary r=Rotary(2,3);

byte butState=0, cnt=0, curSW=0, oldSW=0, menuPos=0, rrr=0, ggg=0, bbb=0;
String save="N";

void setup()//##############################################################
 {lcd.begin(LCDcol,LCDrow);
  pinMode(2,INPUT_PULLUP);
  pinMode(3,INPUT_PULLUP);
  pinMode(RotSW,INPUT_PULLUP);
  PCICR|=(1<<PCIE2); PCMSK2|=(1<<PCINT18)|(1<<PCINT19); //Enable pin change interrupt for encoder
 }

void loop()//###############################################################
 {butState=getButton(); //Read state of pushbutton
  if(butState==3) {lcd.clear(); menu();} //Long press, run menu

  lcd.setCursor( 0,0); lcd.print("MENU~2sec Press");
  lcd.setCursor( 0,1); lcd.print("R="); lcd.print(rrr);
  lcd.setCursor( 5,1); lcd.print("G="); lcd.print(ggg);
  lcd.setCursor(10,1); lcd.print("B="); lcd.print(bbb);
 }

void menu()//##############################################################
 {while(menuPos<5) //While Menu equals less than max number of menus
   {curSW=digitalRead(RotSW); delay(20);
    if(curSW!=oldSW && curSW==0){++menuPos;} //Next menu item
    oldSW=curSW;

    lcd.clear(); lcd.setCursor(0,0);
    switch(menuPos)
     {case 0:
         if(cnt==2){rrr++; if(rrr>255)rrr=0;} //CW
         if(cnt==1){rrr--; if(rrr<0)rrr=255;} //CCW
         lcd.print("Red: "); lcd.print(rrr);
         break;
      case 1:
         if(cnt==2){ggg++; if(ggg>255)ggg=0;} //CW
         if(cnt==1){ggg--; if(ggg<0)ggg=255;} //CCW
         lcd.print("Green: "); lcd.print(ggg);
         break;
      case 2:
         if(cnt==2){bbb++; if(bbb>255)bbb=0;} //CW
         if(cnt==1){bbb--; if(bbb<0)bbb=255;} //CCW
         lcd.print("Blue: "); lcd.print(bbb);
         break;
      case 3:
         if(cnt==2){save="Y";} //CW
         if(cnt==1){save="N";} //CCW
         lcd.print("Save settings: "); lcd.print(save); //Yes/No
         break;
      case 4:
         lcd.print("Exit~Long press");
         butState=getButton(); //Read state of pushbutton
         if(butState==3){menuPos=99;} //Exit Menu
         if(butState==2){menuPos=0;} //Return to first menuPos (case 0:)
         break;
     }
    cnt=0;
   }
  lcd.clear(); //Clear display befor exit menu
 }

ISR(PCINT2_vect)//Interrupt service routine #############################################
 {byte res=r.process();
  if(res==DIR_CW)      {cnt=2;}
  else if(res==DIR_CCW){cnt=1;}
 }

byte getButton()//Read PushBut with debouncing #############################################
 {butState=0; unsigned long dnTime, upTime;
  if(!digitalRead(RotSW))
   {delay(20);
     if(!digitalRead(RotSW))
      {dnTime=millis();
        while(!digitalRead(RotSW));
       } //Wait for button up
    upTime=(millis()-dnTime);
    if(upTime>1000)     return butState=3; //Long press
    else if(upTime>200) return butState=2; //Medium press
    else                return butState=1; //Short click
   }
  return butState=0;
 }

Das funktioniert schon soweit wie ich das möchte, bis auf paar kleinigkeiten die ich nicht hinbekomme.

  • nach dem letzten menüpunkt (case4), soll durch einfacher Klick wieder zu (case0) gespringen werden…alles im Kreis.
  • wenn Menü aktiv, dann flickert das LCD display.

Würde mich freuen wenn mir jemand helfen könnte.
Kann man das alles noch optimieren? Verbraucht etwas über 4kB, das könnte eng werden wenn noch der ganze rest hinzukommt.
Ich habe erstmal nur das menu gemacht, weil das dann in my project am wichtigsten ist.

Thanks

Das Flackern liegt an deinen lcd.clear.
Das würde ich gänzlich vermeiden. Entweder du schreibst den Code so, das alle Zeichen der einzelnen States nur überschrieben werden, oder du setzt ein Flag, daß jeder State nur bei Wechsel auf nen neuen State das Display löscht. Am saubersten läuft es aber wenn gar nicht gelöscht wird.
Dann kannst du das F Makro verwenden. Dann wird der String im Flash gespeichert und nicht im Ram.
lcd.print(F(“mein String”)).
Das dein Menü nicht auf 1 zurück springt liegt wohl daran das du in der while Schleife 2x deinen Button abfrägst.
Einmal am Anfang und einmal im Case 4.
Wenn du das Ganze etwas durchdachter, ohne while Schleife aufbaust reicht es, wenn du einmal im Loop den Taster einliest.
Wenn du so aufbaust, das du die menuPos immer hoch zählst, kann dich aber auch ein fünfter case, der nur die menuPos wieder zurücksetzt ans Ziel bringen.
Ich finde den Aufbau aber ansich recht unvorteilhaft gelöst, da hier vieles doppelt und dreifach gemacht werden muß, das es funktioniert.

Ich kenne mich damit nicht gut aus, sieht man ja an dem code.

lcd.print(F("mein String")). Das habe ich probiert, aber da steht dann nach dem hochladen, das der code mehr bytes verbraucht. Wo kann man das prüfen ob das in dem Flash ist?

etwas durchdachter, ohne while Schleife Wo kann ich so beispiel einsehen, damit ich was lernen kann.

Mehr Flash kann natürlich sein, weil dafür spezielle Methoden gebraucht werden. Der Text liegt so oder so im Flash. Aber es wird weniger RAM benötigt. Das ist i.d.R. wichtiger.

ja das erste Flash bringt etwas overhead mit, aber wenn du das oft verwendest, passt es.

Du musst eher darauf achten, dass auch deine Globals nun Weniger geworden sind.

Probier noch ein paar Zeilen und vergleiche beide Werte

Ok ich habe da was zusammengeschustert und die ergebnisse notiert.
Kann mir jemand erklären warm die ausgebe mit F besser ist?
Die zeilen mit F verbrauchen trotzdem mehr speicher.

#include <LiquidCrystal.h> //bitbucket.org/fmalpartida/new-liquidcrystal/downloads/
LiquidCrystal lcd(13,12,11,10,8,7); //LCD-Connection (RS,E,D4,D5,D6,D7) Pinlayout for my ProMini

void setup()
 {lcd.begin(20,4); lcd.clear();

  //Sketch uses 1946 bytes (6%) of program storage space. Maximum is 30720 bytes.
  //Global variables use 45 bytes (2%) of dynamic memory, leaving 2003 bytes for local variables.
  //Maximum is 2048 bytes.
  //avrdude: 1946 bytes of flash verified
  lcd.print(F("16 Zeilen voll  text")); lcd.print(F("16 Zeilen voll  text"));
  lcd.print(F("16 Zeilen voll  text")); lcd.print(F("16 Zeilen voll  text"));
  lcd.print(F("16 Zeilen voll  text")); lcd.print(F("16 Zeilen voll  text"));
  lcd.print(F("16 Zeilen voll  text")); lcd.print(F("16 Zeilen voll  text"));


  //Sketch uses 1718 bytes (5%) of program storage space. Maximum is 30720 bytes.
  //Global variables use 67 bytes (3%) of dynamic memory, leaving 1981 bytes for local variables.
  //Maximum is 2048 bytes.
  //avrdude: 1718 bytes of flash verified
  lcd.print("16 Zeilen voll  text"); lcd.print("16 Zeilen voll  text");
  lcd.print("16 Zeilen voll  text"); lcd.print("16 Zeilen voll  text");
  lcd.print("16 Zeilen voll  text"); lcd.print("16 Zeilen voll  text");
  lcd.print("16 Zeilen voll  text"); lcd.print("16 Zeilen voll  text");
 }
void loop() {}

Du musst RAM und Flash unterscheiden. Es wird weniger RAM verbraucht.

Außerdem wirst du das Ergebnis verzerren wenn du den Text so künstlich wiederholst. Das kann man anders und besser lösen. Siehe PROGMEM

Hi

Die Zeilen mit F brauchen NUR Flash. Ohne das F brauchen die Zeilen Flash - irgendwo muß der Kram ja gespeichert sein, wenn dem Arduino Mal der Saft ausgeht - UND RAM - weil der Kram vor der Anzeige erst in den RAM kopiert wird um von Dort 'ganz normal' ausgegeben zu werden. Das F-Makro verhindert nur, daß der Kram den Umweg über den RAM macht - weil Das bei Zeichenketten schon sehr viel Platz auf dem Stack braucht (... pro Zeichen ein Byte + /0) und Dieser, im Normalfall, eher anderweitig benötigt wird.

Wenn Du's etwas intelligenter anstellst, kannst Du identischen Text auch nur 1x speichern - und brauchst nicht für 16x identische Ausgaben den 16-fachen Platz.

MfG