Forum: Mikrocontroller und Digitale Elektronik Gibt es listen in C wie in C++


von programmieranfänger (Gast)


Lesenswert?

Hallo,

gibt es in C sowas wie verkettete listen?
Mein Problem: Hab ein Display und verschiedene Menüpunkte die ich 
aufrufen will. zum beilspiel wenn der cursor auf hauptmenü steht soll 
das hauptmenü sich öffnen, und da gibts dann weitere untermenüs

von Bernd (Gast)


Lesenswert?

Moin,

kannst du dir per struct und Pointern selber machen. Einfach verkettet, 
doppelt verkettet, Bäume,.... was du willst.

Gruss,
Bernd

von programmieranfänger (Gast)


Lesenswert?

danke erstmal, könntest du mir ein beispiel machen? wäre sehr hilfreich

von Daniel N. (Gast)


Lesenswert?


von programmieranfänger (Gast)


Lesenswert?

mal eine andere frage. kann ich mit solchen listen überhaupt ein menü 
aufbauen das von einem µc gesteuert wird?

von Christian F. (Gast)


Lesenswert?

ich hab damals mein LCD-Menü mit einer einfachen State-Machine 
aufgebaut.
D.h. wenn auf einem bestimmten Menüpunkt <Enter> gedrückt wird, wird 
eine Variable x auf 1 gesetzt, beim nächsten Schleifendurchlauf wurde 
dann für x=1 das entsprechende Menü auf dem LCD ausgegeben.

gruß

von preller (Gast)


Lesenswert?

natürlich geht das!

ich habe das so gelöst, dass ich mir eine tabelle angelegt habe, welche 
einen Datentyp (struct) mit den Informationen fuer jedes Menü-Element 
enthaelt.

z.b.

typedef struct menu_item_stag{
   u8    u8_current_id;
   u8    u8_next_menu_id;
   u8    u8_prev_item_id;
   u8    u8_prev_menu_id;
   const u8*  str_Titel;
   /* ... */
}menu_item_s;

Mit dieser einfachen Struktur in einer Tabelle kannst du bereits deine 
erste Menue-Ebene aufbauen.

Jedes Elemente muss wissen, zu welcher Menue-Ebene es gehört und welche 
Position es innerhalb dieser Menue-Ebene einnimmt.

Somit kannst du mit entsprechenden Funktionen schön dein Menü aufbauen 
und durch das Menü navigieren.

Ich habe z.b. ein 2.zeilen display.
es werden gleichzeichtig jeweils 2 Menü Punkte dargestellt.

Über einen Dreh-encoder navigiere ich hoch/runter durch das menü, wobei 
der oberste eintrag als ausgewählt markiert ist.

das menü rotiert beim letzten wieder auf den ersten eintrag.


MFG

von Ralf (Gast)


Lesenswert?

Also, ich habs auch in C gemacht. Dazu hab ich ein struct verwendet, 
welches insgesamt fünf Einträge hat. Vier verweisen einfach auf weitere 
structs desselben Typs, sind also im Prinzip nur Adressen, mit denen ein 
Pointer geladen wird. Die Verweise gelten für den über- bzw. 
untergeordneten sowie für den Eintrag drüber/drunter.

Der fünfte Eintrag ist ein Funktionspointer, welcher entsprechend dem 
Menü-Eintrag eine Funktion aufruft. Diese Funktion übernimmt dann eben 
die Display-Anzeige/Eingabe von Daten usw.

Die Menü-Implementation hab ich über ne Tabelle mit den entsprechenden 
Einträgen realisiert.

Funktioniert recht gut.
Bin grad nicht zuhause, sonst könnt ich n bisschen genauer beschreiben, 
wie ich's gelöst habe.

Ralf

von programmieranfänger (Gast)


Lesenswert?

hi ralf,

könntest du mir auch deine lösung schicken?
hier oder auf meine e-mail adresse?

von Ralf (Gast)


Lesenswert?

Hi,

wenn's mir heut abend zeitlich reicht, guck ich danach.

Ralf

von programmieranfänger (Gast)


Lesenswert?

wäre dir sehr dankbar.
meine e-mailaddy lautet: hui_buh3@hotmail.de .

von programmieranfänger (Gast)


Lesenswert?

hallo Ralf,

bist du noch da?

von Ralf (Gast)


Lesenswert?

Ja, bin noch da :)
Sorry, aber bin gestern nicht mehr dazu gekommen, nachzuschauen. Und 
jetzt bin ich wieder nicht am Rechner.

Im Prinzip hab ichs wie Preller gemacht. Was hast du denn bereits an 
Code? Kannst du den mal posten?

Ralf

von art (Gast)


Lesenswert?

an code hab ich folgendes:

struct Menue *element=NULL;
struct Menue *next=NULL;

struct Menue
{
  char Namemenue [15];
  char Menuepunkt1[15];
  char Menuepunkt2[15];
  char Menuepunkt3[15];
  //char Menuepunkt4[15];
  struct Menue *next;
  //struct Menue *before;
};

//Eine Funktion zum Erzeugen eines neuen Listenelementes könnte 
folgendermaßen aussehen:

void erzeugeElement(char *name, char *menue1, char *menue2, char 
*menue3, struct Menue **base)
{
   struct Menue *ptr=NULL;

   ptr = *base;
   if (ptr == NULL)
      {
      /* erstes Listenelement erzeugen */
      ptr = (struct Menue *) malloc(sizeof(struct Menue));
      *base = ptr;
      }
   else
      {
      /* weitere Listenelemente erzeugen */

      /* Das Ende der Liste suchen */
      while (ptr->next != NULL) ptr = ptr->next;

      /* neues Listenelement erzeugen */

      ptr->next = (struct Menue *) malloc(sizeof(struct Menue));
      ptr = ptr->next;
      }
  strcpy(ptr->Namemenue, name);
  strcpy(ptr->Menuepunkt1, menue1);
  strcpy(ptr->Menuepunkt1, menue2);
    strcpy(ptr->Menuepunkt1, menue3);
    //strcpy(ptr->Menuepunkt1, menue4);
    ptr->next = NULL;
   }

und ich hab noch eine funktion lcd_string() womit man strings ausgeben 
kann auf display.

so ich hab jetzt das problem wie kann ich jetzt ein menü ausgeben auf 
dem lcd display wenn ich eine taste drücke.

von art (Gast)


Lesenswert?

also das ist jetzt nur der code von der funktion und der strucktur

von G. O. (aminox86)


Lesenswert?

Habe genau zu diesem Thema einen Wettbewerbsartikel verfasst:
Tinykon.

von art (Gast)


Lesenswert?

Gerhardt habe es mir angeschaut aber irgendwie versteh ich das nicht so 
ganz. weil die Menüpunkte müssen ja einen bestimmten namen enthalten.

von G. O. (aminox86)


Lesenswert?

@art
ich schlage vor, bevor wir ins Detail gehen, die beiden 
Tinykon-Programmbeispiele 'mal runterzuladen und auszuprobieren.
Es handelt sich um kurze Simulationen, in denen verschiedene 
Menüsituationen, wie sie zB auf einem Mikrocontrollerdisplay vorkommen, 
demonstriert werden und die eigentlich auf jedem PC laufen sollten.
Dann sehen wir weiter.
mfg

von art (Gast)


Lesenswert?

es gibt 2 beispiele davon? ich hab gedacht das ist ein kompletes?

von art (Gast)


Lesenswert?

ausserdem wie soll ich das simulieren? ich muss doch was in die main 
funktion schreiben?

von art (Gast)


Lesenswert?

preller auch ne frage an dich: wie erstell ich in C tabellen?

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

> wie erstell ich in C tabellen

wie auch alle anderen Objekte:
- Datentypen definieren
- variablen des definierten Datentypen deklarieren

Dann per Zeiger auf die deklarierten Objekte zugreifen.



Villeicht solltest du dir erst mal ein Standardbuch über  "Algorithmen 
und Datenstrukturen" besorgen ...

von art (Gast)


Lesenswert?

Ralf, hättest du den heute zeit mir den code zu senden?
nochmal meine e-mail adresse: hui_buh3@hotmail.de.
wäre dir echt sehr dankbar.

von Simon K. (simon) Benutzerseite


Lesenswert?

Für ein statisches Menü braucht man verkettete Listen? Ich dachte immer, 
dass verkettete Listen ein dynamisches Programmierkonzept wären...

von art (Gast)


Lesenswert?

Simon K.
wenn du eine andere lösung hast, dann kannst du es mir ja mal mitteilen.
bin für jede hilfe dankbar.

von Simon K. (simon) Benutzerseite


Lesenswert?

art wrote:
> Simon K.
> wenn du eine andere lösung hast, dann kannst du es mir ja mal mitteilen.
> bin für jede hilfe dankbar.

Klar, ein Array.

von Jörg P. (zwiebackfraeser)


Lesenswert?

Eine dynamische Liste sehe ich auch nicht gerade als sinnvoll für ein 
Menue an, da das Menue vom Programmnutzer während der Laufzeit sicher 
nicht verändert werden soll.

