Forum: Compiler & IDEs Menüstruktur für Grafikdisplay


von Robert (Gast)


Lesenswert?

Hallo an alle!

Ich bin gerade am entwerfen ein Menüstrukur für ein Grafikdisplay. Die 
Eingabe erfolgt über ein Touch Panel vor dem Display.

Wenn ich ein Menü habe, wird zuerst die Grundmaske (mit Buttons oder 
Ähnlichem) als bmp von der SD Karte. Natürlich ändern sich danach 
diverse Werte am Display.

Wenn ich am Display mehrere Buttons für ein nächstes Menü habe, wie rufe 
ich die komfortabel auf. Beziehungsweise, wie komm ich vom aufgerufenen 
Menü wieder zurück ins vorige?

Wie lässt sich so etwas am Besten realisieren?

Danke im Voraus
Gruß Robert

von Ben (Gast)


Lesenswert?

Mit einer Zustandsmaschine. Eine Taste ist ein Ereignis, das eine Aktion 
ausloest und dann den Zustand des Systems aendert. Die Zustandsmschine 
ist im Main() und nur dort wird gewartet. Nirgendwo sonst.

Schluesselwort : State-Event-Diagram, State-Event-Action-Diagram

von Robert (Gast)


Angehängte Dateien:

Lesenswert?

Danke für den Tipp mit der State Machine. Nur wie soll ich das am besten 
angehen?

Ich hab für jeden Menüpunkt eine Funktion, die das BMP lädt und auch die 
Werte am Display. Wie komm ich bei Button Back ins letzte Menü, muss ich 
ja irgendwo zwischenspeichern. Beim Sprung ins nächste Menü, einer 
Variable den aktuellen Menüpunkt speichern?

mfg Robert

von Robert (Gast)


Lesenswert?

Ich hab mal was geschrieben:
1
uint8_t state = MAIN;
2
uint8_t back = MAIN;
3
4
void (*statemachine[])() = {menueMain, contrast,...};
5
6
void menueMain(void)
7
{
8
  uint16_t txpos, typos;
9
  
10
  loadBitmapSD("main.bmp", 0, 0, LCD_WIDTH, LCD_HEIGHT);
11
  
12
  while(state == MAIN)
13
  {
14
    printTime();
15
    
16
    getCursorPosition(&txpos, &typos);
17
    
18
    if(BUTTON_CONTR)
19
      state = CONTRAST;
20
    else
21
      if(BUTTON_CLOCK)
22
        state = CLOCK;
23
      else
24
        if(BUTTON_SENSOR)
25
          state = SENSOR;
26
          if(BUTTON_LIGHT)
27
            state = LIGHT
28
          else
29
            if(BUTTON_MP3)
30
              state = MP3;
31
            else
32
              if(BUTTON_SETTINGS)
33
                state = SETTINGS;
34
  }  
35
  back = MAIN;
36
}
37
38
void contrast(void)
39
{
40
  uint16_t txpos, typos;
41
  uint8_t contrLast = contr, lightLast = light;
42
43
  loadBitmapSD("contrast.bmp", 0, 0, LCD_WIDTH, LCD_HEIGHT);
44
45
  while(state == CONTRAST)
46
  {
47
    getCursorPosition(&txpos, &typos);
48
49
    if(BUTTON_BACK)
50
      state = back;
51
    else
52
      if(BUTTON_CONTR_+)
53
      {
54
        contr += 8;
55
        setPot(CONTRAST_POTI, contr);
56
      }
57
      else
58
        if(BUTTON_CONTR_-)
59
        {
60
          contr -= 8;
61
          setPot(CONTRAST_POTI, contr);
62
        }
63
        else
64
          if(BUTTON_LIGHT_+)
65
          {
66
            light += 8;
67
            setPot(CONTRAST_POTI, light);
68
          }
69
          else
70
            if(BUTTON_LIGHT_-)
71
            {
72
              light -= 8;
73
              setPot(CONTRAST_POTI, light);
74
75
            }
76
    
77
    if(contrLast != contr)
78
    {
79
      FillRect(CONTR_BALK_X, CONTR_BALK_Y, CONTR_BALK_WIDTH, CONTR_BALK_HEIGHT, CLEAR);
80
      FillRect(CONTR_BALK_X, CONTR_BALK_Y, CONTR_BALK_WIDTH, CONTR_BALK_HEIGHT, SET);
81
82
      contrLast = contr;
83
    }
84
85
    if(lightLast != light)
86
    {
87
      FillRect(LIGHT_BALK_X, LIGHT_BALK_Y, LIGHT_BALK_WIDTH, LIGHT_BALK_HEIGHT, CLEAR);
88
      FillRect(LIGHT_BALK_X, LIGHT_BALK_Y, LIGHT_BALK_WIDTH, LIGHT_BALK_HEIGHT, SET);
89
90
      lightLast = light;
91
    }
92
  }
93
  back = MAIN;
94
}

Main:
1
int main(void)
2
{
3
   while(1)
4
      (*statemachine)[state]();
5
   return 0;
6
}

Kann das so funktionieren?
mfg Robert

von TheMason (Gast)


Lesenswert?

mal einen tipp :

mach dir enums deiner tasten. dann kannst du die auszuführenden aktionen 
besser in eine case anweisung packen. (macht die sache übersichtlicher)
die tastenabfrage kannst du ja evtl in einem unterprogramm machen (dann 
evtl als übergabe-parameter den kontext, dann kannst du ganz gezielt nur 
die tasten zurückgeben die du für das jeweilige menü brauchst, 
erleichtert die state-behandlung)


