Forum: Compiler & IDEs Funktionszeiger


von Sven D. (Gast)


Lesenswert?

Morgen! Ich grüße Euch!

Ich habe ein kleines Problemchen und hoffe, daß mir jemand helfen kann.

Ich versuche krampfhaft ein Menü für mein Display zu erstellen.

Ich bekomme aber immer eine Fehlermeldung :
main.c:44: error: expected ';', ',' or ')' before 'MainMenu'

Bei dieser Zeile:

...
void DoMenu( int NrEntries, struct MenuEntry[] Menu )
...


Hier ist der Code:
( habe ich von 
http://www.mikrocontroller.net/articles/FAQ#Me%C3%BCs%20mit%20Funktionszeiger 
)

Kann mir jemand sagen warum ?

Danke im voraus.

von Sven D. (Gast)


Lesenswert?

Sorry der Link noch einmal :

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

und dann einpaar zeilen drunter :

Menü mit Funktionszeigern

von Karl H. (kbuchegg)


Lesenswert?

Sven D. wrote:

> Ich bekomme aber immer eine Fehlermeldung :
> main.c:44: error: expected ';', ',' or ')' before 'MainMenu'
>
> Bei dieser Zeile:
>
> ...
> void DoMenu( int NrEntries, struct MenuEntry[] Menu )
> ...

Das kanns nicht sein. In dieser Zeile kommt kein einziges mal der 
Begriff MainMenu vor. Schau mal in die unmittelbar vorhergehende Zeile.

> Hier ist der Code:
> ( habe ich von
> http://www.mikrocontroller.net/articles/FAQ#Me%C3%BCs%20mit%20Funktionszeiger
> )

Das kann nicht der vollständige Code sein. Dort sind lediglich ein paar 
Fragmente.

> Kann mir jemand sagen warum ?

entweder du hast irgendwo einen Tippfehler und dich bei einem Namen 
vertippt oder die STrukturdefinition ist nicht vor der ersten Verwendung 
oder der eigentliche Fehler ist ganz was anderes und die Fehlermeldung 
ist missverständlich oder ....

Zeig den ganzen Code, dann kann man mehr sagen.

von Sven D. (Gast)


Lesenswert?

Hallo Karl.

Hier ist mein Code:

Ich möchte ein Menü aufbauen...
Wie man aber ersehen kann, ist es noch ein langer weg bis dahin...
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include "glcd.h"
4
#include "font_small.h"
5
#include <avr/pgmspace.h>
6
7
typedef void (*VoidFnct)(void);
8
9
struct MenuEntry {
10
  char Text[20];
11
  VoidFnct Function;
12
};
13
14
void HandleEdit()
15
{
16
}
17
 
18
void HandleCopy()
19
{
20
}
21
 
22
void HandlePaste()
23
{
24
}
25
 
26
struct MenuEntry MainMenu[] = {
27
 { "Edit", HandleEdit },
28
 { "Copy", HandleCopy },
29
 { "Paste", HandlePaste }
30
};
31
 
32
#define ARRAY_SIZE(X) ( sizeof(X) / sizeof(*(X)))
33
 