Aber versuchs mal hiermit, habe ich aus meinem Windows-Programm kopiert,
ist ein Teil der Kundekartei, aber vom Prinzip müsste es funktionieren.
kunAnker ist der Startzeiger und über aktKun kann man die aktuelle 
Listenposition ermitteln.
Kann man sicherlich etwas eleganter machen, aber bei mir funktioniert es
soweit.

Jörg
1
struct kunden
2
{
3
  char  anrede[30];
4
  char  vorname[30];
5
  char  name[30];
6
  char  strasse[30];
7
  char  plz[30];
8
  char  ort[30];
9
  char  tel[30];
10
  char  fax[30];
11
  struct rabat   krabat;
12
  struct kunden *prev, *next;
13
14
};
15
struct kunden *kunAnker;
16
struct kunden *aktKun;
17
18
/*--------------------------------------------------*/
19
/*- void DoReadKundenkartei (HWND hDlg): liest die -*/
20
/*- Editfelder aus und speichert sie in globaler   -*/
21
/*- Datenstruktur struct kunde             -*/
22
/*--------------------------------------------------*/
23
void DoReadKundenkartei (HWND hDlg)
24
{
25
  /*- Funktionen -*/
26
  struct kunden *ptrKunden (char *name);
27
28
  /*- Variablen -*/
29
  struct kunden *hp, *sptr;
30
31
  hp = (struct kunden *)malloc (sizeof(struct kunden));
32
33
  if (hp != NULL)
34
  {
35
36
    //hier werden die Variablen in Deiner Struktur gefuellt,
37
                //bei mir steht da Windows-Funktionen, deswegen habe ich    
38
                //das mal weggelassen
39
40
    
41
      /*- Zeiger auf das Listenelement ermitteln,   -*/
42
        /*- ptrKunde gibt einen Zeiger auf die Stelle -*/
43
        /*- zurueck, wo der neue Kunde                -*/
44
        /*- in die Liste geschrieben werden soll      -*/
45
    sptr = ptrKunden (hp->name);
46
47
48
    /*- Liste erstellen -*/
49
    if (sptr == NULL)
50
    {
51
      /*- Liste ist leer -*/
52
      hp->next = kunAnker;
53
      hp->prev = NULL;
54
      if (kunAnker != NULL)
55
        hp->next->prev = hp;
56
      kunAnker = hp;
57
    }
58
    else
59
    {
60
      hp->next   = sptr->next;
61
      hp->prev   = sptr;
62
      if (hp->next != NULL)
63
        hp->next->prev = hp;
64
      if (sptr->prev == NULL)
65
        kunAnker = hp;
66
      else
67
        hp->prev->next = hp;
68
    }
69
    aktKun = hp;
70
  }
71
72
} /*- end of DoReadKundenkartei (..) -*/
73
74
/*---------------------------------------------------------------*/
75
/*- struct kunden *ptrKunden (char *name): durchsucht die Liste -*/
76
/*- und ermittelt das Datenblatt, hinter dem das neue Daten-    -*/
77
/*- blatt eingefuegt werden soll                                -*/
78
/*---------------------------------------------------------------*/
79
struct kunden *ptrKunden (char *name)
80
{
81
82
  struct kunden *hp, *np;
83
84
  np = NULL;
85
  hp = kunAnker;
86
87
  while (hp != NULL)
88
  {
89
    if (strcmp(name, hp->name) < 0)
90
      return (np);
91
    np = hp;
92
    hp = hp->next;
93
  }
94
  return (np);
95
} /*- end of struct kunden *kfind (..) -*/

von art (Gast)


Lesenswert?

mh, aber ich muss doch das ganze irgendwie dann in der main funktion so 
aufrufen das es dann auch auf dem display erscheint. ich stell mich zwar 
vielleicht jetzt mich dumm an, aber ich weiss es wirklich nicht.

von Sebastian B. (mircobolle)


Lesenswert?

Hab hier mal bei youtube ein Video von meinem LCD Menü reingestellt.

--> http://www.youtube.com/watch?v=GhUI7uQmt_c

das ganze ist auch in C mit structs und Tabellen realisiert.
Wie es auch meine Vorposter schon genannt haben.

Gesteuert wird das ganze über einen Dreh-Encoder und Taster.

Viel Spaß beim Entwickeln.

von Jörg P. (zwiebackfraeser)


Lesenswert?

Ob Du eine verkettete Liste oder ein Array (ist sicherlich einfacher) 
benutzt , beide müssen mit Inhalt gefüllt werden. Da Du ja sicherlich 
schon weißt, wie das Menue aussehen soll, machst Du dir ein passendes 
Array z.B.: static char *menue[]{... die einezelnen Menuepunkte, durch 
Komma trennen..}; (array eindimensional), deklarierst das global vor 
Deiner  main-Funktion und schreibst die einzelnen Menuepunkte da rein.
In main kannst Du dann z.B.menue[0] an Deine LCD-Ausgabefunktion 
übergeben,
die dann den ersten Eintrag aus deinem Menue auf dem Display darstellen 
sollte.

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

@art

was genau "weißt" du denn über Menues? (ist wahrscheinlich einfacher, 
daß du uns mitteils was du weißt als das was du nicht weißt, sonst wirds 
zu lang)


Dir ist sicherlich bewußt, daß du
- irgendwas zur Eingabe brauchst, um innerhalb des Menues zu navigieren
- Irenendwas zur Ausgabe brauchst, welches dir den aktuellen Menuepunkt 
anzeigt
- irgendeine Funktion hast, welche durch den jeweiligen Menuepunkt 
aufgerufen wird.

Fangen wir doch mal ganz von vorne an:

- Wie lautet deine Menue-Grundstruktur, z.B.

1. Irgendwas
1.1 Irgendwas spezielles
1.1.1 Irgedwas spezielles lesen
1.1.2 Irgendwas spezielles löschen
2. was_anderes
2.1 was_anderes_Unterpunkt
3. noch_was_anderes

d.h. wieviele Ebenen, wieviele Unter-Menuepunkte?


- was nutzt du zur Eingabe? Welche "Tasten" stehen dir zur Verfügung?
(Cursor hoch/runter, eingabe, ESC)

zu welchem Menuepunkt soll unter welchen Bedingungen wann wohin 
navigiert werden ?



- Was nutzt du zur Ausgabe?

Welche Ausgabegröße hat dein display,
ist scrollen notwendig (horizontal, Vertikal)
wie erfolgt die Darstellung des vorgewählten Menuepunkts?



Diese Grundfunktionalität kannst du gerne in Pseudosprache formulieren, 
und dan in eine beliebige Hochsprahce deiner Wahl zu codieren, Unter 
Zuhilfenahme von allen möglichen Sprachkonstrukten die die Sprache so 
bietet (z.B. Listen, Zeiger, Arrays oder sonstewas)

von art (Gast)


Angehängte Dateien:

Lesenswert?

erstmal das display: das hat vier zeilen.
habe ein pfeilkreuz (rechts, links, oben, unten) zur verfügung und eine 
ok-taste.
also mein menü soll folgendermassen aufgebaut sein:

ich hab ein fenster nachdem ich das gerät einschalte.
und in der vierten zeile steht dann links hauptmenü und rechts modus.
wenn der cursor auf einen der beiden punkte steht. soll er ein neues 
menüfenster aufmachen bei dem in der 1. zeile die überschrifft des 
jeweiligen menüpunktes steht und da drunter weitere menüpunkte.

beispiel

wenn aber im ersten fenster nach dem einschalten cursor auf modus steht 
und ok-taste wird gedrückt dann soll er in das menü modus wählen 
springen

Displayanzeige:
1.zeile: Modus wählen.
2.zeile: xy
3.zeile: xy.
4.zeile: xy.
5.zeile: xy.
6.zeile: zurück.

anmerkung: da das display nur 4 zeilen hat brauch ich noch eine funktion 
die dann das ganze routiert.

wenn cursor auf hauptmenü und ok-taste gedrückt
dann zeige das hauptmenü

Displayanzeige:
1.zeile: Hauptmenü
2.zeile: modus wählen
3.zeile: Einstellungen.
4.zeile: zurück.

wenn in dem hauptmenü cursor auf modus wählen und ok-taste gedrückt
dann zeige modus wählen:

Displayanzeige:
1.zeile: Modus wählen.
2.zeile: xy
3.zeile: xy.
4.zeile: xy.
5.zeile: xy.
6.zeile: zurück.

und noch eine anmerkung zum schluss. wenn man in dem untersten untermenü 
ist und wenn der cursor dann auf einer bestimmten position steht und die 
ok - taste gedrückt wird. soll er das oberste menüfenster aufmachen.


hoffe es ist einigermassen verständlich. wenn nicht mall ich euch mal 
ein bild.

von art (Gast)


Lesenswert?

ach was ich noch vergessen hab zu sagen das ich unter Einstellungen
im menüpunkt helligkeit zum beispiel dann mit hilfe eines drehknopfes 
die helligkeit des displays steuere zum beispiel

von Sebastian B. (mircobolle)


Lesenswert?

