Forum: Mikrocontroller und Digitale Elektronik Aufbau der Grundstruktur vom LCD-Menu


von Peter (Gast)


Lesenswert?

Hi Leute

wollte mich mal mit der Grundstruktur von Menüs beschäftigen. Will die 
Schriften im EEPROM ablegen. Das Funktioniert auch super. Aber wie 
Realisiert man z.b. am besten hier so ein Menu?

http://www.youtube.com/watch?v=YKGr041Erxg

von Karl H. (kbuchegg)


Lesenswert?

Peter schrieb:
> Hi Leute
>
> wollte mich mal mit der Grundstruktur von Menüs beschäftigen. Will die
> Schriften im EEPROM ablegen. Das Funktioniert auch super. Aber wie
> Realisiert man z.b. am besten hier so ein Menu?

:-)
Lustig: Wenn jemand nach 'am Besten' fragt, fragt er meistens nach 
"Hilfe, ich hab überhaupt keine Idee wie ich das machen soll".

Mach doch mal ein Brainstorming. Was fällt dir alles zu diesem Thema 
ein.

* Da gibt es Menüpunkte
* Jeder Menüpunkt besteht aus einem Text
* Wird der Menüpunkt ausgewählt, dann soll er eine Aktion auslösen.
* Es kann mehr Menüpunkte geben, als das Display Anzeigezeilen hat.
* Menüpunkte haben eine bestimmte Reihenfolge in der sie angezeigt
  werden.
* Um ein Menü zu bedienen, braucht es Eingabeelemente.
  Damit muss es möglich sein, diese Operationen auszulösen:
   einen Menüpunkt vorwärts
   einen Menüpunkt rückwärts
   Menüpunkt auswählen
* Es wäre gut, wenn der Benutzer sieht welches der gerade 'aktive'
  Menüpunkt ist. Das ist der, der eine Aktion auslöst, wenn er
  das entsprechende Eingabeelement betätigt.
* Das heißt aber auch, es gibt so etwas wie einen 'aktiven' Menüpunkt.
* Wie signalisiert eigentlich der Benutzer einen Abbruch des jeweiligen
  Menüs als ganzes?

Was fällt dir zu dem Thema sonst noch ein?

Jetzt muss eine Frage geklärt werden: Wie soll das Menü eine Aktion 
auslösen? Soll das Menü zb selbsttätig bei entsprechender Auswahl eine 
Funktion aufrufen? Oder reicht es, wenn die Menüsteuerung den 
entsprechend angewählten Menüpunkt zb in Form einer Nummer 
zurückliefert?


So gehts los:
Ehe man die erste Codezeile schreibt, überlegt man sich erst einmal, was 
man da überhaupt machen will. Welche Eigenschaften soll es haben, was 
weiß ich alles darüber.

Hat man den Punkt erledigt, könnte man zb auf die Idee kommen, dass ein 
Menü programmintern nichts anderes als ein Array von Menüpunkten 
darstellt. Damit wäre zb das Reihenfolgeproblem schon gelöst. Durch das 
Array ist die gegeben. Da das Array größer als die Anzeigeeinheit sein 
kann, wird man wahrscheinlich eine Funktion benötigen, die nur einen 
Ausschnitt aus dem Array auf dem LCD anzeigt. Das heißt aber auch: man 
wird einen Mechanismus benötigen, der zb darüber Buch führt, welches der 
gerade oberste angezeigte  Menüpunkt ist. SCrollt der Benutzer unten um 
1 Zeile raus, dann wandert diese oberste angezeigte Zeile eben um 1 nach 
unten und die Anzeigereoutine stellt dann einen anderen Ausschnitt aus 
dem Array dar: nämlich so, dass das Menü um 1 Zeile gescrollt ist.

Damit wissen wir schon, dass so ein Menü aus diesen Bestandteilen 
bestehen wird:

   Menüpunkte
   aktiver Menüpunkt
   oberster angezeigter Menüpunkt

Und so führt eines zum anderen. Irgendwann wird man seine Überlegungen 
als zunächst abgeschlossen erklären und mal mit einer Implementierung 
anfangen, wobei dann meistens immer noch fehlende Details auftauchen 
werden. Aber da man im Großen und Ganzen weiß, wo die Reise hingehen 
soll, sind die dann eher kein Problem mehr.