von Robert (Gast)


Lesenswert?

Wie meinst du das??

Das Problem, ich muss erkennen wo ich gedrückt habe.

BUTTON_CLOCK steht zum Beispiel für:
1
#define BUTTON_CLOCK   (txpos > 10 && txpos < 30) && (typos > 30 && typos < 50)

Wie soll ich das mit enum machen?
mfg Robert

von Ulrich (Gast)


Lesenswert?

Funktioniert das?:
      (*statemachine)[state]();

von Oliver (Gast)


Lesenswert?

Im ATMEL butterfly source code gibt es eine funktionierende 
statemachine. Eine port Versio für gcc findest du hier:
http://www.siwawi.arubi.uni-kl.de/avr_projects/index.html#bf_app


Oliver

von Rolf Magnus (Gast)


Lesenswert?

> BUTTON_CLOCK steht zum Beispiel für:
>
> #define BUTTON_CLOCK   (txpos > 10 && txpos < 30) && (typos > 30 &&
> typos < 50)

Ich würde den Buttons erstmal eine Struktur spendieren. Sowas wie:
1
struct button
2
{
3
    uint8_t min_x, min_y,
4
            max_x, max_y;   // Umrisse des Buttons
5
    char*   bitmap_name;    // Name des Bildes
6
    void (*action)(void);   // Zeiger auf Funktion beim Drücken
7
};

Ein Menü könnte dann z.B. einfach ein Array aus Buttons sein. Wenn der 
Touchscreen berührt wird, kannst du dann dieses Array einfach per 
Schleife durchlaufen, um zu testen, in welchem Button der Berührpunkt 
liegt. Dann rufst du die Funktion des gefundenen Buttons auf.
Zurück ins darüberliegende Menü kommst du einfach mit einem 
"Back"-Button, dessen Aktion eben das Anzeigen jenen Menüs ist.

von Robert (Gast)


Lesenswert?

Hallo Rolf

Welchen Vorteil bringt mir das??
Im endeffekt brauch ich ja dann für jeden Button eine Solche Struktur. 
Ist das icht ein bisschen RAM Verschwendung?
1
struct Button
2
{
3
    uint8_t min_x, min_y,
4
            max_x, max_y;   // Umrisse des Buttons
5
    char*   bitmap_name;    // Name des Bildes
6
    void (*action)(void);   // Zeiger auf Funktion beim Drücken
7
} button[10];

zB button[0] = Button Clock Settings.

in der Main Menü dann?? das verstehe ich nicht ganz.
Rekursiv soll das ganze ja nicht verzweigt sein, oder?? Da hab ich ja 
eine Stack Overflow Gefahr.

mfg Robert

von Robert (Gast)


Lesenswert?

Beziehungsweise, welchen Nachteil habe ich bei meiner Implementiereung?

von Rolf Magnus (Gast)


Lesenswert?

> Welchen Vorteil bringt mir das??

Der Programmcode wird einfacher und übersichtlicher, weil du nicht mehr 
die ganze Menüstruktur in Code nachbilden mußt, sondern als Variable 
definierst. Das macht auch Änderungen und Hinzufügen neuer Menüs 
einfacher.

> Ist das icht ein bisschen RAM Verschwendung?

Kannst sie ja auch als Flash-Variablen anlegen.

von Robert (Gast)


Lesenswert?

Hallo Rolf,

Somit bruache ich dann für jedden Back Button in einem Menü eine eigene 
Struktur, oder?

Wenn ich ein Array von Button nehme, dan gilt das nur fürr ein Menü, 
oder?
1
struct Button butMain[6] = {{10,10,25,25, "contrast.bmp", menContrast},
2
                            {........................................}};

das nächste Araay dann zB struct Button butContrast[6] {{......}};

Warum wird der NAme des Bildes auch im Button gespeichert? Wenn ich zB 
die Funktion menContrast danach aufrufe, wird das Bild ja von der 
Funktion geladen, oder?

Nur wie schaut das dann aus, wenn ich Buttons habe, mit denen ich keine 
neues Menü lade, sondern eine Variabe oder irgendetwas anderes ändere zB 
Button_PLUS??

Zur Abfrage, in der jeweiligen Funktion hab ich noch eine Frage:

Wie schaut das dann aus?

