Forum: Mikrocontroller und Digitale Elektronik Menü - Array in Struct


von Markus (Gast)


Lesenswert?

Hallo Leute,

ich hab hier ein kleines Problem. Leider hilft mir auch die Suche nicht 
weiter ... ich sitz mittlerweile seit zwei Tagen vor dem Problem.

Ich möchte auf meinem ATMEGA 128 ein Menü auf einem 2x16 Display 
ausgeben. Vom Aufbau bin ich mir eigentlich auch ganz im Klaren, wie ich 
das machen möchte.
Dafür hab ich jetzt Structs "gebaut", die meine einzelnen Menüelemente 
darstellen:
1
typedef struct sValueValue{
2
  const char *text;
3
  int prev;
4
  int next;
5
  char value;
6
} MenuValueValues;
7
8
typedef struct sValue{
9
  const char *text;
10
  int prev;
11
  int next;
12
  MenuValueValues values[];
13
} MenuValue;
14
15
typedef struct sChild{
16
  const char *text;
17
  int prev;
18
  int next;
19
  MenuValue child[];
20
} MenuChild;
21
22
typedef struct sMenu{
23
  const char *text;
24
  int prev;
25
  int next;
26
  MenuChild child[];
27
} MenuEntry;

Dann mache ich ein Array, welches ich manuell fülle:
1
MenuEntry myMenu[];
2
3
void InitMenu(void)
4
{
5
    
6
  MenuEntry myI1;
7
  myI1.text = "System          ";
8
  myI1.prev = 4;
9
  myI1.next = 1;
10
    
11
    MenuChild myC1;
12
    myC1.text = "Kommunikation   ";
13
    myC1.prev = 1;
14
    myC1.next = 1;
15
      
16
      MenuValue myV1;
17
      myV1.text = "Aktiviert       ";
18
      myV1.prev = 0;
19
      myV1.next = 0;
20
      
21
        MenuValueValues myVV1;
22
        myVV1.text = "Ja              ";
23
        myVV1.prev = 1;
24
        myVV1.next = 1;
25
        myVV1.value = 1;
26
        myV1.values[0] = myVV1;
27
        
28
        MenuValueValues myVV2;
29
        myVV2.text = "Nein            ";
30
        myVV2.prev = 0;
31
        myVV2.next = 0;
32
        myVV2.value = 0;
33
        myV1.values[1] = myVV2;
34
        
35
      myC1.child[0] = myV1;
36
      
37
    myI1.child[0] = myC1;
38
  
39
    MenuChild myC2;
40
    myC2.text = "Uhrzeit / Datum ";
41
    myC2.prev = 0;
42
    myC2.next = 0;
43
    myI1.child[1] = myC2;
44
  
45
  myMenu[0] = myI1;
46
  
47
  MenuEntry myI2;
48
  myI2.text = "Beleuchtung     ";
49
  myI2.prev = 0;
50
  myI2.next = 2;
51
    
52
    MenuChild myC3;
53
    myC3.text = "Type            ";
54
    myC3.prev = 0;
55
    myC3.next = 0;
56
    myI2.child[0] = myC3;
57
    
58
  myMenu[1] = myI2;
59
  
60
  MenuEntry myI3;
61
  myI3.text = "Temperatur      ";
62
  myI3.prev = 1;
63
  myI3.next = 3;
64
    
65
    MenuChild myC4;
66
    myC4.text = "Temperaturen    ";
67
    myC4.prev = 3;
68
    myC4.next = 1;
69
    myI3.child[0] = myC4;
70
    
71
    MenuChild myC5;
72
    myC5.text = "Zeitsteuerung   ";
73
    myC5.prev = 0;
74
    myC5.next = 2;
75
    myI3.child[1] = myC5;
76
    
77
    MenuChild myC6;
78
    myC6.text = "Type            ";
79
    myC6.prev = 1;
80
    myC6.next = 3;
81
    myI3.child[2] = myC6;
82
    
83
    MenuChild myC7;
84
    myC7.text = "Kalibrieren     ";
85
    myC7.prev = 2;
86
    myC7.next = 0;
87
    myI3.child[3] = myC7;
88
    
89
  myMenu[2] = myI3;
90
  
91
  MenuEntry myI4;
92
  myI4.text = "Rolladen        ";
93
  myI4.prev = 2;
94
  myI4.next = 4;
95
    
96
    MenuChild myC8;
97
    myC8.text = "Zeitsteuerung   ";
98
    myC8.prev = 0;
99
    myC8.next = 0;
100
    myI4.child[0] = myC8;
101
102
  myMenu[3] = myI4;
103
  
104
  MenuEntry myI5;
105
  myI5.text = "Fernbedienung   ";
106
  myI5.prev = 3;
107
  myI5.next = 0;
108
  myMenu[4] = myI5;
109
  
110
}

