Problem mit Funktionspointern

OP Persönliche Seite #3034283
Lesenswert?

Hallo zusammen!
Ich bin derweil dabei ein Menü zu programmieren und verwende dafür 
Funktionspointer:
1
typedef void (*pMenuFnct)(void);
2

3
struct sMenuEntry
4
{
5
    char text[20];
6
    pMenuFnct function;
7
};

Dafür habe ich zu Testzwecken folgendes geschrieben:
1
#define ARRAY_SIZE(x)  (sizeof(x)/sizeof(*x))
1
void main(void)
2
{
3
    uint8_t actContrast = DEFAULT_CONTRAST;
4

5
    /*Datenstruktur für den Menübaum erstellen*/
6
    struct sMenuEntry meControll[] =
7
    {
8
        { "Energieoptionen", mEnergy },
9
        { "Informationen", mInfo },
10
        { "Exit" }
11
    };
12

13
    initPIC();
14
    configPWM();
15
    initSPI();
16
    initDOGL(&actContrast);
17

18
    charChainDOGL(0, 0, NOINVERSE, "Warte auf Taste");
19

20
    while(1)
21
    {
22
        if(input & KEY_OK)
23
        {
24
            input &= ~KEY_OK;
25
            mControll(ARRAY_SIZE(meControll),meControll);
26
            clearDOGL();
27
            charChainDOGL(0, 0, NOINVERSE, "Warte auf Taste");
28
        }
29
    }
30
}

...
1
void mControll (uint8_t menuSize, struct sMenuEntry menu[])
2
{
3
    uint8_t active = 0;
4
    bool running;
5

6
    drawMenu(menuSize, menu);
7
    drawBar("Einstellungen");
8
    charChainDOGL(active+2,0,NOINVERSE,">");
9

10
    running = true;
11

12
    while(running)
13
    {
14
        if( input & KEY_OK )
15
        {
16
            input &= ~KEY_OK;
17
            if( menu[active].function )
18
            {
19
                clearDOGL();
20
                menu[active].function();
21
                drawMenu(menuSize, menu);
22
                drawBar("Einstellungen");
23
                charChainDOGL(active+2,0,NOINVERSE,">");
24
            }
25
            else
26
                running = false;
27
        }
28
        else if( input & KEY_UP )
29
        {
30
            input &= ~KEY_UP;
31
            /*Geht es höher?*/
32
            if( active > 0)
33
            {
34
                charChainDOGL(active+2, 0, NOINVERSE, "  ");
35
                active--;
36
                charChainDOGL(active+2, 0, NOINVERSE, ">");
37
            }
38
        }
39
        else if( input & KEY_DOWN )
40
        {
41
            input &= ~KEY_DOWN;
42
            /*Geht es tiefer?*/
43
            if( active < (menuSize-1) )
44
            {
45
                charChainDOGL(active+2, 0, NOINVERSE, "  ");
46
                active++;
47
                charChainDOGL(active+2, 0, NOINVERSE, ">");
48
            }
49
        }
50
    }
51
}

Das Menü wird korrekt aufgebaut (Display) und ich kann mit dem Cursor 
zwischen den drei Menüpunkten wandern. Doch egal welchen Menüpunkt ich 
anwähle (activ = 0,1,2) nach dem Drücken der OK (KEY_OK) Taste wird 
IMMER der else Zweig ausgeführt:
1
input &= ~KEY_OK;
2
if( menu[active].function )
3
{
4
   clearDOGL();
5
   menu[active].function();
6
   drawMenu(menuSize, menu);
7
   drawBar("Einstellungen");
8
   charChainDOGL(active+2,0,NOINVERSE,">");
9
}
10
else
11
   running = false;

Sicherlich nur ein kleiner Schnitzer irgendwo aber ich finde ihn nicht 
=/...
Viele Grüße, Nico
OP Persönliche Seite #3034318
Lesenswert?

Das ist alles zugehörige an Code.

>Was genau soll das darstellen? Greifst du damit auf ein Strukt in einer
>Liste zu, und dann auf funktion.

Die Zeile prüft ob es zu dem angewählten Menüpunkt (die Variable active 
ist der Index für das Array (welches aus den Structs besteht - siehe 
main)) eine Funktion vorhanden ist. Dies ist bei "Energieoptionen" und 
"Informationen" der Fall. Nur bei Exit nicht.
1
struct sMenuEntry meControll[] =
2
{
3
    { "Energieoptionen", mEnergy },
4
    { "Informationen", mInfo },
5
    { "Exit" }
6
};

Wähle ich den Menüpunkt 1 (Informationen) an und klicke OK, dann würde 
ich erwarten, dass die zugehörige Funktion aufgerufen werden würde, doch 
er geht immer in den else-Zweig.


Hier würde er dann verweilen, bis ich wieder OK gerückt hätte.
1
void mInfo (void)
2
{
3
    drawBar("Informationen");
4

5
    charChainDOGL(2, 0, NOINVERSE, "Firmware:");
6
    charChainDOGL(2,60, NOINVERSE, FW_VERSION);
7
    charChainDOGL(3, 0, NOINVERSE, "Entwickler:");
8
    charChainDOGL(3,60, NOINVERSE, "N. Meyertoens");
9
    charChainDOGL(4, 0, NOINVERSE, "Siehe auch:");
10
    charChainDOGL(4,60, NOINVERSE, "PIC-Projekte.de");
11

12
    while( !(input & KEY_OK) );
13
    input &= ~KEY_OK;
14
}
#3034359
Lesenswert?

Ich seh keinen Fehler, ausser vielleicht einem race auf input, aber der 
kann ja nicht in den else-Zweig führen. Kannst du die Änderungen von 
active irgendwie mitprotokollieren?

Mike, er will an der Stelle die Funktion nicht aufrufen, er will prüfen 
ob die Funktionsadresse NULL ist (=Exit). Nico, du solltest in deiner 
struct da mal explizit 0 hinschreiben ...
OP Persönliche Seite #3034368
Lesenswert?

Mike Mike schrieb:
> Nochmal,
>
> ist es absicht, das du oben if( menu[active].function ) und unten
> menu[active].function() stehen hast??? Ich glaube der Fehler rührt wohl
> daher

Mit der Zeile:
>> if( menu[active].function )
Beabsichtige ich zu prüfen ob es bei dem angewählten Menüpunkt eine 
Funktion gibt, sprich ob der Funktionszeiger auf eine Funktion zeigt 
oder eben nicht.

Und die Zeile:
>> menu[active].function();
Soll diese Funktion dann aufrufen..

Nochmal zu:
>> if( menu[active].function )
Ich habe doch oben in der main ein Array angelegt, dass drei Elemente 
des structs beinhaltet. Dieses Struct beinhaltet wiederum ein Array aus 
chars und einen Funktionspointer. Und wenn der Funktionspointer nicht 
definiert wird (das wäre dann beim Eintrag "Exit" der Fall), dann zeigt 
dieser Zeiger quasi auf NULL und somit würde der Else-Zweig ausgeführt.
#3034377
Lesenswert?

Mach da
1
    struct sMenuEntry meControll[] =
2
    {
3
        { "Energieoptionen", mEnergy },
4
        { "Informationen", mInfo },
5
        { "Exit" }
6
    };
mal bei "Exit" explizit einen NULL Pointer rein.
1
    struct sMenuEntry meControll[] =
2
    {
3
        { "Energieoptionen", mEnergy },
4
        { "Informationen", mInfo },
5
        { "Exit", NULL }
6
    };

lokale Variablen werden nicht default initialisiert. Auch dann nicht, 
wenn sie in einer Struktur stecken.
Ist aber jetzt wachrscheinlich nicht dein Problem.
Gast #3034381
Lesenswert?

Ich sehe auch keinen Fehler.
Das einzige was mir aufstößt: ich würde den Zeiger bei Ende auch 
initialisieren, also so:
1
    struct sMenuEntry meControll[] =
2
    {
3
        { "Energieoptionen", mEnergy },
4
        { "Informationen", mInfo },
5
        { "Exit",0 }
6
    };

daranwirds aber vermutlich nicht liegen.
#3034428
Lesenswert?