zb
1
struct MenuPoint
2
{
3
  char  displayText[40];
4
};
5
6
struct Menu
7
{
8
  MenuPoint* items;
9
  uint8_t    activeItem;
10
  uint8_t    firstVisibleItem;
11
};
12
13
struct MenuPoint[] testMenuPoints =
14
  {
15
    "1. Eintrag",
16
    "2. Eintrag",
17
    "3. Eintrag"
18
  };
19
20
struct Menu testMenu = { testMenuPoints, 0, 0 };
21
22
void ShowMenu( struct Menu* theMenu )
23
{
24
  // zeige die ersten n Menüeinträge aus dem Menü an
25
  // (falls sie existieren)
26
  // und zwar beginnend mit theMenu->firstVisibleItem
27
  ...
28
}
29
30
uint8_t processMenu( struct Menu* theMenu )
31
{
32
  // am Anfang ist der 1. Eintrag der aktive
33
  // und das Menu wird auch so angezeigt, dass der
34
  // ganz oben ist
35
  theMenu->activeItem = 0;
36
  theMenu->firstVisibleItem = 0;
37
38
  ShowMenu( theMenu );
39
40
  while( ...
41
42
    ....
43
  
44
  return theMenu->activeItem;
45
}


Und nein. Wenn man mit sowas anfängt, fängt man NICHT damit an sich zu 
überlegen, ob die Texte nun im SRAM, im EEPROM oder im Flash liegen. Bei 
einer guten Grundstruktur kann man die im Nachhinein immer noch in den 
Speicher verschieben, in dem man sie haben möchte. Erst mal muss das 
Grundgerüst stehen und funktionieren ohne sich in derartigen Details zu 
verzetteln.

von Falk B. (falk)


Lesenswert?

@  Peter (Gast)

>wollte mich mal mit der Grundstruktur von Menüs beschäftigen. Will die
>Schriften im EEPROM ablegen.

Sowas packt man eher ins Flash, denn die meisten Texte/Daten sind 
konstant.

von cyblord (Gast)


Lesenswert?

Peter schrieb:
> Hi Leute
>
> wollte mich mal mit der Grundstruktur von Menüs beschäftigen. Will die
> Schriften im EEPROM ablegen. Das Funktioniert auch super. Aber wie
> Realisiert man z.b. am besten hier so ein Menu?
>
> http://www.youtube.com/watch?v=YKGr041Erxg

Nennt sich "Programmieren", versuchs doch auch mal.

von Falk B. (falk)


Lesenswert?


von Peter (Gast)


Lesenswert?

Also Grundidee steht ja. Steuerung soll aus 4 Tastern (hoch runter, ins 
menu und aus dem menu) bestehen, oder einem Drehgeber mit Taster (links 
rechts kurzer-langer Tasterdruck.
1
char menutext[18][11]={
2
3
"1.0.0 Test",  //0   Ebene 1
4
"2.0.0 Test",  //1   Ebene 1
5
"3.0.0 Test",  //2   Ebene 1
6
"1.1.0 Test",  //3   Ebene 2
7
"1.2.0 Test",  //4   Ebene 2
8
"1.3.0 Test",  //5   Ebene 2
9
"1.3.1 Test",  //6   Ebene 3
10
"1.3.2 Test",  //7   Ebene 3
11
"1.3.3 Test",  //8   Ebene 3
12
"2.1.0 Test",  //9   Ebene 2
13
"2.2.0 Test",  //10  Ebene 2
14
"2.3.0 Test",  //11  Ebene 2
15
"3.1.0 Test",  //12  Ebene 2
16
"3.2.0 Test",  //13  Ebene 2
17
"3.3.0 Test",  //14  Ebene 2
18
"2.1.0 Test",  //15  Ebene 2
19
"2.2.0 Test",  //16  Ebene 2
20
"2.3.0 Test"  //17  Ebene 2
21
};

Hab leider im mom nur ein 2 Zeilen Display zum Testen. Wie bekomm ich 
jetzt am besten die 3 Ebenen aufgerufen inklusive anwahl?

von Falk B. (falk)


Lesenswert?

Stichwort Stuct und einfach oder doppelt verkettete Liste.

von Karl H. (kbuchegg)


Lesenswert?

Wobei in diesem Fall "doppelt verkette Liste" auch so aussehen kann, 
dass man sich bei jedem Menüpunkt vermerkt, welcher andere Menüpunkt der 
'Nachfolger' bei einer bestimmten Aktion ist.
D.h. zb beim Punkt 0 steht dabei
     wenn Benutzer 'Links' macht, kommt er zum Menüpunkt 2
     wenn Benutzer 'Rechts' macht, kommt er zum Menüpunkt 1
     wenn Benutzer 'Runter' macht, kommt er zum Menüpunkt 3
     wenn Benutzer 'Rauf' macht, passiert gar nichts

also
 {
  { "1.0.0 Test",  2, 1, 3, -1 },
  ...


Und dazu nimmt man sinnvollerweise (wie Falk schon sagte) eine struct 
die EINEN Menüpunkt beschreibt und macht dann ein Array von 
Menuepunkten.
1
struct Menuepunkt
2
{
3
  char    Text[11];
4
  int8_t  Links;
5
  int8_t  Rechts;
6
  int8_t  Runter;
7
  int8_t  Rauf;
8
};
9
10
struct Menuepunkt Menu[] =
11
{
12
13
  { "1.0.0 Test",  2, 1, 3, -1 },
14
  { ...
15
16
};

von Anke (Gast)


Lesenswert?

Steinigt mich ruhig, weil ich den Thread wieder ausgrabe. Leider hat 
sich bislang niemand bei Karl Heinz für seine Bemühungen diese tolle 
Erklärung zu verfassen bedankt.
Daher nochmal von mir ein ganz großes Dankeschön dafür!!
Leider melden sich hier in diesem Forum viel zu häufig irgendwelche 
Klugscheißer mit negativen Kommentaren zu Rechtschreibung oder 
mangelnder Fachkompetenz zu Wort. Aber das ist ja längst Tradition.

von Wolfgang (Gast)


Lesenswert?

Anke schrieb:
> Leider melden sich hier in diesem Forum viel zu häufig irgendwelche
> Klugscheißer mit negativen Kommentaren zu Rechtschreibung oder
> mangelnder Fachkompetenz zu Wort.

Die manchmal etwas bissigen Kommentare drücken wohl eher eine Kritik an 
der Grundeinstellung mancher Frager aus, die sich nicht einmal die Mühe 
machen, ihr Problem strukturiert und in halbwegs vernünftiger 
Schriftsprache zu formulieren oder vorher eigenständig ein bisschen in 
den diversen Tutorials und Grundlagenartikeln zu recherchieren.

von Klaus W. (mfgkw)


Lesenswert?

Anke schrieb:
> Steinigt mich ruhig,

Ist hier Weibsvolk anwesend?

von Hmm (Gast)


Lesenswert?

@ Klaus Wachtler

Ich möchte mir von Dir in Zukunft jede Kritik an "sinnlosen" resp. 
nichts zum Thema beitragenden Beiträgen von mir verbitten. :-)

von Klaus W. (mfgkw)


Lesenswert?

Sorry, habe ich hier einen "Hmm" beleidigt, der noch gar nicht 
aufgetreten ist?
Bist du von der Volksfront von Judäa? Oder einer von der Kampagne für 
ein freies Galiläa? Oder gar einer der Spalter von der judäischen 
Volksfront?

Und vor allem: Geht es hier zur Steinigung?

von Katze D. (katze_d)


Lesenswert?

Karl Heinz schrieb:
> struct Menuepunkt
> {
>   char    Text[11];
>   int8_t  Links;
>   int8_t  Rechts;
>   int8_t  Runter;
>   int8_t  Rauf;
> };
>
> struct Menuepunkt Menu[] =
> {
>
>   { "1.0.0 Test",  2, 1, 3, -1 },
>   { ...
>
> };

Hallo zusammen, entschuldigt, dass ich diesen alten aber in meinen Augen 
sehr wichtigen Thread ausgrabe, aber ich habe zu diesem Thema eine Frage 
und ich wollte jetzt nicht einen neuen Thread aufmachen.

Und zwar hab ich meine Menüstruktur genau so aufgebaut aber ich frage 
mich wie verwende ich jetzt in meinem Programm die int8_t werte (rauf, 
runter, etc.)

finde leider keinen kompletten Code den ich mir mal anschauen könnte, um 
zu verstehen wie die Logik hinter diesem struct und deren weiteren 
Verwendung aussieht..

Vllt. könnte sich ja jemand erbarmen und mir sagen wie ich diese 
erstellte Menüstruktur sinnvoll verwenden kann..

von Karl H. (kbuchegg)


Lesenswert?

Katze D. schrieb:

ganz einfach. In 'links' 'rechts' 'rauf' 'runter' steht jeweils die 
Nummer des Menu-Eintrags, der als aktiver Menüeintrag gilt, wenn die 
entsprechende Taste gedrückt wird. (und es dann auch einen entsprechend 
auszuwählenden Menüpunkt gibt)
1
uint8_t activeEntry;
2
3
..
4
5
   if( Taste links gedrückt && Menu[activeEntry].Links != -1 )
6
     activeEntry = Menu[activeEntry].Links;
7
8
   if( Taste rechts gedrückt && Menu[activeEntry].Rechts != -1 )
9
     activeEntry = Menu[activeEntry].Rechts;
10
11
...

wenn der Menüpunkt gewechselt wird, wird man natürlich dann auch noch 
den neuen aktiven Menüpunkt irgendwo anzeigen oder, wenn er schon mit 
seinen restlichen Menü-Kollegen an der Anzeige ist, irgendwie markieren.

Aus dem Original:
1
//            links  rechts  runter  rauf 
2
 "1.0.0 Test",  2,     1,      3,     -1      //0   Ebene 1
3
 "2.0.0 Test",  0,     2,      9,     -1      //1   Ebene 1
4
 "3.0.0 Test",  1,     0,     12,     -1      //2   Ebene 1
5
 "1.1.0 Test",  5,     4,     -1,      0      //3   Ebene 2
6
 "1.2.0 Test",  3,     5,     -1,      0      //4   Ebene 2
7
 "1.3.0 Test",  4,     3,      6,      0      //5   Ebene 2
8
 "1.3.1 Test", ...............                //6   Ebene 3
9
 "1.3.2 Test", ...............                //7   Ebene 3

Ist der Menüpunkt "1.0.0" aktiv und drückt der Benutzer auf 'rechts', 
dann ist laut
1
//            links  rechts  runter  rauf 
2
 "1.0.0 Test",  2,     1,      3,     -1      //0   Ebene 1
damit der Menüpunkt mit dem Index 1 der nächste aktive. Das ist dieser 
hier
1
 "2.0.0 Test",  0,     2,      9,     -1      //1   Ebene 1
Drückt der Benutzer jetzt auf 'runter', dann aktiviert er damit den 
Menüpunkt mit dem Index 9, welcher dieser hier ist
1
"2.1.0 Test",  .........                        //9   Ebene 2

was ja auch stimmt. Denn er hat ja den Punkt 2.0.0 sozusagen geöffnet 
und ist dadurch zu 2.1.0 'eingestiegen'. In diesem Menüpunkt würde es 
beim drücken von 'rauf' wieder zurückgehen zum Menüpunkt wo er 
hergekommen ist, also zum Punkkt "2.0.0.". Daher wird bei "2.1.0" bei 
rauf dann die 1 als zu aktivierender Menüpunkt eingetragen sein.

: Bearbeitet durch User
von Katze D. (katze_d)


Lesenswert?

...ahhh.... so langsam dämmert es...mhhh.. muss das noch irgendwie 
sortieren aber ich denke deine Hilfe (der Chef persönlich hat 
geantwortet :) ) hat mir die Logik hinter diesem Menüaufbau näher 
gebracht, danke!

LG

von Karl H. (kbuchegg)


Lesenswert?

Katze D. schrieb:
> ...ahhh.... so langsam dämmert es...mhhh.. muss das noch irgendwie
> sortieren

am einfachsten mit Papier und Bleistift aufmalen
von welchem menüpunkt soll es beim drücken welcher Taste zu welch 
anderem Menüpunkt gehen
1
L ... Taste 'links'
2
R ... Taste 'rechts'
3
U ... Taste 'rauf' (up)
4
D ... Taste 'runter (down)
5
             
6
  "Run"     R ---->  "Options"  R -----> "Explode"
7
            <---- L             <----- L
8
     D ^               D ^
9
     | |               | |
10
     | |               v U
11
     | |             
12
     | |             "Color"  R -----> "Time"
13
     | |                      <----- L
14
     | |
15
     v U
16
   "Stop"

Dieses Diagreamm beschreibt die Logik im Menü. Aus ihm ersieht man, in 
welchem Menüpunkt man landen soll wenn die entsprechende Taste gedrückt 
wird. Das kann natürlich auch kreuz und quer sein. Wenn man im Punkt 
"Time" auf Up drückt (rauf), dann will man wieder bei "Options" landen. 
Aber mit ASCII kann ich so schlecht schräge Linien zeichnen :-)

Dann nummerierst du die Punkte in beliebiger Reihenfolge durch
1
 0: "Run"     R ---->  1: "Options"  R -----> 2: "Explode"
2
              <---- L                <----- L
3
       D ^                  D ^
4
       | |                  | |
5
       | |                  v U
6
       | |             
7
       | |             3: "Color"  R -----> 4: "Time"
8
       | |                         <----- L
9
       | |
10
       v U
11
 5:  "Stop"
 und trägst das was du im Diagramm siehst in die Datenstruktur ein
1
...
2
//  Text    links  rechts  runter rauf
3
{ "Run",      -1,      1,     5     -1  },  // 0
4
{ "Options",   0,      2,     3,    -1  },  // 1
5
{ "Explode",   1,     -1,    -1,    -1  },  // 2
6
{ "Color",    -1,      4,    -1,    -1  },  // 3
7
{ "Time",      3,     -1,    -1,    -1  },  // 4
8
{ "Stop",     -1,     -1,    -1,     0  }   // 5

: Bearbeitet durch User
von Katze D. (katze_d)


Lesenswert?

:) Hehe danke für den Tipp mit dem aufzeichnen... war zufälligerweise 
gerade dabei mit Word und SmartArts mir die Menüstruktur "aufzumalen"
finde das hilft ungemein..
Und danke für die Mühe mit der Veranschaulichung der Menüstruktur!

Hatte schon Angst, wenn ich bei dem Thema nachhake, einen Rüffel zu 
bekommen. :)