art wrote:
> ach was ich noch vergessen hab zu sagen das ich unter Einstellungen
> im menüpunkt helligkeit zum beispiel dann mit hilfe eines drehknopfes
> die helligkeit des displays steuere zum beispiel

du kannst auch die ganze Navigation durch das Menü mit einem Drehknopf 
lösen "a la iPod" ;-)

--> http://www.youtube.com/watch?v=GhUI7uQmt_c

alles mit drehknopf
und einem zusätzlichen BACK Knopf

kannst aber stattdessen auch hoch/runter taster verwenden, die andere 
art fand ich eleganter, aber ist geschmackssache.

da ich viele werte mit drehknopf einstelle... frequent, tastgrad, 
ausgangsspannung, helligkeit, war es für mich nahe liegend auch die 
navigation: hoch/runter über diesen drehknopf zu erledigen.

das ganze system kommt dann mit dem drehknopf(mit integriertem schalter 
für "OK") und einer "zurück" taste aus!

von Simon K. (simon) Benutzerseite


Lesenswert?

Sebastian B. wrote:
> das ganze system kommt dann mit dem drehknopf(mit integriertem schalter
> für "OK") und einer "zurück" taste aus!

Du solltest aber dringend was an der Frequenzeinstellweise ändern *dreh 
dreh dreh ;)

Ich habs mal so gemacht, dass man jede Zehnerstelle einzeln einstellen 
kann. Also: Zehnerstelle aussuchen -> Bestätigen -> Einen Wert aus 0-9 
wählen -> Bestätigen -> Stelle fertig eingegeben.

von art (Gast)


Lesenswert?

na sowas wie die helligkeit wird mit dem drehknopf eingestellt
die menünavigation wird mit dem pfeilkreuz getan.

von art (Gast)


Lesenswert?

und was nützt mir das video wenn da keine listdatei ist.

von Sebastian B. (mircobolle)


Lesenswert?

Simon K. wrote:
> Sebastian B. wrote:
>> das ganze system kommt dann mit dem drehknopf(mit integriertem schalter
>> für "OK") und einer "zurück" taste aus!
>
> Du solltest aber dringend was an der Frequenzeinstellweise ändern *dreh
> dreh dreh ;)
>
> Ich habs mal so gemacht, dass man jede Zehnerstelle einzeln einstellen
> kann. Also: Zehnerstelle aussuchen -> Bestätigen -> Einen Wert aus 0-9
> wählen -> Bestätigen -> Stelle fertig eingegeben.

hehe, naja, ne drehgeschwindigkeitserkennung hab ich schon drin, man 
sieht ja das die hunderter stelle dann etwa im sekunden takt springt.

aber das mit der stellenauswahl hab ich auch drin, man kann die 
einzelnen stellen über "links" und "rechts" auswählen. Das sollte nur 
ein Beispiel für die Drehgeschwindigkeitserkennung sein.

ich muss gestehen das video ist eher sub-optimal ;-) aber war ja auch 
mein erstes.

ich finde aber, ganz allgemein, das eine navigation über den drehknopf 
gerade durch sehr verschachtelete menü-strukturen auf dauer angenehmer 
ist, als über ein steuerkreuz.

MFG

von art (Gast)


Lesenswert?

dann könntest du mir doch eigentlich deine lösung mir schicken wie du 
das gemacht hast oder ist das ein geheimnis?

von Sebastian B. (mircobolle)


Lesenswert?

art wrote:
> und was nützt mir das video wenn da keine listdatei ist.

kann später den source-code online stellen.

Ich denke die Grundfunktionen, wie den Aufbau der Menü-Struktur, den 
Aufbau des rotierenden Menü-Anzeige, den Funktionsaufruf kannst du dir 
da abschauen.

Ich habe allerdings viele verschiedene Menü-Elemente.
D.h. du musst den Code dann soweit verstehen um ihn entsprechend auf 
deine BEdürfnisse "abzuspecken" da er so nicht compilierbar ist auf 
Grund fehlender  Header-Dateien.

Aber ich werde das heute abend mal soweit vorbereiten, dass du damit was 
anfangen kannst!

WIe sieht denn dein Display aus? AUch 2-zeilig?

von art (Gast)


Lesenswert?

nein mein display hat 4 zeilen und 18 zeichen in jeder zeile.
wäre dir sehr dankbar.

von Sebastian B. (mircobolle)


Angehängte Dateien:

Lesenswert?

art wrote:
> nein mein display hat 4 zeilen und 18 zeichen in jeder zeile.
> wäre dir sehr dankbar.

Hab gerade leider keine Zeit den code abzuspecken, aber schau dir mal 
die Funktionen:
void    MENU_StateMachine       ();
void         Show_current_Items        ();

diese 2 Funktionen "erklären" eigentlich wie die Navigation durch das 
Menü und der Aufbau der Anzeige funktioniert.

Wie gesagt: So ist das ganze NICHT compilierbar, aber es beschreibt 
zumindest wie man es realisieren kann.

Wenn du fragen haben solltest kannst du dich ja melden.

MFG

von Ralf (Gast)


Lesenswert?

So, hab jetzt mal kurz nachgeforscht. Folgendes hat sich mein Hirn 
damals ausgedacht. Zuerst einmal das struct zur Definition der 
Menu-Einträge:
1
typedef struct m_item{
2
  struct m_item *Up;
3
  struct m_item *Down;
4
  struct m_item *Left;
5
  struct m_item *Right;
6
  void (code *fpPtr)(void);
7
} _stMenu;

Wie man sieht, sind die vier ersten Einträge einfach Pointer auf weitere 
structs dieser Art. Der fünfte Eintrag ist ein Funktions-Pointer, mit 
dessen Hilfe ich die durchzuführende Funktion aufrufe. Das kann 
einerseits das Aktualisieren das Displays sein, oder einfach ein Reset, 
oder, oder, oder...

Desweiteren ist eine Tabelle notwendig, welche die Menü-Struktur 
definiert. Für ein Menü mit dem Aufbau:

- Menü 1
 - Menü 11
  - Menü 111
  - ...
 - Menü 12
  - Menü 121
  - ...
 - Menü 13
  - Menü 131
  - ...
- Menü 2
 - Menü 21
 - ...

muss man dann sowas anlegen:
1
_stMenu code stMenues[] = {
2
  {&stMenues[26], &stMenues[13], NULL, &stMenues[1], m1},        //M1  0
3
  {&stMenues[9], &stMenues[5], &stMenues[0], &stMenues[2], m11},    //M11  1
4
  {&stMenues[4], &stMenues[3], &stMenues[1], NULL, m111},        //M111  2
5
  {&stMenues[2], &stMenues[4], &stMenues[1], NULL, m112},        //M112  3
6
  {&stMenues[3], &stMenues[2], &stMenues[1], NULL, m113},        //M113  4
7
  {&stMenues[1], &stMenues[9], &stMenues[0], &stMenues[6], m12},    //M12  5
8
  {&stMenues[8], &stMenues[7], &stMenues[5], NULL, m121},        //M121  6
9
  {&stMenues[6], &stMenues[8], &stMenues[5], NULL, m122},        //M122  7
10
  {&stMenues[7], &stMenues[6], &stMenues[5], NULL, m123},        //M123  8
11
  {&stMenues[5], &stMenues[1], &stMenues[0], &stMenues[10], m13},    //M13  9
12
  {&stMenues[12], &stMenues[11], &stMenues[9], NULL, m131},      //M131  10
13
  {&stMenues[10], &stMenues[12], &stMenues[9], NULL, m132},      //M132  11
14
  {&stMenues[11], &stMenues[10], &stMenues[9], NULL, m133},      //M133  12
15
  {&stMenues[0], &stMenues[26], NULL, &stMenues[14], m2},        //M2  13
16
  {&stMenues[22], &stMenues[18], &stMenues[13], &stMenues[15], m21},  //M21  14
17
  {&stMenues[17], &stMenues[16], &stMenues[14], NULL, m211},      //M211  15
18
  {&stMenues[15], &stMenues[17], &stMenues[14], NULL, m212},      //M212  16
19
  {&stMenues[16], &stMenues[15], &stMenues[14], NULL, m213},      //M213  17
20
  {&stMenues[14], &stMenues[22], &stMenues[13], &stMenues[19], m22};  //M22  18

Diese Tabelle ist nicht komplett, sie soll nur das Konzept 
verdeutlichen!
NULL ist durch #define NULL ((void *) 0) definiert, das sagt dann 
einfach aus, dass es dort nicht mehr weitergeht, somit kann man Grenzen 
für das Menü in die Tabelle eintragen. Möchte man einen "Wrap-Around" 
realisieren, muss man eben anstatt NULL den entsprechenden Menü-Eintrag 
hinschreiben.
Der fünfte Eintrag enthält jeweils die Adresse der aufzurufenden 
Funktion.

Der dritte Teil des Ganzen ist die Menü-Routine:
1
void MENU(void) {
2
  static _stMenu *stMenu = {&stMenues[0]};
3
4
  if(btMenuBusy == 0) {
5
    if(KBRD_GETSKEY(KBRD_ARR_UP) == 1) {
6
      if(stMenu->Up != NULL) {
7
        stMenu = stMenu->Up;
8
        LCD_CLEAR();
9
      }
10
    }
11
    else if(KBRD_GETSKEY(KBRD_ARR_RI) == 1) {
12
      if(stMenu->Right != NULL) {
13
        stMenu = stMenu->Right;
14
        LCD_CLEAR();
15
      }
16
    }
17
    else if(KBRD_GETSKEY(KBRD_ARR_DN) == 1) {
18
      if(stMenu->Down != NULL) {
19
        stMenu = stMenu->Down;
20
        LCD_CLEAR();
21
      }
22
    }
23
    else if(KBRD_GETSKEY(KBRD_ARR_LE) == 1) {
24
      if(stMenu->Left != NULL) {
25
        stMenu = stMenu->Left;
26
        LCD_CLEAR();
27
      }
28
    }
29
  }
30
  stMenu->fpPtr();
31
}

Ich verwende nur vier Pfeiltasten, um durch das Menü zu navigieren. Die 
jeweils aufgerufene Funktion entscheidet, ob z.B. durch einen Druck auf 
Enter etwas aktiviert, oder ob die Taste Clear etwas löscht, ...
Das erschien mir schön flexibel :)