Wenn ich aber jetzt auf meinen ersten "Untermenüpunkt" zugreifen will, 
bekomme ich den zweite "Hauptmenüeintrag zurück.
1
lcd_string(myMenu[0].child[0].text;

Am Display erscheint dann "Beleuchtung".

Ich muss noch dazu sagen, dass ich mit C als Programmiersprache noch 
ziemlich neu bin. Auch mit uCs hab ich noch nicht viel gemacht.

Vielleicht kann mir jemand von Euch helfen.

Danke schon mal,

Markus

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Markus schrieb:
> Ich muss noch dazu sagen, dass ich mit C als Programmiersprache noch
> ziemlich neu bin.
Das sehe ich auch so. Denn so unbedarft, wie du hier mit (für uCs) 
relativ großen Speichermengen um dich schmeißt...

Das schreibt sich zwar schön kurz:
 myMenu[3] = myI4;
Aber es steckt für den Prozessor durchaus einiges an Arbeit dahinter.

> Auch mit uCs hab ich noch nicht viel gemacht.
Du solltest solche Strukturen nicht im Programm InitMenu() aufbauen, 
sondern gleich vorneweg definieren und im Programmspeicher ablegen.

Seis drum:
Microsoft VC meldet für diese Zeilen
  MenuValue child[];
den Fehler:
error C2233: '<Unknown>' : arrays of objects containing zero-size arrays 
are illegal

Und es ist ja klar: woher soll der Compiler wissen, wieviel 
Speicherplatz für
 MenuEntry myMenu[];
zu reservieren ist?
Du könntest ja später ohne weiteres schreiben:
 lcd_string(myMenu[1234567].child[654321].text;

Also: flugs geändert und deinen Quellcode so geändert:
1
typedef struct sValueValue{
2
:
3
} MenuValueValues;
4
5
typedef struct sValue{
6
:
7
  MenuValueValues values[5];  //!!!!
8
} MenuValue;
9
10
typedef struct sChild{
11
:
12
  MenuValue child[5];  //!!!!
13
} MenuChild;
14
15
typedef struct sMenu{
16
:
17
  MenuChild child[5];  //!!!!
18
} MenuEntry;
19
20
MenuEntry myMenu[5];  //!!!!
21
22
23
int main(int argc, char* argv[])
24
{
25
  MenuEntry myI1;
26
  myI1.text = "System          ";
27
  myI1.prev = 4;
28
  myI1.next = 1;
29
    
30
    MenuChild myC1;
31
    myC1.text = "Kommunikation   ";
32
    myC1.prev = 1;
33
    myC1.next = 1;
34
      
35
      MenuValue myV1;
36
      myV1.text = "Aktiviert       ";
37
      myV1.prev = 0;
38
      myV1.next = 0;
39
:
40
:
41
:
42
  MenuEntry myI5;
43
  myI5.text = "Fernbedienung   ";
44
  myI5.prev = 3;
45
  myI5.next = 0;
46
  myMenu[4] = myI5;
47
48
  printf("%s", myMenu[0].child[0].text);
49
50
  return 0;
51
}
Und heraus kommt: "Kommunikation   "

von Markus (Gast)


Lesenswert?

Hallo Lothar,

vielen Dank für Deine Antwort.

Wie ich gesagt hab, ich bin noch ziemlich neu in C und uC ... deshalb 
musst Du mich auch nicht gleich steinigen ... dass mein Code 
opmimierungsfähig ist, ist mir klar. Anfangs hab ich das auch anders 
gehabt - Arrays bei der Definition gefüllt, aber nachdem das nicht 
funktioniert hat, hab ich mich auf die Fehlersuche gemacht und das ganze 
mal (für mich) übersichtlicher geschrieben.

Ich werd das jetzt mal probieren und mal schaun.

Also, vielen Dank nochmals.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Markus schrieb:
> deshalb musst Du mich auch nicht gleich steinigen ...
Kein Sorge, steinigen sieht anders aus... ;-)

> Ich werd das jetzt mal probieren und mal schaun.
Du hast Glück, denn du bist nicht der erste, der so eine Menüstruktur 
fürs Display aufbauen will. Sieh dich doch einfach mal hier im Forum um, 
wie andere das machen:
http://www.mikrocontroller.net/search?query=men%C3%BC+c+progmem&forums[]=1&forums[]=2&max_age=-&sort_by_date=1

von Markus (Gast)


Lesenswert?

Ok, so weit so schlecht ... ich denke, ich hab das jetzt verstanden, was 
Du versucht hast, mir zu erklären. Allerdings steig ich, was die Structs 
angeht, in C noch nicht so ganz durch. Meine Deklaration sieht jetzt so 
aus:
1
typedef struct sValueValue{
2
  const char *text;
3
  int prev;
4
  int next;
5
  char value;
6
} MenuValueValues;
7
8
typedef struct sValue{
9
  const char *text;
10
  int prev;
11
  int next;
12
  MenuValueValues values[];
13
} MenuValue;
14
15
typedef struct sChild{
16
  const char *text;
17
  int prev;
18
  int next;
19
  MenuValue child[];
20
} MenuChild;
21
22
typedef struct sMenu{
23
  const char *text;
24
  int prev;
25
  int next;
26
  MenuChild child[4];
27
} MenuEntry;
28
29
30
static const MenuChild MenuSystem[2] = {
31
  {"Kommunikation   ",1,1},
32
  {"Uhrzeit / Datum ",0,0},
33
};
34
35
static const MenuChild MenuBeleuchtung[1] = {
36
  {"Type            ",0,0},
37
};
38
39
static const MenuChild MenuTemperatur[4] = {
40
  {"Temperaturen    ",3,1},
41
  {"Zeitsteuerung   ",0,2},
42
  {"Type            ",1,3},
43
  {"Kalibrieren     ",2,0},
44
};
45
46
static const MenuChild MenuRolladen[1] = {
47
  {"Zeitsteuerung   ",0,0},
48
};
49
50
static const MenuChild MenuFernbedienung[1] = {
51
  {"Lernen          ",0,0},
52
};
53
54
static const MenuEntry myMenu[5] = {
55
  {"System          ",4,1,MenuSystem},
56
  {"Beleuchtung     ",0,2,MenuBeleuchtung},
57
  {"Temperatur      ",1,3,MenuTemperatur},
58
  {"Rolladen        ",2,4,MenuRolladen},
59
  {"Fernbedienung   ",3,0,MenuFernbedienung},
60
};

Jetzt bekomm ich aber beim Kompilieren die folgende Warunung:
1
initialization from incompatible pointer type

Ich hab doch in der TypeDef von MenuEntry Child als MenuChild 
deklariert.
Warum ist er der Meinung, dass das der falsche Typ ist (das bedeutet 
diese Fehlermeldung doch, soweit google mir das mitgeteilt hat).

von Markus (Gast)


Lesenswert?

Hab ich übersehen ... ich bekomm auch noch die Warunung
1
 missing braces around initializer

Ich wüsste aber nicht wo die {} abgehen?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

So gehts:
1
typedef struct {
2
  const char *text;
3
  int prev;
4
  int next;
5
  char value;
6
} MenuValueValues;
7
8
typedef struct {
9
  const char *text;
10
  int prev;
11
  int next;
12
  MenuValueValues values[4];
13
} MenuValue;
14
15
typedef struct {
16
  const char *text;
17
  int prev;
18
  int next;
19
  MenuValue child[4];
20
} MenuChild;
21
22
typedef struct sMenu{
23
  const char *text;
24
  int prev;
25
  int next;
26
  MenuChild child[4];
27
} MenuEntry;
28
29
static const MenuEntry myMenu[5] = {
30
  {"System          ",4,1,{{"Kommunikation   ",1,1},{"Uhrzeit / Datum ",0,0}} },
31
  {"Beleuchtung     ",0,2,{{"Type            ",0,0}} },
32
  {"Temperatur      ",1,3,{{"Temperaturen    ",3,1},{"Zeitsteuerung   ",0,2},{"Type            ",1,3},{"Kalibrieren     ",2,0}} },
33
  {"Rolladen        ",2,4,{{"Zeitsteuerung   ",0,0}} },
34
  {"Fernbedienung   ",3,0,{{"Lernen          ",0,0}} },
35
};
36
37
int main(int argc, char* argv[])
38
{
39
  printf("%s", myMenu[0].child[0].text);
40
  printf("%s", myMenu[2].child[3].text);
41
42
  return 0;
43
}
44
45
// --> Ausgabe: Kommunikation   Kalibrieren
Überleg mal warum.
Als Tipp: jetzt passiert die Initialisierung genau an 1 Stelle. Es 
werden keine "Zwischenkonstanten" erzeugt...

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Und dann war da noch...
1
:
2
:
3
} MenuEntry;
4
5
#define MenuSystem         {{"Kommunikation   ",1,1},{"Uhrzeit / Datum ",0,0}}
6
#define MenuBeleuchtung    {{"Type            ",0,0}}
7
#define MenuTemperatur     {{"Temperaturen    ",3,1},{"Zeitsteuerung   ",0,2},{"Type            ",1,3},{"Kalibrieren     ",2,0}}
8
#define MenuRolladen       {{"Zeitsteuerung   ",0,0}}
9
#define MenuFernbedienung  {{"Lernen          ",0,0}}
10
11
static const MenuEntry myMenu[5] = {
12
  {"System          ",4,1, MenuSystem         },
13
  {"Beleuchtung     ",0,2, MenuBeleuchtung    },
14
  {"Temperatur      ",1,3, MenuTemperatur     },
15
  {"Rolladen        ",2,4, MenuRolladen       },
16
  {"Fernbedienung   ",3,0, MenuFernbedienung  }
17
};
18
:
19
:

von Markus (Gast)


Lesenswert?

Ok, vielen Dank.

So ähnlich hatte ich das schon gehabt. Hat aber irgendwie nicht 
funktioniert. Bin leider im Moment nicht bei meinem uC, werd das aber 
dann gleich mal probieren.

Das mait #define schaut für mich am Besten aus ... ich denke, so werde 
ich das machen. Ist auch noch schön übersichtlich für mich.

Funktioniert das auch noch, wenn ich eine zusätzliche Ebene habe?
1
#define NochNMenuy {{"Test",1,1},{"Test2",0,0}}
2
3
#define MenuSystem         {{"Kommunikation   ",1,1,NochNMenu},...}
4
5
static const MenuEntry myMenu[5] = {
6
  {"System          ",4,1, MenuSystem         },
7
  ...
8
};

Das mit den Konstanten und den Zwischekonstanten ist mir schon klar ... 
ein wenig ;o)
Ich weiß ja, dass es wichtig ist, bei einem uC performant und sparsam zu 
Programmieren. Als Windows-Applikations-Programmierer muss man sich ja 
heutzutage nicht mehr um den Speicher kümmern.
Es ist schon noch irgendwo in meinem Hinterkopf und ich versuche auch 
unter Windows so sparsam und performant wie möglich zu programmieren ... 
nur mit C ist es eben noch nicht so leicht für mich.