34
35
void DoMenu( int NrEntries, struct MenuEntry[] Menu )
36
{
37
  int i;
38
  int Auswahl;
39
  
40
  do {
41
42
    for( i = 0; i < NrEntries; ++i )
43
44
  lcd_set_cursor(1,LINE2);lcd_puts_p(small_font,PSTR("MENÜPUNKT 1"));
45
  lcd_set_cursor(1,LINE3);lcd_puts_p(small_font,PSTR("MENÜPUNKT 2"));
46
  lcd_set_cursor(1,LINE4);lcd_puts_p(small_font,PSTR("MENÜPUNKT 3"));
47
  lcd_set_cursor(1,LINE5);lcd_puts_p(small_font,PSTR("MENÜPUNKT 4"));
48
 
49
50
lcd_set_cursor(1,LINE5);lcd_puts_p(small_font,PSTR("Exit - Taster 3"));
51
 
52
  if(PIND&(1<<PIND2)){Auswahl=1;}
53
  if(PIND&(1<<PIND3)){Auswahl=1;}
54
 
55
56
    if(PIND&(1<<PIND4)) //Taster 3
57
      return;
58
 
59
    Auswahl = Auswahl - 1;
60
    if( Auswahl < 0 || Auswahl > NrEntries )
61
62
lcd_set_cursor(1,LINE7);lcd_puts_p(small_font,PSTR("Ungültige Eingabe"));
63
 
64
    else
65
66
      Menu[Auswahl].Function();
67
  }
68
}
69
70
71
int main()
72
{
73
74
DoMenu( ARRAY_SIZE( MainMenu ), MainMenu );
75
 
76
  while( 1 )
77
    ;
78
}

von Stefan E. (sternst)


Lesenswert?

1
void DoMenu( int NrEntries, struct MenuEntry[] Menu )
->
1
void DoMenu( int NrEntries, struct MenuEntry Menu[] )

von Sven D. (Gast)


Lesenswert?

Danke Stefan.
Das hat mich weiter gebracht. Fehler ist weg.
Jedoch mein Menü klappt überhaupt nicht.

Hast Du Erfahrungen mit einfachen one level Menüs???

Sind überhaupt Funktionszeiger das Richtige?

von Karl H. (kbuchegg)


Lesenswert?

Sven D. wrote:
> Danke Stefan.
> Das hat mich weiter gebracht. Fehler ist weg.
> Jedoch mein Menü klappt überhaupt nicht.

