mikrocontroller.net

Forum: Compiler & IDEs Funktionszeiger


Autor: Sven D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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%... 
)

Kann mir jemand sagen warum ?

Danke im voraus.

Autor: Sven D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry der Link noch einmal :

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

und dann einpaar zeilen drunter :

Menü mit Funktionszeigern

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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%...
> )

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.

Autor: Sven D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...
#include <avr/io.h>
#include <util/delay.h>
#include "glcd.h"
#include "font_small.h"
#include <avr/pgmspace.h>

typedef void (*VoidFnct)(void);

struct MenuEntry {
  char Text[20];
  VoidFnct Function;
};

void HandleEdit()
{
}
 
void HandleCopy()
{
}
 
void HandlePaste()
{
}
 
struct MenuEntry MainMenu[] = {
 { "Edit", HandleEdit },
 { "Copy", HandleCopy },
 { "Paste", HandlePaste }
};
 
#define ARRAY_SIZE(X) ( sizeof(X) / sizeof(*(X)))
 

void DoMenu( int NrEntries, struct MenuEntry[] Menu )
{
  int i;
  int Auswahl;
  
  do {

    for( i = 0; i < NrEntries; ++i )

  lcd_set_cursor(1,LINE2);lcd_puts_p(small_font,PSTR("MENÜPUNKT 1"));
  lcd_set_cursor(1,LINE3);lcd_puts_p(small_font,PSTR("MENÜPUNKT 2"));
  lcd_set_cursor(1,LINE4);lcd_puts_p(small_font,PSTR("MENÜPUNKT 3"));
  lcd_set_cursor(1,LINE5);lcd_puts_p(small_font,PSTR("MENÜPUNKT 4"));
 

lcd_set_cursor(1,LINE5);lcd_puts_p(small_font,PSTR("Exit - Taster 3"));
 
  if(PIND&(1<<PIND2)){Auswahl=1;}
  if(PIND&(1<<PIND3)){Auswahl=1;}
 

    if(PIND&(1<<PIND4)) //Taster 3
      return;
 
    Auswahl = Auswahl - 1;
    if( Auswahl < 0 || Auswahl > NrEntries )

lcd_set_cursor(1,LINE7);lcd_puts_p(small_font,PSTR("Ungültige Eingabe"));
 
    else

      Menu[Auswahl].Function();
  }
}


int main()
{

DoMenu( ARRAY_SIZE( MainMenu ), MainMenu );
 
  while( 1 )
    ;
}

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
void DoMenu( int NrEntries, struct MenuEntry[] Menu )
->
void DoMenu( int NrEntries, struct MenuEntry Menu[] )

Autor: Sven D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

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