von Markus (Gast)


Lesenswert?

Funktioniert bestens! Vielen Dank.

von Peter (Gast)


Lesenswert?

Zusatzaufgabe: Wie bringt man ganze noch im FLASH statt im RAM ?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Peter schrieb:
> Zusatzaufgabe: Wie bringt man ganze noch im FLASH statt im RAM ?
Das wird insgesamt aufwendiger....
Dein Stichwort dafür ist: PROGMEM
Siehe AVR-GCC-Tutorial unter "Programmspeicher"

von Markus (Gast)


Lesenswert?

Hallo Leute,

ich wieder ... jetzt steh ich schon wieder da und blick nicht mehr 
durch.

Also, mein Menü funktioniert wunderbar. Jetzt bin ich dabei, das ganze 
im Programmspeicher mit PROGMEM abzulegen. Dabei jammert der Compiler, 
weil der Pointer umgewandelt wird, oder so. Gut, also versuche ich den 
.text im Struct auf eine fixe Größe zu definieren und mein "Konstrukt" 
kann im Programm-Speicher abgelegt werden:
1
typedef struct sValueValue{
2
  char text[16];
3
  uint8_t prev;
4
  uint8_tnext;
5
  uint8_tvalue;
6
} MenuValueValues;
7
8
typedef struct sValue{
9
  char text[16];
10
  uint8_tprev;
11
  uint8_tnext;
12
  MenuValueValues values[5];
13
} MenuValue;
14
15
typedef struct sChild{
16
  char text[16];
17
  uint8_tprev;
18
  uint8_tnext;
19
  MenuValue child[12];
20
} MenuChild;
21
22
typedef struct sMenu{
23
  char text[16];
24
  uint8_t prev;
25
  uint8_t next;
26
  MenuChild child[4];
27
} MenuEntry;

Wenn ich bei MenuEntry und MenuChild die Größe mit 16 angeb, 
funktioniert es auch. Nur sobald ich die Größe von .text bei MenuValue 
und MenuValuesValue auf 16 setze, funktioniert es nicht mehr.

Der Compiler meldet keinen Fehler, aber das Program am uC läuft nicht 
mehr.

Ich hab jetzt leider die genaue Deklaration meiner Struktur nicht hier, 
aber ich poste sie dann noch.