Das eine Zufällige Adresse drinsteht sollte klar sein, und das damit 
auch Exit aufgerufen werden kann ist auch klar, aber er meint ja, dass 
sein Problem wäre, dass er in den else Fall rutscht.
Und das kann ja nur passieren, wenn ein Nullpointer vorhanden ist.

Jetzt wäre die Frage, wann passiert das den.
Immer, direkt beim Einstieg, oder sporadisch.

Meiner Meinung nach wird doch alles ab*acken, wenn er zu einer falschen 
Adresse springt, also kann das Programm auch nicht wirklich 
weiterlaufen, oder?
OP Persönliche Seite #3034456
Lesenswert?

Folgendes (gerade debuggt):

* Ich gehe mit OK in die Funktion mControll
* Ich drehe den Drehgeber, sodass das Display Exit als angewählt zeigt
* Die Variable activ hat nun den Wert 0x02
* Ein weitere Klick OK bringt mich zur if/else
* activ ist definitiv(!) 2
* Es geht in den else-Zweig
* Und somit zurück zu main

So, dass ist bei Exit ja auch so gewollt.
Neues Szenario:

* Ich gehe mit OK in die Funktion mControll
* Ich drehe den Drehgeber, sodass das Display Informationen als 
angewählt zeigt
* Die Variable activ hat nun den Wert 0x01
* Ein weitere Klick OK bringt mich zur if/else
* activ ist definitiv(!) 1
* Es geht in den else-Zweig
* Und somit zurück zu main

Das ist nur leider nicht gewollt.

> Was passiert denn wenn du einen direkten Ausruf machst in der Main mit
> meControll[0].function()
> meControll[1].function()
> meControll[2].function()

Das wiederum funktioniert!
1
void main(void)
2
{
3
    uint8_t actContrast = DEFAULT_CONTRAST;
4

5
    /*Datenstruktur für den Menübaum erstellen*/
6
    struct sMenuEntry meControll[] =
7
    {
8
        { "Energieoptionen", mEnergy },
9
        { "Informationen", mInfo },
10
        { "Exit" , NULL}
11
    };
12

13
    initPIC();
14
    configPWM();
15
    initSPI();
16
    initDOGL(&actContrast);
17

18
    charChainDOGL(0, 0, NOINVERSE, "Warte auf Taste");
19

20
    while(1)
21
    {
22
        if(input & KEY_OK)
23
        {
24
            input &= ~KEY_OK;
25

26
            meControll[1].function();
27

28
//            mControll(ARRAY_SIZE(meControll),meControll);
29
            clearDOGL();
30
            charChainDOGL(0, 0, NOINVERSE, "Warte auf Taste");
31
        }
32
    }
33
}

So bringt mich der Tastendruck direkt in die Funktion mInfo.
OP Persönliche Seite #3034497
Lesenswert?

Mike Mike schrieb:
> Hab das mal gerade am PC Probiert, ich würde so den Fehler "invalid use
> of undefined type 'struct sMenuEntry' bekommen

Entschuldigung, ich denke da habe ich versehentlich Informationen 
vorenthalten; Die Datei main.c, in der das hier alles stattfindet, 
includiert die Datei main.h, welche wiederum dies beinhaltet:
1
typedef void (*pMenuFnct)(void);
2

3
struct sMenuEntry
4
{
5
    char text[20];
6
    pMenuFnct function;
7
};
Somit sollte dieser Typ global bekannt sein, oder?

Um die anderen Fragen zu beantworten habe ich mal einen Screenshot 
gemacht. Ach und: Ja, wenn ich das Struct nicht innerhalb von main 
sondern global definiere, dann geht es.
Angehängte Dateien:
#3034515
Lesenswert?

Aber ich kann mir nicht vorstellen, dass das zum Fehler führt, oder 
überhaupt ein Fehler ist. Evtl zeigt er nur struct*, denn es wird ja vom 
Funktionspointer der richtige Typ erkannt, aber die Werte sind NULL.

Kannst du mal zum Spass in der void mControll (uint8_t menuSize, struct 
sMenuEntry menu[]) den Funktionspointer noch mal zuweisen und kucken obs 
dann geht?
OP Persönliche Seite #3034518
Lesenswert?

Also:

Wenn ich das Array
1
struct sMenuEntry meControll[] =
2
{
3
    { "Energieoptionen", mEnergy },
4
    { "Informationen", mInfo },
5
    { "Exit" , NULL}
6
};
innerhalb der main definiere, dann geht der Aufruf
1
meControll[1].function();
solange ich es direkt in main mache. Rufe ich allerdings die mControll 
auf und versuch dann dort den Aufruf (mit active als Index), dann geht 
es nicht mehr.

Wenn ich das Array oberhalb von main, also global, definiere, dann kann 
ich die mControll aufrufen und es klappt auch der Aufruf dort.
#3034521
Lesenswert?

Okay.

Was du machst, ist den Typen Global definieren, das ist ja okay.
Aber wenn du in der Main dir ein Feld anlegst mit Inhalt und den Pointer 
übergibst solltest du von jeder Funktion drauf auch arbeiten können...

Deine drawMenu() ändert aber nichts am menu oder? Schreib zur Sicherheit 
noch ein const vor den Pointer.
OP Persönliche Seite #3034525
Lesenswert?

Peter II schrieb:
> in der main.h legt du scheinbar einen neue Variable an. Du musst sie
> dort nur mit extern kennzeichnen. du hast also 2 Stukturen mit anderm
> Inhalt und gleichem namen.

Du meinst aus dem:
1
/*Menü Struktur*/
2
typedef void (*pMenuFnct)(void);
3

4
struct sMenuEntry
5
{
6
    char text[20];
7
    pMenuFnct function;
8
};
Soll ich das machen:?
1
/*Menü Struktur*/
2
typedef void (*pMenuFnct)(void);
3

4
extern struct sMenuEntry
5
{
6
    char text[20];
7
    pMenuFnct function;
8
};
Das verstehe ich nicht.. Damit ist doch noch keine Variable bzw. Array 
erzeugt?

> Aber wenn du in der Main dir ein Feld anlegst mit Inhalt und den Pointer
> übergibst solltest du von jeder Funktion drauf auch arbeiten können...
Das scheint auch der Fall zu sein, sonst könnte meine drawMenu Funktion 
ja auch nicht die Menüpunkte aufs Display schreiben oder?
1
void drawMenu(uint8_t menuSize, struct sMenuEntry menu[])
2
{
3
    uint8_t k;
4

5
    clearDOGL();
6
    for( k=0; k < menuSize; k++ )
7
        charChainDOGL(k+2,5,NOINVERSE,menu[k].text);
8
}
#3034528
Lesenswert?

Karl Heinz Buchegger schrieb:
> Woran machst du eigentlich fest, dass du immer in den else Zweig kommst?
>
> Der else Zweig bricht die Schleife ab.
> Aber nur weil die Schleife abgbrochen wird, bedeutet das nicht
> zwangsläufig, dass das über den else passiert.

Sorry.
Zu spät gesehen

> * activ ist definitiv(!) 1
> * Es geht in den else-Zweig
> * Und somit zurück zu main
>
> Das ist nur leider nicht gewollt.


Muss erst mal nachlesen, was in der Zwischenzeit alles passiert ist :-)
Gast #3034533
Lesenswert?

Prüfe in der if (...) explizit auf != NULL und setzt beim else die 
Klammer:

            if( menu[active].function != NULL )
            {
                clearDOGL();
                menu[active].function();
                drawMenu(menuSize, menu);
                drawBar("Einstellungen");
                charChainDOGL(active+2,0,NOINVERSE,">");
            }
            else
            {
                running = false;
            }


Ich habe schon die lustigsten Dinge erlebt wenn in der if Anweisung 
keine explizite steht.

Die Klammer sollte nicht das Problem sein, der Compiler darf eigentlich 
keine Probleme damit habe: ich habe aber auch schon anderes erlebt 
(Fehler im Compiler!).
#3034535
Lesenswert?

Eigentlich ja.

Extern heißt doch in dem Fall nur, dass es sich nur um eine Deklaration 
handelt?
Aber unten legt er sich doch nur vom selben Typen 3 Stück an.

Wie Karl Heinz schon gesagt hat, mach mal in den Else Zweig eine weiter 
Debugvariable rein, und schau ob die erreicht wird.
#3034553
Lesenswert?

Und wenn alles nichts hilft:

Zippe den kompletten Code zusammen und poste das mal.

Mein Bauch sagt, dass dein Problem wo anders liegt und nicht im 
gezeigten Code. Hier siehst du nur die Symptome. Aber die Ursache ist 
was anderes. Denn eigentlich ist das Gezeigte IMHO alles korrekt.
#3034556
Lesenswert?

Dann funktioniert das auch.
Bei deinem Letzten Screenshot war auch schön zu sehen, dass input den 
Wert 1 hatte, aber der dazugehöriger Pointer hatte den Wer NULL.

Prüfe mal vor Aufruf der Funktion, welchen Wer der Pointer in der Main 
hat, und welchen direkt nach dem Einstig in die Funktion.
Gast #3034570
Lesenswert?

Hallo Nico.

Wenn Du deine Struct global definierst, ist die Adresse dieser Struct 
jedem (also global) bekannt.
Wenn Du diese allerdings in main() definierst, dann ist sie nur dort 
lokal bekannt, solange Du nicht die Adresse der Struct an mControll(...) 
übergibst.

Versuchs mal so:
1
mControll(ARRAY_SIZE(meControll),&meControll);


Happy hacking...
#3034580
Lesenswert?

WDT schrieb:
> Hallo Nico.
>
> Wenn Du deine Struct global definierst, ist die Adresse dieser Struct
> jedem (also global) bekannt.

Na ja.
Das will man hier aber nicht.
Der Dreh besteht ja darin, dass man im Endeffekt 15 verschiedene Arrays 
im Programm hat und der Funktion per Argument mitgibt, welches sie 
bearbeiten soll.
D.h. globale ist hier keine Option.

> Versuchs mal so:
>
>
1
mControll(ARRAY_SIZE(meControll),&meControll);

Ohne & ist schon richtig.
Der Name eines Arrays steht in Argumentlisten automatisch für einen 
Pointer auf das erste Element.
#3034585
Lesenswert?

Oh my. != NULL, wow.

Wir können glaube ich davon ausgehen das die Funktionspointer mit null 
überschrieben werden. Das scheint der Debugger ja zu bestätigen. Aber 
warum?

Nico, in welche Richtung wächst der Stack auf deinem Prozessor?

Ich frage, weil im Debugger 'menu' auf 0x66 zu liegen kommt, und wenn 
wie mir scheint der Stack gegen 0 wächst dann kommt da ja bald der 
IO-Bereich ...

LG, Sebastian
OP Persönliche Seite #3034602
Lesenswert?

Mike Mike schrieb:
> Du hast zwei verschiedene mControll funktionen, einmal in der main.c,
> einmal in der menu.c

Die menu.c ist derzeit kein Bestandteil des Projektes. Ich hatte 
ursprünglich die ganzen Menüfunktionen in der menu.c, habe es dann aber, 
aufgrund der Probleme und des für mich noch neuen Compilers, erstmal in 
main.c gelassen.
(Habe die menu.c und das includiren der menu.h (welche ohnehin leer war) 
mal gelöscht - und auch das gab keine Änderung)

Sebastian, zu deiner Frage mit dem Stack: Da bin ich gerade etwas 
überfragt. Ich versuche es mal nachzulesen (Datenblatt ist übrigens im 
Ordner /docs).
#3034611
Lesenswert?

5.1.2.2 Return Stack Pointer (STKPTR)
The STKPTR register (Register 5-1) contains the Stack
Pointer value, the STKFUL (stack full) Status bit and
the STKUNF (Stack Underflow) Status bits. The value
of the Stack Pointer can be 0 through 31. The Stack
Pointer increments before values are pushed onto the
stack and decrements after values are popped off the
stack. On Reset, the Stack Pointer value will be zero.
The user may read and write the Stack Pointer value.
This  feature  can  be  used  by  a  Real-Time  Operating
System (RTOS) for return stack maintenance.
After the PC is pushed onto the stack 31 times (without
popping any values off the stack), the STKFUL bit is
set.  The  STKFUL  bit  is  cleared  by  software  or  by  a
POR.
#3034613
Lesenswert?

Sebastian W. schrieb:
> Oh my. != NULL, wow.
>
> Wir können glaube ich davon ausgehen das die Funktionspointer mit null
> überschrieben werden.

So siehts wohl aus.


Nico. Kann es sein, dass dein Text Feld früher mal eine andere Länge 
hatte?
Lösche mal alle Object Dateien und compiliere ALLES neu. Achte drauf, 
dass auch wirklich alles kompiliert wird!


In den Header Files hat er sich zwar die Dinge unlogisch und nicht 
geschickt verteilt, aber so richtig einen Fehler seh ich da auch nicht 
drinnen.
#3034628
Lesenswert?

Nico M. schrieb:
> Mike Mike schrieb:
>> Du hast zwei verschiedene mControll funktionen, einmal in der main.c,
>> einmal in der menu.c
>
> Die menu.c ist derzeit kein Bestandteil des Projektes. Ich hatte
> ursprünglich die ganzen Menüfunktionen in der menu.c, habe es dann aber,
> aufgrund der Probleme und des für mich noch neuen Compilers, erstmal in
> main.c gelassen.
> (Habe die menu.c und das includiren der menu.h (welche ohnehin leer war)
> mal gelöscht - und auch das gab keine Änderung)


Das war keine gute Idee.
Teil dir die Dinge ruhig auf. Das darf kein Problem sein.

in menu.h kommt alles menüspezifische:
Zunächst mal ist das die Strukturdefinition.
Und dann sind das die Prototypen der Funktionen, die du zum Abarbeiten 
von Menüs brauchst. Also die Control Funktion und die Draw Funktion.

Alles andere fliegt raus!
Also auch deine Info und die andere Funktion.
Die hat da drinn nichts verloren. Denn das ist keine allgemeine Menü 
Funktion, sondern gehört zu einem spezifischen Menü.

Aus main.h mistest du aus dem gleichen Grund aus.
Alles was dem Themenkreis nach zur 'allgemeinen Menübehandlung' gehört, 
fliegt da raus.


Und natürlich auch mit den C-Files. Die Funktionen sauber nach 
Themenkreisen aufteilen und in die jeweiligen Files stecken.

Damit hast du dann erst mal eine saubere Trennung zwischen dem 'Menü an 
sich' und dem Code, der diese FUnktionalität benutzt.


(Hintergrund: ich kann nämlich wirklich nichts großartiges finden, was 
dieses Verhalten erklären könnte. Mglw. ist da in der Toolchain 
irgendwas schief gelaufen. Hatte ich schon mal (mit einem anderen 
Compiler). Da sucht man sich einen Wolf)
OP Persönliche Seite #3034630
Lesenswert?

Karl Heinz Buchegger schrieb
> Nico. Kann es sein, dass dein Text Feld früher mal eine andere Länge
> hatte?
Meinst du in der Deklaration die Größe von 20 oder bei der Definition?

> Lösche mal alle Object Dateien und compiliere ALLES neu. Achte drauf,
> dass auch wirklich alles kompiliert wird!
1) Was heißt alle, ich konnte nur eines finden.
2) Wie kann/soll ich sicherstellen, dass alles kompiliert wurde?

> In den Header Files hat er sich zwar die Dinge unlogisch und nicht
> geschickt verteilt, aber so richtig einen Fehler seh ich da auch nicht
> drinnen.
Ich würde mich freuen, wenn du mir etwas darüber sagen könntest.
#3034640
Lesenswert?

Nico M. schrieb:
> Karl Heinz Buchegger schrieb
>> Nico. Kann es sein, dass dein Text Feld früher mal eine andere Länge
>> hatte?
> Meinst du in der Deklaration die Größe von 20 oder bei der Definition?

Die 20 stehen nur bei der Deklaration.
ALso hier

struct sMenuEntry
{
    char text[20];
    pMenuFnct function;
};

waren die 20 früher mal anders?

>
>> Lösche mal alle Object Dateien und compiliere ALLES neu. Achte drauf,
>> dass auch wirklich alles kompiliert wird!
> 1) Was heißt alle, ich konnte nur eines finden.

Auf deinem Pfad zum Projekt muss es irgendwo ein
main.o, menu.o, dogl.o, isr.o  geben.

lösche alle runter

> 2) Wie kann/soll ich sicherstellen, dass alles kompiliert wurde?

