Hallo Forum,
Ich bin noch recht neu auf dem Gebiet µC und habe schon einpaar
Programme geschrieben und nun würde ich gerne mal mit einem Menü was
machen aber leider weis ich nicht wie
gibt es da ein Tut oder eine Vorlage für?
habe leider nichts selbst gefunden :(
Ich nutzte das Display was auf dem Pollin Addon ist falls das weiter
hilft
Mein Tip:
Stift und Blatt Papier
...zeichne dir dein Vorhaben auf, das hilft weiter.
Läuft vermutlich auf ein großes switch / case raus.
Menü? ..wo? im Hyperterminal? auf einem Display? grafisch?
nur Text? in Restaurant? (ok, Joke)
Ich würde es über vier Taster steuern:
1. Hoch (Menüpunkt++)
2. Runter (Menüpunkt--)
3. Auswahl
4. Zurück
und dahinter wie bereits erwähnt das switch / case...
Hallo,
danke für die schnellen Antworten
Das Menü soll auf einem Text-LCD ablaufen
über drei Taster
Taster1=hoch
Taster2=runter
Taster3=enter
Als Programmiersprache verwende ich übrigens C
pacer schrieb:> Wenn du einigermaßen gut in C bist, schau dir doch mal den Code vom>> AVR-Butterfly an. Der ist frei verfügbar und hat ein mehrstufiges Menü.
Da ich noch recht neu im bereich µC bin sind meine C-Kenntnisse auch
noch nicht so umwerfend :(
Ich habe mir den Code vom Butterfly mal rausgesucht und angeschaut...
der ist im gegensatzt zu meinen bisherigen Programme ein Riese!
Ich wollte erstmal mit drei Menüpunkten anfangen die ich später
erweitern wollte mit untermenüs.
Wisst Ihr da vielleicht etwas einfacheres?
Wäre schön wenn da jemand ein klerines Beispiel hätte =)
Bastler schrieb:> Ich würde es über vier Taster steuern:>>>> 1. Hoch (Menüpunkt++)>> 2. Runter (Menüpunkt--)>> 3. Auswahl>> 4. Zurück
Das könnte ich auch machen über vier Tastern
Das ist ein interessantes Thema.
Ich habe mir in letzter Zeit auch Gedanken gemacht. Wie man Menus am
besten angeht. Wichtig ist ja bei der ganzen Sache auch, das das
Programm flexibel bleibt, man Menu´s ohne viel Aufwand ändern kann und
einzelne Menupunkte auch mal verschieben kann.
Ob man jetzt zur Bedienung vier Tasten braucht ist GeschmacksSache.
So einer Taste kann man verschiedene Zustände entlocken.
Taste kurz gedrückt, Taste lange gedrückt oder Taste sehr lange
gedrückt.
Darüber könnte man am Ende sogar seine "zurückFunktion" machen.
Mal die drei Zustände an einer Taste abfragen, ist eine gute Fingerübung
zum Programmieren.
Eine gute Routine zur Tastenabfrage ist das A & O!
Das mit der Statemachine erschließt sich mir nicht ganz. Das Beispiel
mit der Ampel läßt sich schlecht für ein Menu umdenken.
Hat jemand mal ein Codeschnipsel für ein Menu mit dieser switch/case
Sache?
Ansonsten hab ich mal gesehen das sich Menus recht einfach über
FunktionsZeiger und Tabellen realisieren lassen.
Aber das mit dem switch/case sieht nach ganz schönem Geraffel aus.
Erinnert mich ein bißchen an die ersten Menus in BASIC.
IF A$= "1"
PRINT "eins" .... ;)
Aber wie gesagt. Es wäre schön wenn jemand mal eine Bespiel für ein Menu
mit switch/case hätte.
Hi
>Eine gute Routine zur Tastenabfrage ist das A & O!
Ein Drehgeber mit Tastenfunktion geht auch ganz gut.
>Aber wie gesagt. Es wäre schön wenn jemand mal eine Bespiel für ein Menu>mit switch/case hätte.
Ehrlich gesagt, so etwas tue ich mir noch nicht einmal in Assembler an.
Unter einer Hochsprache bietet sich eine verkettete Liste an.
MfG Spess
Könnte eine Statemachine so aussehen?
Das ist ja einfach, und ich dachte ein Zustandsautomat wäre urst
kompliziert.
Kann man da noch den Überblick behalten, wenn mehrere Tasten zur Auswahl
stehen?
@spess
Wie funktioniert es in Assembler?
Wenn das Menu mit einer verketteten Liste aufgebaut ist, muss man da bei
Veränderungen nicht gleich mehrere Stellen ändern?
Hi
>@spess>Wie funktioniert es in Assembler?>Wenn das Menu mit einer verketteten Liste aufgebaut ist, muss man da bei>Veränderungen nicht gleich mehrere Stellen ändern?
Die Anregung 'Verkettete Liste' war eigentlich primär für Hochsprachen
gedacht. Allerdings dürfte der der 'Veränderungsaufwand' dort genau so
hoch sein wenn die Liste programmiert wird. Also wenn man ein
Listenelement nachträglich einfügt muss man mindesten 4 Werte ändern.
Bei Assembler sind es auch nicht mehr. Für Assembler habe ich das
eigentlich bisher nur angedacht.
Das letzte Menü (Assembler) habe ich über Sprungtabellen/Listen
realisiert. Vereinfacht: jeder Menüpunkt hat eine eigene Nummer über
die Beschriftung, Behandlungsroutine, Exitroutine usw. bestimmt werden.
Also keine verkettet Liste, aber auch keine cpi/brne-Orgie.
MfG Spess
Hi
>Stimmt, Assembler hat ja die Sprungtabellen. Da kann man ja mit ijmp>bequem überall hinspringen.
In dem Fall mit icall über die Sprungtabelle zur Routine.
MfG Spess
Tom schrieb:> Könnte eine Statemachine so aussehen?>> Das ist ja einfach, und ich dachte ein Zustandsautomat wäre urst> kompliziert.> Kann man da noch den Überblick behalten, wenn mehrere Tasten zur Auswahl> stehen?
Na ja, bis zu einem Menüsystem ist da schon noch ein weiter weg
Ein universelles Menüsystem baut darauf auf, dass man in einer
Datenbeschriebung alles zusammenfasst, was es einen Menüpunkt ausmacht.
Also: Den anzuzeigenden Text und eine Möglichkeit, wie man die zum
Menüpunkt gehörende Funktionalität beschreiben kann.
Text ist wohl klar.
Die Funktionalität kann man im einfachsten Fall so aufbauen, dass man
einen Funktionszeiger auf eine Funktion installiert, die aufgerufen
werden soll, wenn genau dieser Menüpunkt ausgewählt wird.
Postulat: gegeben seien 3 Tasten. Ihre Bedeutung ist:
Vorhergehender Menüpunkt (wenn einer existiert)
nachfolgender Menüpunkt (wenn einer existiert)
Menüpunkt auswählen
weiter sei ein Menüpunkt so aufgebaut
1
typedefvoid(*MenuFnct)(void);
2
3
structMenuEntry
4
{
5
charText[20];// anzuzeigender Text
6
MenuFnctFunction;// auszuführende Funktion, wenn der Menüpunkt
7
// ausgewählt wird
8
};
damit kann man erst mal einen Menüpunkt definieren, zb "Help" und wenn
der ausgewählt wird, dann soll die Funktion Help() aufgerufen werden
1
voidHelp()
2
{
3
...machirgendwas
4
}
5
6
structMenuEntry={"Help",Help};
Soweit so gut. Aber das ist ja noch kein Menü.
Was ist denn ein Menü?
Ein Menü ist eine Sammlung von Menüpunkten, also mehrere.
Das stellt jetzt erst mal kein Problem dar, denn das kann im einfachsten
Fall einfach nur ein Array von derartigen Menüpunkten sein. Du möchtest
zb ein Menü haben, welches die Menüpunkte "Options", "Zeit" und "Hilfe"
umfasst. Wenn "Options" ausgewählt wird, dann soll die Funktion
Options() aufgerufen werden, bei "Zeit" die Funktion Time() und bei
"Hilfe" soll Help() aufgerufen werden:
1
voidOptions()
2
{
3
}
4
5
voidTime()
6
{
7
}
8
9
voidHelp()
10
{
11
}
12
13
structMenuEntryMainMenu[]=
14
{
15
{"Optionen",Options},
16
{"Uhrzeit",Time},
17
{"Hilfe",Help}
18
};
soweit so gut.
Was jetzt noch benötigt wird, ist eine Funktion, die das Menü zum Leben
erweckt. Bis jetzt gibt es das Menü ja nur als Datenstruktur.
Was heißt denn zum Leben erwecken? Was muss die Funktion machen?
Sie muss ganz sicher erst einmal alle Menüpunkte an das LCD schreiben.
Dann muss sie weiters die Tasten überwachen. Die Funktion wird so etwas
wie einen gerade "ausgewählten" Menüpunkt brauchen. Mit den Tasten kann
dann der nächste bzw der vorhergehende Menüpunkt ausgewählt werden. Und
wenn der Benutzer diesen Menüpunkt haben will, dann soll diese
Menüsteuerung genau die zu diesem Menüpunkt gehörende Funktion aufrufen.
Kommt die Funktion zurück, dann bekommt diese Menüsteuerung wieder die
Kontrolle und weiter gehts mit der Tastenüberwachung.
Zusätzlich vereinbaren wir noch: Gibt es zu einem Menüpunkt keinen
Funktionspointer, dann behandelt das die Menüsteuerung als "Menü
abbrechen und zurückkehren zum Aufrufer". Wozu das gut sein soll, siehst
du dann, wenn ein Submenü gemacht wird, das einen Menüpunkt "Exit" hat,
der wieder zum Hauptmenü zurückkehrt. Dort wird das dann eingesetzt.
Zurück zur Menüsteuerung. Das ist eine Funktion die 2 Informationen
erhält: Nämlich einen Verweis auf das Menü-Array und die Größe dieses
Arrays (weil ja Funktionen diese Größe nicht selbst eruieren können)
Beschäftigen wir uns mit dem ersten Teil, den die Funktion machen soll:
Das Hinschreiben der Menütexte. Dazu schreiben wir gleich wieder eine
eigene Funktion, um diesen Teil aus der Menüsteuerung rauszuhalten. Die
Hinschreib-Funktion ist sehr einfach. Auch sie kriegt die Größe des
Arrays und einen Verweis auf das Array selber
1
voidDrawMenu(intMenuSize,structMenuEntryMenu[])
2
{
3
inti;
4
5
lcd_clr();
6
for(i=0;i<MenuSize;++i)
7
{
8
lcd_goto_xy(2,i);
9
lcd_write(Menu[i].Text);
10
}
11
}
Das pinselt alle Menüpunkte beginnend in der Spalte 2 untereinander auf
ein LCD. Ich hab jetzt einfach von ein paar LCD Funktionen angenommen,
das sie existieren. Was sie tun sollte selbst erklärend sein.
Warum Spalte 2?
Weil ich vor dem Menüpunkt etwas Platz brauche um dort meinen 'Dieser
Menüpunkt wäre es, wenn du jetzt bestätigen' Marker unterzubringen. Der
Einfachheit halber mach ich einfach vor den ausgwewählten Menüpunkt ein
Sternchen '*' hin, damit der Benutzer eine Rückmeldung hat, welcher
Menüpunkt denn jetzt gilt. Im Grunde verschiebt er also einfach nur
dieses Sternchen in den Menüzeilen am LCD wobei die Menüfunktion
natürlich darüber Buch führen muss, wo denn nun eigentlich das Sternchen
ist.
Also fangen wir mal an
1
voidDoMenu(uint8_tMenuSize,structMenuEntryMenu[])
2
{
3
uint8_tactive=0;// active sei der momentan ausgewählte Menüpunkt
4
uint8_trunning;
5
6
DrawMenu(MenuSize,Menu);// alles hinpinseln lassen
7
8
// die Markierung für den Benutzer (das Sternchen) hinmalen
9
lcd_gotoxy(0,active);
10
lcd_write("*");
11
12
running=TRUE;
13
14
// soweit ist alles aufgesetzt, ab jetzt werden die Tasten überwacht
15
// und das Menü soll arbeiten
16
while(running)
17
{
18
if(get_keypress(UP))// Taste "Rauf" gedrückt ?
19
{
20
if(active>0)// geht da überhaupt was?
21
{
22
lcd_gotoxy(0,active);// ja, tut es. Das Sternchen wegnehmen
23
lcd_write(" ");
24
25
active--;
26
27
lcd_gotoxy(0,active);// und neu hinsetzen, in die Zeile davor
if(Menu[active].Function)// gibt es eine auszuführende Funktion?
49
Menu[active].Function();// Ja: führe sie aus
50
else
51
running=FALSE;// Nein: Das war der "Exit" Menüpunkt
52
// Menüsteuerung beenden
53
}
54
}
55
}
Tja. Und das wars im Großen und Ganzen auch schon.
Die Funktion DoMenu nimmt sich die Menübeschreibung aus dem Array und
führt die entsprechenden auf Benutzerwunsch hin aus.
(2. Posting kommt noch)
Ein Menu kann jetzt auch über Submenues verfügen. Auch das ist kein
Problem. Alles was man braucht, ist eine Funktion die wieder ein neues
Menü startet.
Die Menühierarchie soll zb so aussehen
Optionen -> Funktion Options()
Uhrzeit -> SubMenu Stunden -> Funktion Hours()
^ Minuten -> Funktion Minutes()
+----------------- Exit
(Bei Auswahl von Uhrzeit kommt man in ein Submenu mit den Menüpunkten
"Stunden" und "Minuten". Mit dem Punkt "Exit" kommt man aus dem Submenu
wieder zurück ins Hauptmenü.
1
typedefvoid(*MenuFnct)(void);
2
3
structMenuEntry
4
{
5
charText[20];// anzuzeigender Text
6
MenuFnctFunction;// auszuführende Funktion, wenn der Menüpunkt
7
// ausgewählt wird
8
};
9
10
voidDoMenu(uint8_tMenuSize,structMenuEntryMenu[])
11
{
12
....wiegehabt
13
}
14
15
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
16
17
voidHours()
18
{
19
...
20
}
21
22
voidMinutes()
23
{
24
...
25
}
26
27
structMenuEntryTimeMenu[]=
28
{
29
{"Stunden",Hours},
30
{"Minuten",Minutes},
31
{"Exit",NULL}
32
};
33
34
voidOptions()
35
{
36
}
37
38
voidTime()
39
{
40
DoMenu(ARRAY_SIZE(TimeMenu),TimeMenu);
41
}
42
43
structMenuEntryMainMenu[]=
44
{
45
{"Optionen",Options},
46
{"Uhrzeit",Time},
47
};
48
49
intmain()
50
{
51
....
52
53
while(1)
54
{
55
.....
56
57
DoMenu(ARRAY_SIZE(MainMenu),MainMenu);
58
59
.....
60
}
61
}
Eine kleine Ergänzung sollte man in DoMenu dann noch vornehmen: Wenn
DoMenu die Funktion aufruft, sollte sie vorher das LCD löschen um der
Funktion keine Altlasten zu hinterlasten und es sollte nachdem die
Funktion zurückgekehrt ist, sein Menü-Display wieder neu aufbauen.
Aber das ist ja kein Problem
1
voidDoMenu(uint8_tMenuSize,structMenuEntryMenu[])
2
{
3
uint8_tactive=0;// active sei der momentan ausgewählte Menüpunkt
Anzumerken sei noch, dass das EINE Möglichkeit ist, wie man universelle
Menüs aufbauen kann.
Eine ganz andere Möglichkeit besteht zb darin, dass man in einem
Menüpunkt beschreibt, wer sein vorhergehender Menüpunkt ist, wer sein
Nachfolger ist, welcher Menüpunkt sich als Submenü auftun soll, wie man
wieder zurück kommt und welche Funktion aufgerufen werden soll.
Das oben gezeigte Menü könnte dann zb inclusive des Submenues so
aussehen
1
structLinkedEntry[]=
2
{
3
{"Optionen",1,2,-1,-1,Options},// 0
4
{"Uhrzeit",2,0,3,-1,NULL},// 1
5
{"Hilfe",0,1,-1,-1,Help},// 2
6
{"Stunden",4,5,-1,-1,Hours},// 3
7
{"Minuten",5,4,-1,-1,Minutes},// 4
8
{"Exit",3,4,-1,1,NULL}// 5
9
}
Die 4 Zahlen bei jedem Menüpunkt bedeuten jeweils
* welches ist der nächste Menüpunkt
welches wird also aktiv, wenn Taste "Runter" gedrückt wird
* welches ist der vorhergehende Menüpunkt
welcher wird also aktiv, wenn Taste "Rauf" gedrückt wird
* welches ist der erste Menüpunkt eines Submenüs
wenn die Taste "Ausführen" gedrückt wird und der Menüpunkt in ein
Submenü verzweigt
* welches ist der Menüpunkt (und damit welches Menü) der aktiv
ist, wenn aus einem Submenü wieder ausgestiegen werden soll
(wobei -1 jeweils einen Nicht-Eintrag kennzeichnet)
Auch das ist eine Möglichkeit.
Oder man könnte ....
Man könnte aber auch ....
Es gibt viele Möglichekeiten, wie man allgemeine Menüsysteme aufziehen
kann. Welches vernünftig ist, hängt auch ein wenig von der Anwendung
selber ab.
Einfach mal ein wenig experimentieren.
Wenn man es wie Karl-Heinz macht und es noch etwas weiter spinnt (kein
Array benutzen, sondern direkt Pointer auf die Structs statt Zahlen)
dann kann man die Daten auch noch gleich in den Flash schreiben. So kann
man sich dann immer den gerade nötigen Teil laden.
Warum? Weil so ein Menü schnell mal das eine oder andere kb groß werden
kann. Wenn man nur 2-4 kb RAM hat ist das schlecht auf dem Stack unter
zu bringen.
Ich hoffe es war verständlich was ich meine.
Das was hier Karl-Heinz Buchegger geschrieben hat mir persönlich immens
weiter geholfen. Aber nun hat sich bei mir eine Frage ergeben.
1
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
Mit dieser Definition bekommt man die größe eines beliebigen Array und
der Präprozessor (?) ersetzt diese durch den exakten Wert.
Ich benutzte diese Definition damit ich mit den "UP/DOWN Tasten" einen
Ringdurchlauf durch das Menu machen kann.
Also damit wenn ich mich im Menü auf den letzten Eintrag stehe ich bei
einen erweiterten "UP" Tastendruck auf den ersten Menüeintrag stehe (und
umgekehrt)
Nun habe ich aber das Problem das wenn ich einen Menüeintrag auswähle
(und in ein Submenü reinspringe) , das dieser wieder unterschiedlich
viele Einstellungen hat wo ich wieder die Arraygröße benötige womit ich
wieder einen Ringdurchlauf ermöglichen kann.
Da ich die aus einen Wert die Menüeintrag kenne in den ich
hereingesprungen bin habe ich mir überlegt das ich mir ein Array anlege
in denen ich für jeden Menüeintrag die maximalen Einträge kenne.
Die Frage ist:
Wie kann man sich ein Array mit konstanten Arraygrößen anlegen?
Mit den oben dargestellten Definition Funktioniert das bei einer
Struktur leider nicht. Ansonsten benutze ich diese Definition
erfolgreich.
Natürlich kann ich auch die Werte einfach abzählen und die richtigen
Werte reinschreiben, aber ich würde es gerne per Definition machen. =)
Damit man sich das vorstellen kann:
1
structMenuEntry
2
{
3
charNumber[4];
4
charText[20];
5
MenuFnctFunction;
6
};
7
8
9
structValueEntry
10
{
11
charNumer[4];
12
charText[20];
13
unsignedcharParameter;
14
};
15
16
17
structMenuEntrySettingMenu[]=
18
{
19
{"1.","Backlight",NULL},
20
{"2.","LCD-Contrast",NULL},
21
{"3.","Temperature",NULL},
22
};
23
24
structValueEntryBacklight[]=
25
{
26
{"1.","On",0x01},
27
{"2.","Off",0x00},
28
};
29
30
structValueEntryLCD_Contrast[]=
31
{
32
{"1.","1/8",0x01},
33
{"2.","2/8",0x02}},
34
{"3.","3/8",0x03},
35
{"4.","4/8",0x04},
36
{"5.","5/8",0x05},
37
{"6.","6/8",0x06},
38
{"7.","7/8",0x07},
39
{"8.","8/8",0x08},
40
};
41
42
structValueEntryTemperature[]=
43
{
44
{"1.","°C",NULL},
45
};
46
47
48
49
structArraySize
50
{
51
unsignedcharSize;
52
}
So sollte meine Wunschstruktur aussehen:
1
structArraySizeSettingSubmenu[]=
2
{
3
{/* Hier sollte 1 stehen */},// Anzahl Unterpunkte von Backlight-1
4
{/* Hier sollte 7 stehen */},// Anzahl Unterpunkte von LCD-Contrast-1
5
{/* Hier sollte 0 stehen */},// Antahl Unterpunkte von Temperature-1
Mir ist zwar nicht klar, warum du dafür ein eigenes Array machen willst
und die Größe der Submenüs nicht dort mit abspeicherst, wo sie
hingehört, nämlich in die struct MenuEntry, aber grundsätzlich würde das
so aussehen
1
structArraySizeSettingSubmenu[]=
2
{
3
{ARRAY_SIZE(Backlight)-1},
4
{ARRAY_SIZE(LCD_Contrast)-1},
5
{ARRAY_SIZE(Temperature)-1},
6
};
das erfüllt deine Vorgabe.
Aber wenn du genauer darüber nachdenkst, brauchst du dieses Array an
sich gar nicht. Denn an der Stelle, an der du aus dem Menüpunkt für
"Backlight" ableitest, dass dafür das Array Backlight zuständig ist,
kannst du genausogut dort die ARRAY_SIZE von Backlight benutzen um die
Anzahl der Menüpunkte zu erhalten und mit der an dieser Stelle dort
weiterarbeiten.
Was anderes habe ich ja hier
1
voidTime()
2
{
3
DoMenu(ARRAY_SIZE(TimeMenu),TimeMenu);
4
}
auch nicht gemacht.
Die Time Funktion weiß, dass sie das Array 'TimeMenu' als neues Menü
aktivieren muss, und zu diesem Zwecke lässt es sich mittels ARRAY_SIZE
die größe dieses Arrays ermitteln.
PS: Musst du wirklich die Menupunkt Nummerierung mit in die Menüpunkte
aufnehmen? Die nehmen nur Platz weg und solange die Menüpunkte sowieso
von 1 beginnend durchnummeriert werden, lässt sich dieser Teil ja auch
zur Laufzeit ganz leicht generieren.
Du hast mir wirklich weitergeholfen. Ich habe so viel experimentiert und
habe das eigentliche Ziel verloren. Die Arraygröße selbst hatte ich in
den Unterpunkten des Submenus selbst abspeichern wollen und es hatte
mich gestört das ich dann z.B für die Backlightfunktion 8 mal die
Arraygröße speicher obwohl ich das gar nicht brauche. Dabei kann ich
doch einfach die Struktur "SettingMenu" um einen Wert erweitern und dort
die Arraygröße
des Submenüs ablegen. =D
Eine Erklärung warum ich das so gerne machen würde:
Ich selber habe nicht deine Menüführung übernommen, sondern habe auf
Basis darauf mein eigenes Untermenü entworfen. Es ist aber wirklich gut
gewesen, mal zu sehen wie man es machen könnte. Mit meinen ersten
Versuchen bin ich Kläglich gescheitert da ich gar nicht "wusste" was man
alles mit z.B Strukturen anstellen kann. Ich selber habe viele Blätter
Papier vollgeschrieben und hatte ganz merkwürdige if else if Klamotten
angefangen welche ziemlich daneben gegangen sind.
Meine Menüführung besteht darin das die Funktion "Zeige das Setting
Menü" aus 2 Unterpunkten besteht welche aus einmal den Main Teil und
einen Sub Teil besteht. Den Mainteil habe ich schon fertig und dieser
Funktioniert richtig gut.
Das Problem war wenn man einen Unterpunkt angewählt hatte ich keinen
Wert habe welcher mir Rückschlüsse gibt wie viele Menüunterpunkte es
überhaupt gibt. Erstmal wollte ich es über einen Funktionsaufruf machen,
da man in einer Struktur selbst einen Funktionsaufruf integrieren kann,
aber das wurde mir ein wenig zu unübersichtlich und gefällt mir
persönlich nicht so.
Dementsprechend die Nachfrage nach eine Struktur mit den Arraygrößen der
einzelnen Unterpunkten. - Wobei ich jetzt feststellen muss das dass
natürlich quatsch ist.
Wenn ich vom Hauptmenü in das Submenü gesprungen bin kann ich mit den
Index des MenuEntry SettingMenu [] eindeutig das jetzige Position
bestimmen an der ich mich befinde (Ist ja logisch). Also habe ich
verzweifelt nachgedacht damit ich mit diesen Index gleichzeitig auf die
Array größe schließen kann. Wenn ich zu jeder Zeit die richtige Größe
abfragen kann kann ich das komplette Menü nun konstruieren.
Man stellt sich ganz doof als Anfänger an wenn man gerade
nicht weiter weiß "Wie man es machen würde".
Das P.S: werde ich beachten und ich werde mal schauen was ich mir dafür
speziell ausdenken werde. Das ganze ist jetzt nur ein Prototyp da
hinterher das meinste im Flashspeicher abgelegt wird. (Davon kann ich
viel mehr "verschwenden" als RAM)
Vielen Dank für deine wirklich Hilfreiche Antwort. =)
Ist nicht beabsichtig aber ich habe mal wieder eine Frage und würde
gerne
wissen ob jemand eine Idee hat wie man es auch anders lösen könnte.
Speziell geht es nun darum das ich wenn ich von den MainManu "Options"
in das ein Submenü gesprungen bin - Gar nicht weiß in welche Submenü
Struktur ich anzeigen soll. Gelöst habe ich das mit einen switch case,
da man Anhand der letzten Menüposition schließen kann welches Submenü
angezeigt werden soll.
Das Funktioniert zwar, gefällt mir aber nicht ganz so richtig da ich für
jedes Submenü einen neuen "case" brauche. Es mag zwar sich hier um nur 3
Einträge handeln, aber in wirklichkeit kommen da noch eine Handvoll
Einträge hinzu.
Nachtaktiver schrieb:> Ist nicht beabsichtig aber ich habe mal wieder eine Frage und würde> gerne> wissen ob jemand eine Idee hat wie man es auch anders lösen könnte.>> Speziell geht es nun darum das ich wenn ich von den MainManu "Options"> in das ein Submenü gesprungen bin - Gar nicht weiß in welche Submenü> Struktur ich anzeigen soll. Gelöst habe ich das mit einen switch case,> da man Anhand der letzten Menüposition schließen kann welches Submenü> angezeigt werden soll.
Nun ja,
Ich frage mich schon, warum ich mir den Mund fusselig rede, äh die
Finger wund tippe, um dir zu zeigen, dass der Schlüssel zum Ganzen darin
liegt, dass man sich Strukturen aufbaut, in denen die kompletten
Steuerdaten für das Menü abgelegt werden und die eigentlich
ausprogrammierten Menüfunktionen von diesen Steuerdaten getrieben
werden, wenn du es dann doch
1
>volatileunsignedcharSELECTED_MAIN_MENU;
2
>
3
>#defineNo_Menu0
4
>#defineChangeOptions_Menu1
5
>#defineChangeVoltage_Menu2
6
>#defineChanceCurrent_Menu3
7
>#defineSwitchKeylock_Menu4
8
>
9
>volatileunsignedcharACTIVE_MENU;
10
>
11
>#defineNoMenu0
12
>#defineMainMenu1
13
>#defineSubMenu2
14
>
15
>volatileunsignedcharACTIVE_SUBMENU;
16
>
17
>#defineBacklight_SubMenu0
18
>#defineLcdContrast_SubMenu1
19
>#defineTemperature_SubMenu2
20
>
21
>volatileunsignedcharMENU_POSITION;
22
>volatileunsignedcharSUBMENU_POSITION;
alles wieder anders machst und den Vorschlag nicht aufgreifst, die
Vorteile davon aber nutzen willst.
Entweder du programmierst ein Menüsystem in allen Zweigen komplett aus
oder du überlegst dir welche Progammteile ja eigentlich, von Daten
getrieben, einen immer gleichen Universalmechanismus ansteuern können.
Dein Menüsystem könnte wie der Mechanismus in einer Drehorgel sein.
Immer gleich. Was diese Drehorgel abspielt wird durch die Papierstreifen
mit den Löchern an den richtigen Stellen geregelt.
Der Papierstreifen mit den Löchern: Das sind die Datenstrukturen, die
den kompletten Menüaufbau in allen Einzelheiten beschreiben.
Und der Mechanismus der den Streifen abtastet: Das ist die Menüfunktion,
die sich aus der Beschreibung in den Daten die für sie relevanten
Funktionen herauszieht und entsprechend reagiert. Genauso wie eine
Drehorgel genau dann ein C abspielt, wenn das entsprechende Loch im
Papierstreifen gestanzt ist.
Willst du ein anderes Musikstück, musst du einen anderen Papierstreifen
einlegen aber nicht den Mechanismus ändern.
Willst du ein anderes Menü, musst du die Daten in den Menübeschreibungen
ändern, aber nicht die Menüfunktionen.
Dann muss ich ganz ehrlich zu geben das es mir einfach nicht einleuchten
will wie ich das so hinbekomme.
Also nochmal zurück auf Los und ziehe keine 2000€ ein. Gedankensotieren:
Ich habe bis jetzt eine Struktur für das Hauptmenü in der:
- Alle Unterpunkte
- Nummerierung
- Umfang des Submenüs
drin steht. Anhand der Position im Hauptmenü stelle ich fest welches
Element ich des Hauptmenü ich anzeigen. Da die Größe des Hauptmenüs
bekannt ist, kann ich das ganze so Steuern das ich in einen
Ringdurchlauf die Hauptmenü Unterpunkte blättern kann. Soweit ist ja
noch alles richtig.
Nun wurde ein Tastendruck ausgelöst und ich möchte in einen Unterpunkt
des Hauptmenüs einspringen. Und nun kommen die ersten Probleme.
Neues Problem:
-> Ich weiß nicht wie ich nun Unterscheide zwischen Haupt<->Untermenü:
Meine Lösung: Switch Case
Dadurch wird eine völlig neuer Mechanismus aufbaut, und nicht der
Papierstreifen gewechselt. Dadurch muss man eine komplette
Tasterauswertung neuschreiben. Ich führe einen neuen Zähler ein mit den
ich durch das Submenüblättern. Die Anzahl der Elemente aus den Submenüs
kann ich aus den Hauptmenüzähler aus der Hauptmenüstruktur entnehmen.
Neues Problem:
-> Ich weiß nicht wie ich nun unterscheide in welcher Eintrag
ausgewertet wurde.
Meine Lösung: Noch ein Switch Case
Anhand des Submenü Zählers entnehme die Position an der ich mich
befinde.
In eines Switch Case Werte ich das aus und Löse die entsprechende Aktion
beim Tastendruck aus.
Fazit:
Scheinbar überflüssig.
Ich muss ganz ehrlich zugeben das ich es einfach nicht verstehe wie man
es anders machen kann. Vorallen weil das Menü ja Funktioniert. Ich kann
im Options Menü rumblättern, einen Punkt anschauen und die
Hintergrundbeleuchtung An/Aus schalten, oder kann mir die Aktuelle
Temperatur ansehen. Ich will das irgendwie nicht verstehen.
Nochmal von vorne:
Anhand der Haupmenü Position weiß ich ob ich mich in Backlight,
LCD-Contrast befinde.
Anhand der Submenü Position weiß ich ob ich mich bei An oder Aus
befinde.
Soll die Lösung eine weitere Struktur sein, oder eher ein 2Dimensionales
Array anlege, in der ich mit den Hauptmenü Index ausmache welche
Funktion aufgerufen werden soll, und mit der Submenüposition den
entsprechenden Operanten bestimme?
Irgendwie drehe ich mich im Kreis. :(
Hallo nochmal.
Ich habe in den letzten Tagen verucht mit den den Vorschlägen von
Karl-Heinz ein MenuSystem zu entwerfen. Den Vorschlag von Tom B. habe
ich auch versucht zu berücksichtigen und alles was viel Speicher braucht
ins Flash zu schreiben.
Leider ist mein C etwas eingerostet und ich musste die ganze Zeit an
Klassen denken.
Drei TastenZustände werden abgefrage:
1 Taste1 kurz letzter MenuPunkt
2 Taste2 kurz nächster MenuPunkt
3 Taste3 lang MenuPunkt auswählen.
Für die einzelnen Menus sind die Einträge in Arrays zusammengefasst. Die
Arrays werden dann in struct Menu mit der Anzahl der Einträge und dem
aktuell ausgewählten Eintrag verknüpft. Alle menus bilden wieder ein
array und die Enumeration aktMenu beinhaltet, welches Menu gerade aktiv.
Könnt ihr, bitte, mal drüber schauen, ob man das so machen kann?!
Gibt es eine bessere Lösung für "menuStart()" & "menuEnd()"?
Ist es besser ein Menu ringsrum laufen zu lassen oder nur bis zum
Anfang/Ende?
Könnt ihr, bitte, mal drüber sehen, ob das so geht?!
Hallo,
ich bin jetzt auch dabei eine Menüsteuerung zu realisieren. Ich wollte
eigentlich die oben geschilderte Idee von Karl-Heinz aufgreifen.
Gestalten wollte ich das dann noch so, das Menü - Text und Funktions -
Pointer im Flash liegen. Aber das ist erstmal eine andere Sache.
Allerdings habe ich eine Frage, die vielleicht Karl-Heinz am Besten
beantworten kann:
Mit der oben aufgezeigten Menüsteuerung hänge ich dann ja immer in dem
UP Do_Menu fest, oder sehe ich das falsch. Und wenn ich in ein Untermenü
wechsle, dann hänge ich in der Do_Menu - Routine des UP's fest, oder?
Denn mir ist es wichtig, das die Menü - Programme universell sind. Das
heißt für mich auch automatisch mit, das ich das Menü - Programm (was ja
an sich nichts zeitkritisches ist) einfach in main() mit aufrufen kann.
Also ich möchte mit der Menüsteuerung nicht den µc tot legen, sondern ab
und zu noch was andres in main() machen können. Klappt das in dem oben
gezeigten Beispiel? Bis jetzt eher noch nicht, oder?
Karl Heinz Buchegger schrieb:> // soweit ist alles aufgesetzt, ab jetzt werden die Tasten überwacht> // und das Menü soll arbeiten> while( running )> {> if( get_keypress( UP ) ) // Taste "Rauf" gedrückt ?> {> if( active > 0 ) // geht da überhaupt was?> {> lcd_gotoxy( 0, active ); // ja, tut es. Das Sternchen wegnehmen> lcd_write( " " );>> active--;>> lcd_gotoxy( 0, active ); // und neu hinsetzen, in die Zeile davor> lcd_write( "*" );> }> }>> else if( get_keypress( DOWN ) ) // Taste "Runter" gedrückt?> {> if( active < MenuSize - 1 ) // geht das überhaupt> {> lcd_gotoxy( 0, active ); // ja, da gibt es noch einen Menupunkt> lcd_write( " " );>> active++;>> lcd_gotoxy( 0, active );> lcd_write( "*" );> }> }>> else if( get_keypress( EXECUTE ) ) // Taste "Ausführen" gedrückt?> {> if( Menu[active].Function ) // gibt es eine auszuführende Funktion?> Menu[active].Function(); // Ja: führe sie aus> else> running = FALSE; // Nein: Das war der "Exit" Menüpunkt> // Menüsteuerung beenden> }> }
Ich habe gerade nochmal nachgeschaut:
theoretisch könnte man das festhängen einfach umgehen, wenn man die
erste while() einfach rausnimmt. Dann brüchte man natürlich eine
separate menu_init().
und statt der while einfach rein
1
if(running==FALSE)
2
...
Nur bin ich mir dann noch nicht sicher, wie man dafür sorgt, das beim
nächsten Menüaufruf dann wieder in die obere Menü-Ebene gewechselt wird.
Einfach dem Menüaufruf als nächstes wieder den Pointer / das Array für
die obere Menübene übergeben?
Martin K. schrieb:> Ich habe gerade nochmal nachgeschaut:
Mit der Nebenbedingung
> Das heißt für mich auch automatisch mit, das ich das Menü - Programm> (was ja an sich nichts zeitkritisches ist) einfach in main() mit> aufrufen kan
bedeutet das, dass diese Variante nicht benutzt werden kann.
Aber ich hab ja (nach einem kurzen Überfliegen des Beitrags) ja auch
noch andere Varianten angesprochen.
Du brauchst eine Lösung, bei der ein Menü nicht durch eine Funktion
realisiert ist, sondern ein datengetriebenes Menü, bei dem die komplette
Menüinformation in den Datenstrukturen steckt, so dass eine Funktion
unter Auswertung dieser Datenstruktur und durch Hinzunahme eines
möglichen Tastendrucks entscheiden kann, was zu tun ist. Und zwar in
allen Einzelheiten. Danach gibt diese Funktion die Kontrolle wieder ab
und beim nächsten Aufruf entscheidet sie wieder, wie ein möglicher
Tastendruck durch die in den Strukturen enthaltenen Informationen
auszuwerten ist.
Das ganze geht schon viel mehr in Richtung Zustandsautomat (wobei der
komplette Automat durch Daten beschrieben wird) als in irgend eine
andere Richtung.
>So einer Taste kann man verschiedene Zustände entlocken.
Taste kurz gedrückt, Taste lange gedrückt oder Taste sehr lange
gedrückt.
Ist leider Muell, das ist nicht intuitiv. Nach zehn Jahren weiss niemand
mehr wie's geht, resp weshalb es nicht geht. Lieber ein paar Tasten
mehr...
Ok,
ich bin jetzt in Richtung Zustandsautomat gegangen. Der AVR - Butterfly
Code eignet sich ja wirklich bestens dafür. Und ich bin erstaunt, wie
einfach da das Prinzip eigentlich ist. Alleine wäre ich da zwar nicht
darauf gekommen, aber wie gesagt, das Grundprinzip ist ja äußerst
simpel. Totzdem danke ich euch.
P.S. mal sehen, vllt. stelle ich wenn ich mit dem Butterfly das hab mal
was in die Codesammlung, nur die Sache was das Menü betrifft. Da gehört
ja nicht allzuviel dazu.
Martin K. schrieb:>> P.S. mal sehen, vllt. stelle ich wenn ich mit dem Butterfly das hab mal> was in die Codesammlung, nur die Sache was das Menü betrifft. Da gehört> ja nicht allzuviel dazu.
Hi,
das wäre ne super sache. ich versuch mich auch daran, aber ich hab
einfach nicht die zeit da richtig dran zu bleiben - leider...
Markus