Kann mir da jemand helfen?

Danke, Markus

von Markus (Gast)


Lesenswert?

So, hier ist meine Deklaration:
1
#define MenuSystemKommunikation   {{"Aktiviert       ",2,1},{"Geschwindigkeit ",0,2},{"Adresse         ",1,0}}
2
#define MenuSystemUhrzeit       {{"Uhrzeit         ",1,1},{"Datum           ",0,0}}
3
#define MenuSystem           {{"Kommunikation   ",1,1,MenuSystemKommunikation},{"Uhrzeit / Datum ",0,0,MenuSystemUhrzeit}}
4
5
#define MenuBeleuchtungType     {{"Ansteuerung     ",0,0}}
6
#define MenuBeleuchtung       {{"Type            ",0,0,MenuBeleuchtungType}}
7
8
#define MenuTemperaturTemperaturen   {{"Tagestemperatur ",1,1},{"Absenktemperatur",0,0}}
9
#define MenuTemperaturZeitsteuerung {{"Zeitplanung     ",0,0}}
10
#define MenuTemperaturType       {{"Regelung        ",1,1},{"Thermostat      ",0,0}}
11
#define MenuTemperaturKalibrieren   {{"Raumtemperatur  ",1,1},{"Aussentemperatur",0,0}}
12
#define MenuTemperatur         {{"Temperaturen    ",3,1,MenuTemperaturTemperaturen},{"Zeitsteuerung   ",0,2,MenuTemperaturZeitsteuerung},{"Type            ",1,3,MenuTemperaturType},{"Kalibrieren     ",2,0,MenuTemperaturKalibrieren}}
13
14
#define MenuRolladenZeitsteuerung   {{"Zeitplanung     ",0,0}}
15
#define MenuRolladenKalibrieren    {{"jetzt kalib.    ",0,0}}
16
#define MenuRolladen         {{"Zeitsteuerung   ",1,1, MenuRolladenZeitsteuerung},{"Kalibrieren     ",0,0,MenuRolladenKalibrieren}}
17
18
#define MenuFernbedienungLernen   {{"Licht ein       ",11,1},{"Licht aus       ",0,2},{"Licht heller    ",1,3},{"Licht dunkler   ",2,4},{"Temp. wärmer    ",3,5},{"Temp. kühler    ",4,6},{"Temp. absenken  ",5,7},{"Temp. Tag       ",6,8},{"Rolladen auf    ",7,9},{"Rolladen ab     ",8,10},{"Rolladen öffnen ",9,11},{"Rolladen schl.  ",10,0}}
19
#define MenuFernbedienung       {{"Lernen          ",0,0,MenuFernbedienungLernen}}
20
21
const MenuEntry myMenu[5] = {
22
  {"System          ",4,1,MenuSystem},
23
  {"Beleuchtung     ",0,2,MenuBeleuchtung},
24
  {"Temperatur      ",1,3,MenuTemperatur},
25
  {"Rolladen        ",2,4,MenuRolladen},
26
  {"Fernbedienung   ",3,0,MenuFernbedienung},
27
};

von Karl H. (kbuchegg)


Lesenswert?

2 Dinge

1) wie verwendest du das Ganze? (Code)
2) Hast du dir schon mal ausgerechnet, wie gross deine komplette
   Menüstruktur ist?
   Wenn dir beim Ausrechnen etwas kleineres als 17kByte rauskommt,
   dann hast du dich verrechnet. Und da hab ich die ganzen uint8_t
   noch gar nicht mit eingerechnet, sondern nur die Strings.


Nichts gegen diese fix allokierten Arrays. Für kleinere Sachen kann man 
das schon so machen. Aber hier ist definitiv Schluss damit. Du musst 
lernen, wie man mit Verpointerungen arbeitet. Hilft nichts. Du 
allokierst hier ~20kB und mehr als die Hälfte des Speichers liegt 
komplett brach und/oder enthält Leerzeichen oder 0-en

Ich hab auch den Eindruck, dass deine 4-stufige Hierarchie zu 
kompliziert aufgebaut ist. Was machst du, wenn da noch eine Menüebene 
dazukommt? Ein MenuValueValueValues?

von Karl H. (kbuchegg)


Lesenswert?

Ich hab mich verrechnet. Ich hab übersehen, dass du ja 5 Stück MenuEntry 
anlegst. Damit verbraucht dein komplettes Menu insgesamt über 100kB an 
Speicher!

Und damit bist du in einem Bereich, in dem du mit pgm_read_byte und 
Konsorten schon gar nicht mehr arbeiten kannst, sondern FAR Zugriffe 
benutzen musst. Definitiv viel zu aufwändig, für so ein Micky Mouse 
Menü!