Wundert mich nicht
1
void DoMenu( int NrEntries, struct MenuEntry[] Menu )
2
{
3
  int i;
4
  int Auswahl;
5
  
6
  do {
7
8
    for( i = 0; i < NrEntries; ++i )
9
10
  lcd_set_cursor(1,LINE2);lcd_puts_p(small_font,PSTR("MENÜPUNKT 1"));
11
  lcd_set_cursor(1,LINE3);lcd_puts_p(small_font,PSTR("MENÜPUNKT 2"));
12
  lcd_set_cursor(1,LINE4);lcd_puts_p(small_font,PSTR("MENÜPUNKT 3"));
13
  lcd_set_cursor(1,LINE5);lcd_puts_p(small_font,PSTR("MENÜPUNKT 4"));

Wozu brauchst du dir for-Schleife, wenn du dann sowieso die Menütexte 
hardcoded hinschreibst?

1
  if(PIND&(1<<PIND2)){Auswahl=1;}
2
  if(PIND&(1<<PIND3)){Auswahl=1;}
3
 
4
5
    if(PIND&(1<<PIND4)) //Taster 3
6
      return;
7
 
8
    Auswahl = Auswahl - 1;
9
    if( Auswahl < 0 || Auswahl > NrEntries )

Die Art und Weise wie 'Auswahl' hoch bzw. runter gezählt wird, solltest 
du nochmal überdenken. Edit: Wo wird Auswahl eigentlich hoch gezählt?

> Sind überhaupt Funktionszeiger das Richtige?

Klar sind sie das. Aber die Funktionspointer sind ja noch gar nicht dein 
Problem. Dein ganzer Textaufbau bzw. Auswertung von Tastendrücken stimmt 
hinten und vorne nicht.

Wenn dir das ganze Menü noch zu undurchsichtig ist, dann gehs 
schrittweise an. Konzentrier dich einfach mal nur darauf, die Texte aus 
dem Array korrekt anzuzeigen. Und erst dann, wenn das klappt, kümmerst 
du dich um die Tastenauswertung. Und wenn das klappt, kümmerst du dich 
darum, dass die richtige Funktion aufgerufen wird.

von Sven D. (Gast)


Lesenswert?

Ok Karl.
Ich werde es so angehen wie du das vorgeschlagen hat.
Du hast Recht. Ich habe einfach ein Code aus dem Tutorial kopiert,
ohne zu wissen was da eigentlich passiert.

Ich fange also alles wieder von vorne an.

Könntest du mir ein bißchen behilflich sein? Ein Kopfnicken reicht aus.

1. Der Aufbau
Mein Menü besteht aus 4 Menüpunkten. One Level d.h. Keine Untermenüs
--------------
Menüpunkt 1
Menüpunkt 2
Menüpunkt 3
Menüpunkt 4
--------------
Ich habe 3 Taster zu Verfügung.

Taster 1. nach oben (optisches hervorheben des menüpunktes z.B. rechteck 
drumrum )
Taster 2. nach unten (optisches hervorheben der menüpunktes z.B. 
rechteck drumrum )
Taster 3. Bestätigen der Auswahl.

läßt sich das auch mit switch realisieren oder mit normalen if abragen?

Was wäre sinnvoller ???

Vielen Dank für alle Anregungen

von Sven D. (Gast)


Angehängte Dateien:

Lesenswert?

So würde es auf dem Display aussehen...

Ich glaube was einfacheres kann man nicht machen...

von Karl H. (kbuchegg)


Lesenswert?

Sven D. wrote:

Die erste Frage, die du dir stellen musst, lautet:
Brauche ich ein universelles Menüsystem (so wie du das aus der FAQ 
übernommen hast) oder reicht etwas ganz simples.

Da du anscheinend nur 1 Menü hast und für jeden Menüpunkt feststeht was 
er zu tun hat und es keinerlei sontigen Problemkreise gibt, würde ich 
mal sagen: Es reicht etwas ganz Simples.

Das bedeutet: Du brauchst kein grossartiges Array mit Texten.
Mach dir eine Funktion, die die 4 Texte an die korrekten Positionen 
hinschreibt und fertig.

Dann solltest du dir überlegen, wie du herausstellst, welcher Menüpunkt 
momentan der aktive ist. Aktiv in dem Sinne, dass dieser Menüpunkt 
gewählt wird, wenn der Benutzer die entsprechende Aktion (meist das 
Drücken eines Buttons) wählt. Wie du das machst ist deien Sache, ich 
schlage jetzt der Einfachheit halber mal vor, dass der entsprechende 
Menüpunkt in der 1. Spalte ein '*' erhält. Die Auswertung der 
Tastendrücke für rauf und runter bedeutet daher nichts anderes, als das 
natürlich mitgezählt werden muss, in welcher Zeile man gerade ist, und 
dass dieser '*' seinen Platz wechseln muss. Am einfachsten geschieht 
dies, indem in allen 4 möglichen Zeilen in der *-Spalte ein Leerzeichen 
geschrieben wird und dann der * in der richtige Zeile in die *-Spalte 
platziert wird.

Also fangen wir mal an. Du brauchst eine Funktion, die die Menüpunkte 
ausgibt. Dabei berücksichtigen wir gleich mal, dass es eine *-Spalte 
geben wird und legen diese als Spalte 1 fest. Die Texte können also 
frühestens in Spalte 2 anfangen
1
void DrawMenuTexts()
2
{
3
  lcd_set_cursor( 2, LINE2 );
4
  lcd_puts_p( small_font, PSTR("MENÜPUNKT 1") );
5
6
  lcd_set_cursor( 2, LINE3 );
7
  lcd_puts_p( small_font, PSTR("MENÜPUNKT 2") );
8
9
  lcd_set_cursor( 2, LINE4 );
10
  lcd_puts_p( small_font, PSTR("MENÜPUNKT 3") );
11
12
  lcd_set_cursor( 2, LINE5 );
13
  lcd_puts_p( small_font, PSTR("MENÜPUNKT 4") );
14
}

Und gleich mal ausprobieren!

....

Was brauchst du noch. Eine Funktion, die den * richtig platziert.
Dazu braucht die Funktion die Kenntnis, welche Zeile die auszuwählende 
ist. Gut, damit wissenb wir welche Parameter die Funktion haben wird, 
nämlich die Nummer des Menüpunktes. Wie in C üblich, soll dabei die 
Zählung der Menüpunkte bei 0 beginnen.
Was hat sie zu tun: Alle *-Spalten löschen und in die korrekte (die mit 
dem übergebenen Menüpunkt) einen * setzen.

Na das sollte doch nicht allzu schwer sein.
1
void ActivateEntry( uint8_t Nr )
2
{
3
  lcd_set_cursor( 1, LINE2 ); lcd_putc( ' ' );
4
  lcd_set_cursor( 1, LINE3 ); lcd_putc( ' ' );
5
  lcd_set_cursor( 1, LINE4 ); lcd_putc( ' ' );
6
  lcd_set_cursor( 1, LINE5 ); lcd_putc( ' ' );
7
8
  lcd_set_cursor( 1, LINE2 + Nr ); lcd_putc( '*' );
9
}

Ausprobieren !
1
int main()
2
{
3
   ....
4
5
   DrawMenuTexts();
6
   ActivateEntry( 0 );
7
8
   while( 1 )
9
     ;
10
}

Wenn du jetzt beim ActivateEntry Aufruf in main() andere Werte von 0 bis 
3 eingibst, muss der * in jeweils einer anderen Zeile auftauchen!
Auch mal einen anderen Test machen
1
int main()
2
{
3
   uint8_t Active;
4
5
   ....
6
7
   DrawMenuTexts();
8
9
   Active = 0;
10
   while( 1 ) {
11
     ActivateEntry( Active );
12
     Active++;
13
     if( Active == 4 )
14
       Active = 0;
15
16
     delay_ms( 1000 );
17
   }
18
}

Der * muss im Sekundentakt von einer Zeile zur nächsten hüpfen.

Wenn das klappt, ist der nächste Teil drann: Abfrage der Tasten und eine 
Variable, nenn sie ruhig weiter Active, je nach Tastendruck entweder 
erhöhen oder verringern. Dabei aufpassen, dass die Grenzen 0 und 3 
eingehalten werden. Nach jeder Veränderung von Active wird ActivateEntry 
aufgerufen, damit der * in die richtige Spalte hüpft.
1
void HandleButtons()
2
{
3
  uint8_t Active = 0;
4
5
  ActivateEntry( Active );
6
7
  while( 1 )
8
  {
9
    if( PIND & ( 1<<PIND2 ) )
10
    {
11
      if( Active < 3 )
12
      {
13
        Active++;
14
        ActivateEntry( Active );
15
      }
16
    }
17
18
    if( PIND & ( 1 << PIND3 ) )
19
    {
20
      if( Active > 0 )
21
      {
22
        Active--;
23
        ActivateEntry( Active );
24
      }
25
    }
26
  }
27
}

Neues main schreiben und das ganze testen!
1
...
2
int main()
3
{
4
   ...
5
6
   DrawMenuTexts();
7
   HandleButtons();
8
}

Tja. und jetzt tut sich ein neues Problem auf. Die Abfrage der Tasten 
ist so untauglich. Die Schleife in HandleButtons wird viel zu schnell 
abgearbeitet, als das du gezielt einen Menüpunkt auswählen kannst. Was 
ist das Problem: Die entsprechenden Active-Erhöhungen/Erniedrigungen 
werden nicht ausgeführt wenn eine Taste gedrückt wird, sondern 
solange eine Taste gedrückt ist. Und das rasend schnell.

Also wird das jetzige Projekt mal zur Seite gelegt und ein neues 
Testprojekt angefangen um dieses Problem zu studieren und eine Lösung 
dafür zu finden.
Keine Sorge: In der Codesammlung existiert dazu was. Schau einfach mal 
rein, eigentlich müsstest du das finden

Wenn du dann weist, wie man das richtig macht, dann baust du das in 
obiges ein und .... testest erst mal, ob das ganze benutzbar ist.

Und so geht das Schritt für Schritt weiter.
Fällt dir was auf. Bis jetzt sind sicherlich schon ein paar Stunden 
vergangen, und wir haben uns bisher noch keinerlei Gedanken darüber 
gemacht, was denn eigentlich passieren soll, wenn ein Menüpunkt 
aktiviert wird :-)

Daher ist die Frage, ob du besser if-then-else oder switch-case nimmst 
eine Frage, die sich im Moment für dich noch gar nicht stellt. Du hast 
jetzt am Anfang ganz andere Dinge zu tun. Aber: Wenn du schrittweise an 
die Sache rangehst, dann wirst du irgendwann ein fertiges Menü haben.

von Sven D (Gast)


Lesenswert?

Oj Oj Oj.

Danke Dir Karl. Das sind schon mal sehr gute Vorschläge.
Ich muß mir das erstmal genau anschauen, und dann versuche ich
mein Glück. Ich melde mich heute Abend und sage wie weit ich bin.

Aber vorerst möchte ich mich für die vielen Tips bedanken.

Bis später.

P.S. @Karl.
MAchst Du das alles hobbymäßig oder beruflich? Du scheinst ja  wirklich
viel darüber zu wissen...

Gruß

von Sven D. (Gast)


Lesenswert?

Hallo an Alle!

Besonderes Hallo an Karl! Du hast es drauf :o)

