Forum: Compiler & IDEs Lcd - Menu typedef struct


von Heinz (Gast)


Lesenswert?

Hallo ich bin Anfänger in Programmierung und der Mikrocontroller Welt.
Nun versuche ich ein Menu für ein Lcd zu schreiben, bekomme es aber 
leider nicht zum laufen.

Genutzt wird :
Atmel Studio 6 (Version: 6.0.1843 - ) GCC 4.6.1
1
typedef void (*MenuFnct) (int);
2
3
void dummy_menu_func(int arg)
4
{
5
  
6
}
7
8
typedef struct {
9
  const char  SubLabel[16];
10
  MenuFnct    Function;
11
  int         ArgumentToFunction;
12
} SubMenu;
13
14
typedef struct
15
{
16
  const char MainLabel[16];
17
  SubMenu    *SubMenues[10];
18
  int        subLength;
19
} MainMenu;
20
21
SubMenu subMenu1[] =
22
{
23
{"sub0_1",dummy_menu_func, 0},
24
{"sub0_2",dummy_menu_func, 0},
25
{"sub0_3",dummy_menu_func, 0}
26
};
27
28
SubMenu subMenu2[] =
29
{
30
{"sub1_1",dummy_menu_func, 0},
31
{"sub1_2",dummy_menu_func, 0}
32
};
33
34
MainMenu MMenu[] =
35
{
36
{"Main_eins",subMenu1,2},
37
{"Main_zwei",subMenu2,2},
38
};

Folgende Warnungen werden ausgespuckt :
Warning  5  missing braces around initializer [-Wmissing-braces]
Warning  9  initialization makes pointer from integer without a cast
Warning  10  (near initialization for 'MMenu[1].SubMenues[1]') [enabled 
by default]

Durch das Hauptmenu kann ich ohne Probleme Scrollen, allerdings gibt es 
Probleme bei den Submenueintraegen:

MMenu[0].SubMenues[0]->SubLabel  - Ausgabe : "sub0_1"
MMenu[0].SubMenues[1]->SubLabel  - Ausgabe : komische Zeichen...
MMenu[0].SubMenues[2]->SubLabel  - Ausgabe : komische Zeichen...

Vielen Dank im Voraus
Heinz

von Karl H. (kbuchegg)


Lesenswert?

Heinz schrieb:

> typedef struct
> {
>   const char MainLabel[16];
>   SubMenu    *SubMenues[10];
>   int        subLength;
> } MainMenu;

Ein MainMenu ist also etwas, das einen Namen hat UND aus einem Array von 
10 Pointern besteht.

> MainMenu MMenu[] =
> {
> {"Main_eins",subMenu1,2},
> {"Main_zwei",subMenu2,2},
> };
> [/c]

Das passt nicht. Eine Zeile in dieser Initialisierung ist 1 MainMenu.
Und an der Stelle, an der eigentlich das Pointer Array initialisiert 
werden sollte, hast du nur subMenu1 stehen. Das ist aber keine 
Initialisierung eines Arrays, denn sie steht immer innerhalb von  { }. 
Daher die Meldungen.


Was du (wahrscheinlich) wolltest ist

MainMenu MMenu[] =
{
  { "Main_eins", { subMenu1, subMenu2 }, 2 },
};

Also 1 Hauptmenü, welches den Namen "Main_eins" trägt und welches 2 
Submenüs (nämlich subMenu1 und subMenu2) besitzt.

Zumindest verwendest du das hier

> MMenu[0].SubMenues[0]->SubLabel  - Ausgabe : "sub0_1"
> MMenu[0].SubMenues[1]->SubLabel  - Ausgabe : komische Zeichen...
> MMenu[0].SubMenues[2]->SubLabel  - Ausgabe : komische Zeichen...

in dieser Form.

.... MMenu[0].SubMenues[2] ....
Ähm. Das gibt es nicht. In deinem MMenu gibt es nur 2 Submenüs. Ein 
3-tes existiert nicht.

von Karl H. (kbuchegg)


Lesenswert?

Wednn du mit den Verpointerungen durcheinander kommst, dann mal dir die 
Dinge auf Papier auf!

Du hast gebaut
1
SubMenu subMenu1[] =
2
{
3
{"sub0_1",dummy_menu_func, 0},
4
{"sub0_2",dummy_menu_func, 0},
5
{"sub0_3",dummy_menu_func, 0}
6
};
7
8
SubMenu subMenu2[] =
9
{
10
{"sub1_1",dummy_menu_func, 0},
11
{"sub1_2",dummy_menu_func, 0}
12
};
13
14
MainMenu MMenu[] =
15
{
16
  { "Main_eins", { subMenu1, subMenu2 }, 2 },
17
};