von Markus (Gast)


Lesenswert?

Das ist der Vorteil von diesen neuen WXGA-Displays ... da haben viele 
MenueValuesValuesValues ... Platz.

Du hast schon Recht, ich hab mir das ganze hier nochmal angesehen und 
muss sagen ... das kann nix ... ich denke, ich muss das anders machen. 
Ich glaub ich hab auch schon (wieder) eine (wieder so geistreiche?) Idee 
... aber es happert schon wieder hier.

Ich denke, ich werde es jetzt mal mit Pointern auf Funktionen probieren. 
Für jedes Menü eine eigene Funktion, in welcher die Navigation für das 
jeweilige Menü ist.

Ich hab hier einen Pointer, der ich eine Void zuweise. Aber wenn ich 
diese Void ausführen möchte, meint mein Compiler, ich habe eine 
Anweisung ohne Funktion ...

[c]
void MenuKommunikation(void);

void *myZeiger

int main(void) {

  while(1) {
    //prüfen auf Tasten und so und ggf:
    myZeiger;
  }
}

void MenuKommunikation(void) {
  //hier wird das Menü dargestellt

  //while(nicht auf zurück gedruckt wurde){

  //}
}

von Karl H. (kbuchegg)


Lesenswert?

Markus schrieb:

> Ich denke, ich werde es jetzt mal mit Pointern auf Funktionen probieren.
> Für jedes Menü eine eigene Funktion, in welcher die Navigation für das
> jeweilige Menü ist.

Ist eine Möglichkeit.

ABer besser wäre es, wenn du lernst erst mal mit normalen Pointer 
umzugehen


>
> Ich hab hier einen Pointer, der ich eine Void zuweise. Aber wenn ich
> diese Void ausführen möchte, meint mein Compiler, ich habe eine
> Anweisung ohne Funktion ...

Beschäftige dich definitiv zuerst mit normalen Pointern. Dir fehlt noch 
viel.
http://www.mikrocontroller.net/articles/FAQ#Funktionszeiger

von Albert .. (albert-k)


Angehängte Dateien:

Lesenswert?

Hallo
Die ganze Menustruktur im Flash unterzubringen wird etwas ugneschickt 
sein, da wirst du nciht viel gewinnen. Was sich aber lohnt sind die 
namen der Menüeinträge im Flash abzulegen. Im Anhang findest du mal ein 
Beispiel aus einem meiner Projekte wie es dort aussehen könnte. 
Zusätzlich auch noch die Headerdatei mit den typedefs für die Menu 
structs und die verwendeten Funktionen.
Das ganze stammt vom Projekt tinyMenu für AVR. Dort wurden die Strings 
aber im RAM abgelegt, nun liegen sie alle im Flash.

Im Hauptprogramm sieht das ganze dann so aus:
1
gvMenuEnter(&tdsMenuContext,&tdsMainMenu);
2
gvMenuDisplay(&tdsMenuContext);
3
4
while(1){
5
    s8MenuSelect = s8GetIncEncVal(4); //read Encoder delta
6
    //Encoder turned right
7
    if(s8MenuSelect > 0){
8
      gvMenuNextEntry(&tdsMenuContext);
9
10
    }
11
12
    //Encoder turned left
13
    if(s8MenuSelect < 0){
14
      gvMenuPrevEntry(&tdsMenuContext);
15
16
    }
17
18
    //Check if Button has been pushed
19
    if(u8GetIncEncButtonVal()){
20
      //call Select routine for the selected menu
21
      gvMenuSelect(&tdsMenuContext);
22
    }
23
}

von Markus (Gast)


Lesenswert?

Ich weiß, dass ich mit Pointern noch nicht viel am Hut habe, aber wie 
heißt es so schön? Der Mensch wächst mit seinen Aufgaben.

Ich werd mir jetzt mal überlegen, wie ich mein Menü ändern kann, so dass 
es den gewünschten Komfort bietet und trotzdem sparsam und klein ist.

von Karl H. (kbuchegg)


Lesenswert?