In einer Schleife lese ich zuerst die Touch werte ein und Vergleich dann 
mit den Buttons. Und dann, wenn ein BUtton gedrückt wurde? Wie gehts 
dann weiter?
1
while(!pressed)
2
{
3
   pressed = getTouchWerte(&txpos, &typos);
4
5
   for(i = 0; i < MENU_PUNKTE; i++)
6
   {
7
      if(compareButton(txpos, typos, &butCotrast[i])
8
      {
9
          //Was hier??
10
      }
11
   }
12
}

Passt das so??

danke im Voraus
mfg Robert


Danke im Voraus
mfg Robert

von Karl H. (kbuchegg)


Lesenswert?

Robert wrote:
> Hallo Rolf,
>
> Somit bruache ich dann für jedden Back Button in einem Menü eine eigene
> Struktur, oder?

Ja.

>
> Wenn ich ein Array von Button nehme, dan gilt das nur fürr ein Menü,
> oder?

Sinnvollerweise wird man das so machen.

>
>
1
> struct Button butMain[6] = {{10,10,25,25, "contrast.bmp", menContrast},
2
>                             {........................................}};
3
>

Das siund also alle Buttons, die im Main Menü beisammen sind.

>
> das nächste Araay dann zB struct Button butContrast[6] {{......}};

... und alle Buttons vom Contrast Menü

Ja. So kann man das machen.

> Warum wird der NAme des Bildes auch im Button gespeichert?

Da gitb es ev. ein Missverständnis. Welche Aufgabe hat denn das
Bild? Ich, und wohl auch Rolf sind davon ausgegangen, dass das
Bild ein Bild des Buttons ist. Wenn dem nicht so ist, dann fliegt
das Bild natürlich raus.

> Wenn ich zB
> die Funktion menContrast danach aufrufe, wird das Bild ja von der
> Funktion geladen, oder?

Das versteh ich jetzt nicht.
Nochmal: Welche Aufgabe hat das Bild? Hat das was mit einem
Button zu tun? Wenn ja: Bild blribt in der Struktur. Wenn nein:
Bild fliegt raus.

>
> Nur wie schaut das dann aus, wenn ich Buttons habe, mit denen ich keine
> neues Menü lade, sondern eine Variabe oder irgendetwas anderes ändere zB
> Button_PLUS??

Beim Betätigen eines Buttons wird eine Funktion aufgerufen.
Immer!
Was diese Funktion dann mancht, ist nicht mehr das Bier des
Buttons! Die Button-Steuerung interessiert sich nur für Button:
Wo die am Display sind, ob sie gedrückt wurden, welcher gedrückt
wurde, ev. eine Graphik um den Benutzer anzuzeigen, dass
ein Button gedrückt wurde. Alles darüber hinausgehende, insbesondere
welche Funktionalität beim Drücken eines Buttons abläuft,
interessiert die Buttonsteuerung nicht. Als Folge des Drückens
eines Buttons wird eine Funktion aufgerufen. Welche das ist, steht
in der Struktur drinnen.

>
> Zur Abfrage, in der jeweiligen Funktion hab ich noch eine Frage:
>
> Wie schaut das dann aus?
>
> In einer Schleife lese ich zuerst die Touch werte ein und Vergleich dann
> mit den Buttons.

Exakt

> Und dann, wenn ein BUtton gedrückt wurde? Wie gehts
> dann weiter?

Dann nimmst du vom identifiziertem Button die Struktur her und
holst dir von dort den Pointer auf die Funktion, die aufzurufen
ist. Die Funktion wird dann aufgerufen.

>
>
1
> while(!pressed)
2
> {
3
>    pressed = getTouchWerte(&txpos, &typos);
4
> 
5
>    for(i = 0; i < MENU_PUNKTE; i++)
6
>    {
7
>       if(compareButton(txpos, typos, &butCotrast[i])
8
>       {
9
>           //Was hier??
10
>       }
11
>    }
12
> }
13
>


http://www.mikrocontroller.net/articles/FAQ#Funktionszeiger

von Robert (Gast)


Lesenswert?

Hallo an alle,

Das mit dem Bild ist ein Missverständnis. Das Bild des B uttons, wird am 
Menüanfang automatisch mit der ganzen Maske geladen. Somit fliegt der 
Bildname raus.

Ich dachte mit Bild, das wäre der Name des Bildes der folgenden 
Menümaske...


Jetzt hab ich noch eine Frage, wenn ich nach der Überprüfung in der 
Schleife einen gefunden Button habe und die Funktion dann aufrufe, wird 
da nicht der Stack mit den Rücksprungadressen belastet??

Denn man kommt ja zu der vorigen Funktion nicht mehr zurück, oder? (Es 
sei denn, mitdem Button gelangt man NICHT in ein nächstes Menü, sondern 
man verändert nur eine Variable)

Gruß Robert

von Karl H. (kbuchegg)


Lesenswert?

Robert wrote:

> Das mit dem Bild ist ein Missverständnis. Das Bild des B uttons, wird am
> Menüanfang automatisch mit der ganzen Maske geladen. Somit fliegt der
> Bildname raus.

ALles klar

>
> Jetzt hab ich noch eine Frage, wenn ich nach der Überprüfung in der
> Schleife einen gefunden Button habe und die Funktion dann aufrufe, wird
> da nicht der Stack mit den Rücksprungadressen belastet??

Ja natürlich, so wie es bei jedem Funktionsaufruf ist.

> Denn man kommt ja zu der vorigen Funktion nicht mehr zurück, oder? (Es
> sei denn, mitdem Button gelangt man NICHT in ein nächstes Menü, sondern
> man verändert nur eine Variable)

Das ist keine gute Idee.
Auf lange Sicht ist es besser wenn du eine strikte Menühierarchie
hast, anstatt Menüs wahllos miteinander zu verknüpfen.
Eine Menüstruktur sollte eine saubere Baumstruktur sein und
kein Netzwerk.

(Auf der anderen Seite: Wenn du unbedingt ein Netzwerk bauen
willst, geht auch das, nur muss man das dann anders aufbauen)

von Robert (Gast)


Lesenswert?

Die Bleastungs des Stacks ist sicher nicht gut, da man ja hier 
irgendwann einen Stackoverflow hat...

Wie meinst du sonst. du meintest ja, in der if Abfrage soll ich die 
Funktion des Buttons aufrufen. Oder ist das anders besser zu lösen?
1
while(!pressed)
2
{
3
   pressed = getTouchWerte(&txpos, &typos);
4
5
   for(i = 0; i < MENU_PUNKTE; i++)
6
   {
7
      if(compareButton(txpos, typos, &butCotrast[i])
8
      {
9
          butContrast[i].action();
10
      }
11
   }
12
}

  

von Oliver (Gast)


Lesenswert?

http://www.siwawi.arubi.uni-kl.de/avr_projects/index.html#bf_app

Schau es dir doch einfach mal an. Da ist alles drin, was du brauchst.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Robert wrote:
>
1
> while(!pressed)
2
> {
3
>    pressed = getTouchWerte(&txpos, &typos);
4
> 
5
>    for(i = 0; i < MENU_PUNKTE; i++)
6
>    {
7
>       if(compareButton(txpos, typos, &butCotrast[i])
8
>       {
9
>           butContrast[i].action();
10
>       }
11
>    }
12
> }
13
>

Das ist schon ok so.

Wenn du aber ein Netz aufbauen willst, dann muss jede
Funtion zwangsweise wieder zu dieser Schleife zurückkehren.
Aber: Niemand sagt, dass du alle Buttons immer anzeigen
musst. Du hast also Buttons, die dann durchaus alle an
derselben Stelle im Display liegen können, von denen
aber nur einer aktiv ist. Aktiv bedeutet: Die Anzeige-
routine malt ihn hin, die Auswerteschleife berücksichtigt
ihn. Alle nicht aktiven Buttons werden einfach ignoriert
(sowohl beim Zeichnen als auch beim Auswerten). Ein
komplettes 'Menü' ist dann einfach nur noch eine Datenstruktur,
die beschreibt, welche Buttons jetzt gerade aktiv sind und
welche nicht. Eine Bearbeitungsfunktion hat dann die Möglichkeit
ein anderes 'Menü' zu aktivieren, wodurch sich bei der Rückkehr
zu dieser Schleife eine andere Anzeige ergibt, bzw. der Aufruf
anderer Funktionen möglich wird.
1
struct Button buttons[] =   // hier sind alle überhaupt möglichen
2
                            // Buttons gespeichert
3
  { { "Help", 10, 10, 20, 20, false, HandleHelp },
4
    { "ABC",  30, 10, 20, 20, false, HandleABC },
5
    ...
6
  };
7
8
struct Button* Main[] = {   // zum Main Menü gehören diese Buttons
9
     &buttons[0],
10
     &buttons[1]
11
};
12
13
struct Button* ABC[] = {   // zum ABC Menü gehören diese Buttons
14
     ...
15
};
16
17
  ...
18
19
  ActivateMenu( Main );
20
  ShowActiveButtons();
21
22
  while(!pressed)
23
  {
24
     pressed = getTouchWerte(&txpos, &typos);
25
  
26
     for(i = 0; i < MENU_PUNKTE; i++)
27
     {
28
        if(compareButton(txpos, typos, &button[i])
29
        {
30
            button[i].action();
31
            ShowActiveButtons();
32
        }
33
     }
34
  }

ActivateMenu klappert die Datenstruktur 'Main' ab (das wird
wahrscheinlich einfach nur ein Array sein, in dem Verweise
auf die Buttons gespeichert sind, die zu diesem Menü gehören)
und setzt die entsprechenden Buttons auf aktiv, alle anderen
auf inaktiv.
ShowActiveButtons pinselt nur aktive Buttons und compareButton
akzeptiert einen Button nur dann als gedrückt, wenn er auch aktiv
ist.

Die Behandlungsfunktion macht dann ihren Stiefel und aktiviert
vielleicht andere Buttons und kehrt zur Hauptschleife zurück
(die dann die neuen aktiven Buttons hinpinselt und darauf wartet
dass einer gedrückt wird)
1
void HandleABC()
2
{
3
  ....
4
5
  ActivateMenu( ABC );
6
}
Der Wege gibt es viele. Sei kreativ.

von Robert (Gast)


Lesenswert?

So jetzt wirds kompliziert für mich. ;), Aber eagl...

Es gibt ein feld, in dem alle Buttons, die irgendwo im Programm 
vorkommen, gespeichert sind.

Weiters gibt es für jedes Menü ein feld von Zeigern auf die 
dazugehörigen Buttons.

Für was ist der String in der Struktur des Buttons?

ActivateMenu() aktiviert die zum Menü gehörigen Buttons, also die, die 
im Feld aus Zeigern auf Buttons des Menü besteht, oder??

Wie meinst du das genau auf aktiv setzen??

Zum ShowButton, es gibt Buttons, die außerhalb des displays liegen, und 
somit auch nicht angezeigt werden können. Das sind einfach Buttons, die 
fix sind (das Touch Panel ist größer als das display) zum Beispiel Back, 
Main oder ein anderer Button.

So werden die Buttons ja nicht mir als ein Bild vom Menü geladen, 
sonderen immer der aktive Button einzeln auf das Display geladen.

Und die Abfrage des touchscreens wird nur in dieser einen 
Verarbeitungsmenüfunktion aufgerufen. Die Funktion button[i].action() 
zeichnet ein neues Menü auf das Display (Bild von der SD Karte), und 
aktiviert ein neues Menü;
1
struct Button *active;
2
3
void handleABC(void)
4
{
5
   active = ABC;
6
7
   loadBitmapSD("imageABC.bmp", 0, 0, LCD_WIDTH, LCD_HEIGHT);
8
9
   ....
10
}

mfg Robert


von Karl H. (kbuchegg)


Lesenswert?

Robert wrote:
> So jetzt wirds kompliziert für mich. ;), Aber eagl...
>
> Es gibt ein feld, in dem alle Buttons, die irgendwo im Programm
> vorkommen, gespeichert sind.

Yep.

>
> Weiters gibt es für jedes Menü ein feld von Zeigern auf die
> dazugehörigen Buttons.

Yep.

>
> Für was ist der String in der Struktur des Buttons?

Die Malroutine sollte dann doch wissen welchen Text
sie in den Button hineinschreiben soll :-)

>
> ActivateMenu() aktiviert die zum Menü gehörigen Buttons, also die, die
> im Feld aus Zeigern auf Buttons des Menü besteht, oder??

Genau.
Jedem Button verpasst du in der Struktur noch einen boolschen
Wert der angibt, ob der Button aktiv ist oder nicht.

Die ActivateFunktion hat dann die Aufgabe:
  Alle Buttons mal auf inaktiv setzen.
  Vom übergebenen Menü alle Buttons durchzugehen
  und die jeweiligen Buttons auf aktiv zu setzen
1
void ActivateMenu( Button* Menu, uint8_t NrMenuButtons )
2
{
3
  uint8_t i;
4
5
  // mal alle Buttons auf inaktiv
6
  for( i = 0; i < NrAllButtons; ++i )
7
    buttons[i].Active = FALSE;
8
9
  // und jetzt die vom Menue wieder aktivieren
10
  for( i = 0; i < NrMenuButtons; ++i )
11
    buttons[i].Active = TRUE;
12
}
>
> Wie meinst du das genau auf aktiv setzen??

Ein Button sieht so aus:
1
struct Button {
2
  char     Text[20];
3
  uint8_t  PosX;
4
  uint8_t  PosY;
5
  uint8_t  SizeX;
6
  uint8_t  SizeY;
7
  uint8_t  Active;
8
};
d.h. von jedem Button ist bekannt:
  welcher Text angezeigt werden soll
  wo am Display der Button mit welcher Groesse angezeigt
    werden soll
  ob er ueberhaupt angezeigt/ausgewertet/berücksichtigt werden soll

Oft ist es zb. noch Sinnvoll einem Button noch ein Flag "Enabled"
zu verpassen. Das ist dann ein Button, der zwar angezeigt aber
nicht gedrückt werden kann (weil es den Menüpunkt zwar gibt,
er aber in der jetzigen Situation keinen Sinn macht). Musst
du entscheiden ob du das brauchst.

>
> Zum ShowButton, es gibt Buttons, die außerhalb des displays liegen, und
> somit auch nicht angezeigt werden können. Das sind einfach Buttons, die
> fix sind (das Touch Panel ist größer als das display) zum Beispiel Back,
> Main oder ein anderer Button.

?
Wie betätigst du einen Button der nicht sichtbar ist?

>
> So werden die Buttons ja nicht mir als ein Bild vom Menü geladen,
> sonderen immer der aktive Button einzeln auf das Display geladen.

Ganz genau. Die Show Funktion macht im Grunde:
1
vois ShowActiveButtons()
2
{
3
  uint8_t i;
4
5
  for( i = 0; i < NrAllButtons; ++i ) {
6
7
    // nur aktive Buttons werden berücksichtigt
8
    if( buttons[i].Active ) {
9
      MaleRechteck( buttons[i].PosX, buttons[i].PosY,
10
                    buttons[i].SizeX, buttons[i].SizeY );
11
      MaleText( buttons[i].PosX, buttons[i].PosY, buttons[i].Text );
12
    }
13
  }
14
}

>
> Und die Abfrage des touchscreens wird nur in dieser einen
> Verarbeitungsmenüfunktion aufgerufen.

Ganz genau.
Wenn irgendjamdn ein Menü manipulieren will, dann kann er das
tun, indem er für den entsprechenden Button den Eintrag
'Active' auf TRUE oder FALSE setzt.

> Die Funktion button[i].action()
> zeichnet ein neues Menü auf das Display

nein das tut sie nicht. Die action Funktion
kann ein Bild von der SD Karte hinmalen.
Die action Funktion kann auch die entsprechenden
Buttons aktivieren oder deaktivieren, aber
sie zeichnet nicht das Menü. Das Menü wird
gezeichnet, wenn die action Funktion ihren
return gemacht hat. Dann landet die Programm-
ausführung wieder beim Aufruf der action Funktion
und gleich dahinter wird standardmässig immer das
Menü neu hingemalt.

> (Bild von der SD Karte), und
> aktiviert ein neues Menü;

das allerdings tut sie.

von Robert (Gast)


Lesenswert?

Danke für die ausführliche Antwort :)

Zu den Buttons, die es nicht auf dem Sisplay gibt. Das Touch Panel ist 
viel größer als das Display. dasten wie zurück, Hilfe oder Enter gibt es 
bei vieleswn Menüs. Die werden außerhalb des Displybereiches angelegt. 
HIer klebe ich eine Karte/Folie von innen auf des Touch auf dem Der 
Menüpukt steht. Ich hoffe das ist verständlich.

Bei der Struktur fehlt aber der Funktionszeiger.

An die Funktion ActivateMenu, wird dann das Feld von Zeigern auf Buttons 
übergeben, oder? Die Anzahl der einzelnen Buttons,, mss ich vorher 
wissen und mitübergeben. So ist das gemeint oder??

Noch eine Frage, zur Struktur, kann ich schreiben char* text; oder muss 
ich hier fix die Anzahl der Elemente vorgeben?

mfg Robert

von Karl H. (kbuchegg)


Lesenswert?

Robert wrote:
> Danke für die ausführliche Antwort :)
>
> Zu den Buttons, die es nicht auf dem Sisplay gibt. Das Touch Panel ist
> viel größer als das Display. dasten wie zurück, Hilfe oder Enter gibt es
> bei vieleswn Menüs. Die werden außerhalb des Displybereiches angelegt.
> HIer klebe ich eine Karte/Folie von innen auf des Touch auf dem Der
> Menüpukt steht. Ich hoffe das ist verständlich.

