Forum: Mikrocontroller und Digitale Elektronik Problem mit Funktionspointern


von PIC N. (eigo) Benutzerseite


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

von Mike M. (mikeii)


Lesenswert?

if( menu[active].function )

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

Oben machst du:

menu[active].function();

Poste mal den ganzen Code

von PIC N. (eigo) Benutzerseite


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
}

von Mike M. (mikeii)


Lesenswert?

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

von PIC N. (eigo) Benutzerseite


Lesenswert?

Btw.: Compiler ist der XC8

von Mike M. (mikeii)


Lesenswert?

Wie gesagt in dem IF rufst du nicht die Funktion auf, da müssen Klammern 
hin, wenn das was zurück geben soll



So hast du nur den Pointer, wenn du die Klammer weg lässt, nicht den 
Rückgabewert

Soll wohl ( menu[active].function() ) heißen?

von Sebastian W. (sebastian_w29)


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 ...

von Mike M. (mikeii)


Lesenswert?

Ah, dann macht das Sinn. Dann wie Sebastian schon sagt, prüfen welchen 
Wert active annimmt, der müsste ja dann ständig 2 sein

von PIC N. (eigo) Benutzerseite


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.

von Karl H. (kbuchegg)


Lesenswert?

@Mike Mike

Das ist alles ok.
Er überprüft, ob der Funktionspointer ungleich NULL ist und ruft nur 
dann die Funktion über den Pointer auf.

von PIC N. (eigo) Benutzerseite


Lesenswert?

@all: Ich habe es debuggt. Die Variable activ ist definitiv 1. Ich werde 
es jetzt aber nochmal genau prüfen..

von Karl H. (kbuchegg)


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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Was soll man mit einem Code anfangen, wo nirgendwo gezeigt wird, wann 
und wo die Variable "active" gesetzt bzw. geändert wird?

von Steel (Gast)


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.

von Sebastian W. (sebastian_w29)


Lesenswert?

Anscheinend zerhaut es dir den stack? Mach deine struct doch mal 
testweise global.

von Steel (Gast)


Lesenswert?

Frank M. schrieb:
> Was soll man mit einem Code anfangen, wo nirgendwo gezeigt wird, wann
> und wo die Variable "active" gesetzt bzw. geändert wird?

Wird doch oben gezeigt im Code, musst du gucken.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:
> Was soll man mit einem Code anfangen, wo nirgendwo gezeigt wird, wann
> und wo die Variable "active" gesetzt bzw. geändert wird?

Sorry, falsch gelesen. Ich ziehe meinen Beitrag zurück :-)

von Mike M. (mikeii)


Lesenswert?

Frank M. schrieb:
> Was soll man mit einem Code anfangen, wo nirgendwo gezeigt wird, wann
> und wo die Variable "active" gesetzt bzw. geändert wird?


Das passiert in void mControll (uint8_t menuSize, struct sMenuEntry 
menu[]);

Initialisiert mit 0

und bei Tastendruck wird die Variable verändert

von PIC N. (eigo) Benutzerseite


Lesenswert?

Sebastian W. schrieb:
> Anscheinend zerhaut es dir den stack? Mach deine struct doch mal
> testweise global.

Das ist es! Aber ist der Stack so klein? Ich meine so tief gehe ich doch 
eigentlich gar nicht, oder?

von Sebastian W. (sebastian_w29)


Lesenswert?

Der stack ist wohl nicht zu klein, der stack wird wohl zerschossen. Aber 
wo? Das könnte überall sein. Hast du beim Testen bisher immer auch Exit 
getestet? Wenn dann die NULL in der struct fehlt, wird vielleicht 
zufällig eine Funktionsadresse angesprungen, und der Code dort macht 
üble Sachen mit dem SP?

von PIC N. (eigo) Benutzerseite


Lesenswert?

Also so und lokal:
1
struct sMenuEntry meControll[] =
2
{
3
    { "Energieoptionen", mEnergy },
4
    { "Informationen", mInfo },
5
    { "Exit" , NULL}
6
};
Funktioniert es wieder nicht..

von Mike M. (mikeii)


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?