Würde deinen Beiträgen 5* geben, wenn man das hier könnte und über:

"
> Lustig: Wenn jemand nach 'am Besten' fragt, fragt er meistens nach
> "Hilfe, ich hab überhaupt keine Idee wie ich das machen soll".
"

musste ich sehr lachen. daumenHoch

: Bearbeitet durch User
von Katze D. (katze_d)


Lesenswert?

Hallo nochmal,

ich hätte noch eine kleine Frage, wenn ich die Struktur auslagern will, 
dass heißt ich würde gerne das Struct und die Variablen in eine .h Datei 
auslagern wäre das der korrekte Weg? Also, müsste ich alles in die z.B. 
LCD.h Datei packen oder müsste noch was davon in die LCD.c?
Ich denke an der Frage erkennt man schon, dass ich noch nicht ganz die 
grundlegende Struktur kenne und mir bei dem Thema noch recht unsicher 
bin.

Gruß
KatzE

/EDIT
zur Verdeutlichung:
das würde ich jetzt in meine LCD.h schreiben:
1
void menu_punkt ( uint16_t ADC_READ_IN );
2
3
void punkt1(void);
4
5
void punkt1.1(void);
6
7
void punkt1.2(void);
8
9
usw...
10
11
12
static const char Menue_E1[]    = "punkt1      "        ;  //0
13
static const char Menue_E2[]    = "punkt2"  ;  //1
14
static const char Menue_E1_1[]         ="punkt1.1"        ;  //2
15
static const char Menue_E1_2[]    = "punkt1.2 "        ;  //3
16
static const char Menue_E1_1_1[]  = "punkt1.1.1        "        ;  //4
17
usw....
18
19
20
21
//Struktur festlegen
22
typedef struct Menu{
23
  const unsigned char *text;
24
  int8_t hoch;          
25
  int8_t runter;          
26
  int8_t rechts;          
27
  int8_t links;          
28
  void (*fp)(void);
29
  
30
}MENU_ENTRY;
31
32
33
34
//Menü einträge plus Nachtbarn auflisten
35
const MENU_ENTRY menue[] ={
36
  {Menue_E1,        9, 9, 1, -1          },        //0
37
  {  Menue_E1_1,      5, 5, 2, 0,          },        //1
38
  {    Menue_E1_1_1,  4, 3, -1, 1,  blabla},        //2
39
  {    Menue_E1_1_2,  2, 4, -1, 1,  blalba  },        //3
40
  {    Menue_E1_1_3,  3, 2, -1, 1,  bla  },        //4
41
  
42
usw...
43
  
44
};



würde das alles so in die lcd.h datei kommen? Bin ich da auf dem 
richtigen Weg?

: Bearbeitet durch User
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
Noch kein Account? Hier anmelden.