OK. Jetzt versteh ich das.

>
> Bei der Struktur fehlt aber der Funktionszeiger.

:-)
Ich bitte das zu entschuldigen.

>
> An die Funktion ActivateMenu, wird dann das Feld von Zeigern auf Buttons
> übergeben, oder? Die Anzahl der einzelnen Buttons,, mss ich vorher
> wissen und mitübergeben. So ist das gemeint oder??

Genau so ist das gemeint.
Aber lass die Größe den Compiler bestimmen:

struct Button buttons[] =
  { { ... },
    { ... },
    ...
  };

#define NrAllButtons ( sizeof( buttons ) / sizeof( buttons[0] ) )

struct Button* Main[] = {   // zum Main Menü gehören diese Buttons
     &buttons[0],
     &buttons[1]
};

#define NrMainButtons ( sizeof( Main ) / sizeof( Main[0] ) )

Die Anzahl der definerten Buttons ist die Größe des
kompletten Arrays (in Bytes) geteilt durch die Größe eines
einzelnen Elements (in Bytes).

Der Compiler errechnet das zur Compilezeit und setzt
die errechnete Zahl ein.

>
> Noch eine Frage, zur Struktur, kann ich schreiben char* text; oder muss
> ich hier fix die Anzahl der Elemente vorgeben?