von Mike M. (mikeii)


Lesenswert?

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

von PIC N. (eigo) Benutzerseite


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.

von Mike M. (mikeii)


Lesenswert?

Und kannst du den Ausdruck menu[active].function auch auswerten, welcher 
Wert rauskommt? Ich sehe nicht, aus welchem Grund da NULL rauskommen 
sollte...

Und das Struct global funktioniert?

von Sebastian W. (sebastian_w29)


Lesenswert?

Ok, vielleicht läuft der Stack wirklich über. Wie ist denn die 
Speicherbelegung zur Zeit? Du benutzt ja sehr viele Textkonstanten die 
alle sowohl flash als auch SRAM fressen ...

von Mike M. (mikeii)


Lesenswert?

Ne andere Frage
Das Strukt wo steht das denn?? In der Main?

Denn der Typ struct sMenuEntry findet man ja nur in der Main?

D.h. wenn du eine Funktion auserhalb hast, kennt die diesen Typen doch 
garnicht.

Eventuell ist das dein Problem

von Mike M. (mikeii)


Lesenswert?

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

von PIC N. (eigo) Benutzerseite


Angehängte Dateien:

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.

von PIC N. (eigo) Benutzerseite


Angehängte Dateien:

Lesenswert?

Da scheint in der Tat bei "menu" was nicht zu stimmen oder?

von Mike M. (mikeii)


Lesenswert?

Ja so ist es global, aber was meinst du mit

>Ja, wenn ich das Struct nicht innerhalb von main
>sondern global definiere, dann geht es.

??

Innerhalb der main Funktion oder innerhalb der main.c

von Mike M. (mikeii)


Lesenswert?

menu sollte doch vom typ struct sMenuEntry * sein, keine Ahnung wie dein 
Debugger das handhabt

von PIC N. (eigo) Benutzerseite


Lesenswert?

Sebastian W. schrieb:
> Ok, vielleicht läuft der Stack wirklich über. Wie ist denn die
> Speicherbelegung zur Zeit? Du benutzt ja sehr viele Textkonstanten die
> alle sowohl flash als auch SRAM fressen ...

Kann man sowas auch anders, sprich speicheroptimierter, lösen?

von Mike M. (mikeii)


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?

von PIC N. (eigo) Benutzerseite


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.

von Peter II (Gast)


Lesenswert?

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.

von Mike M. (mikeii)


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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von PIC N. (eigo) Benutzerseite


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
}

von Karl H. (kbuchegg)


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 :-)

von Alex (Gast)


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!).

von Mike M. (mikeii)


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.

von PIC N. (eigo) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb
> Sorry.
> Zu spät gesehen
>
> Muss erst mal nachlesen, was in der Zwischenzeit alles passiert ist :-)

Kein Problem, komme selbst kaum noch hinterher..

@Alex und Mike: Okay.. Ich versuche es, uno Momento

von Mike M. (mikeii)


Lesenswert?

Ach blödsinn, brauchst du ja nicht, schau nur nach was running für einen 
Wert hat!

von PIC N. (eigo) Benutzerseite


Angehängte Dateien:

Lesenswert?

Ich mache es jetzt mal so mit Screenshots.. Habe nämlich langsam schon 
eckige Augen o.O

Also running wird definitiv auf null gesetzt.

von Karl H. (kbuchegg)


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.

von Mike M. (mikeii)


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.

von PIC N. (eigo) Benutzerseite


Angehängte Dateien:

Lesenswert?

Alex schrieb:
> Prüfe in der if (...) explizit auf != NULL und setzt beim else die
> Klammer:
Hat leider auch nicht geholfen. Das gesamte Projekt nun anbei.
Schon mal viele Dank an alle!

von WDT (Gast)


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...

von Mike M. (mikeii)


Lesenswert?

Das wäre ein Doppelpointer. Er hat das schon richtig gemacht, der Name 
des Feldes ist ein Pointer

von PIC N. (eigo) Benutzerseite


Lesenswert?

@WDT:
Ich habe mal gelernt der Name eines Arrays ist die Adresse auf sein 
erstes Element von daher...?! Aber ich versuche es dennoch.