Ich wollte mich nochmal herzlich bedanken.
Dein Code war wirklich super. Eigentlich hast Du alles nötige gemacht.
Ich habe es noch ein bißchen erweitert und das Problem so gelöst.
Es ist nicht optimal aber es funktioniert.

Vielleicht hilft es irgendwann Jemanden, der solch ein einfaches Menü 
sucht...

Falls Du eine optimalere Lösung hast, ich bin für alle Vorschläge offen.
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
#include "glcd.h"
5
#include "font_small.h"
6
#include "image_load.h"
7
8
9
void DrawMenuTexts()
10
{
11
  lcd_set_cursor(43,LINE0+6);lcd_puts_p(small_font,PSTR("MENÜPUNKT 1"));
12
  lcd_set_cursor(43,LINE2+5);lcd_puts_p(small_font,PSTR("MENÜPUNKT 2"));
13
  lcd_set_cursor(43,LINE4+4);lcd_puts_p(small_font,PSTR("MENÜPUNKT 3"));
14
  lcd_set_cursor(43,LINE6+3);lcd_puts_p(small_font,PSTR("MENÜPUNKT 4"));
15
}
16
17
void ActivateEntry( uint8_t Nr )
18
{
19
    switch (Nr)   // Menü-Umrandung zuordnen
20
    {
21
      case 0:
22
        Nr=2;  
23
        break;
24
      case 1:
25
        Nr=17;
26
        break;
27
      case 2:
28
        Nr=32;
29
        break;
30
      case 3:
31
        Nr=47;
32
        break;
33
    }
34
35
    lcd_draw_rect(25,2,80,14,WHITE); //Rahmen um den MENÜPUNKT1 entfernen
36
    lcd_draw_rect(25,17,80,14,WHITE);//Rahmen um den MENÜPUNKT2 entfernen
37
    lcd_draw_rect(25,32,80,14,WHITE);//Rahmen um den MENÜPUNKT3 entfernen
38
    lcd_draw_rect(25,47,80,14,WHITE);//Rahmen um den MENÜPUNKT4 entfernen
39
40
      lcd_draw_rect(25,Nr,80,14,BLACK);//Rahmen um den MENÜPUNKTX zeichnen
41
}
42
43
void HandleButtons()
44
{
45
  uint8_t Active = 0;
46
47
  ActivateEntry( Active );
48
49
  while( 1 )
50
  {
51
    if( PIND & ( 1<<PIND2 ) )
52
    {
53
      if( Active < 3 )
54
      {
55
        Active++;
56
        ActivateEntry( Active );
57
      }
58
    }
59
60
    if( PIND & ( 1 << PIND3 ) )
61
    {
62
      if( Active > 0 )
63
      {
64
        Active--;
65
        ActivateEntry( Active );
66
      }
67
    }
68
69
70
    if( PIND & ( 1 << PIND4 ) )
71
  {
72
73
    _delay_ms(10);    //Taste entprellen
74
75
          switch (Active) 
76
     {
77
     case 0:
78
        menu0_show();
79
        break;
80
     case 1:
81
        menu1_show();
82
        break;
83
     case 2:
84
        menu2_show();
85
        break;
86
     case 3:
87
        menu3_show();
88
        break;
89
       }
90
  }
91
92
  }
93
}
94
95
96
void menu0_show()//MENÜPUNKT1
97
{
98
99
  lcd_clear();
100
  lcd_set_cursor(43,LINE0);lcd_puts_p(small_font,PSTR("MENÜPUNKT 1"));
101
  
102
  lcd_set_cursor(43,LINE7);lcd_puts_p(small_font,PSTR("Zurueck mit Taste 3"));
103
104
  _delay_ms(10);    //Taste entprellen
105
  while(1)
106
  {
107
  if( PIND & ( 1 << PIND4 ) ){show_menu();}
108
  }
109
110
}
111
112
void menu1_show()//MENÜPUNKT2
113
{
114
115
  lcd_clear();
116
  lcd_set_cursor(43,LINE0);lcd_puts_p(small_font,PSTR("MENÜPUNKT 2"));  
117
118
  lcd_set_cursor(43,LINE7);lcd_puts_p(small_font,PSTR("Zurueck mit Taste 3"));  
119
120
  _delay_ms(10);    //Taste entprellen
121
  while(1)
122
  {
123
  if( PIND & ( 1 << PIND4 ) ){show_menu();}
124
  }
125
}
126
127
void menu2_show()//MENÜPUNKT3
128
{
129
130
  lcd_clear();
131
  lcd_set_cursor(43,LINE0);lcd_puts_p(small_font,PSTR("MENÜPUNKT 3"));
132
133
  lcd_set_cursor(43,LINE7);lcd_puts_p(small_font,PSTR("Zurueck mit Taste 3"));
134
135
  _delay_ms(10);    //Taste entprellen
136
  while(1)
137
  {
138
  if( PIND & ( 1 << PIND4 ) ){show_menu();}
139
  }
140
}
141
142
void menu3_show()//MENÜPUNKT4
143
{
144
145
  lcd_clear();
146
  lcd_set_cursor(43,LINE0);lcd_puts_p(small_font,PSTR("MENÜPUNKT 4"));
147
148
  lcd_set_cursor(43,LINE7);lcd_puts_p(small_font,PSTR("Hauptmenue Taste 3"));
149
150
  _delay_ms(10);    //Taste entprellen
151
  while(1)
152
  {
153
  if( PIND & ( 1 << PIND4 ) ){show_menu();}
154
  }
155
}
156
157
158
159
void show_menu()
160
{
161
  lcd_clear();
162
  DrawMenuTexts();
163
  HandleButtons();
164
}
165
166
167
int main (void)
168
{
169
170
  DDRD=0xf0;
171
  uint8_t Active;
172
173
  lcd_init();
174
     lcd_draw_fullscreen_bmp(img); // Startbild (kurz nur beim Start)
175
  _delay_ms(100);
176
  
177
      show_menu();
178
179
}

Viel Spaß damit

von Karl H. (kbuchegg)


Lesenswert?

Sven D wrote:

> P.S. @Karl.
> MAchst Du das alles hobbymäßig oder beruflich?

beruflich

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.