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
typedefstructsValueValue{
2
constchar*text;
3
intprev;
4
intnext;
5
charvalue;
6
}MenuValueValues;
7
8
typedefstructsValue{
9
constchar*text;
10
intprev;
11
intnext;
12
MenuValueValuesvalues[];
13
}MenuValue;
14
15
typedefstructsChild{
16
constchar*text;
17
intprev;
18
intnext;
19
MenuValuechild[];
20
}MenuChild;
21
22
typedefstructsMenu{
23
constchar*text;
24
intprev;
25
intnext;
26
MenuChildchild[];
27
}MenuEntry;
Dann mache ich ein Array, welches ich manuell fülle:
1
MenuEntrymyMenu[];
2
3
voidInitMenu(void)
4
{
5
6
MenuEntrymyI1;
7
myI1.text="System ";
8
myI1.prev=4;
9
myI1.next=1;
10
11
MenuChildmyC1;
12
myC1.text="Kommunikation ";
13
myC1.prev=1;
14
myC1.next=1;
15
16
MenuValuemyV1;
17
myV1.text="Aktiviert ";
18
myV1.prev=0;
19
myV1.next=0;
20
21
MenuValueValuesmyVV1;
22
myVV1.text="Ja ";
23
myVV1.prev=1;
24
myVV1.next=1;
25
myVV1.value=1;
26
myV1.values[0]=myVV1;
27
28
MenuValueValuesmyVV2;
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
MenuChildmyC2;
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
MenuEntrymyI2;
48
myI2.text="Beleuchtung ";
49
myI2.prev=0;
50
myI2.next=2;
51
52
MenuChildmyC3;
53
myC3.text="Type ";
54
myC3.prev=0;
55
myC3.next=0;
56
myI2.child[0]=myC3;
57
58
myMenu[1]=myI2;
59
60
MenuEntrymyI3;
61
myI3.text="Temperatur ";
62
myI3.prev=1;
63
myI3.next=3;
64
65
MenuChildmyC4;
66
myC4.text="Temperaturen ";
67
myC4.prev=3;
68
myC4.next=1;
69
myI3.child[0]=myC4;
70
71
MenuChildmyC5;
72
myC5.text="Zeitsteuerung ";
73
myC5.prev=0;
74
myC5.next=2;
75
myI3.child[1]=myC5;
76
77
MenuChildmyC6;
78
myC6.text="Type ";
79
myC6.prev=1;
80
myC6.next=3;
81
myI3.child[2]=myC6;
82
83
MenuChildmyC7;
84
myC7.text="Kalibrieren ";
85
myC7.prev=2;
86
myC7.next=0;
87
myI3.child[3]=myC7;
88
89
myMenu[2]=myI3;
90
91
MenuEntrymyI4;
92
myI4.text="Rolladen ";
93
myI4.prev=2;
94
myI4.next=4;
95
96
MenuChildmyC8;
97
myC8.text="Zeitsteuerung ";
98
myC8.prev=0;
99
myC8.next=0;
100
myI4.child[0]=myC8;
101
102
myMenu[3]=myI4;
103
104
MenuEntrymyI5;
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
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:
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.
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
typedefstructsValueValue{
2
constchar*text;
3
intprev;
4
intnext;
5
charvalue;
6
}MenuValueValues;
7
8
typedefstructsValue{
9
constchar*text;
10
intprev;
11
intnext;
12
MenuValueValuesvalues[];
13
}MenuValue;
14
15
typedefstructsChild{
16
constchar*text;
17
intprev;
18
intnext;
19
MenuValuechild[];
20
}MenuChild;
21
22
typedefstructsMenu{
23
constchar*text;
24
intprev;
25
intnext;
26
MenuChildchild[4];
27
}MenuEntry;
28
29
30
staticconstMenuChildMenuSystem[2]={
31
{"Kommunikation ",1,1},
32
{"Uhrzeit / Datum ",0,0},
33
};
34
35
staticconstMenuChildMenuBeleuchtung[1]={
36
{"Type ",0,0},
37
};
38
39
staticconstMenuChildMenuTemperatur[4]={
40
{"Temperaturen ",3,1},
41
{"Zeitsteuerung ",0,2},
42
{"Type ",1,3},
43
{"Kalibrieren ",2,0},
44
};
45
46
staticconstMenuChildMenuRolladen[1]={
47
{"Zeitsteuerung ",0,0},
48
};
49
50
staticconstMenuChildMenuFernbedienung[1]={
51
{"Lernen ",0,0},
52
};
53
54
staticconstMenuEntrymyMenu[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).
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?
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.
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"
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
typedefstructsValueValue{
2
chartext[16];
3
uint8_tprev;
4
uint8_tnext;
5
uint8_tvalue;
6
}MenuValueValues;
7
8
typedefstructsValue{
9
chartext[16];
10
uint8_tprev;
11
uint8_tnext;
12
MenuValueValuesvalues[5];
13
}MenuValue;
14
15
typedefstructsChild{
16
chartext[16];
17
uint8_tprev;
18
uint8_tnext;
19
MenuValuechild[12];
20
}MenuChild;
21
22
typedefstructsMenu{
23
chartext[16];
24
uint8_tprev;
25
uint8_tnext;
26
MenuChildchild[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
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?
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ü!
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){
//}
}
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
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:
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.
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.
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.
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.
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.
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.
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 ...