Wundert mich nicht
void DoMenu( int NrEntries, struct MenuEntry[] Menu )
{
  int i;
  int Auswahl;
  
  do {

    for( i = 0; i < NrEntries; ++i )

  lcd_set_cursor(1,LINE2);lcd_puts_p(small_font,PSTR("MENÜPUNKT 1"));
  lcd_set_cursor(1,LINE3);lcd_puts_p(small_font,PSTR("MENÜPUNKT 2"));
  lcd_set_cursor(1,LINE4);lcd_puts_p(small_font,PSTR("MENÜPUNKT 3"));
  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?

  if(PIND&(1<<PIND2)){Auswahl=1;}
  if(PIND&(1<<PIND3)){Auswahl=1;}
 

    if(PIND&(1<<PIND4)) //Taster 3
      return;
 
    Auswahl = Auswahl - 1;
    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.

Autor: Sven D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Sven D. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
So würde es auf dem Display aussehen...

Ich glaube was einfacheres kann man nicht machen...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
void DrawMenuTexts()
{
  lcd_set_cursor( 2, LINE2 );
  lcd_puts_p( small_font, PSTR("MENÜPUNKT 1") );

  lcd_set_cursor( 2, LINE3 );
  lcd_puts_p( small_font, PSTR("MENÜPUNKT 2") );

  lcd_set_cursor( 2, LINE4 );
  lcd_puts_p( small_font, PSTR("MENÜPUNKT 3") );

  lcd_set_cursor( 2, LINE5 );
  lcd_puts_p( small_font, PSTR("MENÜPUNKT 4") );
}

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.
void ActivateEntry( uint8_t Nr )
{
  lcd_set_cursor( 1, LINE2 ); lcd_putc( ' ' );
  lcd_set_cursor( 1, LINE3 ); lcd_putc( ' ' );
  lcd_set_cursor( 1, LINE4 ); lcd_putc( ' ' );
  lcd_set_cursor( 1, LINE5 ); lcd_putc( ' ' );

  lcd_set_cursor( 1, LINE2 + Nr ); lcd_putc( '*' );
}

Ausprobieren !
int main()
{
   ....

   DrawMenuTexts();
   ActivateEntry( 0 );

   while( 1 )
     ;
}

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
int main()
{
   uint8_t Active;

   ....

   DrawMenuTexts();

   Active = 0;
   while( 1 ) {
     ActivateEntry( Active );
     Active++;
     if( Active == 4 )
       Active = 0;

     delay_ms( 1000 );
   }
}

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.
void HandleButtons()
{
  uint8_t Active = 0;

  ActivateEntry( Active );

  while( 1 )
  {
    if( PIND & ( 1<<PIND2 ) )
    {
      if( Active < 3 )
      {
        Active++;
        ActivateEntry( Active );
      }
    }

    if( PIND & ( 1 << PIND3 ) )
    {
      if( Active > 0 )
      {
        Active--;
        ActivateEntry( Active );
      }
    }
  }
}

Neues main schreiben und das ganze testen!
...
int main()
{
   ...

   DrawMenuTexts();
   HandleButtons();
}

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.

Autor: Sven D (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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ß

Autor: Sven D. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
#include <avr/io.h>
#include <util/delay.h>

#include "glcd.h"
#include "font_small.h"
#include "image_load.h"


void DrawMenuTexts()
{
  lcd_set_cursor(43,LINE0+6);lcd_puts_p(small_font,PSTR("MENÜPUNKT 1"));
  lcd_set_cursor(43,LINE2+5);lcd_puts_p(small_font,PSTR("MENÜPUNKT 2"));
  lcd_set_cursor(43,LINE4+4);lcd_puts_p(small_font,PSTR("MENÜPUNKT 3"));
  lcd_set_cursor(43,LINE6+3);lcd_puts_p(small_font,PSTR("MENÜPUNKT 4"));
}

void ActivateEntry( uint8_t Nr )
{
    switch (Nr)   // Menü-Umrandung zuordnen
    {
      case 0:
        Nr=2;  
        break;
      case 1:
        Nr=17;
        break;
      case 2:
        Nr=32;
        break;
      case 3:
        Nr=47;
        break;
    }

    lcd_draw_rect(25,2,80,14,WHITE); //Rahmen um den MENÜPUNKT1 entfernen
    lcd_draw_rect(25,17,80,14,WHITE);//Rahmen um den MENÜPUNKT2 entfernen
    lcd_draw_rect(25,32,80,14,WHITE);//Rahmen um den MENÜPUNKT3 entfernen
    lcd_draw_rect(25,47,80,14,WHITE);//Rahmen um den MENÜPUNKT4 entfernen

      lcd_draw_rect(25,Nr,80,14,BLACK);//Rahmen um den MENÜPUNKTX zeichnen
}

void HandleButtons()
{
  uint8_t Active = 0;

  ActivateEntry( Active );

  while( 1 )
  {
    if( PIND & ( 1<<PIND2 ) )
    {
      if( Active < 3 )
      {
        Active++;
        ActivateEntry( Active );
      }
    }

    if( PIND & ( 1 << PIND3 ) )
    {
      if( Active > 0 )
      {
        Active--;
        ActivateEntry( Active );
      }
    }


    if( PIND & ( 1 << PIND4 ) )
  {

    _delay_ms(10);    //Taste entprellen

          switch (Active) 
     {
     case 0:
        menu0_show();
        break;
     case 1:
        menu1_show();
        break;
     case 2:
        menu2_show();
        break;
     case 3:
        menu3_show();
        break;
       }
  }

  }
}


void menu0_show()//MENÜPUNKT1
{

  lcd_clear();
  lcd_set_cursor(43,LINE0);lcd_puts_p(small_font,PSTR("MENÜPUNKT 1"));
  
  lcd_set_cursor(43,LINE7);lcd_puts_p(small_font,PSTR("Zurueck mit Taste 3"));

  _delay_ms(10);    //Taste entprellen
  while(1)
  {
  if( PIND & ( 1 << PIND4 ) ){show_menu();}
  }

}

void menu1_show()//MENÜPUNKT2
{

  lcd_clear();
  lcd_set_cursor(43,LINE0);lcd_puts_p(small_font,PSTR("MENÜPUNKT 2"));  

  lcd_set_cursor(43,LINE7);lcd_puts_p(small_font,PSTR("Zurueck mit Taste 3"));  

  _delay_ms(10);    //Taste entprellen
  while(1)
  {
  if( PIND & ( 1 << PIND4 ) ){show_menu();}
  }
}

void menu2_show()//MENÜPUNKT3
{

  lcd_clear();
  lcd_set_cursor(43,LINE0);lcd_puts_p(small_font,PSTR("MENÜPUNKT 3"));

  lcd_set_cursor(43,LINE7);lcd_puts_p(small_font,PSTR("Zurueck mit Taste 3"));

  _delay_ms(10);    //Taste entprellen
  while(1)
  {
  if( PIND & ( 1 << PIND4 ) ){show_menu();}
  }
}

void menu3_show()//MENÜPUNKT4
{

  lcd_clear();
  lcd_set_cursor(43,LINE0);lcd_puts_p(small_font,PSTR("MENÜPUNKT 4"));

  lcd_set_cursor(43,LINE7);lcd_puts_p(small_font,PSTR("Hauptmenue Taste 3"));

  _delay_ms(10);    //Taste entprellen
  while(1)
  {
  if( PIND & ( 1 << PIND4 ) ){show_menu();}
  }
}



void show_menu()
{
  lcd_clear();
  DrawMenuTexts();
  HandleButtons();
}


int main (void)
{

  DDRD=0xf0;
  uint8_t Active;

  lcd_init();
     lcd_draw_fullscreen_bmp(img); // Startbild (kurz nur beim Start)
  _delay_ms(100);
  
      show_menu();

}


Viel Spaß damit

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sven D wrote:

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

beruflich

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.