Hallo Jörg
Wunderbares Menüdesign.
Das mit dem utility folder ist doof. Danke für den Hinweis. Ich dachte ich hätte das mittlerweile behoben.
Aber lass uns loslegen. Am besten nimmst du eines der funktionierenden Beispiele. Dort findest Du irgendwo diese Zeile:
M2tk m2(&el_top, m2_es_arduino, m2_eh_4bs, m2_gh_lc);
Diese Zeile initalisiert die library und stellt auch gleich das erste Menü dar (Erster Parameter: el_top).
Der zweite Parameter "m2_es_arduino", gibt an woher m2tklib seine daten bekommt (also die angeschlossenen Taster).
Der dritte Parameter "m2_eh_4bs" gibt an, wie viele Taster bedient werden sollen und nach welchem Konzept M2tklib diese Tasten verarbeiten soll. Ist nicht weiter wichtig, ich denke m2_eh_4bs ist genau der richtige für deine drei Tasten.
Der vierte Parameter "m2_gh_lc" ist der graphic handler für die LiquidCrystal library, das wird sich auch nicht ändern.
Auf den setup() und loop() gehe ich erst mal nicht ein, das sollte ungefähr so aussehen wie hier (wobei die 2,3 &4 die Arduino pins sind, an denen du die Taster angeschlossen hast... muss ggf angepasst werden):
void setup() {
m2_SetLiquidCrystal(&lcd, 20, 4);
m2.setPin(M2_KEY_SELECT, 2);
m2.setPin(M2_KEY_NEXT, 3);
m2.setPin(M2_KEY_PREV, 4);
}
void loop() {
m2.checkKey();
if ( m2.handleKey() )
m2.draw();
m2.checkKey();
}
Jetzt zum eigentlichen Menü. In m2tklib kann es beliebig viele Menüs geben, aber nur eines ist immer aktiv (logisch..).
Man kann jederzeit ein anderes Menü darstellen. Dazu benutzt man entweder die m2.setRoot() funktion oder die aktive Schaltfläche M2_ROOT(). Dazu später mehr. Ausserdem wird durch den M2tk initalisierung (siehe oben) auch gleich das erste aktive Menü festgelegt.
Wie aber sieht ein Menü aus? Es wird aus sogenannten Elementen zusammengesetzt. Diese Elemente beginnen alle mit "M2_". Die Beschreibung aller Elemente gibt es hier:
https://code.google.com/p/m2tklib/wiki/elref
Es gibt aktive Elemente (Buttons, Zahleneingabe, Texteingabe), passive Elemente (Labels, Bitmaps) und sogenannte Kontainerelemente, die wiederum andere (atomare) Elemente beinhalten können.
Schauen wir uns mal das Untermenü von Dir an. Es könnte wie folgt aussehen
M2_BUTTON(el_svo_um_pos1, NULL, "Servostellung 1", &fn_svo_pos1);
M2_BUTTON(el_svo_um_pos2, NULL, "Servostellung 2", &fn_svo_pos2);
M2_BUTTON(el_svo_um_pos3, NULL, "Servostellung 3", &fn_svo_pos3);
M2_BUTTON(el_svo_um_pos4, NULL, "Servostellung 4", &fn_svo_pos4);
M2_LIST(list_svo_um) = { &el_svo_um_pos1, &el_svo_um_pos2, &el_svo_um_pos3, &el_svo_um_pos4};
M2_VLIST(top_el_svo_um, NULL, list_svo_um);
M2_BUTTON ist ein atomares aktives Element, das eine Unterfunktion aufruft, wenn es angeklickt wird.
Erstes Argument ist der Name des Elements
Zweites Argument ist der sogenannte Format String, der hier nicht verwendet wird, deshalb steht hier NULL.
Drittes Argument ist der String, der angezeigt werden soll
Viertes Argument ist der Name der Funktion die aufgerufen werden soll (siehe unten).
Jetzt müssen diese vier Buttons zu einem Menü zusammengeschnürt werden. Das erledigt ein Kontainer-Element (M2_VLIST). Kontainer-Elemente benötigen meistens noch ein Hilfskonstrukt, nämlich eine Liste mit Elementen. Das macht M2_LIST(). M2_LIST und M2_VLIST gehören eigentlich immer zusammen.
Der Kontainer heisst übrigens VLIST, weil die Elemente in der Liste vertikal (untereinander) angeordnet werden.
Jetzt fehlen noch die vier Funktionen für die M2_BUTTON Elemente. Die Namen sind ja schon angegeben (letztes Argument!), aber die Funktion fehlt noch. Die sehen beispielsweise so aus:
char *svo_pos_str = "<keine>";
int svo_pos = 0;
void fn_svo_pos1(m2_el_fnarg_p fnarg) {
svo_pos_str = "Servostellung 1";
svo_pos = 1;
m2_SetRoot(&top_el_svo_um);
}
Es handelt sich um eine Funktion ohne Rückgabewert und mit einem seltsamen Argument (fnarg), das wir aber einfach als gegeben hinnehmen und nicht weiter betrachten.
Ich habe noch zwei globale Variablen eingeführt, in denen die Auswahl gespeichert wird. Am Ende der Funktion wird noch ein neues Menü aktiviert (bzw hier in das selbe Menü zurückgesprungen). Allerdings haben wir hier ein C/C++ Problem: Möglicherweise steht die Funktion im Quelltext VOR der eigentlichen Definition des Menüs, d.h. der Menü-Name "top_el_svo_um" ist dem Compiler zu diesem Zeitpunkt noch gar nicht bekannt. Die Lösung besteht darin eine Vorwärtsdeklaration zu machen (hier M2_EXTERN_VLIST).
Der vollständige Code sieht dann etwa so aus:
char *svo_pos_str = "<keine>";
int svo_pos = 0;
/* svo_hm: Servo Untermenue */
M2_EXTERN_VLIST(top_el_svo_um);
void fn_svo_pos1(m2_el_fnarg_p fnarg) {
svo_pos_str = "Servostellung 1";
svo_pos = 1;
m2_SetRoot(&top_el_svo_um);
}
void fn_svo_pos2(m2_el_fnarg_p fnarg) {
svo_pos_str = "Servostellung 2";
svo_pos = 2;
m2_SetRoot(&top_el_svo_um);
}
void fn_svo_pos3(m2_el_fnarg_p fnarg) {
svo_pos_str = "Servostellung 3";
svo_pos = 3;
m2_SetRoot(&top_el_svo_um);
}
void fn_svo_pos4(m2_el_fnarg_p fnarg) {
svo_pos_str = "Servostellung 4";
svo_pos = 4;
m2_SetRoot(&top_el_svo_um);
}
M2_BUTTON(el_svo_um_pos1, NULL, "Servostellung 1", &fn_svo_pos1);
M2_BUTTON(el_svo_um_pos2, NULL, "Servostellung 2", &fn_svo_pos2);
M2_BUTTON(el_svo_um_pos3, NULL, "Servostellung 3", &fn_svo_pos3);
M2_BUTTON(el_svo_um_pos4, NULL, "Servostellung 4", &fn_svo_pos4);
M2_LIST(list_svo_um) = { &el_svo_um_pos1, &el_svo_um_pos2, &el_svo_um_pos3, &el_svo_um_pos4};
M2_VLIST(top_el_svo_um, NULL, list_svo_um);
Damit würde ich jetzt erst mal Schluss machen und Dir ein paar Hausaufgaben mitgeben:
Nehme eines der Beispiele aus m2tklib (z.B. Combo) und ersetze das Menü dort durch das hier diskutierte Menü. Denke daran, dass Du auch das erste Argument des Construktors (M2tk m2) ändern musst.
Spiele ein bischen herum. Du solltest den Cursor über die vier Menüs lenken können. Wenn Du einen Menü-Eintrag aktivierst, wird das gleiche Menü wieder aufgerufen (der Cursor springt wieder an die oberste Stelle).
Wenn das klappt, machen wir mit dem Hauptmenü weiter.
Sage mir Bescheid, wenn ich zu langsam bin, zu viel erzähle oder wenn Du sonst noch Fragen hast.
Bis dann,
Oliver