Gibt es in deiner IDE kein Logging Fenster, in der Meldungen 
durchlaufen, was beim Build alles passiert?


> Ich würde mich freuen, wenn du mir etwas darüber sagen könntest.
Siehe die unmittelbar vorhergehende Post
#3034641
Lesenswert?

Einfach alles aus dem build Verzeichnis löschen, nur den Sourcecode 
übrig lassen, dann sollte alles neu compiliert werden.

Mit den Header Files meint er folgendes:

Nur die Main Funktion in der main.c und in der main.h das nötigste.

Eigentlich sollte alles in die menu.c und menu.h ausgelagert werden was 
eben mit deiner Menüführung zu tun hat. Display Steuerung in eine 
display.h und display.c, etc.

So hast du alles sauber aufgeräumt.
Und dann bindest du alles über das main-File ein.
OP Persönliche Seite #3034654
Lesenswert?

Karl Heinz Buchegger schrieb:
>> Ich würde mich freuen, wenn du mir etwas darüber sagen könntest.
> Siehe die unmittelbar vorhergehende Post

Das hatte ich zu spät gesehen, hattest deinen Beitrag wohl noch einmal 
bearbeitet. Zu meiner Verteidigung: Genau so hatte ich es auch alles bis 
dieses Problem auftrat..

Ich werde das Projekt jetzt nochmal komplett neu machen. Ich hole mir 
die C und H Dateien raus, lösche das Projekt erstelle ein neues und 
binde die Dateien wieder ein. Ich melde mich..
#3034655
Lesenswert?

Hier
1
/* *****************************************************************************
2
 * File:        menu.h
3
 * Project:     Mini Anzeige Modul
4
 * Author:      Nicolas Meyertöns
5
 * Version:     1.0.xc8
6
 * Web:         www.PIC-Projekte.de
7
 * ****************************************************************************/
8

9
#ifndef MENU_H
10
#define  MENU_H
11

12
#include <stdint.h>
13

14
typedef void (*pMenuFnct)(void);
15

16
struct sMenuEntry
17
{
18
    char text[20];
19
    pMenuFnct function;
20
};
21

22
/*Prototypen*/
23
void mControll (uint8_t menuSize, struct sMenuEntry menu[]);
24
void drawMenu(uint8_t menuSize, struct sMenuEntry menu[]);
25
void drawBar(const uint8_t *pName);
26

27
#endif  /* MENU_H */

So sollte deine menu.h aussehen.
Da ist alles drinnen was zu einem 'Menü' gehört. Ob die drawMenu 
Funktion bzw. die drawBar Funktion da drinnen aufscheinen sollten, kann 
ich so nicht sagen, weil ich nicht weiß, ob diese Funktionen nicht auch 
Anwendungen ausserhalb eines Menues haben.

Aber: da ist jetzt nichts mehr drinnen, was NICHT mit allgemeinen Menues 
zu tun hat. UNd im speziellen ist nichts mehr drinnen, was zu einem 
SPEZIELLEN Menu gehört. Nur lauter allgemeine Dinge, die man braucht, 
wenn man ein Menü machen und in Betrieb nehmen will. Die allerdings 
werden tatsächlich benötigt und um die führt kein Weg herum.
#3034670
Lesenswert?

1
/* *****************************************************************************
2
 * File:        menu.c
3
 * Project:     Mini Anzeige Modul
4
 * Author:      Nicolas Meyertöns
5
 * Version:     0.1.a.xc8
6
 * Web:         www.PIC-Projekte.de
7
 * ****************************************************************************/
8

9
#include <xc.h>
10
#include "main.h"
11
#include "menu.h"
12
#include "font.h"
13
#include "dogl.h"
14
#include <stdbool.h>
15

16
/*
17
 * Ein Menü
18
 */
19
void mControll (uint8_t menuSize, struct sMenuEntry menu[])
20
{
21
    uint8_t active = 0;
22
    bool running;
23

24
    drawMenu(menuSize, menu);
25
    drawBar("Einstellungen");
26
    charChainDOGL(active+2,0,NOINVERSE,">");
27

28
    running = true;
29

30
    while(running)
31
    {
32
        if( input & KEY_OK )
33
        {
34
            input &= ~KEY_OK;
35
            if( menu[active].function )
36
            {
37
                clearDOGL();
38
                menu[active].function();
39
                drawMenu(menuSize, menu);
40
                drawBar("Einstellungen");
41
                charChainDOGL(active+2,0,NOINVERSE,">");
42
            }
43
            else
44
                running = false;
45
        }
46
        else if( input & KEY_UP )
47
        {
48
            input &= ~KEY_UP;
49
            /*Geht es höher?*/
50
            if( active > 0)
51
            {
52
                charChainDOGL(active+2, 0, NOINVERSE, "  ");
53
                active--;
54
                charChainDOGL(active+2, 0, NOINVERSE, ">");
55
            }
56
        }
57
        else if( input & KEY_DOWN )
58
        {
59
            input &= ~KEY_DOWN;
60
            /*Geht es tiefer?*/
61
            if( active < (menuSize-1) )
62
            {
63
                charChainDOGL(active+2, 0, NOINVERSE, "  ");
64
                active++;
65
                charChainDOGL(active+2, 0, NOINVERSE, ">");
66
            }
67
        }
68
    }
69
}
70

71
/*
72
 * Diese Funktion schreibt die einzelnen Menüpunkte eines Menüs
73
 *
74
 * menuSize:    Anzahl der Menüpunkte
75
 * sMenuEntry:  Zeiger auf die Struktur mit den Menüpunkten
76
 */
77
void drawMenu(uint8_t menuSize, struct sMenuEntry menu[])
78
{
79
    uint8_t k;
80

81
    clearDOGL();
82
    for( k=0; k < menuSize; k++ )
83
        charChainDOGL(k+2,5,NOINVERSE,menu[k].text);
84
}
85

86
/*
87
 * Diese Funktion schreibt die Statusbar mit variablen Text
88
 *
89
 * pName: Der Text, welcher in der Statusbar stehen soll
90
 */
91
void drawBar(const uint8_t *pName)
92
{
93
    uint8_t k=0;
94

95
    charChainDOGL(0, 0, INVERSE, pName);
96
    
97
    while(*pName)
98
    {
99
        k += font[(*pName)-32][0];
100
        pName++;
101
    }
102

103
    while( k < 128 )
104
    {
105
        sendSPI(0xFF);
106
        k++;
107
    }
108
    /*Dies sollte noch dynamisch gestaltet werden!*/
109
    drawSignDOGL(0,122,INVERSE,BAT_FULL);
110
}

Und das ist die zugehörige menu.c

Die Funktionen mEnergy und mInfo haben da drinn nichts verloren. Die 
gehören zu einem speziellen Menü, aber nicht zur allgemeinen 
Menüverwaltung.

Im Prinzip bin ich auch mit dem
#include "main.h"
unglücklich.
Denn der sollte nicht da sein.
Den main.h brauchst du nur deswegen, weil du Zugriff auf die globale 
Variable input brauchst. Die allerdings sollte gar nicht in main.h sein, 
sondern in einem isr.h (welches du nicht hast), weil dort die Encoder 
Auswertung drinnen ist, und die Variable da logisch dazugehört.


Funktionen und globale Variable nach Themenkreisen sortieren!

Das ist das Um- und Auf um komplexere Programme, die aus mehreren Teilen 
und daher auch mehreren Files bestehen, in den Griff zu kriegen. Und 
ausserdem ist es die Grundvoraussetzung für Wiederverwendbarkeit :-)
OP Persönliche Seite #3034671
Lesenswert?

Vielen Dank, nochmal.

@KHB: Genau so hatte ich es und habe ich es nun wieder.
@Mike: Ich denke auch, dass ist keine schlechte Idee (hatte bisher immer 
nur eine ältere Version in der Cloud).

Noch eine allgemeine Frage:

Die Datei main.c - In wie weit kann ich bzw. sollte ihr hier Funktionen 
rein pfeffern. Denn zum Beispiel die Funktion für meinen Drehgeber, 
dafür lohnt es sich doch nicht eine extra C Datei zu erstellen.. Also in 
wie weit schreibt man IN die main.c?
#3034680
Lesenswert?

Nico M. schrieb:

> Die Datei main.c - In wie weit kann ich bzw. sollte ihr hier Funktionen
> rein pfeffern. Denn zum Beispiel die Funktion für meinen Drehgeber,
> dafür lohnt es sich doch nicht eine extra C Datei zu erstellen.. Also in
> wie weit schreibt man IN die main.c?

Stell dir die Frage:

WEnn ich ein neues Programm schreiben würde, könnte ich dann diese 
Funktion dort gebrauchen - ja oder nein?

Sprich: handelt es sich bei der FUnktion um etwas das du nur ganz 
speziell in diesem einen Projekt benötigst, oder ist das ganze mehr von 
der Form: tja, eigentlich ist das ein Baustein, den man überall 
einsetzen wird. Wenn ich ein neues Projekt mache und dort einen 
Drehgeber einsetzen will, dann ist das doch im Grunde genau der gleiche 
Code. In dieser Funktion ist nichts drinnen, was sie jetzt irgendwie 
speziell an dieses Projekt binden würde.

Funktionsgruppen abzusplitten und in eigene C/H Files auszulagern lohnt 
sich praktisch immer. Aber wichtig: Man sollte für den abgesplitteten 
Teil schon so was wie eine Überschrift formulieren können, in der das 
Wort 'MOdul' vorkommt. Du hast ein Modul für ein LCD. Du hast ein MOdul 
für einen Drehencoder. Du hast ein Modul für Menüs. Du hast ein Modul 
für einen PID Regler. Du hast ein Modul für lineare Transformationen. Du 
hast ein Modul für Soundausgabe. Du hast ein Modul für allgemeine 
Hilfsfunktionen. Du hast ein Modul für ...

Einfach nur jede Funktion in ein eigenes C File auszulagern ist dann 
doch übers Ziel hinausgeschossen.
Aber dein Hauptmenü: das wäre zb etwas. was man aus main.c rausnehmen 
könnte und selbst wieder in ein eigenes File stecken könnte. Denn da 
gehören ja zb die Aufbaudefinition, Funktionen die vom Menü aufgerufen 
werden dazu und das ganze hat auch eine gewisse kritische Masse an 
Komplexität überschritten, die es lohnenswert erscheinen lässt, dafür 
eine in sich abgeschlossene Einheit, ein Modul, zu schaffen.
Gast #3034731
Lesenswert?

warum machst du statt:
1
 
2
struct sMenuEntry
3
 {
4
     char text[20];
5
     pMenuFnct function;
6
 };

nicht:
1
struct sMenuEntry
2
 {
3
     const char *text;
4
     pMenuFnct function;
5
 };

das kostet dann auch nicht 2*20byte auf dem Stack nur für die Menütexte 
die ja auch im Flash stehen könnten. Kann sein dass ich was übersehe, 
ich habe den Thread aus Zeitgründen nicht komplett gelesen.
OP Persönliche Seite #3034824
Lesenswert?

1
CLEAN SUCCESSFUL (total time: 54ms)
2
make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-conf
3
make[1]: Entering directory `C:/Users/Nico/MPLABXProjects/MiniAnzeigeModul.X'
4
make  -f nbproject/Makefile-default.mk dist/default/production/MiniAnzeigeModul.X.production.hex
5
make[2]: Entering directory `C:/Users/Nico/MPLABXProjects/MiniAnzeigeModul.X'
6
"C:\Program Files (x86)\Microchip\xc8\v1.12\bin\xc8.exe" --pass1  --chip=18LF45K22 -Q -G --asmlist  --double=24 --float=24 --emi=wordwrite --opt=default,+asm,-asmfile,+speed,-space,-debug,9 --addrqual=ignore -P -N255 --warn=0 --summary=default,-psect,-class,+mem,-hex,-file --runtime=default,+clear,+init,-keep,-no_startup,-download,+config,+clib,+plib "--errformat=%%f:%%l: error: %%s" "--warnformat=%%f:%%l: warning: %%s" "--msgformat=%%f:%%l: advisory: %%s"  -obuild/default/production/main.p1  main.c 
7
"C:\Program Files (x86)\Microchip\xc8\v1.12\bin\xc8.exe" --pass1  --chip=18LF45K22 -Q -G --asmlist  --double=24 --float=24 --emi=wordwrite --opt=default,+asm,-asmfile,+speed,-space,-debug,9 --addrqual=ignore -P -N255 --warn=0 --summary=default,-psect,-class,+mem,-hex,-file --runtime=default,+clear,+init,-keep,-no_startup,-download,+config,+clib,+plib "--errformat=%%f:%%l: error: %%s" "--warnformat=%%f:%%l: warning: %%s" "--msgformat=%%f:%%l: advisory: %%s"  -obuild/default/production/dogl.p1  dogl.c 
8
"C:\Program Files (x86)\Microchip\xc8\v1.12\bin\xc8.exe" --pass1  --chip=18LF45K22 -Q -G --asmlist  --double=24 --float=24 --emi=wordwrite --opt=default,+asm,-asmfile,+speed,-space,-debug,9 --addrqual=ignore -P -N255 --warn=0 --summary=default,-psect,-class,+mem,-hex,-file --runtime=default,+clear,+init,-keep,-no_startup,-download,+config,+clib,+plib "--errformat=%%f:%%l: error: %%s" "--warnformat=%%f:%%l: warning: %%s" "--msgformat=%%f:%%l: advisory: %%s"  -obuild/default/production/isr.p1  isr.c 
9
"C:\Program Files (x86)\Microchip\xc8\v1.12\bin\xc8.exe" --pass1  --chip=18LF45K22 -Q -G --asmlist  --double=24 --float=24 --emi=wordwrite --opt=default,+asm,-asmfile,+speed,-space,-debug,9 --addrqual=ignore -P -N255 --warn=0 --summary=default,-psect,-class,+mem,-hex,-file --runtime=default,+clear,+init,-keep,-no_startup,-download,+config,+clib,+plib "--errformat=%%f:%%l: error: %%s" "--warnformat=%%f:%%l: warning: %%s" "--msgformat=%%f:%%l: advisory: %%s"  -obuild/default/production/menu.p1  menu.c 
10
"C:\Program Files (x86)\Microchip\xc8\v1.12\bin\xc8.exe" --pass1  --chip=18LF45K22 -Q -G --asmlist  --double=24 --float=24 --emi=wordwrite --opt=default,+asm,-asmfile,+speed,-space,-debug,9 --addrqual=ignore -P -N255 --warn=0 --summary=default,-psect,-class,+mem,-hex,-file --runtime=default,+clear,+init,-keep,-no_startup,-download,+config,+clib,+plib "--errformat=%%f:%%l: error: %%s" "--warnformat=%%f:%%l: warning: %%s" "--msgformat=%%f:%%l: advisory: %%s"  -obuild/default/production/setup.p1  setup.c 
11
"C:\Program Files (x86)\Microchip\xc8\v1.12\bin\xc8.exe"  --chip=18LF45K22 -G --asmlist -mdist/default/production/MiniAnzeigeModul.X.production.map  --double=24 --float=24 --emi=wordwrite --opt=default,+asm,-asmfile,+speed,-space,-debug,9 --addrqual=ignore -P -N255 --warn=0 --summary=default,-psect,-class,+mem,-hex,-file --runtime=default,+clear,+init,-keep,-no_startup,-download,+config,+clib,+plib "--errformat=%%f:%%l: error: %%s" "--warnformat=%%f:%%l: warning: %%s" "--msgformat=%%f:%%l: advisory: %%s"   -odist/default/production/MiniAnzeigeModul.X.production.cof  build/default/production/main.p1 build/default/production/dogl.p1 build/default/production/isr.p1 build/default/production/menu.p1 build/default/production/setup.p1     
12
Microchip MPLAB XC8 C Compiler V1.12
13
Copyright (C) 2012 Microchip Technology Inc.
14
License type: Node Configuration
15

16
:: warning: Omniscient Code Generation not available in Free mode
17
:: warning: missing configuration setting for config word 0x300008, using default
18
:: warning: missing configuration setting for config word 0x300009, using default
19
:: warning: missing configuration setting for config word 0x30000A, using default
20
:: warning: missing configuration setting for config word 0x30000B, using default
21
:: warning: missing configuration setting for config word 0x30000C, using default
22
:: warning: missing configuration setting for config word 0x30000D, using default
23