Die Menütexte werden sich ja nicht ändern, also kannst
du auch einen Pointer auf einen konstanten Text angeben.

Allerdings: wenn du die ganzen Buttons ins Flash verschiebst
(und das wirst du irgendwann), dann gibt es da eine Falle:

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Vereinfachung_f.C3.BCr_Zeichenketten_.28Strings.29_im_Flash
>
> mfg Robert

von Robert (Gast)


Lesenswert?

Danke, jetzt ist vieles erstmal geklärt :)

Also bei char text[] anstelle von char *text, gibts keine Probleme beim 
verschieben in den Flash. Zum Glück hab ich genug Ram (64kB Extern :) )

Jetzt werd ich erstmal schreiben ;) Irgendwann nächste Woche kommt dann 
die Hardware :)

mfg Robert

von Dirk (Gast)


Lesenswert?

Danke Karl Heinz für die Nachwelt sollten deine Beispiele ins Wiki 
verschoben werden.

von Robert S. (razer) Benutzerseite


Lesenswert?

Ich hab jetzt noch eine Frage zum Aufruf von activateButton. Da gibt mir 
der Compiler immer eine Fehlermeldung.

So ist butMain definiert:
1
sButton *butMain[] = { &button[1],
2
          &button[3],
3
          &button[9],
4
          &button[12],
5
          &button[14]};
