Hallo, ich benutze WINAVR4 / PN2 und bin dran ein Menü mit verschiedenen Untermenüs zur Darstellung auf einem LCD zu schreiben. Dabei nutze ich, wie in vielen Beiträgen beschrieben structs, die im RAM liegen und Strings im Flash. Das Problem ist, dass es mir nicht gelingt der Variablen (act_menue) die das jeweils aktive Menü representiert die Daten zuzuweisen. Hier der code dazu: aus dem Header file: // Struct of Menue Entrys typedef struct MENU_ENTRY { const unsigned char *Menu_Name; //Name of Menu const unsigned char *Entry_Name; //Name of Entry INT Max_Ct; //Number of entrys struct MENU_ENTRY *Next_Menu; //Next Menu void ( *fp )(void); //Function of Entry } MENU_t; // Texte der Menüeinträge // Strings Mainmenue static const char menu_main_str[] PROGMEM = "Hauptmenue"; static const char menu_set_str[] PROGMEM = "Einstellungen"; static const char menu_par_str[] PROGMEM = "Parameter"; // Strings Settingsmenue static const char set1_str[] PROGMEM = "Set 1"; static const char set2_str[] PROGMEM = "Set 2"; // Strings Parametermenue static const char par1_str[] PROGMEM = "Par 1"; static const char par2_str[] PROGMEM = "Par 2"; und hier das Hauptfile: const MENU_t MENU_MAIN[]; MENU_t *act_menu[]; *act_menue = (MENU_t*) &MENU_MAIN; //erstmalige Zuweisung des gültigen Menüs // Definition der Menüs // Menü Einstellungen const MENU_t MENU_SET[] ={ { menu_set_str, set1_str, 1, NULL, func0 }, { menu_set_str, set2_str, 1, NULL, func1 } }; // Menü Parameter const MENU_t MENU_PAR[] ={ { menu_par_str, par1_str, 1, NULL, func2 }, { menu_par_str, par2_str, 1, NULL, func3 } }; // Hauptmenü const MENU_t MENU_MAIN[] ={ { menu_main_str, menu_set_str, 1, (MENU_t*)MENU_SET, NULL }, { menu_main_str, menu_par_str, 1, (MENU_t*)MENU_PAR, NULL } }; // Menüsteuerung void update_menue(void) { if( get_key_press( 1<<KEY_UP )) // Taste + gedrückt? { MENU_COUNT ++; } if( get_key_press( 1<<KEY_DWN )) // Taste - gedrückt? { MENU_COUNT --; } // Limitierung von "MENU_COUNT" if( MENU_COUNT > act_menue[MENU_COUNT].Max_Ct) MENU_COUNT = act_menue[MENU_COUNT].Max_Ct; if( MENU_COUNT < 0) MENU_COUNT = 0; if( get_key_press( 1<<KEY_ENT )) // Taste Enter gedrückt? { if( act_menue[MENU_COUNT].Next_Menu != NULL) { // Umschalten auf Submenü act_menue = act_menue[MENU_COUNT].Next_Menu; show_menue(); } else { //Aufruf der zugehörigen Funktion act_menue[MENU_COUNT].fp(); } } // Update der Anzeige bei neuem Stand von MENU_COUNT if( MENU_COUNT != MENU_COUNT_OLD ) { show_menue(); MENU_COUNT_OLD = MENU_COUNT; } } Im Programm soll dann bei Drücken der Entertaste das jeweils neue Menü oder die Funktion des Menüeintrages aufgerufen werden. Kann jemand helfen?? Gruß Daniel
Deine struct-Deklarationen sind falsch. Das typedef hat da nix verloren. Wenn Du struct MENU_ENTRY { //... } MENU_t; schreibst, dann ist MENU_t eine struct vom "Typ" MENU_ENTRY. MENU_t ist kein Strukturtyp, den Du für weitere Definitionen verwenden kannst, sondern eine Strukturvariable mit den Eigenschaften der zuvor deklarierten struct MENU_ENTRY! Also weiter unten z.B. struct MENU_ENTRY *act_menu[]; Damit wird ein Zeiger *act_menu auf ein Array von structs vom Typ MENU_ENTRY deklariert, wobei das auch so nicht funktioniert, da eine Array-Deklaration immer entweder eine Anzahl der Array-Elemente oder eine direkte Definition (also eine Aufzählung der Komponenten) benötigt. Also mindestens mal struct MENU_ENTRY *act_menu[ANZAHL]; oder so ähnlich. Abgesehen davon schreibst Du in der struct-Deklaration INT Max_Ct; Da C Case-sensitiv ist, klappt das so nicht. Anstelle von "INT" muss "int" da hin! Das Beste dürfte sein, Du schaust mal in Deinem C-Buch nach, wie Strukturen und Arrays deklariert bzw. definiert werden.
Die Strukturen sollten funktionieren, in einem kleineren Menü ohne Untermenüs gibt es keine Probleme und da arbeite ich mit einer ähnlichen Typendefinition. // Definition of Menu entrys static const char menu_str0[] PROGMEM = "Eintrag 1"; static const char menu_str1[] PROGMEM = "Eintrag 2"; static const char menu_str2[] PROGMEM = "Eintrag 3"; static const char menu_str3[] PROGMEM = "Eintrag 4"; typedef struct MENU { const unsigned char *text; INT next; void ( *fp )(void); } MENU_ENTRY; const MENU_ENTRY menue[] ={ { menu_str0, 0, func0 }, { menu_str1, 0, func1 }, { menu_str2, 0, func2 }, { menu_str3, 0, func3 } }; void update_menue(void) { if( get_key_press( 1<<KEY_UP )) { MENU_COUNT ++; } if( get_key_press( 1<<KEY_DWN )) { MENU_COUNT --; } if( MENU_COUNT > 3) MENU_COUNT = 3; if( MENU_COUNT < 0) MENU_COUNT = 0; if( get_key_press( 1<<KEY_ENT )) { //Aufruf der zugehörigen Funktion menue[MENU_COUNT].fp(); } if( MENU_COUNT != MENU_COUNT_OLD ) { lcd_clrscr(); lcd_gotoxy(1,0); strcpy_P(HelpStr, (menue[MENU_COUNT].text)); lcd_puts(HelpStr); MENU_COUNT_OLD = MENU_COUNT; } /*Da C Case-sensitiv ist, klappt das so nicht. Anstelle von "INT" muss "int" da hin!*/ Stimmt, habe ich vergessen zu schreiben, im Header wird INT als "int" definiert. Ich habe nur das Problem, dass ich mit act_menue nicht auf die verschiedenen Menüs zugreifen kann ohne Fehler aufzuwerfen.
Ah, jetzt verstehe ich auch, was das mit dem typedef sollte... Ist in meiner Erfahrung eher unüblich, das so zu schreiben, deshalb war ich drüber gestolpert.
Das erste was du mal tun solltest: Schmeiss mal alle Casts raus. Eigentlich solltest du nirgends einen Cast brauchen. Noch steig ich nicht ganz durch was du da eigentlich geschrieben hast und wie das alles zusammenhängen soll. Allerdings: MENU_t *act_menu[]; macht was anderes als das was du wahrscheinlich vorgehabt hast: act_menu ist ein Array von Pointern auf MENU_t Ich denke, du möchtest eigentlich haben, dass act_menu lediglich ein Pointer auf ein MENU_t (oder ein Array von MENU_t) darstellt. MENU_t *act_menu; Ich werd den Code noch weiter durchschauen und versuchen ihn zu verstehen.
Über act_menue möchte ich den Zugriff auf das jeweils aktuelle Menü machen d.h.: act_menue = MENU_MAIN - die Menüpunkte vom Hauptmenü werden angezeigt act_menue = MENU_SET - die Menüpunkte vom Menü Einstellungen werden gezeigt... wenn ich statt act_menue direkt mit dem Menünamen ersetze (z.B.MENU_SET) wird das Menü Einstellungen angezeigt. Ich weiß nur nicht wie ich die Zuweisung des aktuellen Menüs auf act_menue machen kann. Gruß Daniel
Das hier > if( MENU_COUNT > act_menue[MENU_COUNT].Max_Ct) MENU_COUNT = > act_menue[MENU_COUNT].Max_Ct; geht sowieso schon mal nicht. Wenn MENU_COUNT zu gross geworden ist, dann kannst du das nicht prüfen indem du auf act_menue[MENU_COUNT].Max_Ct zugreifst. MENU_COUNT ist bereits zu gross und damit geht die Array Indizierung in die Hose. Du solltest dir mal die Frage stellen, ob dein Strukturaufbau sinnvoll ist. Zb. Stelle ich dir die Frage: Warum steht eigentlich bei jedem MENU_ENTRY dabei, wieviele MENU_ENTRY dieses Menue aufbauen? Das hat doch nichts in einem MENU_ENTRY verloren (der ja einen Menuepunkt darstellt), sondern ist eine Eigenschaft des Menues an sich (fuer welches du keine Strktur hast). Ich schlage mal folgende Änderung vor: typedef void (*MenuFnct)(void); struct MENU; struct MENU_ENTRY { const unsigned char *Entry_Name; MENU *SubMenu; MenuFnct Function; }; struct MENU { const unsigned char *Menu_Name; int NrEntries; MENU_ENTRY *Entries; }; Damit baust du die Einträge für die SubMenues: const struct MENU_ENTRY MENU_SET_ENTRIES[] ={ { set1_str, NULL, func0 }, { set2_str, NULL, func1 } }; const struct MENU_ENTRY MENU_PAR_ENTRIES[] ={ { par1_str, NULL, func2 }, { par2_str, NULL, func3 } }; Soweit, so gut. Jetzt werden die eigentlichen Menues aufgebaut: const struct MENU MENU_SET = { menu_set_str, sizeof( MENU_SET_ENTRIES ) / sizeof( *MENU_SET_ENTRIES ), MENU_SET_ENTRIES }; const struct MENU MENU_PAR[] = { menu_par_str, sizeof( MENU_PAR_ENTRIES ) / sizeof( *MENU_PAR_ENTRIES ), MENU_PAR_ENTRIES }; Auch noch nichts überaschendes. Schau dir auch noch mal genau an, was wo definiert wird. Jetzt macht das alles Sinn. In einem Menü ist der Titel nur einmal gespeichert. Ein Menü weiss wieviele Einträge es hat und es besitzt einen Pointer zu den Einträgen. Jeder Eintrag wiederum hat einen anzuzeigenden Text, einen Pointer auf ein mögliches SubMenü und einen Funktionspointer auf die Funktion. Jede Information ist nur einmal angegeben, was ein Indiz dafür ist, dass die generelle Struktur schon mal nicht so schlecht ist. Lass uns noch das Hauptmenü aufbauen. Wieder, wir brauchen die Einträge selbst: const struct MENU_ENTRY MENU_MAIN_ENTRIES[] ={ { menu_set_str, MENU_SET, NULL }, { menu_par_str, MENU_PAR, NULL } }; Mit diesen Einträgen bauen wieder das eigentliche Menu auf: const struct MENU MENU_MAIN { menu_main_str, sizeof( MENU_MAIN_ENTRIES ) / sizeof( *MENU_MAIN_ENTRIES ), MENU_MAIN_ENTRIES }; und zuguter letzt haben wir noch den Pointer auf das zunächst oberste Menü: struct MENU *ActMenu = MENU_MAIN; (Ich schicke das jetzt mal ab, damit der Text nicht verloren geht falls was passiert, weiter gehts im nächsten Posting)
ActMenu zeigt auf das aktuelle Menu. Du wirst mal eine Funktion brauchen, die ein Menü ausgibt. Da ich nicht weiss wo du die Ausgabe machst, gebe ich die Texte einfach mit printf aus. Das musst du dann an deine Gegebenheiten anpassen. Also ein Code der das aktuelle Menu anzeigt: void ShowMenu( MENU* TheMenu ) { // zuerst den Titel ausgeben // das ist leicht, der steht in der Menu Struktur selbst // drinnen. TheMenu ist ein Pointer auf das gewünschte Menu printf( "%s\n", TheMenu->MenuName ); // Danach kommen alle Einträge dieses Menüs for( i = 0; i < TheMenu->NrEntries; ++i ) printf( "%s\n", TheMenu->Entries[i].EntryName ); // Das wars dann auch schon. Schau dir genau an, wann ein -> // kommt und wann ein . // Wenn du das verstanden hast, ist das schon mal die halbe // Miete } Als nächstes deine Auswertefunktion int ActEntry; // Nummer des aktuell ausgewählten // Menüpunktes im Aktuellen Menü void update_menue(void) { if( get_key_press( 1<<KEY_UP )) // Taste + gedrückt? ActEntry++; if( get_key_press( 1<<KEY_DWN )) // Taste - gedrückt? ActEntry--; if( ActEntry < 0 ) ActEntry = 0; if( ActEntry >= ActMenu->NrEntries; ActEntry = ActMenu->NrEntries - 1; if( get_key_press( 1<<KEY_ENT )) { // Taste Enter gedrückt? // // Den selektierten Menüpunkt mal besser zugreifbar // machen. Das vereinfacht im weiteren die Syntax erheblich // (und ist auch schneller) // MENU_ENTRY *SelectedEntry = ActMenu->Entries[ ActEntry ]; // Hat dieser Eintrag selber ein SubMenu if( SelectedEntry->SubMenu != NULL ) { ActMenu = SelectedEntry->SubMenu; ActEntry = 0; ShowMenu( ActMenu ); } // hat er kein SubMenu, bleibt noch der Fall, dass eine // Funktion aufzurufen sein koennte else if( SelectedEntry->Function != NULL ) { SelectedEntry->Function(); } } } Dein Menü hat übrigens einen Schönheitsfehler: Wenn ich mal in einem Sub-Menü drinnen bin, wie komme ich wieder zurück? Warnung: Ich habe den Code direkt im Web-Browser geschrieben. Es ist also möglich (und wahrscheinlich), dass da Tippfehler drinnen sind.
Da haben wir schon den ersten
> MENU_ENTRY *SelectedEntry = ActMenu->Entries[ ActEntry ];
das muss heissen:
MENU_ENTRY *SelectedEntry = & ActMenu->Entries[ ActEntry ];
Wow. Da brauch ich erst mal ne Stunde um durchzukommen. Ich meld mich morgen nochmal mit dem Testbericht. Danke Dir erstmal.
@ Karl Heinz /*Dein Menü hat übrigens einen Schönheitsfehler: Wenn ich mal in einem Sub-Menü drinnen bin, wie komme ich wieder zurück?*/ Dazu will ich das gleichzeitige Drücken der Tasten Auf und Ab als Escape-Befehl verwenden, damit dann wieder zum Hauptmenü wechseln. Gruß Daniel
Ich denke Karl Heinz meinte nicht, wie du den Benutzer das Menü zurückspringen lässt, sondern wie der Vorgang innerhalb deiner Verketteten Liste gehen soll. ->Man könnte die Liste doppelt verketten. Sprich: Ein Pointer zum Submenü (wie bisher geschehen) und im Submenü wiederum ein Pointer zum Parent-Menü.
Ja, das war klar. Bei Auslösen von Escape wird act_menue wieder MENU_MAIN zugewiesen -> Menü Update
Hallo, ich habe heute das Ganze ausprobiert, leider läuft es aber noch nicht. @ Karl Heinz, kannst Du bitte nochmal mit reinschauen? Die entsprechenden Fehlermeldungen habe ich mit reingeschrieben. //typedef void (*MenuFnct)(void); struct MENU; struct MENU_ENTRY { const unsigned char *Entry_Name; struct MENU *SubMenu; void ( *Function )(void); //struct MenuFnct *Function; }; >> Den Funktionspointer musste ich in die struct verlegen struct MENU { const unsigned char *Menu_Name; int NrEntries; struct MENU_ENTRY *Entries; }; const struct MENU_ENTRY MENU_SET_ENTRIES[] ={ { set1_str, NULL, func0 }, { set2_str, NULL, func1 } }; const struct MENU_ENTRY MENU_PAR_ENTRIES[] ={ { par1_str, NULL, func2 }, { par2_str, NULL, func3 } }; const struct MENU MENU_SET ={ menu_set_str, sizeof( MENU_SET_ENTRIES ) / sizeof( *MENU_SET_ENTRIES ), MENU_SET_ENTRIES }; >> warning: initialization discards qualifiers from pointer target type const struct MENU MENU_PAR ={ menu_par_str, sizeof( MENU_PAR_ENTRIES ) / sizeof( *MENU_PAR_ENTRIES ), MENU_PAR_ENTRIES }; >> warning: initialization discards qualifiers from pointer target type const struct MENU_ENTRY MENU_MAIN_ENTRIES[] ={ { menu_set_str, MENU_SET, NULL }, { menu_par_str, MENU_PAR, NULL } }; error: initializer element is not constant error: (near initialization for `MENU_MAIN_ENTRIES[0].SubMenu') error: initializer element is not constant error: (near initialization for `MENU_MAIN_ENTRIES[0]') error: initializer element is not constant error: (near initialization for `MENU_MAIN_ENTRIES[1].SubMenu') error: initializer element is not constant error: (near initialization for `MENU_MAIN_ENTRIES[1]') const struct MENU MENU_MAIN = { menu_main_str, sizeof( MENU_MAIN_ENTRIES ) / sizeof( *MENU_MAIN_ENTRIES ), MENU_MAIN_ENTRIES }; >> warning: initialization discards qualifiers from pointer target type //Pointer auf das zunächst oberste Menü: struct MENU *ActMenu = MENU_MAIN; >> error: incompatible types in initialization error: initializer element is not constant Ich glaube ich bin langsam code-blind geworden. Gruß Daniel
> //typedef void (*MenuFnct)(void); > struct MENU; > > struct MENU_ENTRY > { > const unsigned char *Entry_Name; > struct MENU *SubMenu; > void ( *Function )(void); > //struct MenuFnct *Function; > }; >>> Den Funktionspointer musste ich in die struct verlegen Ja, ok. Macht keinen Unterschied. Der typedef da oben haette Tipparbeit gespart wenn dieser Funktionspointertyp öfter vorkommt. > struct MENU { > const unsigned char *Menu_Name; > int NrEntries; > struct MENU_ENTRY *Entries; > }; > > const struct MENU_ENTRY MENU_SET_ENTRIES[] ={ > { set1_str, NULL, func0 }, > { set2_str, NULL, func1 } > }; > > const struct MENU_ENTRY MENU_PAR_ENTRIES[] ={ > { par1_str, NULL, func2 }, > { par2_str, NULL, func3 } > }; > > const struct MENU MENU_SET ={ > menu_set_str, > sizeof( MENU_SET_ENTRIES ) / sizeof( *MENU_SET_ENTRIES ), > MENU_SET_ENTRIES > }; > >>> warning: initialization discards qualifiers from pointer target Bei solchen Fehlermeldungen dreht es sich meistens um const / nicht const - Dinge Schaun wir mal. menu_set_str wirds nicht sein. Also der Pointer auf MENU_SET_ENTRIES. MENU_SET_ENTRIES ist definiert als const struct MENU_ENTRY MENU_SET_ENTRIES[] also mit const. In der Struktur ist der entsprechende Pointer deklariert als: struct MENU_ENTRY *Entries also ohne const. Jetzt steckt der Compiler in der Klemme: Auf der einen Seite teilst du ihm mit, dass sich MENU_SET_ENTRIES niemals aendern wird. Daher das const, und das ist auch gut so, wann immer du versuchst in diesem Array irgendwas zu ändern klopft dir der Compiler auf die Finger: Das Array ist const, du kannst dort nichts aendern. Auf der anderen Seite versuchst du einen Alternativen Zugriffsweg über den Pointer Entries aufzubauen, der nicht const ist. D.h über diesen Pointer wäre eine Änderung in diesem Array möglich. Tja. Und das teilt der Compiler mit. > const struct MENU MENU_PAR ={ > menu_par_str, > sizeof( MENU_PAR_ENTRIES ) / sizeof( *MENU_PAR_ENTRIES ), > MENU_PAR_ENTRIES > }; > >>> warning: initialization discards qualifiers from pointer target > type Selbiges hier > const struct MENU_ENTRY MENU_MAIN_ENTRIES[] ={ > { menu_set_str, MENU_SET, NULL }, > { menu_par_str, MENU_PAR, NULL } > }; > > error: initializer element is not constant > error: (near initialization for `MENU_MAIN_ENTRIES[0].SubMenu') > error: initializer element is not constant > error: (near initialization for `MENU_MAIN_ENTRIES[0]') > error: initializer element is not constant > error: (near initialization for `MENU_MAIN_ENTRIES[1].SubMenu') > error: initializer element is not constant > error: (near initialization for `MENU_MAIN_ENTRIES[1]') Ich denke mal, das ist ähnlich gelagert. const / non const. Wenn du obiges korregiert hast (falls du es noch nicht erraten hast, mach einfach den Pointer Entries zu einem Pointer auf const const struct MENU_ENTRY *Entries; ), denke ich muesste dieser Fehler alleine verschwinden. Wenn nicht melde dich noch mal. > const struct MENU MENU_MAIN = { > menu_main_str, > sizeof( MENU_MAIN_ENTRIES ) / sizeof( *MENU_MAIN_ENTRIES ), > MENU_MAIN_ENTRIES > }; > > >> warning: initialization discards qualifiers from pointer target > type Ja das kennen wir schon. > //Pointer auf das zunächst oberste Menü: > > struct MENU *ActMenu = MENU_MAIN; > > >> error: incompatible types in initialization > error: initializer element is not constant > Ist dasselbe in Grün: MENU_MAIN ist eine const Struktur, also muss der Pointer auch auf eine const Struktur zeigen: const struct MENU *ActMenu = MENU_MAIN; > Ich glaube ich bin langsam code-blind geworden. Wenn du noch nicht so fit damit bist: lass die const einfach mal alle weg. const-korrektes arbeiten ist zwar nicht so schwer, ist aber wie ein Rattenschwanz: Ein Fehler zieht eine ganze Lawine nach sich.
> //typedef void (*MenuFnct)(void); > struct MENU; > > struct MENU_ENTRY > { > const unsigned char *Entry_Name; > struct MENU *SubMenu; > void ( *Function )(void); > //struct MenuFnct *Function; > }; >>> Den Funktionspointer musste ich in die struct verlegen Jetzt seh ich's erst. Du hast das deswegen gemacht, weil du einen Fehlermeldung bekommen hast. Schau dir den typedef noch mal genau an: > //typedef void (*MenuFnct)(void); ^ | hier MenuFnct ist bereits ein Pointer! Daher ist das hier: > //struct MenuFnct *Function; die Dekleration eines Pointers auf einen Pointer auf eine Funktion. Dem die Adresse einer Funktion zuzuweisen klappt klarerweise nicht. Du brauchst einen Pointer auf eine Funktion. Nun der Datentyp 'MenuFnct' ist bereits als Pointer Datentyp definiert worden. Daher: struct MenuFnct Function; ^ | Hier kein * mehr!
So, jetzt läufts. <Stein vom Herzen fällt> Vielen Dank an Karl Heinz für den Super-Support ***** hier noch der lauffähige code: // im Header: struct MENU; struct MENU_ENTRY { const unsigned char *Entry_Name; const struct MENU *SubMenu; void ( *Function )(void); }; struct MENU { const unsigned char *Menu_Name; int NrEntries; const struct MENU_ENTRY *Entries; }; // Texte der Menüeinträge // Strings Mainmenue static const char menu_main_str[] PROGMEM = "Hauptmenue"; static const char menu_set_str[] PROGMEM = "Einstellungen"; static const char menu_par_str[] PROGMEM = "Parameter"; // Strings Settingsmenue static const char set1_str[] PROGMEM = "Set 1"; static const char set2_str[] PROGMEM = "Set 2"; // Strings Parametermenue static const char par1_str[] PROGMEM = "Par 1"; static const char par2_str[] PROGMEM = "Par 2"; static const char exit_str[] PROGMEM = "Exit"; // // im c file: const struct MENU_ENTRY MENU_SET_ENTRIES[] ={ { set1_str, NULL, func0 }, { set2_str, NULL, func1 }, { exit_str, NULL, getback } }; const struct MENU MENU_SET ={ menu_set_str, sizeof( MENU_SET_ENTRIES ) / sizeof( *MENU_SET_ENTRIES ), MENU_SET_ENTRIES }; const struct MENU_ENTRY MENU_PAR_ENTRIES[] ={ { par1_str, NULL, func2 }, { par2_str, NULL, func3 }, { exit_str, NULL, getback } }; const struct MENU MENU_PAR ={ menu_par_str, sizeof( MENU_PAR_ENTRIES ) / sizeof( *MENU_PAR_ENTRIES ), MENU_PAR_ENTRIES }; const struct MENU_ENTRY MENU_MAIN_ENTRIES[] ={ { menu_set_str, &MENU_SET, NULL }, { menu_par_str, &MENU_PAR, NULL } }; const struct MENU MENU_MAIN = { menu_main_str, sizeof( MENU_MAIN_ENTRIES ) / sizeof( *MENU_MAIN_ENTRIES ), MENU_MAIN_ENTRIES }; //und zuguter letzt haben wir noch den Pointer //auf das zunächst oberste Menü: const struct MENU *ActMenu = &MENU_MAIN; void update_menue(void) // Die Menüfunktionen { if( get_key_press( 1<<KEY_UP )) // Taste + gedrückt? { ActEntry ++; } if( get_key_press( 1<<KEY_DWN )) // Taste - gedrückt? { ActEntry --; } if( ActEntry < 0 ) ActEntry = 0; if( ActEntry >= ActMenu->NrEntries) // Begrenzung auf max Anzahl der Entries ActEntry = ActMenu->NrEntries - 1; if( get_key_press( 1<<KEY_ENT )) { // Taste Enter gedrückt? if( ActMenu->Entries[ActEntry].SubMenu != NULL ) { ActMenu = ActMenu->Entries[ActEntry].SubMenu; ActEntry = 0; update_display(); } else if( ActMenu->Entries[ActEntry].Function != NULL ) { ActMenu->Entries[ActEntry].Function(); } } if( ActEntry != ActEntryOld) update_display(); } void update_display(void) // Update der Anzeige { lcd_clrscr(); lcd_gotoxy(0,0); strcpy_P(HelpStr, ActMenu->Menu_Name); lcd_puts(HelpStr); lcd_gotoxy(0,1); strcpy_P(HelpStr, ActMenu->Entries[ActEntry].Entry_Name); lcd_puts(HelpStr); ActEntryOld = ActEntry; } void getback(void) { ActMenu = &MENU_MAIN; } // Für den Rücksprung zum Hauptmenü musste ich eine Funktion anlegen, da beim Anlegen der Submenüs das Hauptmenü noch nicht deklariert ist. Dank nochmal und ein schönes Wochenende Gruß Daniel
Die Sache mit sizeof( MENU_SET_ENTRIES ) / sizeof( *MENU_SET_ENTRIES ), ist dir klar?
Ich denke schon, die Anzahl der Einträge wird aus der Gesamtgröße aller Einträge geteilt durch die Einzelgröße der Einträge(Pointer) errechnet und in die Struct eingetragen. Stimmt das??
Ja, das stimmt so. Man haette auch schreiben können sizeof( MENU_SET_ENTRIES ) / sizeof( MENU_SET_ENTRIES[0] ); das ist der übliche Weg, wie man die Anzahl an Einträgen in einem Array vom Compiler ermitteln kann. int ai[10]; int size = sizeof( ai ) / sizeof( ai[0] ); Wichtig ist nur: Dazu muss das Array auch als Array sichtbar sein. Im folgenden Fall void foo( int bi[] ) { int i = sizeof( bi ) / sizeof( bi[0] ); } geht das ganz grauslich schief, weil in der Funktion das Array bereits zu einem Pointer auf das erste Arrayelement degeneriert ist. sizeof( bi ) ist hier nicht die Bytegröße des Arrays, sondern die Bytegröße des Pointers! Hüte dich auch davor so zu schreiben: int size = sizeof( ai ) / sizeof( int ); Das sieht harmlos aus, aber wenn du je den Datentyp von ai änderst: long ai[10]; und vergisst die Berechnung anzupassen int size = sizeof( ai ) / sizeof( int ); dann geht das furchtbar schief :-) Besser ist es, den eigentlichen Datentyp aus der Berechnung herauszulassen und den vom Array selbst abzunehmen. int size = sizeof( ai ) / sizeof( *ai ); oder int size = sizeof( ai ) / sizeof( ai[0] );
Hallo Daniel, bin auf der Suche nach einem geeigneten Ansatz für ein Displaymenü über die obigen Beiträge gestolpert. Leuchtet mir soweit auch ein, nur habe ich das Problem wenn ich's so realisiere, dass ums verrecken keine Anzeige auf dem Display erscheint. update_display() übergibt HelpStr an lcd_puts(). Wie ist den HelpStr deklariert? Mein lcd_puts() und lcd_putchar() schaut folgendermaßen aus: void lcd_puts(unsigned char *s) { while (*s) lcd_putchar (*s++); } void lcd_putchar(unsigned char value) { LCD_RS = LCD_DATA_REG; lcd_data_write((value>>4)& 0x0F); lcd_data_write(value & 0x0F); delay(4); return; } Übergeb ich nun z.B: lcd_puts("Test"); wird Test auch wunderbar dargestellt. In der Debugsimulation funzt auch die Menügeschichte mit Pointerübergabe usw. nur in der realen Schaltung klappts nicht. Eine Idee?
Also ich nutze eine leicht angepasste lcd lib von P. Fleury. Da sieht die Funktion lcd puts so aus: void lcd_puts(const char *s) /* print string on lcd (no auto linefeed) */ { register char c; while ( (c = *s++) ) { lcd_putc(c); } ...ist ja schon mal nix anderes. Help_Str ist als char array mit der Größe der Display Zeile deklariert(char HelpStr[20];) ist die string.h mit eingebunden? hast Du schon mal das probiert: snprintf(HelpStr, "Test"); lcd_puts(HelpStr);
Hallo Daniel, ich werde langsam zum Hirsch... was ich auch probiere, es funzt einfach nicht. Sicher ist, dass es ein Pointerproblem sein muss. Übergebe ich nämlich: strcpy(HelpStr, "test"); anstelle strcpy(HelpStr, ActMenu->Menu_Name); wird das einwandfrei dargestellt. Was mir aufgefallen ist: Einer als const unsigned char* deklarierten Variable wird ein const char* zugewiesen. Sollte soweit ja eigentlich kein Problem sein. Komischerweise erhalte ich nicht einmal eine Compilerwarnung. Es muss aber irgendwo vielleicht noch an anderer Stelle einen s.g. "pointer missmatch", geben den ich übersehe, der viel schlimmer ist. Welchen Compiler verwendest Du denn? Ich nutze Keil uVision C51. Vielleicht kannst Du ja mal so frei sein und mir Dein lauffähiges Menü mit zugehörigen Displaydateien schicken, vielleicht sehe ich dann noch was, was ich bisher übersehen habe. Wenn ich das gnaze Debugge (halt nur über Software Emulation, geht's einwandfrei, nur die HW will nicht.
@Derek Mach doch bitte mal ein kleines, komplettes Program fertig. Irgendetwas hast du bei der Code-Übernahme übersehen.
Hallo Karl Heinz, habe mal ein kleines Proggi angehängt. Im Grunde entspricht es der Vorlage und im ganzen verstehe ich auch wie's funktionieren soll. Geht halt nur nicht. Ist ja nicht wirklich viel Code. Bekomme auch keine Compilerfehler angeziegt, evtl. ist's eine Einstellung die ich noch nicht entdeckt habe. Vielleicht siehst Du den Knackpunkt auf Anhieb, bei mir gehen die Scheuklappen runter. Gruß Derek
Nein. Da seh ich eigentlich auch nichts. Eigenartiges Phänomen
Hallo Derek, anbei ein lauffähiges Programm von mir (Du musst noch Header und c-file auseinander kopieren) Ich nutze Programmers Notepad2 und WINAVR. Ansosnsten kann ich im code auch nichts entdecken.
Hallo, kurze Frage benoetigt man keine PROGMEM Angabe mehr im WinAVR? Reicht als Angabe nur noch const ?? >const struct MENU MENU_SET Dirk
PROGMEM legt die Daten im Flash ab. Das spart also kostbaren Speicher im SRAM. Daniel hat das so gemacht und hat auch sein Program so angepasst, dass die entscheidende Funktion das weiss. Genau aus diesem Grund verwendet er das Hilfsarray und kopiert mittels strcpy_P zuerst vom Flash in dieses Hilfsarray und von dort weiter zum LCD. Derek hat das zunächst noch nicht so gemacht. Eigentlich könnte er sich das Hilfsarray einsparen. Muss er aber nicht. Da die Texte nicht im Flash liegen, verwendet er folgerichtig auch keinen strcpy_P sondern einen normalen strcpy. Soweit ist das alles richtig.
Hallo Karl Heinz, habe nochmal ein weng rumgespielt und dabei festgestellt, sobald ich die const struct MENU_ENTRY MENU_MAIN_ENTRIES[] und const struct MENU MENU_MAIN[] und const struct MENU *ActMenu = &MENU_MAIN; nicht mehr global, sondern in void mnu_update_display(void) definiere das Proggi funzt. So kann ich's halt nicht gebrauchen. Aber immerhin habe ich eine Spur.
Hallo habe mein Pointerproblem gelöst. Das Problem bestand darin, dass ich dem Compiler/Locater nicht genügend internes Prozessorram zur Verfügung gestellt habe um die Structuren anzulegen. Nachdem ich das Programm auf die Displayanzeige zusammengestrichen und den Compiler/Locater gezwungen habe bestimmte RAM Bereiche zu nutzen bekam ich die helfende Fehlermeldung. Nutze einen Analog Devices Microconverter 8052 Derivat mit 265 Byte Near RAM und 2k Far XRAM. Das XRAM sollte genutzt werden, war aber in der Startupdatei mit 0Btye angegeben. Komischerweise gabs keinen Compiler, bzw. Locaterfehler und der Debugger hat auch nicht gemeckert, sondern so getan als ob alles OK wäre. Jetzt gehts. War eine schwere Geburt, aber habe viel über den Prozessor gelernt. Gruß Derek
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.