Da ich das ganze so geschrieben habe, dass nicht auf einen Tastendruck 
gewartet werden muss, kann die Software noch andere Sachen nebenbei 
machen, in meinem Fall z.B. eine DCF77-Auswertung, usw.
Das heisst, meine main-Funktion sieht grob geschrieben etwa so aus:
1
void main(void) {
2
  INIT_UART();
3
  INIT_TIMER();
4
  INIT_LCD();
5
  while(1) {
6
    MENU();
7
    DCF77_DECODE();
8
    ...
9
  }
10
}

Hierfür ist es notwendig, dass der Pointer *stMenu static ist, damit 
beim nächsten Aufruf von MENU() wieder dasselbe Menü angezeigt wird.
Den Aufruf von LCD_CLEAR() kann man weder vor dem Funktions-Aufruf noch 
in der jeweiligen Funktion machen, da das Display ansonsten flackert. 
Man könnte ihn höchstens hinter den letzten else-if-Block setzen.
Eine Möglichkeit wäre, sich über ein Flag zu merken, ob das Menü bereits 
gezeigt wurde. Da meine ganze Software darauf basiert, dass niemals 
irgendwo auf etwas gewartet wird, habe ich das auch nicht gebraucht.

Das globale Flag btMenuBusy kann in einer aufgerufenen Funktion gesetzt 
werden, um das Navigieren des Menüs zu unterbinden (hilfreich bei 
Eingaben) bzw. um die vier Pfeiltasten für die aufgerufene Funktion 
freizumachen.

Jo, das wär dann mein Anteil zu der Sache... Hoffe, ich hab nichts 
vergessen zu beschreiben.

Ralf

von art (Gast)


Lesenswert?

Mal ein anderes konzept:

wenn ich mir zu jedem einzelnen menüpunkt eine funktion schreibe, also 
ein const char funktion, und dann ruf ich sie über einen zeiger auf wenn 
der cursor auf der jeweiligen position steht und die ok taste gedrückt 
wird.

würde das funktionieren?

von Ralf (Gast)


Lesenswert?

> wenn ich mir zu jedem einzelnen menüpunkt eine funktion schreibe, also
> ein const char funktion, und dann ruf ich sie über einen zeiger auf wenn
> der cursor auf der jeweiligen position steht und die ok taste gedrückt
> wird.
Das würde ich dann so lösen, dass du bei jedem Weiterschalten im Menü 
eine Variable in-/dekrementierst, die quasi der Menü-Nummer entspricht, 
beginnend bei 0 fürs erste Menü. Mit dieser Variablen als Index gehst du 
in eine Tabelle, welche die zugehörigen Funktionsadressen beinhaltet, 
und lädst damit den Funktionspointer, also so, wie ich es in meinem 
Beitrag von gestern abend geschrieben habe.
Wenn du es über if-else if oder switch/case löst, dann wird das ein 
riesiges Konstrukt, welches schlecht erweiterbar ist.

Ralf

von art (Gast)


Lesenswert?

ja aber du hast ja in deiner tabelle arrays drin die noch gefühlt werden 
müssen mit inhalt oder nicht?
weil bei mir muss dann auf dem display konkrett sowas stehen:

1. zeile: name des menüs z.b hauptmenü
2. zeile: untermenü      z.b Einstellungen
3. zeile: untermenü2     z.b Modus wählen

und in die untermenüs müssen auch anwählbar sein

von Ralf (Gast)


Lesenswert?

> ja aber du hast ja in deiner tabelle arrays drin die noch gefühlt werden
> müssen mit inhalt oder nicht?
Ja, klar. Anders gehts ja nicht. Bei meiner Variante bestimmt die 
Tabelle, wie das Menü aufgebaut ist.
Was macht dir an dem Füllen sorgen? Ich weiss, dass es je nach Menügröße 
ein Aufwand ist, das zu füllen, deswegen arbeite ich parallel an einem 
Windows-Programm, mit dem ich die Menü-Tabelle automatisch erstellen 
kann. Aber fürs manuelle Füllen der Tabelle für insgesamt 39 Menüs habe 
ich etwa 20 Minuten gebraucht.
Du musst dir erstmal das Menü aufmalen, also:

- Menü 1                           //0, Stufe 0
 - Menü 1, Eintrag 1               //1, Stufe 1
 - Menü 1, Eintrag 2               //2, Stufe 1
  - Menü 1, Eintrag 2, Eintrag 1   //2, Stufe 2
- Menü 2                           //3, Stufe 0
 - Menü 2, Eintrag 1               //4, Stufe 1
- ...                              //5, ...

Das Durchnummerieren hilft beim Tabellen-Füllen, weil es dem Index 
entspricht, an dem der jeweilige Eintrag hinterlegt werden muss.
Und dann schreibst du eben in die Tabelle die entsprechenden Einträge.
So steht dann an Index 0 für DOWN der Index von Menü 2, für UP das 
letzte Menü der Stufe 0.
Umgekehrt für UP des Menü 2 der Index 0 -> Menü 1.
LEFT von Menü 1 erhält NULL, weils links daneben ja nix mehr gibt, und 
RIGHT von Menü 1 zeigt auf Index 1, weil dort der erste Untereintrag von 
Menü 1 steht.
Wenn du an der höchsten Stufe eines Zweiges angelangt bist, bekommt 
RIGHT NULL zugewiesen, weil auch da gehts ja nimmer weiter.

Ralf

von art (Gast)


Lesenswert?

also anstatt der zahlen in den klammern z.b [0] kann ich den menünamen 
reinschreiben oder wie?

von Ralf (Gast)


Lesenswert?

Nachtrag:

Da du das ganze Cursorgesteuert machst und mehrere Auswahlmöglichkeiten 
pro angezeigtem Menü hast, müsstest du im Prinzip bei meiner Lösung 
folgendes machen:

1. Die Einträge der Tabelle müssen so angeordnet sein, dass die 
anzuzeigenden Untermenüs direkt hintereinander in der Tabelle liegen.
2. Du verwendest wie ich heute morgen geschrieben habe, eine Variable, 
die dir anzeigt, auf welchen Eintrag du mit dem Cursor zeigst, beginnend 
bei Null. Die Funktion "Zurück" liegt demnach immer bei 3.
3. Ist die Variable = drei, gehst du ins übergeordnete Menü zurück, bei 
Null bis zwei addierst du diese Variable zum entsprechenden Index des 
ersten Untermenüs dazu, und rufst den entsprechenden Funktionspointer 
auf.

Habe ich so aber nicht getestet, da ist sicher noch austesten nötig!

Ralf

von Ralf (Gast)


Lesenswert?

> also anstatt der zahlen in den klammern z.b [0] kann ich den menünamen
> reinschreiben oder wie?
Nein, die Zahlen bleiben. Aber wenn du das Menü erstmal aufmalst (Papier 
oder Texteditor) und die Zahlen dahinterschreibst, weisst du welche Zahl 
in welche Klammer muss.

Ralf

von art (Gast)


Lesenswert?

und wie ich das so verstehe, wählst du die untermenüs mit dem 
fadenkreutz aus, ich will es aber so machen das ich nur dann in das 
nächste untermenü springe wenn ich die ok taste drücke.

von art (Gast)


Lesenswert?

ja aber meine menüpunkte müssen auch verschieden heißen

das obermenü z.b hauptmenü:
und dann darunter stehen auch noch menüpunkte wie einstellungen die nur 
dann angewählt dürfen wenn der cursor sich darauf befindet und die ok 
taste gedrückt wird

von art (Gast)


Lesenswert?

ist das deine funktion zur return taste?
KBRD_GETSKEY(KBRD_ARR_UP)

von Ralf (Gast)


Lesenswert?