6
#define BUT_ANZ_MAIN  (sizeof(*main[]) / sizeof(sButton *))

In der Funktion menMain() ruf ich activateButton() auf:
1
void menMain(void)
2
{
3
  loadMenuScreen("menMain.men");
4
  activateButton(butMain, BUT_ANZ_MAIN);
5
}

Fehlermeldung vomm GCC:
1
menu.c:372: warning: implicit declaration of function `activateButton'
2
menu.c:372: error: `main' undeclared (first use in this function)
3
menu.c:372: error: (Each undeclared identifier is reported only once
4
menu.c:372: error: for each function it appears in.)
5
menu.c:372: error: syntax error before ']' token

Zeile 372 ist di, in der activateButton() aufgerufen wird.

Ich hoffe es kann wer helfen

von Karl H. (kbuchegg)


Lesenswert?

>
1
> 
2
> sButton *butMain[] = { &button[1],
3
>           &button[3],
4
>           &button[9],
5
>           &button[12],
6
>           &button[14]};
7
> #define BUT_ANZ_MAIN  (sizeof(*main[]) / sizeof(sButton *))
8
>

Das letzte sollte heissen

#define BUT_ANZ_MAIN  (sizeof(butMain)/ sizeof(*butMain))

Immer: Die Größe über alles  ( sizeof(butMain) ) dividiert
durch die Größe des ersten Elementes  ( sizeof(*butMain) )

>
> In der Funktion menMain() ruf ich activateButton() auf:
>
1
> void menMain(void)
2
> {
3
>   loadMenuScreen("menMain.men");
4
>   activateButton(butMain, BUT_ANZ_MAIN);
5
> }
6
>
>
> Fehlermeldung vomm GCC:
> [pre]
> menu.c:372: warning: implicit declaration of function `activateButton'

'Implizit declaration' bedeutet immer, dass eine Funktion
verwendet wurde, bevor der Compiler die Funktion selbst oder
einen Prototypen dafür gesehen hat.

Wie zb hier:
1
int main()
2
{
3
  foo();
4
}
5
6
void foo()
7
{
8
}

Immer dran denken: Der Compiler geht deinen Quellcode von oben
nach unten durch. Und zwar nur einmal!
Wenn er also in main() auf den Aufruf von foo() stößt, dann weiss
er zu diesem Zeitpunkt noch nichts über foo(). Also nimmt
er Standardannahmen an. Die können stimmen, müssen aber nicht
stimmen (im obigen wären die Standardannahmen falsch. Standard-
annahme ist, dass die Funktion einen int returniert. foo ist
aber eine void-Funktion). Daher warnt der Compiler.

Abhilfe:
Wenn geht, dann zieh die Funktionsdefinition vor die erste
Verwendung:
1
void foo()
2
{
3
}
4
5
int main()
6
{
7
  foo();
8
}

Jetzt hat der Compiler foo() bereits gesehen, bevor der Aufruf
erfolgt. Er hat daher alle Informationen die er braucht um den
Aufruf korrekt zu machen und braucht keine Annahmen treffen.

Geht das Vorziehen (aus welchen Gründen auch immer) nicht, dann
macht man einen Prototypen:
1
void foo(void);   // Prototyp: Es gibt eine Funktion foo
2
3
int main()
4
{
5
  foo();
6
}
7
8
void foo()
9
{
10
}

Ein Protoyp ist in Kurzform einfach nur eine Funktionsdekleration:
So sieht die Funktion aus, diese Argumente nimmt sie und
diesen Returntyp hat sie. Die Details der Implementierung
gibt es woanders.

> menu.c:372: error: `main' undeclared (first use in this function)
> menu.c:372: error: (Each undeclared identifier is reported only once
> menu.c:372: error: for each function it appears in.)
> menu.c:372: error: syntax error before ']' token