und jetzt malen wir mal ein bischen. Wie sieht das ganze graphisch aus
1
  MMenu
2
  +-------------+
3
  | "Main_eins" |                     subMenu1
4
  | +----------+|                     +----------------+
5
  | |    o--------------------------->| "sub0_1"       |
6
  | +----------+|                     |    o-------------> dummy_func
7
  | |    o---------------+            | 0              |  ^^
8
  | +----------+|        |            +----------------+  ||
9
  | |          ||        |            | "sub0_2"       |  ||
10
  | +----------+|        |            |    o--------------+|
11
  |      2      |        |            | 0              |   |
12
  +-------------+        |            +----------------+   |
13
                         |            | "sub0_3"       |   |
14
                         |            |    o---------------+
15
                         |            | 0              |
16
                         |            +----------------+
17
                         v subMenu2
18
                       +--------------+
19
                       | "sub1_1"     |                      |
20
                       |    o--------------------------------+
21
                       | 0            |
22
                       +--------------+
23
                       | "sub1_2"     |                      |
24
                       |    o--------------------------------+
25
                       | 0            |
26
                       +--------------+

wie kommst du, ausgehend von MMenu zb auf den Menüpunkt ganz unten?
Lies es einfach von der Grafik ab.
Ausgangspunkt ist MMenu.

   MMenu

Das ist ein riesen Kasten. Wo findet sich dort der Pfeil, der zum Kasten 
mit dem Menüpunkt führt? Er ist im Array und dort ist es der Eintrag mit 
dem Index 1. Also

  MMenu.SubMenues[1]

An dieser Stelle dort beginnt ein Pfeil. Um also vom MMenu Kasten zum 
Kasten mitte/unten zu kommen, brauchen wir

  MMenu.SubMenues[1]->

Jetzt sind wir beim Kasten mitte/unten angelangt. Wie gehts weiter? Der 
Kasten ist selber wieder ein Array. Ein Array von SubMenu Einträgen. 
Davon würden wir den Eintrag mit dem Index 1 brauchen.

  MMenu.SubMenues[1]->[1]

Nur: Diese Syntax gibt es nicht.
Jetzt muss man sich an die Pointer/Array-Indizierungs-Dualität erinnern. 
Dann weiß man, dass man diese Operation so

  (MMenu.SubMenues[1])[1]

schreiben kann.
(Allerdings: lieber eine Hilfsvariable benutzen. Das ist 
durchschaubarer!)

Damit sind wir beim kleinen Kasten innerhalb des grossen Kastens, der 
einen Eintrag beschreibt. Und von diesem Eintrag möchten wir den Namen.

  ((MMenu.SubMenues[1])[1]).SubLabel

So. Jetzt geb ich unumwunden zu, dass diese Syntax grauenhaft ist. 
Daher: Eine Zwischenvariable einführen! Ein Pointer auf das Array von 
SubMenus bietet sich an:

  SubMenu* completeSubMenu = MMenu.SubMenues[1];
  lcd_string( completeSubMenu[1].SubLabel );

und bringt wieder Klarheit in den ganzen Ausdruck.

von Heinz (Gast)


Lesenswert?

Oh Backe...

Meine Bezeichnungen sind ein wenig Irreführend.


MMain            SubMenu_1

LCD      - Helligkeit
EcoMode          |
 *        0..100      | 5,10,20,30Sec, AlwaysOn

 * Messung    - Intervall    | Logging          |
 *        x Min      | Temp1,Temp2,Temp3

von Heinz (Gast)


Lesenswert?

Entschuldigung für den letzten Beitrag, ich bin aus versehen auf 
Absenden gekommen.

Oh Backe...

Meine Bezeichnungen sind ein wenig Irreführend.


MMain         SubMenu

Main_eins     Sub_eins
LCD           - Helligkeit  - dummy_func
              - EcoMode     - dummy_func

Main_zwei     Sub_zwei
Messung       - Intervall   - dummy_func
              - Logging     - dummy_func

...           ...           ...

Das würde mir bereits genügen.
Deshalb dachte ich das man mit: MMenu[x].SubMenues[x]->SubLabel
auf das Array der struct SubMenu einfach zugreifen kann (was mit dem 
ersten Eintrag aus dem SubMenu Array auch funktioniert)
Ich werde mir deine Erklärung später nochmal gründlich durchlesen und 
versuchen die Syntax zu verstehen.
Das mit dem Aufmalen war ein guter Tipp, das vereinfacht es schonmal 
enorm.