Nachtrag: ...Genau

von Karl H. (kbuchegg)


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.

von Sebastian W. (sebastian_w29)


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

von WDT (Gast)


Lesenswert?

Auch ich lerne gerne dazu!
Ziehe meinen Beitrag zurück....

von Mike M. (mikeii)


Lesenswert?

Du hast zwei verschiedene mControll funktionen, einmal in der main.c, 
einmal in der menu.c

von PIC N. (eigo) Benutzerseite


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).

von Mike M. (mikeii)


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.

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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)

von PIC N. (eigo) Benutzerseite


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.

von Karl H. (kbuchegg)


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

von Mike M. (mikeii)


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.

von PIC N. (eigo) Benutzerseite


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..

von Karl H. (kbuchegg)


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.

von Mike M. (mikeii)


Lesenswert?

Kleiner Tipp für die Zukunft, Leg dir einen Projektordner an und in 
diesem Umterordner, dor speichere deine Zwischenstände immer ab. Und 
nummeriere einfach die Ordner durch. So kannst du immer auf ältere 
Stände zurückgreifen.
Hat mir schon sehr oft geholfen.

von Karl H. (kbuchegg)


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 :-)

von PIC N. (eigo) Benutzerseite


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?

von Karl H. (kbuchegg)


Lesenswert?

> Ich werde das Projekt jetzt nochmal komplett neu machen.

Bin gespannt.
Denn wie gesagt. So einen richtigen Showstopper-Fehler kann ich nicht 
entdecken. Das gezeigte Verhalten dürfte eigentlich nicht auftreten. 
IMHO.

von Karl H. (kbuchegg)


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.

von PIC N. (eigo) Benutzerseite


Lesenswert?

Danke, danke, danke!

Ich bin derweil erstmal dabei eine vernünftige Struktur herzustellen was 
C/H Dateien betrifft, kann daher noch einen Augenblick dauern bis ich 
das Projekt neu erstelle.

von Karl H. (kbuchegg)


Lesenswert?

Nico M. schrieb:
> Danke, danke, danke!
>
> Ich bin derweil erstmal dabei eine vernünftige Struktur herzustellen was
> C/H Dateien betrifft, kann daher noch einen Augenblick dauern bis ich
> das Projekt neu erstelle.

Ist schon ok.
Lass dir Zeit.

Die jetzt investierte Zeit kriegst du später 10-fach wieder.

von temp (Gast)


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.

von Sebastian W. (sebastian_w29)


Lesenswert?

... und dann ist der Fehler weg und wir bekommen nie heraus was nun 
wirklich die Ursache war ... ;)

von Karl H. (kbuchegg)


Lesenswert?

Sebastian W. schrieb:
> ... und dann ist der Fehler weg und wir bekommen nie heraus was nun
> wirklich die Ursache war ... ;)

Wenn nicht noch wer eine geniale Idee hat, fürchte ich werden wir das 
sowieso nicht rauskriegen. Ist zwar unbefriedigend aber im Moment siehts 
danach aus.

Hoffen wir mal die Umkehrung: Das sich im neuen Projekt nicht wieder 
dasselbe Symptom zeigt.

von PIC N. (eigo) Benutzerseite


Angehängte Dateien:

Lesenswert?

Alter Falter, da hat mich der Compiler aber gut mit errors/warnings 
beworfen ^^ Bevor ich das Projekt jetzt neu aufsetzte wäre es super wenn 
ihr mal einen klitze kleinen Blick über die Strukturen werfen könnt (nur 
grob).

von Mike M. (mikeii)


Lesenswert?

Poste mal bitte das Output vom Compiler, hab grad keinen da

von Mike M. (mikeii)


Lesenswert?

Was ich nur grade sehe, das
typedef unsigned char uint8_t;

machst du nur einmal, nicht mehrfach!

von PIC N. (eigo) Benutzerseite


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

von PIC N. (eigo) Benutzerseite


Lesenswert?

Mike Mike schrieb:
> Was ich nur grade sehe, das
> typedef unsigned char uint8_t;
>
> machst du nur einmal, nicht mehrfach!