Na ja. Was war bei dir in ...

#define BUT_ANZ_MAIN  (sizeof(*main[]) / sizeof(sButton *))

... falsch. Schau dir die Variablennamen genau an.

Wenn du willst kannst du auch ein Makro für solche Dinge
machen:

#define ARRAY_SIZE(x)  (sizeof(x) / sizeof(*x))

und das dann benutzen:

#define BUT_ANZ_MAIN    ARRAY_SIZE( butMain )

von Robert S. (razer) Benutzerseite


Lesenswert?

Danke für die Erklärung.

das mit deen Prototypen weiß ich, hab ich auch oben einen geshrieben. 
activateButton() ist auch die erste Funktion im C File. Trotzdem wirds 
als implicit declaration gemeldet.

Ich hab jetzt auch eine fehlermeldunf, als ob der Linker die Funktion 
nicht kennt: D:\Eigene 
Dateien\Elektronik\Projekte\Grafikdisplay\Source/menu.c:372: undefined 
reference to `activateButton'

ka warum

von Karl H. (kbuchegg)


Lesenswert?

Robert Schilling wrote:
> Danke für die Erklärung.
>
> das mit deen Prototypen weiß ich, hab ich auch oben einen geshrieben.
> activateButton() ist auch die erste Funktion im C File. Trotzdem wirds
> als implicit declaration gemeldet.
>
> Ich hab jetzt auch eine fehlermeldunf, als ob der Linker die Funktion
> nicht kennt: D:\Eigene
> Dateien\Elektronik\Projekte\Grafikdisplay\Source/menu.c:372: undefined
> reference to `activateButton'

Am häufigsten hat man so ein Problem, wenn es einen Tippfehler
gibt. Du hast vielleicht beim Aufruf 'ActiveButton' geschrieben
oder sonst irgendeinen lausigen Tippfehler. Zeichen für zeichen
vergleichen. Oder einfach den Funktionsnamen an der Definition
mit Cut&Paste ausschneiden (kopieren) und an der Aufrufstelle
noch mal neu einsetzen. Dann kannst du sicher gehen, dass
die Schreibweisen übereinstimmen.

Ansonsten: poste das Programm. Du hast irgendwo anders einen
lausigen Fehler drinnen, der sich so zeigt.

von Robert S. (razer) Benutzerseite


Lesenswert?

Ja war ein Tippfehler, danke. Jetzt gehts :)

von Dirk (Gast)


Lesenswert?

Karl Heinz B. schrieb:

>Die Menütexte werden sich ja nicht ändern, also kannst du auch einen Pointer
>auf einen konstanten Text angeben.
>Allerdings: wenn du die ganzen Buttons ins Flash verschiebst(und das wirst
>du irgendwann), dann gibt es da eine Falle:

Sollte man trotz Speichermedium SD Card die Daten lieber im internen 
Flash halten? Wenn ja, nur um Speicher zusparen o. gibt es noch mehr 
Argumente?

Gruß,
Dirk

von Karl H. (kbuchegg)


Lesenswert?

Dirk wrote:
> Karl Heinz B. schrieb:
>
>>Die Menütexte werden sich ja nicht ändern, also kannst du auch einen Pointer
>>auf einen konstanten Text angeben.
>>Allerdings: wenn du die ganzen Buttons ins Flash verschiebst(und das wirst
>>du irgendwann), dann gibt es da eine Falle:
>
> Sollte man trotz Speichermedium SD Card die Daten lieber im internen
> Flash halten? Wenn ja, nur um Speicher zusparen o. gibt es noch mehr
> Argumente?
>

Das ist eine "Ja/Nein" Frage.

Wenn du Flash frei hast, dann lass doch das Zeugs inm Flash
und freue dich an kurzen Zugriffszeiten und einfachen Funktionen
zum Auslesen

Wenn du den Speicher nicht hast und dir die Zugriffszeit
bzw. der kompliziertere Zugriff egal sind, dann leg sie
ins Flash.

Wenn du Texte/Strukturen auf einfache Art auswechseln können
möchtest dann ist eine wechselbare SD Karte mit einem FAT-
Dateisystem sicher eine gute Lösung.

Du bestimmst was für dich sinnvoll ist.

von Rolf Magnus (Gast)


Lesenswert?

> Wenn du den Speicher nicht hast und dir die Zugriffszeit
> bzw. der kompliziertere Zugriff egal sind, dann leg sie
> ins Flash.

Du meintest sicher die SD-Karte. Die ist zwar auch Flash, aber halt 
unter dem Namen schwer vom eingebauten Flash zu unterscheiden ;-)