> und wie ich das so verstehe, wählst du die untermenüs mit dem
> fadenkreutz aus, ich will es aber so machen das ich nur dann in das
> nächste untermenü springe wenn ich die ok taste drücke.
Okay, bei mir wechselt man zwischen den Menüs direkt beim Druck auf eine 
der Pfeiltasten. Aktionen wie Eingaben usw. werden wie bei dir durch 
Druck auf die Enter-Taste ausgeführt, das entscheidet wie gesagt die 
jeweilige Funktion.
Für dich wäre die notwendige Änderung dann, dass du beim Druck auf die 
Pfeiltasten den Funktionspointer neu lädst, die Funktion aber nur dann 
aufrufst, wenn Enter gedrückt wird.

> ja aber meine menüpunkte müssen auch verschieden heißen
>
> das obermenü z.b hauptmenü:
> und dann darunter stehen auch noch menüpunkte wie einstellungen die nur
> dann angewählt dürfen wenn der cursor sich darauf befindet und die ok
> taste gedrückt wird
Wie gesagt, die Anzeige übernimmt die jeweils aufgerufene Funktion. In 
deinem Fall musst du eben eine Funktion aufrufen, die einerseits die 
Anzeige übernimmt, und zusätzlich das Abfragen/Bearbeiten der 
Cursorposition.

> ist das deine funktion zur return taste? KBRD_GETSKEY(KBRD_ARR_UP)
Fast. Diese Funktion prüft, ob die aktuell gedrückte Taste dem Wert 
KBRD_ARR_UP (Pfeil oben) entspricht. Wenn ja, löscht sie den 
Tastaturpuffer, damit beim nächsten Aufruf nicht nochmal die gleiche 
Taste erkannt wird, und gibt 1 zurück.
Ist es nicht die gewünschte Taste, wird auf die nächste Taste geprüft, 
usw.

Ralf

von art (Gast)


Lesenswert?

Ralf was ich nicht verstehe ist: wo du den einzelnen menüpunkten namen 
gibst wie sie heissen sollen.

von Sebastian B. (mircobolle)


Lesenswert?

art wrote:
> Ralf was ich nicht verstehe ist: wo du den einzelnen menüpunkten namen
> gibst wie sie heissen sollen.

@Ralf:

Ja, das ist eine gute Frage von art: Wo weist du den Einträgen die Namen 
zu?

Ich habe einen Post darüber gelesen, dass die aufzurufende Funktion dies 
übernimmt. Aber ich finde das etwas unsauber - also vom Modul-Gedanken 
her. Es mag ja alles schön funktionieren und aussehen, aber 
übersichtlich ist das meiner Meinung nach nicht. Da die Funktion dies 
wie eine "Black-Box" übernimmt.

Wäre es nicht sinniger dein struct um einen String-Eintrag für den Titel 
zu erweitern?

Was mich auch etwas stutzig gemacht hat ist folgendes:
in deiner Funktion MENU() wird diese Zeile immer ausgeführt:
stMenu->fpPtr();

und MENU wiederum wird in der while(1) Schleife von main() aufgerufen.

Das bedeutet, dass die aktuelle Funktion permanent aufgerufen wird, wenn 
nichts am Menü verändert wird.

Liegt dies im Sinne des Erfinders???

Ich würde die Funktion nur OnEvent aufrufen, also wenn tatsächlich ein 
Tastendruck statt gefunden hat, ansonsten muss die Funktion dies 
abfangen, was wiederum unschön ist und nicht dem Modul-Gedanken 
entspricht.

MFG

von art (Gast)


Lesenswert?

mal eine andere frage wieso kennt der compiler das wörtchen debounce 
nicht?

ich hab an den PD2 einen taster angeschlossen
und damit möcht ich ausdrücken wenn er gedrückt worden ist soll er den 
cursor um eine zeile weiter nach unten bewegen.
so und jetzt nach dem ich die neuere version installiert hab kennt er 
das nicht mehr der compiler

debounce(&PIND, PD2)

von P. S. (Gast)


Lesenswert?

Entschuldige, aber irgendwie werde ich das Gefuehl nicht los, das ist 
dein erstes Programm? Moechtest du nicht lieber beim C-Buch auf der 
ersten Seite anfangen?

von Ralf (Gast)


Lesenswert?

Wie gesagt, die Anzeige wird bei mir komplett von der jeweils 
aufgerufenen Funktion übernommen. Das entspricht meiner Vorstellung von 
Modularisierung. Es macht m.E. keinen Sinn, einmal einen String aus 
einem Struct auszugeben, und den Rest des Menüs durch die aufgerufene 
Funktion.

Dass die Funktion ständig durchlaufen wird, hat ebenfalls seinen Sinn, 
da ich in einigen, aber nicht allen Menüs z.B. die Uhrzeit ausgebe, das 
wird somit erschlagen. Ausserdem mache ich in den Funktionen jeweils die 
Tastenauswertung. Wird keine Taste gedrückt, wird sie wieder verlassen, 
ansonsten wird eben die Taste ausgelesen und entsprechend verwertet. Wie 
gesagt, das entspricht meiner Vorstellung von Modularisierung, ich lasse 
mich aber gerne eines besseren belehren.

Ralf

von Sebastian B. (mircobolle)


Lesenswert?

art wrote:
> mal eine andere frage wieso kennt der compiler das wörtchen debounce
> nicht?
>
> ich hab an den PD2 einen taster angeschlossen
> und damit möcht ich ausdrücken wenn er gedrückt worden ist soll er den
> cursor um eine zeile weiter nach unten bewegen.
> so und jetzt nach dem ich die neuere version installiert hab kennt er
> das nicht mehr der compiler
>
> debounce(&PIND, PD2)

Warum sollte er das Wörtchen "debounce" kennen?
Debounce heißt meines Wissens nach Entprellen, ich würde nun vermuten, 
dass dies eine Funktion zur Entprellung der Taster ist.

Ausserdem wüsste ich nicht wie man das Entprellen eines Tasters sauber 
über eine Funktion lösen sollte. Da Entprellen etwas mit "waren" zu tun 
hat... und warten in einer Funktion ist nie eine gute Idee. 
Timer-gesteuert ist das etwas anderes.

Ist das dein erstes Mikrocontroller-Projekt? (nicht als Kritik 
verstehen, aber wenn man mehr zu deinen Hintergründen erfährt kann man 
dir auch mehr helfen)

von art (Gast)


Lesenswert?

ja das erste grössere, bzw programmier das erste mal in c einen µc, 
davor auf assembler programmiert aber auch nicht sowas grosses.
ja aber debounce ist eine funktion die in einer library doch enthalten 
sein soll von avr studio.

von Sebastian B. (mircobolle)


Lesenswert?

Ralf wrote:
> Wie gesagt, die Anzeige wird bei mir komplett von der jeweils
> aufgerufenen Funktion übernommen. Das entspricht meiner Vorstellung von
> Modularisierung. Es macht m.E. keinen Sinn, einmal einen String aus
> einem Struct auszugeben, und den Rest des Menüs durch die aufgerufene
> Funktion.

Ja, ok, die Funktion übernimmt die Anzeige des Strings, welcher mit der 
Funktion verbunden ist. Aber wer übernimmt die Anzeige der eigentlichen 
Menü-Struktur?

Z.B.:
"1. Menü-Punkt 1"
"2. Menü-Punkt 2" usw. ... ?

> Dass die Funktion ständig durchlaufen wird, hat ebenfalls seinen Sinn,
> da ich in einigen, aber nicht allen Menüs z.B. die Uhrzeit ausgebe, das
> wird somit erschlagen.

Ist das ganze denn deterministisch, also zeitlich konstant? Denn mal 
dauert ein Aufruf vielleicht 30 Mikrosekunden dann mal 33 Mikrosekunden 
oder noch länger, wenn dir ein Interrupt dazwischen haut. Wie willst du 
damit eine saubere Zeitbasis für die Uhrzeit bekommen, wenn du so einen 
zeitlichen Jitter drin hast?

>Ausserdem mache ich in den Funktionen jeweils die
> Tastenauswertung. Wird keine Taste gedrückt, wird sie wieder verlassen,
> ansonsten wird eben die Taste ausgelesen und entsprechend verwertet.

das meinte ich mit OnEvent.

>Wie
> gesagt, das entspricht meiner Vorstellung von Modularisierung, ich lasse
> mich aber gerne eines besseren belehren.
>

Ja, Modularisierung hat ja als grundlegende Konzepte:
- die Funktion eines Moduls z.b. Menü in sich "zu binden", d.h. intern 
Funktion bereitstellen um alles realisieren zu können, was zur 
Darstellung und Steuerung des Menüs benötigt wird. Hier darf z.b. keine 
Display-Ansteuerung oder Taster-Auswertung erfolgen... dies geschieht 
über  Schnittstellen, welche das Modul Display und das Modul Taster 
bereit stellt.

Das wäre dann die "Kopplung", die Schnittstellen zu den anderen Modulen 
sollen möglichst "schmal" sein (also möglichst wenige Parameter 
beinhalten)  damit die Module gut austauschbar oder erweiterbar sind.

von Sebastian B. (mircobolle)


Lesenswert?

art wrote:
> ja das erste grössere, bzw programmier das erste mal in c einen µc,
> davor auf assembler programmiert aber auch nicht sowas grosses.
> ja aber debounce ist eine funktion die in einer library doch enthalten
> sein soll von avr studio.

sorry benutze kein avr studio. Kann dir da also nicht weiterhelfen.

Hast du denn die Header-Datei richtig inkludiert? Befindet sich die Lib 
im richtigen Verzeichnis?

ansonsten Entprellung über Timer-Funktion realisieren.

von Ralf (Gast)


Lesenswert?

> Ja, ok, die Funktion übernimmt die Anzeige des Strings, welcher mit der
> Funktion verbunden ist. Aber wer übernimmt die Anzeige der eigentlichen
> Menü-Struktur?
Okay, also mal ganz von vorne:

- Die Anzeige der einzelnen Menü-Inhalte wird von der jeweils zum 
Menü-Eintrag gehörenden Funktion übernommen
- Die Struktur des Menüs, also welcher Eintrag unter/über/links/rechts 
von einem anderen Eintrag steht, wird durch die Einträge in der Tabelle 
übernommen -> das entspricht den Zahlen in den eckigen Klammern
- Die Funktion MENU() übernimmt nur das navigieren zwischen den 
einzelnen Elementen und den Aufruf der zum jeweiligen Menü-Eintrag 
gehörenden Funktion
- Das Auswerten von nicht der Navigation dienenden Tastatureingaben ist 
Aufgabe der jeweiligen Funktion

Soweit ich das verstanden habe, habt ihr ein Menü, in dem ihr einen 
Cursor dazu verwendet, auf den jeweils nächsten Eintrag zu zeigen.
Ich dagegen habe etwas in der Art:

Betriebsart
+- Autonom
   +- Starten
   +- Konfigurieren
+- Online
   +- Verbinden
   +- Upload
   +- Download
+- Offline
Einstellungen
+- System
   +- Schnittstelle
      +- Baudrate
+- Zeit/Datum
   +- Uhrzeit
   +- Datum
+- ...

Jeder Eintrag bringt wie gesagt seine eigene Displaymaske mit. Das soll 
jetzt nur mal dem besseren Verständnis dienen, warum meine Funktionen so 
sind, wie sie sind :)

Um wieder zu eurem Menü zurück zu kommen:
Ihr wollt also etwas in der Art auf dem Display:

[1. Menü]
--- Hauptmenu ---
* 1. Einstellungen
  2. Starten
  3. Stoppen
  4. ---

[2. Menü]
--- Einstellungen ---
* 1. Uhrzeit
  2. Datum
  3. Schnittstelle
  4. zurück

Hab ich das so richtig verstanden?

Das Sternchen markiert jetzt mal den Cursor. Durch die Pfeiltasten setzt 
ihr also den Cursor hoch/runter, Enter geht ins entsprechende Menü bzw. 
einen Zweig zurück. Soweit richtig?

Ich kann das jetzt leider nicht eben "mal schnell" hinzaubern, ich muss 
mir erstmal überlegen, ob das mit meiner Software so überhaupt möglich 
ist.

> Ist das ganze denn deterministisch, also zeitlich konstant? Denn mal
> dauert ein Aufruf vielleicht 30 Mikrosekunden dann mal 33 Mikrosekunden
> oder noch länger, wenn dir ein Interrupt dazwischen haut. Wie willst du
> damit eine saubere Zeitbasis für die Uhrzeit bekommen, wenn du so einen
> zeitlichen Jitter drin hast?
Ja, weil die Uhrzeit in einem Timer-Interrupt läuft. Da ich das auf 
einem 8051-Derivat mache, habe ich natürlich unterschiedliche Zeiten pro 
Befehl, dies wird jedoch vom Timer-Interrupt beachtet, indem er nicht 
einfach mit einem festen Wert neu geladen wird, sondern den Reload-Wert 
aufaddiert bekommt.
Kurze Erläuterung: von Eintreten des Interrupts in die Service-Routine 
bis zum Laden des Timers vergehen noch eine Befehlszyklen fürs Pushen 
der Register usw. Der Timer läuft ja derweil noch weiter. Indem der 
Reload-Wert drauf addiert wird, wird der "Jitter" wieder ausgeglichen.

Bzgl. der Modularisierung, ich finde das eigentlich gut modularisiert:
- Es gibt Funktionen für Tastatur und Display
- Die MENU()-Funktion und zugehörige Tabelle sowie die aufgerufenen 
Funktionen kapseln die jeweils notwendigen Schritte für das Darstellen 
und Ausführen des Menüs.

Ralf

von Sebastian B. (mircobolle)


Lesenswert?

Zunächst: Vielen Dank für die ausführliche Erläuterung.

Ralf wrote:
>> Ja, ok, die Funktion übernimmt die Anzeige des Strings, welcher mit der
>> Funktion verbunden ist. Aber wer übernimmt die Anzeige der eigentlichen
>> Menü-Struktur?
> Okay, also mal ganz von vorne:
>
> - Die Anzeige der einzelnen Menü-Inhalte wird von der jeweils zum
> Menü-Eintrag gehörenden Funktion übernommen
Ja, das war soweit klar.

> - Die Struktur des Menüs, also welcher Eintrag unter/über/links/rechts
> von einem anderen Eintrag steht, wird durch die Einträge in der Tabelle
> übernommen -> das entspricht den Zahlen in den eckigen Klammern
Ok, das habe ich auch verstanden.

> - Die Funktion MENU() übernimmt nur das navigieren zwischen den
> einzelnen Elementen und den Aufruf der zum jeweiligen Menü-Eintrag
> gehörenden Funktion
> - Das Auswerten von nicht der Navigation dienenden Tastatureingaben ist
> Aufgabe der jeweiligen Funktion
Das ist soweit auch völlig verständlich und i.O.

> Soweit ich das verstanden habe, habt ihr ein Menü, in dem ihr einen
> Cursor dazu verwendet, auf den jeweils nächsten Eintrag zu zeigen.
> Ich dagegen habe etwas in der Art:
>
> Betriebsart
> +- Autonom
>    +- Starten
>    +- Konfigurieren
> +- Online
>    +- Verbinden
>    +- Upload
>    +- Download
> +- Offline
> Einstellungen
> +- System
>    +- Schnittstelle
>       +- Baudrate
> +- Zeit/Datum
>    +- Uhrzeit
>    +- Datum
> +- ...

Wo ist da jetzt der Unterschied zu einem Menü mit cursor? Wie wechselst 
du beispielsweise zwischen Betriebsart und Einstellungen? Meiner Meinung 
nach sollte es da ein "Hauptmenü" geben das ein Wechseln zwischen diesen 
Untermenüs zulässt.

> Jeder Eintrag bringt wie gesagt seine eigene Displaymaske mit. Das soll
> jetzt nur mal dem besseren Verständnis dienen, warum meine Funktionen so
> sind, wie sie sind :)
Das mit der Display-Maske und Verarbeitung in der Funktion ist auch 
völlig ok und sinnig.

> Um wieder zu eurem Menü zurück zu kommen:
> Ihr wollt also etwas in der Art auf dem Display:
>
> [1. Menü]
> --- Hauptmenu ---
> * 1. Einstellungen
>   2. Starten
>   3. Stoppen
>   4. ---
>
> [2. Menü]
> --- Einstellungen ---
> * 1. Uhrzeit
>   2. Datum
>   3. Schnittstelle
>   4. zurück
>
> Hab ich das so richtig verstanden?
Ja, das ist doch der Sinn eines Menüs ?! Eine Navigation durch 
verschiedene  System-Funktionalitäten.

Das mit den display-Masken und so weiter ist klar! Aber wenn du das 
System startest was zeigst du dann auf dem Display an?

Interessant wäre ja auch was für ein Display du hast? Bei dir klingt es 
so als wird bei dir kein "Menü" dargestellt, sondern immer nur der 
aktuell aktive menü-punkt! Du hast sozusagen ein "virtuelles Menü".
Was Art aber will ist doch ein sichtbares Menü?
Du hingegen wechselst doch nur zwischen den Funktionen deines Systems, 
oder?

>> Ist das ganze denn deterministisch, also zeitlich konstant? Denn mal
>> dauert ein Aufruf vielleicht 30 Mikrosekunden dann mal 33 Mikrosekunden
>> oder noch länger, wenn dir ein Interrupt dazwischen haut. Wie willst du
>> damit eine saubere Zeitbasis für die Uhrzeit bekommen, wenn du so einen
>> zeitlichen Jitter drin hast?
> Ja, weil die Uhrzeit in einem Timer-Interrupt läuft. Da ich das auf
> einem 8051-Derivat mache, habe ich natürlich unterschiedliche Zeiten pro
> Befehl, dies wird jedoch vom Timer-Interrupt beachtet, indem er nicht
> einfach mit einem festen Wert neu geladen wird, sondern den Reload-Wert
> aufaddiert bekommt.
> Kurze Erläuterung: von Eintreten des Interrupts in die Service-Routine
> bis zum Laden des Timers vergehen noch eine Befehlszyklen fürs Pushen
> der Register usw. Der Timer läuft ja derweil noch weiter. Indem der
> Reload-Wert drauf addiert wird, wird der "Jitter" wieder ausgeglichen.
Ok, dann ist es soweit klar! Ich hätte mir nur Gedanken gemacht, wenn 
das ganze ohne Timer realisiert wäre.

Aber warum nutzt du nicht einfach den Timer um das Menü z.b. nur alle 50 
ms aufzurufen. Das würde doch völlig reichen, da der Benutzer ja sowieso 
nicht schneller die Tasten betätigen kann... (?)

> Bzgl. der Modularisierung, ich finde das eigentlich gut modularisiert:
> - Es gibt Funktionen für Tastatur und Display
> - Die MENU()-Funktion und zugehörige Tabelle sowie die aufgerufenen
> Funktionen kapseln die jeweils notwendigen Schritte für das Darstellen
> und Ausführen des Menüs.
Ja, das ist auch meine Meinung. Es klingt modularisiert. Nur weiß ich 
nicht ob das Modul "Menü" schon eine vollständige Menü Realisierung in 
sich trägt oder nur Navigationsmöglichkeiten.

Ist deine Menü-Tabelle eigentlich const? Und wo weißt du die 
Funktionspointer zu?
(das würde art vielleicht zum besseren Verständnis dienen)

Das was du dargestellt hast war ja nur die Menü-Struktur mit den 
Pointern in einer Tabelle, aber auf die Initialisierung der Strukturen 
bist du nicht eingegangen ;-) ganz so funktioniert die Tabelle dann eben 
auch nicht..

MFG

von Ralf (Gast)


Lesenswert?

> Das mit den display-Masken und so weiter ist klar! Aber wenn du das
> System startest was zeigst du dann auf dem Display an?
Der Menü-Pointer ist static und wird auf den ersten Eintrag der Tabelle 
initialisiert. Ich habe keinen Ober-Menüeintrag, nur einen "obersten" 
Eintrag, der zuerst angezeigt wird.

> Interessant wäre ja auch was für ein Display du hast? Bei dir klingt es
> so als wird bei dir kein "Menü" dargestellt, sondern immer nur der
> aktuell aktive menü-punkt!
> ...
> Du hingegen wechselst doch nur zwischen den Funktionen deines Systems,
> oder?
Das ist im Prinzip korrekt. Wobei ich dazu sagen muss, dass mir die 
Variante, die ihr haben möchtest, gleichwertig zusagen würde. Die hatte 
ich anfangs auch im Sinn, ich habe mich deswegen dagegen entschieden, 
weil es mir zu aufwendig vorkam, einerseits das Cursor-Menü und 
andererseits auch noch die Eingabemasken separat zu machen.

> Du hast sozusagen ein "virtuelles Menü".
> Was Art aber will ist doch ein sichtbares Menü?
Deswegen grübel ich bei Gelegenheit mal nach, wie sich das geschickt 
lösen lässt, damit es für ART brauchbar ist.


Was mir auf die schnelle einfällt: Ließe es sich lösen, wenn man mein 
struct so abwandelt, dass es:

1. einen Pointer auf den auszugebenden Menütext enthält
   Der Text wird entsprechend bei einem Menüwechsel ausgegeben
2. Vier Unions enthält, welche jeweils aus einem Funktionspointer und
   einem Pointer auf das struct gebildet werden

Punkt 1. ist denke ich klar.
Zu Punkt 2, der Funktionspointer soll verwendet werden, wenn der 
entsprechende Menü-Punkt eine "richtige" Funktion enthält, also z.B. 
eine Tastatureingabe. Der Pointer auf ein struct soll verwendet werden, 
wenn sich unter dem Menüpunkt noch ein weiteres Menü befindet.

Das war jetzt wirklich mal in zwei Minuten zusammengedacht, aber könnte 
prinzipiell klappen. Was meint ihr dazu?

> Aber warum nutzt du nicht einfach den Timer um das Menü z.b. nur alle 50
> ms aufzurufen. Das würde doch völlig reichen, da der Benutzer ja sowieso
> nicht schneller die Tasten betätigen kann... (?)
Korrekt, das wäre eine Möglichkeit, mehr Rechenzeit freizuschaufeln.
Falls jemand das Prinzip mal brauchen kann:
Der Timer läuft auf einer Zeitbasis von 3,333ms. Innerhalb der ISR gibt 
es eine State-Variable, welche bei jedem Durchlauf erhöht wird, bis sie 
den Wert drei erreicht, in diesem Fall wird sie auf Null zurückgesetzt. 
Das heisst, ich habe drei Abschnitte der ISR, welche jeweils alle 10ms 
abgearbeitet werden, mit einem Versatz von eben 3,333ms zueinander. Hier 
mache ich dann z.B. die Tastaturabfrage und die Uhrzeit sowie ein paar 
Software-Timer.

> Ja, das ist auch meine Meinung. Es klingt modularisiert. Nur weiß ich
> nicht ob das Modul "Menü" schon eine vollständige Menü Realisierung in
> sich trägt oder nur Navigationsmöglichkeiten.
Die Funktion MENU() an sich bietet noch kein Menü, das ist richtig, 
meine Lösung besteht aus den drei Teilen MENU(), der Tabelle und den 
jeweils aufgerufenen Funktionen.

> Ist deine Menü-Tabelle eigentlich const? Und wo weißt du die
> Funktionspointer zu? (das würde art vielleicht zum besseren Verständnis
> dienen)
Ja, die Tabelle ist im Code-Speicher abgelegt. Die Funktionspointer sind 
im letzten Eintrag zugewiesen:
1
_stMenu code stMenues[] = {
2
  {&stMenues[26], &stMenues[13], NULL, &stMenues[1], m1},        //M1
3
  {&stMenues[9], &stMenues[5], &stMenues[0], &stMenues[2], m11}, //M11
4
  {...}
5
};

m1 und m11 wären dann die Adressen der aufzurufenden Funktionen.

> Das was du dargestellt hast war ja nur die Menü-Struktur mit den
> Pointern in einer Tabelle, aber auf die Initialisierung der Strukturen
> bist du nicht eingegangen ;-) ganz so funktioniert die Tabelle dann eben
> auch nicht..
Sorry, da versteh ich jetzt nicht, was du mit Initialisieren meinst. Die 
Tabelle ist fix im Code-Speicher hinterlegt, und da die Tabelle ein 
Array des Menü-Structs ist, sind die Strukturen ja initialisiert.

So, ich klopp mich jetzt in die Falle, bis morgen dann. Ich glaube, wenn 
wir so weitermachen, wird da am Ende ein schönes Stück Code rauskommen, 
welches wir dann in die Code-Sammlung stellen können :)

Ralf

von art (Gast)


Lesenswert?

also ich hab jetzt folgendes gemacht Ralf:

void hauptmenue()
{
  lcd_clear();
  set_cursor(0, 1);
  lcd_string("Hauptmenue:");
  set_cursor(0, 2);
  lcd_string("1/4 Modus waehlen");
  set_cursor(0, 3);
  lcd_string("2/4 Einstellungen");
  set_cursor(0, 4);
  lcd_string("zurueck");
  set_cursor(4, 2);
}

void modus_waehlen()
{
  lcd_clear();
  set_cursor(0, 1);
  lcd_string("Modus wählen:");
  set_cursor(0, 2);
  lcd_string("1/4 xy");
  set_cursor(0, 3);
  lcd_string("2/4 xy");
  set_cursor(0, 4);
  lcd_string("zurück");
  set_cursor(4 , 2);
}

void einstellungen()
{
  lcd_clear();
  set_cursor(0, 1);
  lcd_string("Einstellungen:");
  set_cursor(0, 2);
  lcd_string("1/3 Spannung");
  set_cursor(0, 3);
  lcd_string("2/3 Helligkeit");
  set_cursor(0, 4);
  lcd_string("zurück");
}

char (*menuestruktur[])() = {
    hauptmenue, modus_waehlen, einstellungen
    };

habe char funktionen gemacht und eine tabelle
so und jetzt hab ich schonmal angefangen in die main funktion zu 
schreiben

static char a;

if (debounce(&PIND, PD3))
    a = (*menuestruktur[0])();


so jetzt brauch ich aber noch einen zeiger mit dem ich hin und her 
springen kann oder? damit ich in das nächste untermenü komme vom 
hauptmenü

von art (Gast)


Lesenswert?

kommt aber auch ein warning, kann damit aber nichts anfangen:

warning: initialization from incompatible pointer type

heisst es das ich einen pointer vom typ habe der nicht kompatibel ist zu 
dem was ich mache?

von art (Gast)


Lesenswert?

ist keiner mehr da der mir helfen könnt?

von Sebastian B. (mircobolle)


Lesenswert?

Ralf wrote:


> Ja, die Tabelle ist im Code-Speicher abgelegt. Die Funktionspointer sind
> im letzten Eintrag zugewiesen:
>
>
1
> _stMenu code stMenues[] = {
2
>   {&stMenues[26], &stMenues[13], NULL, &stMenues[1], m1},        //M1
3
>   {&stMenues[9], &stMenues[5], &stMenues[0], &stMenues[2], m11}, //M11
4
>   {...}
5
> };
6
>
>
> m1 und m11 wären dann die Adressen der aufzurufenden Funktionen.

Ah, ok das hatte ich übersehen, dann ist das auch klar!

Zusammenfassen müsste ein Menü-Element nun folgendes behinhalten:
- unsigned char * p_MenueEintragTitel;
- menu_struct   * p_Vorgaenger_MenueEintrag;
- menu_struct   * p_Nachfolger_MenueEintrag;
- void (* p_Funktionszeiger) (void);
- menu_sturct   * p_Naechstes_Menue;

-> nennen wir es einfach mal menu_struct;

Ist das soweit ok?
Damit koennte man schon mal ein Menue in der Hauptebene definieren.

menu_struct ErsteMenueEbene[3]={

{"Hauptmenue", &(ErsteMenueEbene[2]), &(ErsteMenueEbene[1]),NULL, 
&(ZweiteMenueEbene[0])},
{"Modus waehlen", &(ErsteMenueEbene[0]), &(ErsteMenueEbene[2]),NULL, 
&(DritteMenueEbene[0])},
{"Einstellungen", &(ErsteMenueEbene[1]), &(ErsteMenueEbene[0]),NULL, 
&(VierteMenueEbene[0])}
};

Das ganze nur mal als Prinzip-Aufbaue der ersten Menue-Ebene.
Die übrigen Menue-Ebenen müssten jetzt nicht in einzelne Tabellen, 
sondern die Menue Tabelle koennte einfach 2D werden.

also

menu_struct Menue_Tabelle[MENUE_EBENEN][MENUE_EINTRAEGE];


MFG

von Sebastian B. (mircobolle)


Lesenswert?

Ralf wrote:

Mir ist gerade noch was anderes aufgefallen (oder ins Auge gesprungen)
für was steht dieser Zusatz "code" bei dir?

>
1
>   void (code *fpPtr)(void);
2
> } _stMenu;
3
>

> [c]
> _stMenu code stMenues[] = {
>

von Ralf (Gast)


Lesenswert?

"code" sagt meinem Compiler, dass die Variable im Code-Speicher zu 
landen hat.

Ralf

von art (Gast)


Lesenswert?

Ralf prinzip von der tabelle verstanden. aber jetzt brauch ich noch in 
der main funktion einen pointer der dann nach jedem tastendruck hoch bzw 
runtergezählt werden oder nicht?

von Sebastian B. (mircobolle)


Lesenswert?

art wrote:
> Ralf prinzip von der tabelle verstanden. aber jetzt brauch ich noch in
> der main funktion einen pointer der dann nach jedem tastendruck hoch bzw
> runtergezählt werden oder nicht?

Du brauchst einen Zeiger, der auf den aktuellen Menue-Eintrag zeigt, das 
ist richtig.

z.b.
1
_stMenu * p_current_Menu = Menu_Tab[EBENE_1][EINTRAG_1];

angenommen der Struct sieht wie folgt aus:
1
- unsigned char * p_MenueEintragTitel;
2
- menu_struct   * p_Vorgaenger_MenueEintrag;
3
- menu_struct   * p_Nachfolger_MenueEintrag;
4
- void (* p_Funktionszeiger) (void);
5
- menu_sturct   * p_Naechstes_Menue;

dann würdest du so auf den naechsten Eintrag kommen;
1
p_current_Menu = p_current_Menu->p_Nachfolger_MenueEintrag;
2
oder
3
p_current_Menu = p_current_Menu->p_Vorgaenger_MenueEintrag;
4
oder
5
p_current_Menu = p_current_Menu->p_Naechstes_Menue;

den Titel erhaelst du so:
1
p_current_Menu->p_MenueEintragTitel;

MFG

PS: Wie weit bist du denn mit deinem Projekt?

von art (Gast)


Lesenswert?

noch nicht sehr weit, und wie geb ich das ganze auf dem display aus? da 
muss ich dann praktisch ne neue funktion schreiben, weil momentan kann 
ich nur strings an des lcd display senden

von art (Gast)


Lesenswert?

muss es dann erst nächste woche ausprobiern, hab die woche ka zeit, weil 
ich am fr ne prüfung hab und die woche dazu noch den unterricht

von art (Gast)


Lesenswert?

menu_struct ErsteMenueEbene[3]={

{"Hauptmenue", &(ErsteMenueEbene[2]), &(ErsteMenueEbene[1]),NULL,
&(ZweiteMenueEbene[0])},
{"Modus waehlen", &(ErsteMenueEbene[0]), &(ErsteMenueEbene[2]),NULL,
&(DritteMenueEbene[0])},
{"Einstellungen", &(ErsteMenueEbene[1]), &(ErsteMenueEbene[0]),NULL,
&(VierteMenueEbene[0])}
};

Das ganze nur mal als Prinzip-Aufbaue der ersten Menue-Ebene.
Die übrigen Menue-Ebenen müssten jetzt nicht in einzelne Tabellen,
sondern die Menue Tabelle koennte einfach 2D werden.

also

menu_struct Menue_Tabelle[MENUE_EBENEN][MENUE_EINTRAEGE];


wo menü_eintrag steht also bei dem menu struct menue_tabelle ist das 
dann die ebene wo ich die unterpunkte zu den einzelnen menüs 
reinschreibe?

von art (Gast)


Lesenswert?

habe versucht das zu implementieren bekomme aber errors und warnings

von Ralf (Gast)


Lesenswert?

Hi art,

> habe versucht das zu implementieren bekomme aber errors und warnings
Mit der Aussage wirst du keinen Erfolg haben ;)
Poste den Code, den du aktuell verwendest, und die Warnings/Errors dazu.

Ralf

von art (Gast)


Lesenswert?

das ist der ausschnitt

struct menu_struct
{
   unsigned char *MenueEintragTitel;
  struct menu_struct *Vorgaenger_MenueEintrag;
            struct menu_struct *Nachfolger_MenueEintrag;
   void (* p_Funktionszeiger) (void);
   struct menu_sturct *Naechstes_Menue;
}

struct menu_struct ErsteMenueEbene[3]={
{"Hauptmenue", &(ErsteMenueEbene[2]), &(ErsteMenueEbene[1]),NULL, 
&(ZweiteMenueEbene[0])},
{"Modus waehlen", &(ErsteMenueEbene[0]), &(ErsteMenueEbene[2]),NULL, 
&(DritteMenueEbene[0])},
{"Einstellungen", &(ErsteMenueEbene[1]), &(ErsteMenueEbene[0]),NULL, 
&(VierteMenueEbene[0])}
};

und das sind die errors und warnings die ich dazu bekomme:

../Menü-Funktionen.c:49: error: two or more data types in declaration 
specifiers
../Menü-Funktionen.c:50: warning: pointer targets in initialization 
differ in signedness
../Menü-Funktionen.c:50: error: 'ZweiteMenueEbene' undeclared here (not 
in a function)
../Menü-Funktionen.c:51: warning: pointer targets in initialization 
differ in signedness
../Menü-Funktionen.c:51: error: 'DritteMenueEbene' undeclared here (not 
in a function)
../Menü-Funktionen.c:52: warning: pointer targets in initialization 
differ in signedness
../Menü-Funktionen.c:52: error: 'VierteMenueEbene' undeclared here (not 
in a function)

von Ralf (Gast)


Lesenswert?

Wenn das direkt der Code ist, den du verwendest, und nicht Copy&Paste 
vom obigen Beitrag, dann hast du einen Schreibfehler in deinem struct:

> struct menu_struct
> {
>    unsigned char *MenueEintragTitel;
>   struct menu_struct *Vorgaenger_MenueEintrag;
>             struct menu_struct *Nachfolger_MenueEintrag;
>    void (* p_Funktionszeiger) (void);
>    struct menu_sturct *Naechstes_Menue;  <-- hier steht stu_rc_t!
> }

Zu den anderen Fehlern/Warnings, sind denn 'ZweiteMenueEbene, 
DritteMenueEbene, VierteMenueEbene' überhaupt irgendwo definiert? Das 
ist jetzt hier nicht ersichtlich...

Ralf

von art (Gast)


Lesenswert?

nein sind nirgendswo definiert aber hab auch schon versucht sie zu 
definieren aber dann kommen andere errors. kannst mir sagen wie ich die 
definieren soll?

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

AUTSCH!


A: das sind die errors und warnings die ich dazu bekomme: [ ... 
Fehlermeldungen zu verschiedenen Objekten ... ]

R: sind denn [ ... verschiedene Objekte ... ]  überhaupt irgendwo 
definiert?

A: nein sind nirgendswo definiert aber hab auch schon versucht sie zu
definieren aber dann kommen andere errors


@art
Bist du dir ganz sicher daß du die richtige systematische Vorgehensweise 
zur Programmentwicklung drauf hast? Vielleicht solltest du mal stärker 
drüber nachdenken WIE du Probleme löst / programmierst, und weniger 
WELCHE Algorithmen du implementieren willst.

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.