Vorschlag für eine rudimentäre Menüstruktur und Programmteilen, die 
dieses Menü ausdumpen. Getestet auf einem PC, daher die printf.
1
#include <stdio.h>
2
#include <string.h>
3
4
typedef unsigned char uint8_t;
5
6
struct ;
7
8
typedef struct sMenuChild
9
{
10
  const char*   text;
11
  uint8_t       prev;
12
  uint8_t       next;
13
  struct sMenu* subMenu;
14
} MenuChild;
15
16
typedef struct sMenu
17
{
18
  uint8_t     nrChilds;
19
  MenuChild*  childs;
20
} Menu;
21
22
#define NR_ENTRIES(x)  (sizeof(x)/sizeof(*x))
23
24
25
MenuChild commMenu[] = {
26
  { "Aktiviert",       2, 1, NULL },
27
  { "Geschwindigkeit", 0, 2, NULL },
28
  { "Adresse",         1, 0, NULL },
29
};
30
31
Menu menuComm = { NR_ENTRIES( commMenu ), commMenu };
32
33
34
MenuChild timeMenu[] = {
35
  { "Uhrzeit",         1, 1, NULL },
36
  { "Datum",           0, 0, NULL },
37
};
38
39
Menu menuTime = { NR_ENTRIES( timeMenu ), timeMenu };
40
41
42
MenuChild systemMenu[] = {
43
  { "Kommunikation",   1, 1, &menuComm },
44
  { "Uhrzeit / Datum", 0, 0, &menuTime },
45
};
46
47
Menu menuSystem = { NR_ENTRIES( systemMenu ), systemMenu };
48
49
50
MenuChild lightsMenu[] = {
51
  { "Ansteuerung",     0, 0, NULL },
52
};
53
54
Menu menuLights = { NR_ENTRIES( lightsMenu ), lightsMenu };
55
56
57
MenuChild mainMenu[] = {
58
  { "System",        4, 1, &menuSystem },
59
  { "Beleuchtung",   0, 2, &menuLights },
60
  { "Temperatur",    1, 3, NULL },
61
  { "Rolladen",      2, 4, NULL },
62
  { "Fernbedienung", 3, 0, NULL },
63
};
64
65
Menu menu = { NR_ENTRIES( mainMenu ), mainMenu };
66
67
void printIndent( uint8_t level )
68
{
69
  uint8_t i;
70
  for( i = 0; i < level; ++i )
71
    printf( " " );
72
}
73
74
75
void dumpMenu( Menu* whichMenu, uint8_t indent );
76
77
void dumpChilds( MenuChild* whichChild, uint8_t indent )
78
{
79
  printIndent( indent );
80
  printf( "%-16s %d %d\n", whichChild->text, (int)whichChild->next, (int)whichChild->prev );
81
82
  if( whichChild->subMenu )
83
    dumpMenu( whichChild->subMenu, indent + 2 ); 
84
}
85
86
void dumpMenu( Menu* whichMenu, uint8_t indent )
87
{
88
  uint8_t i;
89
90
  for( i = 0; i < whichMenu->nrChilds; ++i )
91
    dumpChilds( &whichMenu->childs[i], indent );
92
}
93
94
int main()
95
{
96
  dumpMenu( &menu, 0 );
97
98
  return 0;
99
}

Jedes Menu besteht aus einer gewissen Anzahl an Childs. Jeder Child kann 
selbst wieder einen Verweis zu einem Submenu enthalten (das dann 
seinerseits wieder Childs hat welche Submenues beinhalten können, 
welches wiederrum Childs enthält, welche ..... ad infinitum).

und so sieht der Dump aus
1
System           1 4
2
  Kommunikation    1 1
3
    Aktiviert        1 2
4
    Geschwindigkeit  2 0
5
    Adresse          0 1
6
  Uhrzeit / Datum  0 0
7
    Uhrzeit          1 1
8
    Datum            0 0
9
Beleuchtung      2 0
10
  Ansteuerung      0 0
11
Temperatur       3 1
12
Rolladen         4 2
13
Fernbedienung    0 3

Jede Einrückung kennzeichnet eine neue Menüebene, in der die zugehörigen 
Menüpunkte samt Verknüpfungsinformation (wozu brauchst du die 
eigentlich, ist doch sowieso durch die Reihenfolge der Einträge 
festgelegt) aufgelistet sind.

von Markus (Gast)


Lesenswert?

Ja, das macht natürlich Sinn ...

Die Verknüpfungs-Information brauche ich eigentlich nicht wirklich. Es 
war nur eine Version, die ich mir von jemanden aus dem Forum abgeschaut 
habe. Aber Du hast recht. Die Reihenfolge ist eh mit der Reihenfolge der 
Einträge festgelegt.

Wenn ich das richtig verstehe, werden nur die wirklich benötigten 
Einträge deklariert, nicht wie bei mir, dass das Array auf eine Größe 
von 12 deklariert ist, auch wenn es nur 5 Einträge hat ... nur weil ein 
anderes Menü 12 Einträge hat.