SChönen Dank.

von Heinz (Gast)


Lesenswert?

Wollte nur noch kurz Bescheid geben, anscheinend habe ich es verstanden. 
Zumindest läuft es nun.

Schönen Feierabend noch. Grüße
Heinz

von Karl H. (kbuchegg)


Lesenswert?

Heinz schrieb:
> Entschuldigung für den letzten Beitrag, ich bin aus versehen auf
> Absenden gekommen.
>
> Oh Backe...
>
> Meine Bezeichnungen sind ein wenig Irreführend.
>
>
> MMain         SubMenu
>
> Main_eins     Sub_eins
> LCD           - Helligkeit  - dummy_func
>               - EcoMode     - dummy_func
>
> Main_zwei     Sub_zwei
> Messung       - Intervall   - dummy_func
>               - Logging     - dummy_func
>
> ...           ...           ...
>
> Das würde mir bereits genügen.

OK.
Das ist dann allerdings eine andere Struktur. Wie man ja auch deiner 
jetzt konzeptionell sauberen 'Skizze' entnehmen kann, muss die so 
aussehen
1
MainMenu MMenu[] =
2
{
3
  { "LCD",       { Sub_eins }, 1 },
4
  { "Messung",   { Sub_zwei }, 1 }
5
};

Kannst ja mal vergleichen, ob du auf dieselbe Schlussfolgerung gekommen 
bist.


(Allerdings: Wenn das der Standardfall ist, frage ich mich, wozu du as 
10-er Array von Pointern zu den Submenüs überhaupt brauchst :-)
IMHO braucht das eigentlich keiner.