Aber dann müsste ich ja überall, wo ich das verwende, das Headerfile 
einbinden, in dem im es einmal geschrieben hab?

von Mike M. (mikeii)


Lesenswert?

Was die warnings sollen kann ich dir nicht sagen, aber ich sehe da 
nichts C relevantes

von PIC N. (eigo) Benutzerseite


Lesenswert?

Die Warnings betreffen nur das Konfigurationswort des PIC, das sind aber 
keine relevanten Einstellungen für mich.

von Mike M. (mikeii)


Lesenswert?

Ja aber ich sehe keine Warnings, oder Errors zu deinem Sourcecode.

Wenn du in der Main verschiedene Headerdateien einbindest, und da kommen 
immer die gleichen typedefs vor, sollte der Compiler eigentlich meckern.

von Sebastian W. (sebastian_w29)


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 ...

von Fabian O. (xfr)


Lesenswert?

1
typedef unsigned char bool;
2
typedef unsigned char uint8_t;

Das gehört überhaupt nicht in das Programm. Diese Definitionen stehen in 
stdint.h und stdbool.h.

von PIC N. (eigo) Benutzerseite


Lesenswert?

Das Neuaufsetzten des Projektes hat leider nichts gebracht. Es 
funktioniert nach wie vor nicht :(

von Fabian O. (xfr)


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

von PIC N. (eigo) Benutzerseite


Lesenswert?

Danke Fabian, das klingt gut!

Um mal zu dem eigentlichen Problem zurück zu kommen:
Ich bin nicht sicher ob es bei der Fehlersuche hilft aber wenn ich im 
Array nur ein Element habe:
1
struct sMenuEntry meControll[] = { "Energieoptionen", mEnergy };
Dann geht es!

von Mike M. (mikeii)


Lesenswert?

Ich kann mir nicht vorstellen, dass es da Probleme beim Speicher 
reservieren gibt, aber probiere mal:
1
struct sMenuEntry meControll[3] =
2
    {
3
        { "Energieoptionen", mEnergy },
4
        { "Informationen", mInfo },
5
        { "Exit" }
6
    };

von PIC N. (eigo) Benutzerseite


Lesenswert?

Leider nein.

von Mike M. (mikeii)


Lesenswert?

Wenn ich doch nur genug Kenntnisse über den Stack hätte :P

Aber das kann doch auch nicht sein, das der so schnell voll wäre...

Wie gesagt, prüf genau vor dem Funktionseintritt und direkt danach, 
welche Werte die Funktionspointer haben und poste das Ergebnis

von PIC N. (eigo) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hier einmal beim Aufruf (in main.c) und direkt nach dem Ankommen in 
(menu.c).

von Mike M. (mikeii)


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

von Mike M. (mikeii)


Lesenswert?

Und poste den ersten Screenshot, wobei du das meControl aufklappst

von Fabian O. (xfr)


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
}

von PIC N. (eigo) Benutzerseite


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.

von Mike M. (mikeii)


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 },
};

von PIC N. (eigo) Benutzerseite


Lesenswert?

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..

von Mike M. (mikeii)


Lesenswert?

Ich weiß leider auch nicht wie das jetzt mit dem Stack ist, vllt. können 
die Profis was dazu sagen.

Wenn du dein großes Array anlegst, dann wird das in den RAM gelegt.

Mit Progmem bekommst du das ganze im Flash abgespeichert:

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#progmem_und_pgm_read_xxx

Sind immerhin über 500Byte die du da verbläst

von Mike M. (mikeii)


Lesenswert?

Ja, warscheinlich gehts deswegen, weil globales nun mal auf dem Heap 
abgelegt wird.

von Mike M. (mikeii)


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

von Fabian O. (xfr)


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.

von Mike M. (mikeii)


Lesenswert?

Ach und ich glaube man soll noch casten:
1
char c = (char)pgm_read_byte(&font[x][y]);

von Fabian O. (xfr)


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?

von Mike M. (mikeii)


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!

von Sebastian W. (sebastian_w29)


Lesenswert?

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