In diese Richtung ist sind auch meine Gedanken am Anfang gegangen, 
allerdings wurde mir im Forum versichert, dass es nicht möglich ist, die 
Größe eines Arrays zu ermitteln. Meiner Meinung nach, ist diese Funktion 
aber Voraussetzung (in meinem Fall) für eine "sparsame" Programmierung.

Ich denke, diese Zeile ist das, was ich gesucht hab.
1
#define NR_ENTRIES(x)  (sizeof(x)/sizeof(*x))

Ich werde Deinen Vorschlag am Wochenende gleich mal testen.

von Karl H. (kbuchegg)


Lesenswert?

Markus schrieb:
> Ja, das macht natürlich Sinn ...
>
> Die Verknüpfungs-Information brauche ich eigentlich nicht wirklich. Es
> war nur eine Version, die ich mir von jemanden aus dem Forum abgeschaut
> habe. Aber Du hast recht. Die Reihenfolge ist eh mit der Reihenfolge der
> Einträge festgelegt.

Die Verknüpfungsinformation kann man auch brauchen. Aber das ist dann 
ein komplett anderer Menüaufbau.
Die Datenstruktur ist dann nicht hierarchisch, sondern einfach nur ein 
Array (und du brauchst noch up und down Einträge um die Ebene wechseln 
zu können). D.h. alle Menüebenen sind in einem einzigen großen Array 
enthalten und nur mit den prev/next/up/down Einträgen wird in diesem 
Array navigiert bzw. die Menüs zur Laufzeit zusammengestellt.

> Wenn ich das richtig verstehe, werden nur die wirklich benötigten
> Einträge deklariert,

genau

> In diese Richtung ist sind auch meine Gedanken am Anfang gegangen,
> allerdings wurde mir im Forum versichert, dass es nicht möglich ist, die
> Größe eines Arrays zu ermitteln.

C-Buch!
Solange dein Array ein Array ist, funtioniert sizeof perfekt. Nur wenn 
du das Array an eine Funktion übergibst, dann übergibst du nicht 
wirklich das Array sondern einen Pointer auf das erste Array Element. 
sizeof liefert dann klarerweise die Größe eines Pointers.

Das ist aber ein anderer Schuh!
Hier ist das Array als Array verfügbar, daher kann man sizeof 
nutzbringend einsetzen.

> Meiner Meinung nach, ist diese Funktion
> aber Voraussetzung (in meinem Fall) für eine "sparsame" Programmierung.

Nimms mir nicht übel.
Aber Voraussetzung für eine 'sparsame' Programmierung ist, dass man 
seinen Werkzeugkasten kennt. Soll heißen, dass man C nicht nur 
oberflächlich durch sammeln von Halbwissen in Foren lernt, sondern dass 
man ein C-Buch durcharbeitet und die Sprache systematisch in all ihren 
Facetten lernt. Da bleibt dann immer noch genügend übrig, was nicht in 
den Büchern steht.

von Markus (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Nimms mir nicht übel.
>
> Aber Voraussetzung für eine 'sparsame' Programmierung ist, dass man
>
> seinen Werkzeugkasten kennt. Soll heißen, dass man C nicht nur

Nehm ich Dir nicht übel. Ich geb Dir da vollkommen recht, das man 
Bescheid weiß und sparsam und performant zu Programmieren. Allerdings 
sind das zwei komplett verschiedene Sachen. Ich kann nicht sparsam 
Programmieren, so lange ich die jeweiligen Funktionen nicht kenne, klar 
... andererseits kann man auch nicht sparsam Programmieren, wenn man die 
Sprach zwar perfekt beherscht, aber die Technologien die man dafür 
braucht, nicht vorhanden sind.

von Karl H. (kbuchegg)


Lesenswert?

Bei seinen ersten Programmierschritten, ist 'sparsames Programmieren' 
noch gar nicht gefragt. Es soll nicht ausser Acht gelassen werden, ok, 
aber es hat für einen Neuling nicht die Top-Priorität. So wie ein 
Radfahr-Neuling seine Top-Priorität auf dem Obenhalten auf dem Rad hat 
und nicht darauf, welche Atmentechnik er über 2500m Seehöhe für eine 
Alpenüberquerung braucht.

von Markus (Gast)


Lesenswert?

Ja, so sehe ich das schon auch. Aber ich denke, speziel bei uCs ist es 
wichtig, dass man eben diese Sache nicht aus den Augen verlieren darf. 
Besonders wichtig ist es dann, wenn man auf einem uC mit gewissen 
Ressourcen entwickelt und sein Programm dann auch auf andere portieren 
möchte ...

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.