24
Memory Summary:
25
    Program space        used  105Eh (  4190) of  8000h bytes   ( 12.8%)
26
    Data space           used    D4h (   212) of   600h bytes   ( 13.8%)
27
    Configuration bits   used     7h (     7) of     7h words   (100.0%)
28
    EEPROM space         used     0h (     0) of   100h bytes   (  0.0%)
29
    ID Location space    used     8h (     8) of     8h nibbles (100.0%)
30

31
Running this compiler in PRO mode, with Omniscient Code Generation enabled,
32
often produces code which is 60% smaller and at least 400% faster than in
33
Free mode. The MPLAB XC8 PRO compiler output for this code could be
34
4331 bytes smaller and run 4 times faster.
35
See http://www.microchip.com for more information.
36

37
make[2]: Leaving directory `C:/Users/Nico/MPLABXProjects/MiniAnzeigeModul.X'
38
make[1]: Leaving directory `C:/Users/Nico/MPLABXProjects/MiniAnzeigeModul.X'
39

40
BUILD SUCCESSFUL (total time: 5s)
41
Loading code from C:/Users/Nico/MPLABXProjects/MiniAnzeigeModul.X/dist/default/production/MiniAnzeigeModul.X.production.hex...
42
Loading symbols from C:/Users/Nico/MPLABXProjects/MiniAnzeigeModul.X/dist/default/production/MiniAnzeigeModul.X.production.cof...
43
Loading completed
#3034847
Lesenswert?

Ich würde isr.[ch] in input.[ch] umbenennen und keyPressed und encoder 
da mit rüberschieben: Braucht sonst keiner. lastKeyDown und encState 
brauchen dann auch nicht extern zu sein. input würde auch in isr.c und 
nicht in main definiert. Dann noch eine initInput-Prozedur.

Das wird dann jetzt aber langsam auch ein wenig Geschmackssache ...
#3034887
Lesenswert?

1
uint8_t input = NO_KEY;         // Maskiertes Byte für Eingabezustände
2
uint8_t lastKeyDown = NO_KEY;   // Letzter Zustand
3
uint8_t encState = 0x00;        // Zustand des Drehgebers (Default V00)

Diese globale Variablen gehören nicht in main.c. Die letzten beiden 
werden dort nicht mal verwendet ... Eine main.h braucht man sowieso 
nicht. Denn das würde bedeuten, dass ein anderes Modul vom Hauptprogramm 
abhängig ist. Das wäre extrem schlechtes Softwaredesign.

Wenn Du globale Variablen brauchst, dann definier sie in dem Modul, zu 
dem sie gehören. In der Headerdatei zu diesem Modul werden sie mit 
extern deklariert. Wenn ein anderes Modul auf diese Variablen zugreifen 
muss, bindet es den entsprechenden Header ein. Es deklariert nicht 
einfach selber Variablen, die zu anderen Modulen gehören!

Am besten lässt man globale Variablen aber komplett sein, wenn man keine 
sehr guten Gründe hat. Man braucht sie nämlich nicht. Man kann alles mit 
Modulvariablen (static) abdecken. Sämtliche Interaktion zwischen Modulen 
finden dann nur über Funktionsaufrufe statt. Das erleichtert den 
Überblick über das Programm enorm.

Lies Dir am besten mal diesen Artikel durch. Da wird genau erklärt, wie 
man ein Programm richtig in c- und h-Dateien aufteilt:
http://www.mikrocontroller.net/articles/FAQ#Header_File.2C_wie_geht_das.3F
#3034936
Lesenswert?

Ich weiß über den Stack nur, das dort Rücksprungadressen gespeichert 
werden und bisschen kleinkram. Aber die paar Funktionsaufrufe dürfen 
kein Problem sein.
Was ich vermute ist dein Charset:

const unsigned char font[95][6] =
 {
  {1,0x00},        // Space
  {1,0x5E},        // !
  {3,0x06,0x00,0x06},      // "
  {5,0x28,0x7C,0x28,0x7C,0x28},    // #
  {5,0x24,0x2A,0x7F,0x2A,0x10},    // $
  {3,0x62,0x18,0x46},      // %
  {5,0x30,0x4C,0x5A,0x24,0x50},    // &
  {1,0x06},        // '
  {2,0x3C,0x42},        // (
  {2,0x42,0x3C},                          // )
  {5,0x28,0x10,0x7C,0x10,0x28},    // *
  {3,0x10,0x38,0x10},      // +
  {1,0xC0},        // ,
  {3,0x10,0x10,0x10},      // -
  {1,0x40},        // .
  {3,0x60,0x18,0x06},      // /
  {4,0x3C,0x42,0x42,0x3C},    // 0
  {3,0x44,0x7E,0x40},      // 1
  {4,0x44,0x62,0x52,0x4C},    // 2
  {3,0x4A,0x4A,0x34},      // 3
  {4,0x1E,0x10,0x7C,0x10},    // 4
  {3,0x4E,0x4A,0x32},      // 5
  {4,0x3C,0x4A,0x4A,0x30},    // 6
  {3,0x62,0x12,0x0E},      // 7
  {4,0x34,0x4A,0x4A,0x34},    // 8
  {4,0x0C,0x52,0x52,0x3C},    // 9
  {1,0x48},        // :
  {2,0x80,0x68},        // ;
  {3,0x10,0x28,0x44},      // <
  {3,0x28,0x28,0x28},      // =
  {3,0x44,0x28,0x10},      // >
  {3,0x02,0x52,0x0C},      // ?
  {5,0x3C,0x42,0x52,0x2A,0x3C},    // @
  {4,0x7C,0x12,0x12,0x7C},    // A
  {4,0x7E,0x4A,0x4A,0x34},    // B
  {4,0x3C,0x42,0x42,0x24},    // C
  {4,0x7E,0x42,0x42,0x3C},    // D
  {3,0x7E,0x4A,0x4A},      // E
  {3,0x7E,0x0A,0x0A},      // F
  {4,0x3C,0x42,0x52,0x34},    // G
  {4,0x7E,0x08,0x08,0x7E},    // H
  {3,0x42,0x7E,0x42},      // I
  {3,0x42,0x42,0x3E},      // J
  {4,0x7E,0x08,0x14,0x62},                // K
  {3,0x7E,0x40,0x40},      // L
  {5,0x7E,0x04,0x08,0x04,0x7E},    // M
  {5,0x7E,0x04,0x18,0x20,0x7E},    // N
  {4,0x3C,0x42,0x42,0x3C},    // O
  {4,0x7E,0x12,0x12,0x0C},    // P
  {4,0x3C,0x42,0x42,0xBC},    // Q
  {4,0x7E,0x12,0x12,0x6C},    // R
  {4,0x44,0x4A,0x4A,0x30},    // S
  {3,0x02,0x7E,0x02},      // T
  {4,0x3E,0x40,0x40,0x3E},    // U
  {5,0x06,0x18,0x60,0x18,0x06},    // V
  {5,0x3E,0x40,0x3E,0x40,0x3E},    // W
  {5,0x42,0x24,0x18,0x24,0x42},    // X
  {4,0x9E,0xA0,0xA0,0x7E},    // Y
  {4,0x62,0x52,0x4A,0x46},    // Z
  {2,0x7E,0x42},        // [
  {3,0x06,0x18,0x60},      // Backslash
  {2,0x42,0x7E},        // ]
  {3,0x20,0x10,0x20},      // ^
  {4,0x80,0x80,0x80,0x80},    // _
  {2,0x04,0x08},        // `
  {4,0x20,0x54,0x54,0x78},    // a
  {4,0x7E,0x44,0x44,0x38},    // b
  {4,0x38,0x44,0x44,0x28},    // c
  {4,0x38,0x44,0x44,0x7E},    // d
  {4,0x38,0x54,0x54,0x58},    // e
  {2,0x7C,0x0A},        // f
  {4,0x98,0xA4,0xA4,0x7C},    // g
  {4,0x7E,0x04,0x04,0x78},    // h
  {1,0x7A},        // i
  {2,0x40,0x3A},        // j
  {4,0x7E,0x10,0x28,0x44},    // k
  {1,0x7E},        // l
  {5,0x7C,0x04,0x78,0x04,0x78},    // m
  {4,0x7C,0x04,0x04,0x78},    // n
  {4,0x38,0x44,0x44,0x38},    // o
  {4,0xFC,0x24,0x24,0x18},    // p
  {4,0x18,0x24,0x24,0xFC},    // q
  {2,0x7C,0x04},        // r
  {4,0x48,0x54,0x54,0x20},    // s
  {3,0x04,0x3E,0x44},      // t
  {4,0x3C,0x40,0x40,0x3C},                // u
  {5,0x0C,0x30,0x40,0x30,0x0C},    // v
  {5,0x3C,0x40,0x3C,0x40,0x3C},    // w
  {5,0x44,0x28,0x10,0x28,0x44},    // x
  {4,0x9C,0xA0,0xA0,0x7C},    // y
  {3,0x64,0x54,0x4C},      // z
  {3,0x08,0x36,0x41},      // {
  {1,0xFF},        // |
  {3,0x41,0x36,0x08},               // }
  {4,0x20,0x10,0x20,0x10},    // ~
 };