Es ist nicht mehr auf dem (Software-)Stack. Wir wüssten aber alle gerne, 
wieso meControll zerschossen wird, wenn es auf dem Stack liegt ...

von PIC N. (eigo) Benutzerseite


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?

von Fabian O. (xfr)


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).

von Sebastian W. (sebastian_w29)


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?

von Fabian O. (xfr)


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 ...

Ach so, wart ihr schon so weit. Ich habe zugegebenermaßen nicht den 
ganzen Thread gelesen ...

von PIC N. (eigo) Benutzerseite


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

von Mike M. (mikeii)


Lesenswert?

Okay wenn das so geht ist es ja okay.

Aber poste doch mal den Screenshot mit dem geöffneten Baum

von PIC N. (eigo) Benutzerseite


Angehängte Dateien:

Lesenswert?

Verzeihung, dass habe ich glatt überlesen.

Das ist direkt vor dem Aufruf.

von Mike M. (mikeii)


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
};

von PIC N. (eigo) Benutzerseite


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
};

von Mike M. (mikeii)


Lesenswert?

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

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

von Fabian O. (xfr)


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.

von Fabian O. (xfr)


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.

von PIC N. (eigo) Benutzerseite


Lesenswert?

Das hört sich gut an, danke!

von Sebastian W. (sebastian_w29)


Lesenswert?

Nico M. schrieb:
> Verzeihung, dass habe ich glatt überlesen.
> Das ist direkt vor dem Aufruf.
> [open_tree.png]

Die Adresse von input liegt in einer Lücke innerhalb von meControll? 
Dieser compiler ist aber hoch intelligent.

von PIC N. (eigo) Benutzerseite


Lesenswert?

Ist das jetzt ironisch gemeint? ^^

von Sebastian W. (sebastian_w29)


Lesenswert?

Was sind denn die Adressen von lastKeyDown und encstate?

von PIC N. (eigo) Benutzerseite


Lesenswert?

lastKeyDown --> 0x3C
encState    --> 0x3B

von Sebastian W. (sebastian_w29)


Lesenswert?

Kannst du im Debugger eingrenzen wann genau die Funktionspointer 
überschrieben werden?

von PIC N. (eigo) Benutzerseite


Lesenswert?

Jo geht gleich an.. Erstmal was essen ^^

von PIC N. (eigo) Benutzerseite


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

von Mike M. (mikeii)


Lesenswert?

Du hast aber mittlerweile die [20] weg und machst es so:
1
struct sMenuEntry
2
{
3
    char* text;
4
    pMenuFnct function;
5
};

?

Sind so immerhin 25Byte gespart

von PIC N. (eigo) Benutzerseite


Lesenswert?

Ja.

von Sebastian W. (sebastian_w29)


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)

von Fabian O. (xfr)


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.

von WDT (Gast)


Lesenswert?

Ich glaube, dass der Compiler wirklich nicht in Ordnung ist.
Gibt es für die PICs keinen gnu-compiler?

von Sebastian W. (sebastian_w29)


Lesenswert?

Na, nun debuggen wir erstmal auf Assemblerebene weiter ...

von Nico (Gast)


Lesenswert?

Der Compiler ist ja nun auch noch relativ neu..

von Nico (Gast)


Lesenswert?

Ich kann übrigens erst ab Mo weiter machen..

von Sebastian W. (sebastian_w29)


Lesenswert?

Da hab ich meine Grippe hoffentlich auskuriert. Schönes Wochenende!

LG, Sebastian

von Der Rächer der Transistormorde (Gast)


Lesenswert?

Fabian O. schrieb:
> WTF ist das denn für ein Compiler?!

Microchip halt. Die Idee das ein IC Hersteller gute compiler baut muss 
nicht immer stimmen. Aber mit Fehlern wird offen umgegangen.

Nico schrieb:
> Der Compiler ist ja nun auch noch relativ neu..

Den Spruch höre ich bei Mplab gefühlt seit Version 5.x ;-)

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Mir schwirrt da irgendwie noch gnupic überm Tellerrand.

von Nico (Gast)


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?

von PIC N. (eigo) Benutzerseite


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.

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.