Forum: Compiler & IDEs Menüstruktur mit Pointern und structs


von Daniel Held (Gast)


Lesenswert?

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

von johnny.m (Gast)


Lesenswert?

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.

von Daniel Held (Gast)


Lesenswert?

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.

von johnny.m (Gast)


Lesenswert?

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.

von Karl heinz B. (kbucheg)


Lesenswert?

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.

von Daniel Held (Gast)


Lesenswert?

Ü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

von Karl heinz B. (kbucheg)


Lesenswert?

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)

von Karl heinz B. (kbucheg)


Lesenswert?

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.

von Karl heinz B. (kbucheg)


Lesenswert?

Da haben wir schon den ersten

> MENU_ENTRY *SelectedEntry = ActMenu->Entries[ ActEntry ];

das muss heissen:

MENU_ENTRY *SelectedEntry = & ActMenu->Entries[ ActEntry ];

von Daniel Held (Gast)


Lesenswert?

Wow.
Da brauch ich erst mal ne Stunde um durchzukommen.
Ich meld mich morgen nochmal mit dem Testbericht.
Danke Dir erstmal.

von Daniel Held (Gast)


Lesenswert?

@ 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

von Simon K. (simon) Benutzerseite


Lesenswert?

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ü.

von Daniel Held (Gast)


Lesenswert?

Ja, das war klar. Bei Auslösen von Escape wird act_menue wieder
MENU_MAIN zugewiesen -> Menü Update

von Daniel Held (Gast)


Lesenswert?

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

von Karl heinz B. (kbucheg)


Lesenswert?

> //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.

von Karl heinz B. (kbucheg)


Lesenswert?

> //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!

von Daniel Held (Gast)


Lesenswert?

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

von Karl heinz B. (kbucheg)


Lesenswert?

Die Sache mit

sizeof( MENU_SET_ENTRIES ) / sizeof( *MENU_SET_ENTRIES ),

ist dir klar?

von Daniel Held (Gast)


Lesenswert?

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??

von Karl heinz B. (kbucheg)


Lesenswert?

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] );

von Daniel Held (Gast)


Lesenswert?

Danke, wieder was dazugelernt.

von Derek S. (roepke)


Lesenswert?

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?

von Daniel Held (Gast)


Lesenswert?

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);

von Derek Schupp (Gast)


Lesenswert?

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.

von Karl heinz B. (kbucheg)


Lesenswert?

@Derek

Mach doch bitte mal ein kleines, komplettes Program fertig.
Irgendetwas hast du bei der Code-Übernahme übersehen.

von Derek Schupp (Gast)


Angehängte Dateien:

Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

Nein. Da seh ich eigentlich auch nichts.
Eigenartiges Phänomen

von Daniel Held (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Dirk (Gast)


Lesenswert?

Hallo,

kurze Frage benoetigt man keine PROGMEM Angabe mehr im WinAVR? Reicht
als Angabe nur noch const ??

>const struct MENU MENU_SET

Dirk

von Karl heinz B. (kbucheg)


Lesenswert?

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.

von Derek Schupp (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Derek Schupp (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.