Kannst du das nicht mit progmem machen?

Ich kann mich nur daran erinnern das ich das Problem mal hatte, aber auf 
nem Winzlingscontroller
#3034940
Lesenswert?

Die Menüstruktur ist imo ungeschickt gewählt. Du musst doch vermutlich 
die Texte zur Laufzeit nicht mehr verändern. Also reicht ein char* auf 
ein Stringliteral statt einem char-Array mit fester Länge.

Außerdem legst Du das komplette Array in der main-Funktion auf dem Stack 
an. Mach daraus lieber eine Modulvariable:
1
// menu.h
2

3
struct sMenuEntry
4
{
5
    char* text;
6
    pMenuFnct function;
7
};
1
// main.c
2

3
static struct sMenuEntry meControll[] =
4
{
5
    { "Energieoptionen", mEnergy },
6
    { "Informationen", mInfo },
7
    { "Exit" , 0 },
8
};
9

10
int main(void) {
11
    // ...
12
}
OP Persönliche Seite #3034958
Lesenswert?

Fabian O. schrieb:
> Du musst doch vermutlich die Texte zur Laufzeit nicht mehr verändern.
> Also reicht ein char* auf ein Stringliteral statt einem char-Array mit
> fester Länge.
Dadurch, dass ich kein Array fester Länge sondern einen Pointer auf eine 
feste Zeichenkette verwende spare ich Speicherplatz richtig?

> Außerdem legst Du das komplette Array in der main-Funktion auf dem Stack
> an.
Wieso liegt das Array auf dem Stack? Ich denke auf dem Stack werden nur 
Rücksprungadressen gespeichert?

Mike Mike schrieb:
> Kannst du das nicht mit progmem machen?
Mit dem Begriff kann ich leider nichts anfangen.
#3034962
Lesenswert?

Und wenn wir schon dabei sind, kannst du gleichzeitig noch deine 
Zeichenketten etwas kürzer machen. Wenn du ja sagst, es geht mit einem 
Element.
Dann schau mal ob es mir allen Dreien geht, aber schreibs so:

static struct sMenuEntry meControll[] =
{
    { "E", mEnergy },
    { "I", mInfo },
    { "X" , 0 },
};
#3034972
Lesenswert?

Für das Progmem müsstest du schreiben:
1
#include <avr/pgmspace.h>
2

3

4
const unsigned char font[95][6] PROGMEM =
5
{
6
  {1,0x00},        // Space
7
.....
8
}


Und wenn du es lesen willst:

statt vorher
1
font[x][y]

jetzt mit:
1
char c = pgm_read_byte(&font[x][y]);

Ich hoffe das stimmt so, wenn nicht bitte korrigieren
#3034973
Lesenswert?

Noch ein allgemeiner Tipp:

Es hat sich bewährt, dass man sämtliche Funktionen und Strukturen, die 
ein Modul nach außen anbietet, mit dem Modulnamen (= Dateiname) als 
Präfix versieht. Dann weiß man nämlich auf einen Blick, in welche Datei 
man schauen muss, wenn man eine bestimmte Funktion sucht.

In Deinem Fall also beispielsweise statt
1
typedef void (*pMenuFnct)(void);
2

3
struct sMenuEntry
4
{
5
    char text[20];
6
    pMenuFnct function;
7
};
8

9
void mControll(uint8_t menuSize, struct sMenuEntry menu[]);
10
void drawMenu(uint8_t menuSize, struct sMenuEntry menu[]);
11
void drawBar(const uint8_t *pName);

besser:
1
typedef void (*menu_function_t)(void);
2

3
typedef struct {
4
    char text[20];
5
    menu_function_t function;
6
} menu_entry_t;
7

8
void menu_control(menu_entry_t* menu, uint8_t menuSize);
9
void menu_draw(menu_entry_t* menu, uint8_t menuSize);
10
void menu_drawBar(const char *name);

Dann weiß man wie gesagt sofort, wo man nachsehen muss. Und man macht 
sich dabei auch ganz von alleine Gedanken, welche Funktionen eigentlich 
zusammengehören.

Ich habe beispielsweise erstmal sämtliche Dateien nach mEnergy() und 
mInfo() durchsuchen müssen, bis ich sie in setup.h gefunden habe. Das 
darf nicht sein. Stell Dir mal vor, Dein Projekt wird größer ... da 
findest Du dich ja ohne Suchfunktion nie mehr zurecht.
#3034982
Lesenswert?

Vergiss das mit dem PROGMEM, das betrifft nur AVRs. Ich weiß nicht, wie 
es beim PIC bzw. Deinem Compiler funktioniert, ein Array nicht in den 
RAM, sondern den Programmspeicher (Flash) zu legen. Eventuell reicht es 
schon, das Array const zu machen:
1
static const struct sMenuEntry meControll[] =
2
{
3
    { "Energieoptionen", mEnergy },
4
    { "Informationen", mInfo },
5
    { "Exit" , 0 },
6
};

Nico M. schrieb:
> Wenn ich es jetzt so schreibe wie Fabian vorgeschlagen hat, dann geht
> es. Aber dann ist das Array ja auch wieder global =| Soweit waren wir ja
> schon..

Das Array ist nicht global. Es ist nur innerhalb von main.c sichtbar 
(deshalb das static). Was soll daran schlimm sein?
#3034988
Lesenswert?

1
// main.c
2

3
static struct sMenuEntry meControll[] =
4
{
5
    { "Energieoptionen", mEnergy },
6
    { "Informationen", mInfo },
7
    { "Exit" , 0 },
8
};
9

10
int main(void) {
11
    // ...
12
}

Das ist sehr wohl so global, wenns auserhalb der main ist.

Aber das mit dem Progmem war nicht auf seine drei Menüoptionen bezogen, 
sondern auf sein fast 600Byte Charset Array!
OP Persönliche Seite #3034990
Lesenswert?

Sebastian W. schrieb:
> Es ist nicht mehr auf dem (Software-)Stack. Wir wüssten aber alle gerne,
> wieso meControll zerschossen wird, wenn es auf dem Stack liegt ...
Achso. Das kleine Wörtchen "Software" hatte mir gefehlt. Hier werden 
Variablen etc. gesichert, damit sie, wenn man über den LIFO Stack wieder 
in Funktionen zurück kehrt, diese wieder bzw. noch vorhanden sind. Habe 
ich das richtig verstanden?
#3034993
Lesenswert?

Nico M. schrieb:
> Dadurch, dass ich kein Array fester Länge sondern einen Pointer auf eine
> feste Zeichenkette verwende spare ich Speicherplatz richtig?

Wenn die Zeichenketten unterschiedlich lang sind, ja. So kostet jeder 
Eintrag die Länge des Strings + Terminierungsbyte + Größe eines Zeigers. 
Wenn Du feste Zeichenketten verwendest, kostet dagegen jeder Eintrag die 
maximale Länge (+ das Terminierungsbyte).

>> Außerdem legst Du das komplette Array in der main-Funktion auf dem Stack
>> an.
> Wieso liegt das Array auf dem Stack? Ich denke auf dem Stack werden nur
> Rücksprungadressen gespeichert?

Alle lokalen Variablen, die Du in einer Funktion definierst, werden auf 
dem Stack angelegt, sofern sie der Compiler nicht wegoptimiert bzw. in 
Registern ablegt. Das gilt auch für die main-Funktion.