Eine MMenu 'Zeile' besteht aus einem Text, der angezeigt wird. Und wenn 
dieser Menüpunkt angewählt wird, dann wird das angegebene Sub-Menü 
angewählt. Das kann aber sowieso nur eines sein! Bei Anwahl des 
Menüpunktes "LCD" geht es ja immer weiter ins Sub Menü Sub_eins, welches 
die LCD-Menüpunkte beinhaltet. (d.h. das Array braucht keiner und die 
dort gespeicherte Anzahl ist in Wirklichkeit die Anzahl der Menüpunkte 
im SubMenü!

1
typedef struct
2
{
3
  const char  MainLabel[16];
4
  SubMenu    *SubMenue;
5
  int         subLength;
6
} MainMenu;
7
8
typedef struct {
9
  const char  SubLabel[16];
10
  MenuFnct    Function;
11
  int         ArgumentToFunction;
12
} SubMenu;
13
14
#define ARRAY_SIZE(x)  (sizeof(x) / sizeof(*x))
15
16
SubMenu Sub_LCD[] =
17
{
18
  { "Helligkeit", dummy_menu_func, 0 },
19
  { "EcoMode",    dummy_menu_func, 1 },
20
};
21
22
SubMenu Sub_Measure[] = 
23
{
24
  { "Intervall",  dummy_menu_func, 2 },
25
  { "Logging",    dummy_menu_func, 3 },
26
};
27
28
MainMenu MMenu[] =
29
{
30
  { "LCD",       Sub_LCD,     ARRAY_SIZE( Sub_LCD ) },
31
  { "Messung",   Sub_Measure, ARRAY_SIZE( Sub_Measure ) },
32
};

Jetzt wird schön langsam ein Schuh draus.

> Ich werde mir deine Erklärung später nochmal gründlich durchlesen
> und versuchen die Syntax zu verstehen.

Wenn du dir eine vernünftige Funktionsaufteilung machst, brauchst du die 
Komplettsyntax nirgends. Teile deine Funtionen auf in Funktionen die mit 
einem MainMenu arbeiten und Funktionen die mit einem SubMenu arbeiten.

von Heinz (Gast)


Lesenswert?

Hallo.

Karl Heinz Buchegger schrieb:
> Kannst ja mal vergleichen, ob du auf dieselbe Schlussfolgerung gekommen
> bist.

Ja das bin ich. MenuFnct ist kein Array mehr was sich glücklicherweise 
auch im Speicherverbrauch bemerkbar macht.

Program Memory Usage   :  1908 bytes   23,3 % Full
Data Memory Usage   :  438 bytes   42,8 % Full

Allerdings wird es langsam eng auf dem ATMega8, zumal das nur der 
Optische Teil ist.

Karl Heinz Buchegger schrieb:
> Wenn du dir eine vernünftige Funktionsaufteilung machst, brauchst du die
> Komplettsyntax nirgends. Teile deine Funtionen auf in Funktionen die mit
> einem MainMenu arbeiten und Funktionen die mit einem SubMenu arbeiten.

Gut, den Tipp werde ich wohl auch beherzigen, zur Zeit ist alles in 
einer Funktion, was ein wenig Chaotisch aussieht.

von Karl H. (kbuchegg)


Lesenswert?

Heinz schrieb:

> Program Memory Usage   :  1908 bytes   23,3 % Full
> Data Memory Usage   :  438 bytes   42,8 % Full
>
> Allerdings wird es langsam eng auf dem ATMega8, zumal das nur der
> Optische Teil ist.

Auch dagegen lässt sich was tun.
Die Texte können ins Flash (ins Program Memory) ausgelagert werden und 
anstatt fixen 16 Zeichen kann man dann auch nur den Platz in Anspruch 
nehmen, den die Texte auch wirklich brauchen.

Aber alles zu seiner Zeit. Noch hast du genügend Platz.

von JHS (Gast)


Lesenswert?

Moin,
ersteinmal herzlichen Dank an "kbuchegg" für die tolle und (für mich) 
sehr verständliche Erklärung! Daraus entstand ein wunderbar 
funktionierendes LCD-Menü.

Gibt es Verbesserungsvorschläge zu meiner Verfahrensweise der Nutzung?

Was ich mir vorstellen könnte wäre eine Funktion die meine defines 
ersetzt, habe aber noch keine Idee wie ich das derart einfach anstellen 
könnte, daß das Kosten/Nutzen-Verhältnis noch passt.

Vielen Dank!
1
  int mm=0;
2
  #define mmax 1  //Anzahl Mainmenülabels
3
  int sm=0;          
4
  #define smax 1  //Anzahl Submenülabels    
5
  
6
  int menueebene=0;  
7
8
//*************************************************************************************************//
9
      switch (menueebene)
10
      {
11
      case 0:    switch (prtC)
12
            {
13
            case dwn:  if (mm==0){mm=mmax;}  else  {mm--;}  break;
14
            case up:   if (mm==mmax){mm=0;}  else  {mm++;}  break;
15
            case rgt:  menueebene++;  break;
16
            case lft:  menueebene--;  break;
17
            }
18
        break;
19
      case 1:
20
            switch (prtC){/*...wie auch bei "0" nur für die submenüs (sm)*/}
21
        break;
22
      case 2:
23
            ((MMenu[mm].SubMenue)[sm]).Function(((MMenu[mm].SubMenue)[sm]).ArgumentToFunction);
24
        break;
25
      default:  menueebene=0;
26
        break;
27
      }
28
      
29
//----------Mainmenüpunkt ausgeben
30
      if (menueebene == 0)
31
      {
32
        lcd_string((MMenu[mm]).MainLabel);
33
      }
34
//----------Submenüpunkt ausgeben
35
      if (menueebene == 1)
36
      {
37
        lcd_string(((MMenu[mm].SubMenue)[sm]).SubLabel);
38
      }

von Karl H. (kbuchegg)


Lesenswert?

1
//*************************************************************************************************//
2
      switch (menueebene)
3
      {
4
      case 0:    switch (prtC)
5
            {
6
            case dwn:  if (mm==0){mm=mmax;}  else  {mm--;}  break;
7
            case up:   if (mm==mmax){mm=0;}  else  {mm++;}  break;
8
            case rgt:  menueebene++;  break;
9
            case lft:  menueebene--;  break;
10
            }
11
...

Ich würde die Funktionalität aufteilen:
Eine Funktion, die das Hauptmenü behandelt
und eine Funktion die das jeweilige Submenü behandelt.

Deine Tasten rgt/lft sind dann ganz einfach
  rgt: vom Hauptmenü ins aktuelle ausgewählte Submenü wechseln
       (Funktionsaufruf der Funktion, die das Submenü behandelt)
  lft: aus der Submenüfunktion wieder aussteigen (return)

Die Variable menueebene brauchst du dann nicht mehr und die 
durchgeknallte Syntax beim eigentlichen Funktionsaufruf vereinfacht sich 
ebenfalls.

Ausserdem eröffnet sich dann auch die Möglichkeit, dass ein Submenü 
wieder neue Submenüs haben könnte (mit entsprechender Erweiterung der 
Datenstruktur)

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.