Hi Experten,
gleich die nächste Frage.
Ich möchte ein variable Menustruktur im FLASH ablegen und habe Probleme
mit dem richtigen Design, Typdeklarationen etc. pp.
Da ich noch nicht so fit bin hier mal das was ich mir am liebsten
"wünschen" würde:
Ich weis das das so nicht geht, wäre aber ideal. Wichtig ist aber auch
das die Captions im FLASH abgelegt sind, bzw. die ganze Struktur menu im
FLASH landet.
Bisher meine ich das ich für die Strings globale Konstanten einzeln
anlegen muß. Das gleiche gilt für die Strukturen der Submenus. Das führt
aber dazu das die ganze Sache ziemlich unübersichtlich wird und auf
Grund der fehlenden forward Deklarationsmöglichkeit ich diese
Deklartation quasi in umgekehrter Reihenfolge durchführen muß. Das sind
alles Punkte die finally die Deklaraion einer solchen Struktur
unübersichtlich werden lassen, und das möchte ich im Grunde nicht.
Gruß hagen
Deinen Text kann man schwer lesen, weil das so ein unnötiger Mix aus
Deutsch und Englisch ist. Arbeite bitte daran.
Hier war mal ein Beitrag dazu:
Beitrag "LCD Menü"
Der hat mir damals gut geholfen.
>>Deinen Text kann man schwer lesen, weil das so ein unnötiger Mix>>aus Deutsch und Englisch ist.
hm, du bist der Erste der mir das so sagt.
>> Arbeite bitte daran.
Ja werde ich Chef, nach den Erfordernisse im Programmiereralltag, wenn
es dir Recht ist. Und da ist es eben so das man Englisch spricht ;)
Nichts für ungut. Deinen querverwiesenen Strang hatte ich schon
durchgearbeitet, eigentlich hatte ich alles im Forum gelesen was mit
Speisekarten zu tuen hat bevor ich diesen Faden hier im Forum geöffnet
habe.
...
Nachdem ich nun alles an Docus, speziell zu GCC durchgearbeitet habe,
entscheide ich mich für folgende Lösung:
1
typedefint(*menuFunc_t)(intmsg,intparam);
2
typedefprog_charmenuCap_t;
3
4
typedefstruct{
5
constmenuCap_t*caption;
6
constmenuFunc_tfunc;
7
constprog_uint8_tparent;
8
constprog_uint8_tsubmenu;
9
}menuDef_tPROGMEM;
10
11
constmenuCap_tss[]="Hauptmenu";
12
13
constmenuCap_ts0[]="Metronom";
14
constmenuCap_ts1[]="Drumpad spielen";
15
constmenuCap_ts2[]="Instrumente einstellen";
16
constmenuCap_ts3[]="Konfiguration";
17
constmenuCap_ts4[]="Drumpad auschalten";
18
19
constmenuCap_ts00[]="Status";// aus/ein
20
constmenuCap_ts01[]="Beats per Minute";// 32-228
21
constmenuCap_ts02[]="Ticks per Beat";// 1-8
22
constmenuCap_ts03[]="Lautstärke";// 1-100%
23
constmenuCap_ts04[]="zurück zum Hauptmenu";
24
25
...
26
...
27
28
constmenuDef_tmenu[]={
29
{ss,fdef,-1,2},// 0
30
{NULL,fplay,-1,-1},// 1
31
32
{s0,fdef,0,7},// 2
33
{s1,fdef,0,1},// 3
34
{s2,fdef,0,11},// 4
35
{s3,fdef,0,16},// 5
36
{s4,foff,0,18},// 6
37
38
{s00,f00,2,-1},// 7
39
{s01,f01,2,-1},// 8
40
{s02,f02,2,-1},// 9
41
{s03,f03,2,-1},// 10
42
{s04,fback,2,-1}// 11
43
44
...
45
...
46
47
};
Also Titelzeile (Caption) zum Menu, Nachrichtenfunktion (Messageproc),
übergeordnetes Menu (Parent) und untergeorndeter Menubaum (Submenu).
Statt für Parent und Submenu die Zeiger (Pointer) in der Konstanten Menu
zu speichern benutze ich die Indizes in dieses Array.
Das ist die kompakteste Speicherstruktur die nur 6 Bytes pro Menueintrag
benötigt und denoch alle relevanten Operationen ermöglicht, sprich
Navigation innerhalb dieses Baumes -> Submenu öffnen, alle Childs zum
Submenu finden, Nachrichten speziell pro Menu abarbeiten und wieder
zurück zum übergeordneten Menu wechseln.
Großer Nachteil ist eben der Fakt das man beim späteren Einfügen eines
neuen Menueintrages alles manuell nacharbeiten muß und nicht wie erhofft
der Compiler einem die Arbeit dabei abnimmt. Allerdings, ich sage mal,
wie oft kommt es vor das ich so was nochmal benötige und daran ständig
rumbauen muß ? ;)
Gruß Hagen
> Allerdings, ich sage mal, wie oft kommt es vor das ich so was> nochmal benötige und daran ständig rumbauen muß ? ;)
Sag das nicht.
Ich bau meine Menüs meistens so auf
typedef void (*MenuFnct)( void );
struct MenuItem {
const char* Text;
MenuFnct* Fnct;
};
struct Menue {
const char* Caption;
int NrItems;
MenueItem[] Items;
};
void StartMetronom()
{
}
void EndMetronom()
{
}
void MetronomMen()
{
struct Menue menue = {
"Metronom",
4,
{ "Einstellen", ConfigMetronom },
{ "Starten", StartMetronom },
{ "Stoppen", StopMetronom },
{ "Zurück", NULL }
}
while( HandleMenu( menue ) )
;
}
void InstrumenteMen()
{
struct Menue menue = {
"Instrumente einstellen",
3,
{ "Auswählen", SelectInstrument },
{ "Was weis ich", NochWas },
{ "Zurück", NULL }
}
while( HandleMenu( menue ) )
;
}
int main()
{
struct Menu menue = {
"Hauptmenü",
2,
{ "Metronom", MetronomMen },
{ "Instrumente", InstrumentMen },
{ "Konfiguration", KonfigurationMen }
};
while( 1 )
HandleMenu( menue );
}
Also: Im Menue gibt es eine Auflistung aller Menüpunkte.
Bei jedem Menüpunkt steht dabei, welche Funktion für
die Bearbeitung dieses Menüpunktes zuständig ist. So
eine Funktion kann ihrerseits allerdings wieder ein
Menü enthalten, dass mittels HandleMenü angezeigt
und bearbeitet wird.
HandleMenu macht nichts anderes als die Menüpunkte
anzuzeigen und Tasten auszuwerten. Wird ein Punkt
ausgewählt, so wird die entsprechende Funktion aufgerufen.
Kommt die Funktion zurück, so wird das Menü entsprechend
neu aufgebaut (die Funktion könnte ja ihrerseits ein
Menü hingepinselt haben, oder sonst irgendwie das
Dispaly zerstört haben) und weiter gehts. Ist der
Funktionspointer NULL, so wird nichts aufgerufen und
HandleMenu gibt einfach die Kontrolle an den Aufrufer
zurück.
HandleMenu kümmert sich also gar nicht um SubMenues.
Wozu auch? Wenn eine Funktion ein SubMenü braucht,
dann soll sie gefälligst eines vereinbaren und
seinerseits HandleMenu aufrufen.
Die Menüs müssen natürlich nicht in den Funktionen
definiert werden, sondern können auch global sein.
Ich hab sie nur deswegen Funktionslokal gemacht, damit
man sieht, wie Sub-Menues im Grunde durch Aufruf von
Funktionen realisiert sind.
@Karl Heinz:
Hm, das ist garnicht so schlecht. Ich wollte zwar eine zu starke
Verknüpfung von Deklaration eines Menus zu dessen Implementation
(Callbacks) verhindern, aber im Grunde ist das ja sowieso unrealistisch.
Mein Gedanke war es eben das viele der Callbacks zu einem MenuItem eh
immer eine Standard-Funktion aufrufen. Diese dient quasi als
DefaultHandler und erledigt alle Standardreaktionen, wie Zeichnen, Keys,
Messages, Timers etc.pp.
Die eigentlichen Spezial-Funktionen konzentrieren sich dann nur noch auf
die Unterschiede und reichen die meisten Nachrichten an den
DefaultHandler weiter. Quasi ein ganz ganz primitives OOP
(objektorientiert) Design.
Aber der wichtigste Punkt deines Vorschlages ist das quasi ein Kette von
verlinkten Callbacks zur Laufzeit enstehen würde, denn ich werde vom
Hauptmenu-Callback zum nächsten SubMenu->Callback usw. usw. die
Nachrichten durchreichen. Das hat den Vorteil das quasi alle
übergeordneten Menus immer noch die Benachrichtigungskette
abfangen/manipulieren können. Desweiten kann ich so lokal benötigte
Variablen in einem SubMenu auf dem Stack ablegen statt sie entweder fix
global zu deklarieren oder per malloc() arbeiten zu müssen.
Ich werde nach deiner Methode vorgehen allerdings mit dem Unterschied
das je nach geöffneter Menu-Tiefe der MenuHandler von innen nach aussen
die Nachrichten weitereicht -> quasi in umgedrehter Reihenfolge wie die
Menus geöffnet wurden. Die ganze Kette der so verknüpften MenuHandler
bleibt so lange bestehen wie die SubMenus geöffnet wurden. Die äußerste
MenuHandler Funktion kehrt quasi garnicht mehr zum Aufrufer zurück, bzw.
erst wenn das Drumpad ausgeschaltet wird. Der Ontop MenuHandler -> also
der innerste -> der zum aktuellesten Menu, wird auch das
Polling/Auswertung von Tasten etc. übernehmen.
Ich kann das so machen da in meinem Projekt alles in ISRs abläuft und
ansonsten nur das Menu offen ist.
Gute Idee dein Vorschlag, das ist viel besser. Man kann so sogar die
angezeigten SubMenus je nach Status dynamisch verändern.
Gruß Hagen
Hi Leute,
also ich bin wirklich am verzweifeln und komme nicht weiter.
Nachfolgendes Skeleton
1
#define MM_PLAY 0xFFF7
2
#define MM_EXIT 0xFFF9
3
#define MM_NEXT 0xFFFB
4
#define MM_PRIOR 0xFFFD
5
#define MM_SELECT 0xFFFF
6
7
typedefuint16_t(*menuFunc_t)(void);
8
typedefconstPROGMEMchar*menuCap_t;
9
10
typedefstruct{
11
menuCap_tcaption;
12
menuFunc_tfunc;
13
}menuSub_t;
14
15
typedefstruct{
16
menuCap_tcaption;
17
glcdColor_tcolors[4];
18
menuSub_tmenus[];
19
}menuDef_t;
20
21
22
uint16_tmenuProcess(constmenuDef_tmenu){
23
24
return(MM_EXIT);
25
}
26
27
uint16_tmenuMetronom(void){
28
29
return(MM_EXIT);
30
}
31
32
uint16_tmenuInstrumente(void){
33
34
return(MM_EXIT);
35
}
36
37
uint16_tmenuKonfiguration(void){
38
39
return(MM_EXIT);
40
}
41
42
uint16_tmenuMain(void){
43
44
asm("nop");
45
46
menuCap_tmcMain=PSTR("Hauptmenü");
47
menuCap_tmcPlay=PSTR("Drum spielen");
48
menuCap_tmcMetro=PSTR("Metronom einstellen");
49
menuCap_tmcInstr=PSTR("Instrumente einstellen");
50
menuCap_tmcKonf=PSTR("Drum konfigureren");
51
menuCap_tmcOff=PSTR("Gerät ausschalten");
52
53
menuDef_tmmMain={mcMain,{RED,WHITE,BLUE,BLACK},
54
{{mcPlay,MM_PLAY},
55
{mcMetro,menuMetronom},
56
{mcInstr,menuInstrumente},
57
{mcKonf,menuKofiguration},
58
{mcOff,MM_EXIT}
59
{0,0}}};
60
61
return(menuProcess(mmMain));
62
}
in der Funktion menuMain() möchte ich nun eine konstante und im FLASH
gespeicherte Struktur mmMain anlegen. Egal was ich mache ich bekomme
immer wieder Fehlermeldungen oder Warnungen das ein impliziter Typcast
durchgeführt wurde, das eine "non-static initialization of flexible
Member" vorliegt, "near initialization for mmMain" etc.pp.
Mein Ziel ist es das ich die TypeDef's oben so deklariere das später bei
der realen Definition einer Menustruktur diese immer im FLASH als
Konstante gespeichert wird.
Gruß Hagen
Noch ein paar Worte zum Stil:
uint16_t menuMetronom(void) {
Das void in der Argumentliste ist überflüssig.
Das ist kein Prototyp.
return(MM_EXIT);
return ist kein Funktionsaufruf. Die Klammern können
weg.
return MM_EXIT;
Sieht doch gleich besser aus :-)
Hallo, ich verfolge diesen Thread mit grossen Interesse, aber ich den
Unteschied zwischen deinem (Karl Heinz B.) und Hagens nicht. Koenntest
du mich kurz aufklaeren? Ich seh den Fehler somit nicht :(
Schau dir die Texte an.
1) raus aus der Funktion und global definieren
2) Texte nicht mittels Pointer allokieren, sondern
in Arrays.
3) Die Menüstruktur ebenfalls per PROGMEM als globale
Variable anlegen.
Alternativ könnte auch gehen (hab ich jetzt nicht probiert)
@Karl Heinz:
soweit bzw. ähnlich habe ich auch schon eine Lösung gefunden. Nun machen
wir es mal komplizierter ;)
Wie sähe das ganze aus wenn man die Deklaration der Menu Strukturen
_nicht_ global sondern lokal innerhalb einer Funktion machen möchte ??
Zu den anderen Bemerkungen von dir:
- return haste Recht
>>Das void in der Argumentliste ist überflüssig.>>Das ist kein Prototyp.
Hm ich mache immer ein void da rein, liegt aber daran das ich aus der
PASCAL/COBOL/PL4 Schiene komme und dort das ganze Typhandling/Variablen
etc.pp. viel strikter sind. Ist also eine "Gewohnheit" und ich empfinde
das irgendwie als sauber.
Das ist ja auch der Grund warum ich nicht damit zurecht komme wenn man
in C hunderte Möglichkeiten habe einen Zeiger zu deklarieren. Wenn ich
in PASCAL irgendwas als CONST deklariere dann ist das Ganze auch
wirklich CONST und nicht wie im C das ein Zeiger als Member die Sache
wieder nicht-CONST macht.
Egal, die wichtigste Frage ist also:
Wie deklariere ich so eine Struktur im FLASH lokal innerhalb einer
Funktion ?
Das ist jetzt aber nur aus reinem Interesse und dem Bedürfnis den
Zugriff auf diese Deklarationen stärker zu kapseln als wenn sie global
verfügbar sind. Also eine ästetische Sache für mich ;)
Gruß Hagen
PS: nachfolgend das was ich bisher selber ausgewurschtelt hatte
1
typedefuint8_t(*menuFunc_t)(void);
2
typedefprog_charmenuCap_t;
3
4
typedefstruct{
5
constmenuCap_t*caption;
6
constmenuFunc_tfunc;
7
}menuSub_tPROGMEM;
8
9
typedefstruct{
10
constmenuCap_t*caption;
11
constglcdColor_tcolors[4];
12
constuint8_tcount;
13
constmenuSub_tmenus[];
14
}menuDef_tPROGMEM;
15
16
17
#define MM_PLAY 0
18
#define MM_OFF 1
19
#define MM_BACK 2
20
#define MM_NONE 3
21
#define MM_NEXT 4
22
#define MM_PRIOR 5
23
#define MM_SELECT 6
24
#define MM_FUNC(value) ((menuFunc_t)value)
25
26
27
menuCap_tmcMain[]="Hauptmenü";
28
menuCap_tmcPlay[]="Drum spielen";
29
menuCap_tmcMetro[]="Metronom einstellen";
30
menuCap_tmcInstr[]="Instrumente einstellen";
31
menuCap_tmcKonf[]="Drum konfigureren";
32
menuCap_tmcOff[]="Gerät ausschalten";
33
34
menuDef_tmmMain={mcMain,{RED,WHITE,BLUE,BLACK},5,
35
{{mcPlay,MM_FUNC(MM_PLAY)},
36
{mcMetro,mfMetronom},
37
{mcInstr,mfInstrumente},
38
{mcKonf,mfKonfiguration},
39
{mcOff,MM_FUNC(MM_OFF)}}};
das const der Members in den Structs habe ich reingemacht um die blöden
Warnings bei der Deklaration von mmMain zu vermeiden.
>Wie sähe das ganze aus wenn man die Deklaration der Menu Strukturen>__nicht__ global sondern lokal innerhalb einer Funktion machen möchte ??
Was soll dir das bringen?
(nur interessehalber)
Es landet sowieso irgendwo im FLASH.
Stärkere Restriktion bei den Zugriffsmöglichkeiten.
Auf eine globale Konstante kann jede Funktion zugreifen und deren
deklarierte Variablen/Konstanten Namen sind global sichtbar.
In einer lokalen Funktion gilt alles auch nur lokal und somit ist die
Scope dieser Strukturen restriktiver.
Ist einfach eine "Gewöhnungssache" die ich bei meiner professionellen
Arbeit in anderen Sprachen so benutze. Einfach um sicheren Source zu
benutzen. Was nicht global deklariert wurde kann auch nicht global
"mißbraucht" werden. Was nur lokal gültig sein kann sollte auch nur
lokal als gültig deklariert werden.
Wenn's jetzt mit GCC nicht geht kann ich durchaus damit leben, besonders
wenn ich davon ausgehen muß das ich noch Tage nach einer Lösung suchen
müsste. Schöner wäre es aber, und rein aus Interesse wüsste ich gern ob
und wie es geht ;) (man lernt ja nie aus).
Gruß Hagen