Variablen, die nicht auf dem Stack sollen, musst Du entweder in der 
Funktion als static definieren (dann sind sie nur innerhalb der Funktion 
sichtbar) oder außerhalb der Funktion als Modulvariable (mit static) 
bzw. globale Variable (ohne static).
#3034994
Lesenswert?

Nico, du solltest (nach deiner Verschönerungsaktion) vielleicht mal die 
Adressen aller auto-Variablen deines Programms überprüfen. Vielleicht 
geht bei der Bildung dieses komischen Aufrufgraphen (siehe Compiler User 
Manual) was schief und irgendwelche auto-Variablen landen auf derselben 
Adresse wie deine Funktionspointer?
OP Persönliche Seite #3035015
Lesenswert?

Fabian O. schrieb:
> Vergiss das mit dem PROGMEM, das betrifft nur AVRs. Ich weiß nicht, wie
> es beim PIC bzw. Deinem Compiler funktioniert, ein Array nicht in den
> RAM, sondern den Programmspeicher (Flash) zu legen. Eventuell reicht es
> schon, das Array const zu machen

Mal am Beispiel des font Arrays: Das Array habe ich ja als const 
deklariert. Somit habe ich eine Belegung von 6% RAM und 14% FLASH. Wenn 
ich das const vor dem Array entferne, dann habe ich 43% RAM und 13% 
FLASH.

Also würde ich sagen (XC8 Compiler):
* const --> FLASH
* sonst --> RAM
#3035040
Lesenswert?

Okay... ich weiß auch nicht weiter, bis auf eben noch folgendes 
Probieren um "sicher" zu sein.

1
static struct sMenuEntry meControll[] =
2
{
3
    { "E", mEnergy },
4
    { "I", mInfo },
5
    { "X" , 0 },
6
};

und eben
1
struct sMenuEntry
2
{
3
    char *text;
4
    pMenuFnct function;
5
};
OP Persönliche Seite #3035052
Lesenswert?

Mal eine kleine Zusammenfassung:

Das innerhalb der main geht nicht (das war ja der Ursprung):
1
struct sMenuEntry meControll[3] =
2
{
3
    { "Energieoptionen", mEnergy },
4
    { "Informationen", mInfo },
5
    { "Exit" , 0 }
6
};

Das *innerhalb/außerhalb* der main geht :
1
static struct sMenuEntry meControll[3] =
2
{
3
    { "Energieoptionen", mEnergy },
4
    { "Informationen", mInfo },
5
    { "Exit" , 0 }
6
};

Das innerhalb der main geht nicht :
1
struct sMenuEntry meControll[3] =
2
{
3
    { "E", mEnergy },
4
    { "I", mInfo },
5
    { "X" , 0 }
6
};
#3035086
Lesenswert?

Mike Mike schrieb:
> Mit static legst du es ja im Heap an.
> Dann lass es doch da wenn es dich nicht stört.

Nein, mit static liegt es im Datensegment. Auf dem Heap würde es liegen, 
wenn man den Speicher mit malloc() alloziert.

> Gibts hier keine Profis, die sagen können, warum das auf dem Stack so
> viele Probleme bereitet?

In der Version mit den char-Zeigern sind es ja nur noch ein paar Byte. 
Das sollte eigentlich keine Probleme machen. Wenn da was überschrieben 
wird, ist das ein Programmfehler. Der kann aber überall sein.

@Nico: Du hast ja anscheinend einen Debugger. Hast Du den Inhalt der 
Strukturen zwischen der funktionierenden und nicht funktionierenden 
Variante mal verglichen? Falls es im nicht funktionierenden Programm zu 
Beginn passt, musst Du überwachen, ob bzw. wann die Werte darin 
überschrieben werden.
#3035091
Lesenswert?

Noch etwas, das mir aufgefallen ist: Das Array font definierst Du in 
jeder c-Datei, die font.h einbindet, neu als globale Variable. Das ist 
großer Murks. Wenn es schon eine globale Variable sein soll, dann mach 
es wenigstens so:
1
// font.h
2
extern const unsigned char font[95][6];
3
extern const unsigned char sign[3][6];
1
// font.c
2
const unsigned char font[95][6] = {...};
3
const unsigned char sign[3][6] = {...};

Wobei der Name sign für eine globale Variable ziemlich bescheuert ist. 
Da kommt man nicht auf die Idee, dass das zum Font gehören soll.

Pack doch die Batteriesymbole mit in den normalen Font hinten dran und 
definiere BAT_FULL, BAT_OK und BAT_EMPTY entsprechend. Dann brauchst Du 
nicht mal eine eigene Funktion für Symbole, sondern nimmst die ganz 
normale für Zeichen bzw. Text.
OP Persönliche Seite #3035247
Lesenswert?

Dauert noch etwas. Das Problem ist i.M. der Umgang mit der IDE. Ich 
versuche gerade eine Speicherzelle permanent zu überwachen, indem ich 
die Adresse dieser eingebe.. Doch wenn ich aus main z.B. in meControll 
wechsele macht er wieder alle watches weg und ich kann nix hinzufügen 
sondern nur die, die in meControll gerade zur Verfügung stehen.. Dann 
scheint es allerdings schon zu spät zu sein.. nerv
#3035491
Lesenswert?

Nico, lies mal Abschnitt 6 in 
http://ww1.microchip.com/downloads/en/DeviceDoc/xc8-v1_12-readme.pdf. Da 
ist ein Compilerproblem erwähnt das auf unser Problem passen könnte:

Initialization of auto structures Structures which are auto cannot be 
initialized. Simply define the structure and assign values separately.

Also, kannst du meControl1 mal wie gehabt in main() definieren, aber die 
Werte einzeln setzen?

LG, Sebastian

PS: In dem Dokument sind noch weitere Probleme beschrieben, auf die du 
achten solltest, z.B.:

Functions returning pointers (XC8-344)
Wrong watch values
Constant index into int array
Indirect function calls
Procedural abstraction, mit einer extrem abstrakten Beschreibung ;)
Pointer assignments
Assignment to pointer member of a union (XC8-341)
#3035606
Lesenswert?

WTF ist das denn für ein Compiler?! Meinen die das ernst? Abseits von 
trivialen Programmen kann man sich da ja im Prinzip auf nichts 
verlassen:

Call to empty function after comparison (XC8-398):
Code which calls a function containing no statements (an empty 
definition) which is preceded by an if() statement may fail for 16F1xxx 
parts. If the assembler optimizer is enabled, the call to the empty 
function, or subsequent code may fail. Adding any code, such as NOP();, 
to the empty function is enough to work around this issue.

Functions returning pointers (XC8-344):
If a function returns a pointer, the size of this pointer is larger than 
1 byte, and also larger than the total size of all the parameters passed 
to this function, then the memory used by the return value may not be 
allocated. The function's return value may overwrite other local 
variables and lead to code failure. Consider adding a dummy parameter to 
the function with the same size as the return value type.

Constant index into int array:
If a constant is used as the index into an array of const int type, the 
element read may be incorrect. This issue does not affect arrays of 
different types, or when the index is a variable.

Indirect function calls:
The parameters to functions called indirectly via a pointer may not be 
passed correctly if the following conditions are met. [...]

Pointer assignments:
In PRO mode, if a pointer assignment is made where one of the pointers 
is a member of a structure, the compiler may fail to copy the whole 
pointer, causing the pointer to register an incorrect address.

usw.
Gast #3036525
Lesenswert?

Also so schlimm finde ich es jetzt nicht. Aber das liegt evtl. auch 
daran, das ich es nicht besser weiß. Womit programmiert ihr denn eure 
z.B. oder sind hier nur böse Zungen am Werk, welche schlecht reden ohne 
selber mal damit gearbeitet zu haben?
OP Persönliche Seite #3039542
Lesenswert?

Sebastian W. schrieb:
> Initialization of auto structures Structures which are auto cannot be
> initialized. Simply define the structure and assign values separately.
>
> Also, kannst du meControl1 mal wie gehabt in main() definieren, aber die
> Werte einzeln setzen?

Das funktioniert! Ist aber umständlich. Ich denke, dann werde ich bei 
der Version mit in main() als static definiert bleiben.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren