Display Menü LCD 4x20

Gast #261242
Lesenswert?

Hallo an alle!

Hat irgendjemand von euch schon mal ein Menu für ein LCD realisiert?
Über drei Tasten steuerbar über 2 tasten (gleichzeitig) ins Menu.
1 Taste hoch. 1 Taste runter. 1 bestätigen.
Mir geht eigentlich nicht darum wie man es tun sollte sondern wie es am
sinnvollsten wäre es zu implementieren?

Danke

Gruß

Andreas
Gast #261243
Lesenswert?

Das macht man am besten objektorientiert (also in C++) Eine Klasse für
einen Menueintrag und eine Klasse für ein Menu. Das ganze kann man dan
beliebig kombinieren und erweitern. In C shreibste dich damit tot.

Z.B. so (muss natürlich noch für dein µC angepasst werden):
1
// menu.cpp
2
#include "menu.hpp"
3

4
entry::entry(string t, int i)
5
{
6
  text = t;
7
  id = i;
8
  next = NULL;
9
}
10

11
entry::entry(string t, menu* n)
12
{
13
  text = t;
14
  id = 0;
15
  next = n;
16
}
17

18
menu::menu(string n)
19
{
20
  count = 0;
21
  name = n;
22
}
23

24
void menu::add_entry(entry *a)
25
{
26
  entrys.push_back(a);
27
  count++;
28
}
29

30
bool isnum(string a)
31
{
32
  for (int c = 0; c < a.length(); c++)
33
    {
34
      if (!('0' <= a[c] && a[c] <= '9')) return false;
35
    }
36
  return true;
37
}
38

39
int menu::run(menu *last)
40
{
41
  string choice;
42
  int start = 0;
43
  int end = 2; // LCD-Display-Height minus 2
44
  int t;
45
  do
46
    {
47
      do
48
        {
49
          cout << name << endl;
50
          for(int i = start; i < end; i++)
51
            {
52
              cout << i+1 << ": " << entrys[i]->text << endl;
53
            }
54
          cout << "u/d/b/n>"; // _u_p, _d_own, _b_ack, _n_umber
55
          cin >> choice;
56
          if (choice == "u" && start > 0) { start--; end--; }
57
          else if (choice == "d" && end < count) { start++; end++; }
58
          else if (choice == "b") if (last != NULL) return
59
last->run(this);
60
        }
61
      while(!isnum(choice));
62
      t = atoi(choice.c_str());
63
    }
64
  while(t > count || t == 0);
65
  if(entrys[t-1]->next != NULL) t = entrys[t-1]->next->run(this);
66
  else t = entrys[t-1]->id;
67
  return t;
68
}
1
// menu.hpp
2
#ifndef MENU_H
3
#define MENU_H
4

5
#include<iostream>
6
#include<vector>
7
#include<string>
8
#include<cstdlib>
9
using namespace std;
10

11
class entry;
12
class menu;
13

14
class menu
15
{
16
public:
17
  menu();
18
  menu(menu &);
19
  menu(string n);
20
  void add_entry(entry*);
21
  int run(menu *last = NULL);
22
private:
23
  string name;
24
  vector<entry*> entrys;
25
  int count;
26
};
27

28
class entry
29
{
30
public:
31
  entry();
32
  entry(entry &);
33
  entry(string t, int i);
34
  entry(string t, menu *n);
35
  friend int menu::run(menu *last = NULL);
36
private:
37
  menu *next;
38
  string text;
39
  int id;
40
};
41

42
#endif
1
// sample main.cpp
2
#include<iostream>
3
#include<vector>
4
#include<string>
5
using namespace std;
6
#include"menu.hpp"
7

8
main()
9
{
10
  menu *menu0 = new menu("Where do you go?");
11

12
  menu *menu1 = new menu("North/South?");
13
  entry *entry10 = new entry("I go to Korea", menu1);
14
  entry *entry11 = new entry("North", 11);
15
  entry *entry12 = new entry("South", 12);
16

17
  entry *entry20 = new entry("I go to England", 20);
18

19
  menu *menu2 = new menu("East/West?");
20
  entry *entry30 = new entry("I go to Germany", menu2);
21
  entry *entry31 = new entry("East.", 31);
22
  entry *entry32 = new entry("West.", 32);
23

24
  entry *entry40 = new entry("I dunno.", 40);
25

26
  menu0->add_entry(entry10);
27
  menu0->add_entry(entry20);
28
  menu0->add_entry(entry30);
29
  menu0->add_entry(entry40);
30
  menu1->add_entry(entry11);
31
  menu1->add_entry(entry12);
32
  menu2->add_entry(entry31);
33
  menu2->add_entry(entry32);
34

35
  switch(menu0->run())
36
    {
37
    case 11: cout << "North? Nice." << endl; break;
38
    case 12: cout << "South? Nice." << endl; break;
39
    case 20: cout << "England? Nice." << endl; break;
40
    case 31: cout << "East? Nice." << endl; break;
41
    case 32: cout << "West? Nice." << endl; break;
42
    case 40: cout << "Huh?" << endl; break;
43
    }
44

45
  return 0;
46
}

cout und cin musste durch lcd-output und schalter-abfrage ersetztn.
Ich weiß nicht wie weit die C++ lib ist. vielliecht musste noch ne
kleine vector- und string-klasse schreiben; das geht aber schnell.
Gast #261244
Lesenswert?

Hi,

habe sowas mal in der Firma realisiert. Natürlich in C.
das ganze läuft eventorientiert. Jedes tastedrücken erzeugt ein event.
natürlich muss man sich immer merken in welchem menü man sich grade
befindet. über geschachtelte case anweisung kann dann der tastendruck
und die aktuelle ausgabe realisiert werden.

unten nur eine grobe skizze:

CASE TASTE1
              case menu1:
                    funktion A
                    set aktives menu2
              case menu1:
                    funktion B
                    set aktives menu3
              case menu1:
                    funktion C
                    set aktives menu4
CASE TASTE2
              case menu3:
                    funktion D
                    set aktives menu3
              case menu4:
                    funktion E
                    set aktives menu4
CASE TASTE3
              case menu2:
                    funktion F
                    set aktives menu3
Gast #261245
Lesenswert?

Der aufgezeigte C++-Ansatz ist fuer kleine Mikroconroller nicht gut
geeignet. Switch/Case-Ketten koennen bei Erweiterungen unuebersichtlich
werden. Ein Ansatz wie er z. B. im Projekt "tinymenu" (avrfreak.net
academy user-projects) ersichtlich wird, erscheint mir zumindest
brauchbarer.
#261246
Lesenswert?

@c++'lerin

"In C shreibste dich damit tot."


Aber ganz im Gegenteil !

Du legst einfach ne Struktur an, die aus dem Text und dem
dazugehörenden Funktionspointer besteht.

Dann einfach damit eine Tabelle schreiben, die alles enthält.

Und dann einfach den Index mit den Tasten hoch und runter zählen, den
Text anzeigen und die Funktion aufrufen.

Als maximalen Index nimmt man per sizeof(table)/sizeof(structur) die
Anzahl Elemente in der Tabelle oder legt einen letzten Eintrag fest
(Null-Text, Null-pointer).
D.h. man kann ganz easy weitere Punkte hinzufügen oder welche löschen.

Und für Untermenüs muß man nur weitere Tabellen anlegen, die
Auswahlfunktion kann die gleiche bleiben (rekursiver Aufruf).

Sieht übersichtlich aus und ist einfach zu erweitern.


Das wird vor allem nicht so ein ellenlanges Case-Monster wie dieses C++
Beispiel.

Und solche manuellen Zählungen (entry10..entry40) werden schnell
unübersichtlich und fehleranfällig in größeren Programmen (wie war
nochmal der letzte bisher vergebene entry ?).


Ich arbeite gerne mit Tabellen, das ist kompakt und Code sparend.


Peter
Gast #261249
Lesenswert?

Vielen Dank für eure regen Antworten.
Peter seine Variante klingt sehr vielversprechend und sinnvoll.
Danke Peter.
Werde das mal umsetzen, natürlich nicht mehr heute.
Es ist ja nu auch Schlafenszeit.

Andreas
Gast #261255
Lesenswert?

Hallo,

... der Thread ist zwar schon ein paar Tage alt, aber ich hoffe, er
wird noch gelesen.

@...HanneS... : Da ich auch noch ein paar 8x24-er LCDs rumliegen habe,
kamen mir die Routinen (s.o.) sehr gelegen.
Jedoch scheint in dem Locate x,y-Macro ein kleiner Fehler zu sein:
Im RAM-Ringpuffer werden doch vor dem eigentlichen auszugebenen
Zeichern die DD-Ram-Addr. des LCD hinterlegt. Bei mir ergibt sich
jedoch folgende Zuordnung:

Locate 0,0 -> Addr.0x00 lt. Datenblatt Zeile 0
Locate 1,0 -> Addr.0x30 lt. Datenblatt Zeile 2
Locate 2,0 -> Addr.0x60 lt. Datenblatt Zeile 4
Locate 3,0 -> Addr.0x90 lt. Datenblatt Zeile 6
Locate 4,0 -> Addr.0x18 lt. Datenblatt Zeile 1
Locate 5,0 -> Addr.0x48 lt. Datenblatt Zeile 3
Locate 6,0 -> Addr.0x78 lt. Datenblatt Zeile 5
Locate 7,0 -> Addr.0xA8 lt. Datenblatt Zeile 7

Ich habe es bisher nur trocken durchpropiert, jedoch nicht getestet.
Hab ich irgendwo nen Denkfehler??

Gruß Rudi
#261256
Lesenswert?

Es gibt vermutlich verschiedene Versionen dieses Displays. Das
Datenblatt, welches ich von Pollin habe, stimmt nicht mit der Realität
meiner Displays überein. Siehe Anhang (Datenblattauszug): mein Display
(und daher auch mein Locate) hat die Aufteilung, die links mit
Bleistift nachgetragen wurde.

Falls deine Displays anders organisiert sind, dann musst du Locate an
deine Displays anpassen. Wenn du anhand meiner Aufteilung den Algo
ermittelt hast, dürfte das kein Problem sein.

Viel Spaß...

Übrigens lässt sich das noch vereinfachen. Wenn man das LCD nur bei der
ersten Initialisierung löscht, während des Normalbetriebs auf das
Löschen verzichtet und stattdessen Leerzeichen schreibt, dann genügt
immer die kurze "Wartezeit", da nur LCD-Clear die lange Wartezeit
benötigt. Man kann sich also das Umschalten des Timers sparen oder das
Ausgabeflag gleich nebenbei von einem anderen, sowiso laufenden Timer
zyklisch setzen.
Das Löschen per Leerzeichen wäre dann:
 locate 0,0     ;erste Zeile, erste Spalte
 print 32,192   ;192 Leerzeichen schreiben

Berichte mal, was draus geworden ist...

Bit- & Bytebruch, guten Rutsch...
...
Angehängte Dateien:
Gast #261258
Lesenswert?

So ist das, wenn man sich auf Datenblätter verlässt - man ist verlassen
!!!

Getestet habe ich, wie schon erwähnt, die Displays nur kurz mit div.
Par-Port-Programmen, jedoch nicht mit dem ATMegaxx. Nur so als
Fkt.-Test, ohne mir über die Zeilenaddressierung klar zu sein.
Nun aber im aktuellen Projekt spielt das sehr wohl eine Rolle.

Danke auch für den Tip, der Vereinfachung. Sicher weniger
Programmieraufwand, aber zeitintensiver 192 Byte * 20µsec = 3,84
msec(gegenüber 1,25 msec beim ClearHome-Befehl), bevor alles gelöscht
ist. Aber einfacher - werde ich mal testen.

Im nächsten Jahr werde ich das mal testen ;-)

Bis dahin und Guten Rutsch

Rudi

PS: @...HanneS...
Werde die Raketen noch manuell zünden, obwohl das Zünduhr-Pgm. dafür
bestensgeeignet wäre !!
#261259
Lesenswert?