von Karl H. (kbuchegg)


Lesenswert?

Rolf Magnus wrote:
>> Wenn du den Speicher nicht hast und dir die Zugriffszeit
>> bzw. der kompliziertere Zugriff egal sind, dann leg sie
>> ins Flash.
>
> Du meintest sicher die SD-Karte. Die ist zwar auch Flash, aber halt
> unter dem Namen schwer vom eingebauten Flash zu unterscheiden ;-)

Äh. Ja natürlich.
Danke

von menuchaos (Gast)


Lesenswert?

Hallo,

entschuldigt, dass ich diesen sehr angestaubten Thread wieder 
hervorzerre, aber ich kämpfe gerade mit dem Problem, das hier diskutiert 
worden ist.

Ich habe mir die Ansätze, die von Karl heinz Buchegger damals 
vorgeschlagen worden sind, durchgelesen und habe versucht sie 
nachzuvollziehen. Allerdings bekam ich an der folgenden Stelle (vgl.: 
Beitrag "Re: Menüstruktur für Grafikdisplay" ), das Problem, dass 
der Compiler die Parameterliste der ActivateMenu Funktion nicht mochte:

Karl heinz Buchegger schrieb:
> void ActivateMenu( Button* Menu, uint8_t NrMenuButtons )
                   ^^^^
könnte es sein das hier ein "struct" fehlt?

Und wenn dem so ist, wie können in der Funktion:
1
void ActivateMenu( Button* Menu, uint8_t NrMenuButtons )
2
{
3
  uint8_t i;
4
5
  // mal alle Buttons auf inaktiv
6
  for( i = 0; i < NrAllButtons; ++i )
7
    buttons[i].Active = FALSE;
8
9
  // und jetzt die vom Menue wieder aktivieren
10
  for( i = 0; i < NrMenuButtons; ++i )
11
    buttons[i].Active = TRUE;
12
}

alle Buttons auf inaktiv gesetzt werden, um danach mit Hilfe des 
Pointerarray, welches über den Parameter "Menu" übergeben worden ist, 
alle dort referenzierten Buttons auf aktiv zu setzen?  Beisst sich das 
nicht? Vermutlich habe ich die Funktionsweise noch nicht ganz richtig 
geblickt. :)

Würde mich sehr freuen, wenn mir hier jemand weiterhelfen könnte.

Grüße,
menuchaos

von Karl H. (kbuchegg)


Lesenswert?

menuchaos schrieb:

> Ich habe mir die Ansätze, die von Karl heinz Buchegger damals
> vorgeschlagen worden sind, durchgelesen und habe versucht sie
> nachzuvollziehen. Allerdings bekam ich an der folgenden Stelle (vgl.:
> Beitrag "Re: Menüstruktur für Grafikdisplay" ), das Problem, dass
> der Compiler die Parameterliste der ActivateMenu Funktion nicht mochte:
>
> Karl heinz Buchegger schrieb:
>> void ActivateMenu( Button* Menu, uint8_t NrMenuButtons )
>                    ^^^^
> könnte es sein das hier ein "struct" fehlt?

Kann sein.

> Und wenn dem so ist, wie können in der Funktion:
>
1
void ActivateMenu( Button* Menu, uint8_t NrMenuButtons )
2
> {
3
>   uint8_t i;
4
> 
5
>   // mal alle Buttons auf inaktiv
6
>   for( i = 0; i < NrAllButtons; ++i )
7
>     buttons[i].Active = FALSE;
8
> 
9
>   // und jetzt die vom Menue wieder aktivieren
10
>   for( i = 0; i < NrMenuButtons; ++i )
11
>     buttons[i].Active = TRUE;
12
> }
>
> alle Buttons auf inaktiv gesetzt werden, um danach mit Hilfe des
> Pointerarray, welches über den Parameter "Menu" übergeben worden ist,
> alle dort referenzierten Buttons auf aktiv zu setzen?  Beisst sich das
> nicht? Vermutlich habe ich die Funktionsweise noch nicht ganz richtig
> geblickt. :)

Ich hab jetzt die Details auch nicht mehr im Kopf. Aber IMHO ist da ein 
Tippfehler. Das müsste so aussehen.
1
void ActivateMenu( Button* Menu, uint8_t NrMenuButtons )
2
{
3
  uint8_t i;
4
5
  // mal alle Buttons auf inaktiv
6
  for( i = 0; i < NrAllButtons; ++i )
7
    buttons[i].Active = FALSE;
8
9
  // und jetzt die vom Menue wieder aktivieren
10
  for( i = 0; i < NrMenuButtons; ++i )
11
    Menu[i]->Active = TRUE;
12
}

Alles andere macht keinen Sinn.

von menuchaos (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Ich hab jetzt die Details auch nicht mehr im Kopf. Aber IMHO ist da ein
> Tippfehler. Das müsste so aussehen.

Hut ab, dass war die Lösung! :)
1
void ActivateMenu(struct Button* Menu[], uint8_t NrMenuButtons)  {
2
3
  uint8_t i;
4
5
  // mal alle Buttons auf inaktiv
6
  for( i = 0; i < NrAllButtons; ++i )
7
    buttons[i].Active = 0;
8
9
  // und jetzt die vom Menue wieder aktivieren
10
  for( i = 0; i < NrMenuButtons; ++i )
11
    Menu[i]->Active = 1;
12
13
  return;
14
}

Allerdings, ohne dem "struct" vor "Button* Menu[]" mag der Compiler das 
nicht. ;)

Vielen Dank für deine Hilfe!

Grüße,
menuchaos

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.