Man muss ja das Display nicht löschen, wenn man darauf achtet, dass man
immer volle Zeilen schreibt und denzufolge alle vorherigen Zeichen
überschreibt. Im Zünduhr-Programm wird das Display nur beim Init
gelöscht.

Ich zünde übrigens garkeine Raketen mehr, das muss ich nicht unbedingt
haben. In meiner Jugend habe ich genug Knallzeugs gebaut und gezündet
(ohne dass es jemals "eng" wurde), da kann ich inzwischen gut drauf
verzichten.

Die Zünduhr kommt aber heute Nacht zum Einsatz (mit einem
"Verteiler"), die habe ich nämlich nicht für mich gebaut, sondern für
einen Nachbarn, der vor einigen Monaten seine Pyrotechnik-Ausbildung
abschloss und nun offiziell Feuerwerke Klasse 4 abbrennen darf.

Das wird heute der zweite Einsatz, aber erstmalig mit "Verteiler"
(weiteres von der Uhr über Draht seriell gesteuertes Zündgerät für 31
Zündkreise). Geplant ist später der Einsatz von Zünduhr (16 Kanäle) mit
2 Verteilern (je 31 Kanäle). Das System lässt sich bis zu 6 Verteiler
ausbauen, wobei es für Zündbefehle noch den "logischen" Verteiler 7
(H) gibt, auf den Verteiler 1 und 2 (B und C) synchron reagieren. Das
ermöglicht Synchronzündungen auf beiden Seiten.

Der erste Einsatz (nur Hauptgerät mit 16 Zündkreisen) vor einigen
Wochen hat dem "Betreiber" schon die Finger gerettet. Zwei für
Luntenzündung zugelassene Vulkane (vom deutschen Fachhandel)
detonierten ohne Verzögerung. Bei Luntenzündung wär das peinlich
geworden.

An der PC-Software muss ich noch etwas basteln. Aber erstmal sehen, was
der "Betreiber" da für Wünsche äußert. Bis jetzt ist neben
Schusslistentausch mit der Uhr nur das Erstellen, Editieren und
Laden/Speichern der Schusslisten vorgesehen. Eine "Simulation" wäre
noch interessant, aber leider stimmen die vom Hersteller angegebenen
Abbrennzeiten von Batterien und Vulkanen selten mit der Realität
überein. Aber irgendeine Art Simulation muss noch rein, um im Vorfeld
die Musiksynchronität austesten zu können.

Guten Rutsch... - Und passt beim Knallen auf Eure Finger auf!!!
...
Gast #261260
Lesenswert?

@Hannes: Was für ein Datenblatt hast Du denn von Pollin bekommen?
Ich hab Bestell-Nr. 120387 gekauft (4 Zeilen, 1xM50530 und 1xM50521)
und weder Datenblatt dazubekommen noch ist ein Download verfügbar.
Nciht mal die Belegung der 15 Pins ist nachvollziehbar.
#261261
Lesenswert?

Das von Dir genannte LCD (120387) habe und kenne ich nicht, das gibt es
vermutlich auch noch nicht lange.

Ich hatte vor langer Zeit mal ein 8x24 mit Controller M50530 und bekam
über Ebay etliche von dieser Sorte nachgekauft (gebraucht, aus
Zerlegung von Telefonen). Die sind zwar teils leicht zerkratzt, aber
ich mache ja keine Produktion für den Verkauf.

Woher das Datenblatt zum M50530 stammt, kann ich jetzt gar nicht sagen.
Ich hänge es mal mit an, vielleicht hilft es Dir oder jemand Anderen ja
weiter.

...
Angehängte Dateien:
Gast #470493
Lesenswert?

@ peter dannegger

Du hast oben von einer Menüstruktur gesprochen, die als Tabelle angelegt 
ist und in Untermenüs verzweigen kann. Eine flache Menüstruktur ist klar 
- da gibt es nur einen Menüindex. Wie erweitert man diese 
Tabelle/Struktur nun um Untermenüs? Wie verwaltet man dann die jeden 
Index der Unter- und Untermenüs??

Bitte ein kleines Programm anfügen.

Danke, MiGu.

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