Forum: Compiler & IDEs Flashstring für Menu in struct


von Sam .. (sam1994)


Lesenswert?

Hi

Ich versuche gerade OO Menüstrukturen für mein 2x16 Lcd zu erstellen. 
Die Strukturen sehen wie folgt aus:
1
#define MENU_TYPE_INT 0
2
#define MENU_TYPE_STR 1
3
#define MENU_TYPE_MENU 2
4
5
typedef union 
6
{
7
    char* str;
8
    uint8_t uint;
9
    void* menu;   
10
}Data;
11
12
typedef struct 
13
{
14
    char* name;
15
    uint8_t type;
16
    uint8_t current;
17
    uint8_t len;
18
    Data value[];
19
}MenuEntry;
20
21
typedef struct 
22
{
23
    void* prev; 
24
    uint8_t current; 
25
    uint8_t len;
26
    MenuEntry* entry[];
27
}Menu;

Dazu habe ich eine Testmenü erstellt:
1
const char test[] PROGMEM = "Test";
2
const char on[]   PROGMEM = "An";
3
const char off[]  PROGMEM = "Aus";
4
5
const MenuEntry entry1 PROGMEM = {test, MENU_TYPE_STR, 0, 2, {{on}, {off}}};  Z.7 1-3. Warnung
6
const Menu optionen PROGMEM = {0, 0, 1, {&entry1}};Z.8 4. Warnung

Code für das ausführen:
1
void do_menu(Menu m)
2
{
3
    lcd_clear();
4
    lcd_setcursor(0,1);
5
    write_entry(1, m.entry[0]);
6
}
7
8
void write_entry(uint8_t i, MenuEntry* m)
9
{
10
    char c;
11
    lcd_putc('0' + i);
12
    lcd_putc(' ');
13
    uint8_t len = 12;                   //Länge des Lcds - "i [...]: "
14
                                        //          16      -2     -2 = 12
15
                                        //Len ist wichtig um Data später rechtsbündig darzustellen
16
    char* p = m->name;                  //char* name: Zeiger auf array im Flash
17
    while(c = pgm_read_byte(p++))       //nächtes Byte lesen und Zeiger um 1 erhöhen Z.26 5. Warnung
18
    {
19
        len--;
20
        lcd_putc(c);
21
    }
22
    lcd_putc(':');
23
    lcd_putc(' ');
24
    //Der rest ist zurzeit auskommentiert und daher unwichtig, da schon das nicht funktioniert.
25
    //[...]
26
}

Problem ist aber, wenn ich den einen Menüeintrag auf dem Lcd ausgeben 
will, erscheint nur Murks. Die "1 " erscheint noch, dann kommen aber ein 
wildes Sammelsurium.

Ich vermute das der µC irgendwo liest wo er nicht lesen soll. Kurzzeitig 
wird der volle Block auf dem ganzen Display angezeigt. Er liest also 
erstmal länger 0xFF im leeren Flash.
Ich habe mal die HexDatei nach 0x00 durchsucht, aber nichts gefunden. Er 
könnte höchstens im Bootloader zum stoppen kommen. Sonst fällt mir keine 
Stelle ein, wo noch 0x00 sein könnte.

Der Compiler zeigt ein paar Warnungen an. Ich wüsste aber nicht was ich 
da casten sollte:
1
7  [Warning] initialization discards qualifiers from pointer target type 
2
7  [Warning] initialization discards qualifiers from pointer target type
3
7  [Warning] initialization discards qualifiers from pointer target type
4
8  [Warning] initialization discards qualifiers from pointer target type 
5
26 [Warning] suggest parentheses around assignment used as truth value

von Falk B. (falk)


Lesenswert?

@  Samuel K. (sam1994)

>void do_menu(Menu m)
>{
>    lcd_clear();
>    lcd_setcursor(0,1);
>    write_entry(1, m.entry[0]);

Hier gibt es AFAIK schon Probleme, weil eben dein Menu im PROGMEM liegt, 
Zugriff geht nur über pgm_read_byte etc.

>    char* p = m->name;                  //char* name: Zeiger auf array im >Flash

char* ist ein Zeiger auf den RAM. Für Flash gibt es extra PGM_P, siehe 
doku der libc. Aber auch hier gibt es wahrscheinlich ein 
Dereferenzierungsproblem.

Ne Lösung hab ich aber nicht :-0

MFG
Falk

von Sam .. (sam1994)


Lesenswert?

Falk Brunner schrieb:
>>void do_menu(Menu m)
>>{
>>    lcd_clear();
>>    lcd_setcursor(0,1);
>>    write_entry(1, m.entry[0]);
>
> Hier gibt es AFAIK schon Probleme, weil eben dein Menu im PROGMEM liegt,
> Zugriff geht nur über pgm_read_byte etc.

Danke, das hab ich glatt vergessen zu lesen. Mal schauen ob das hilft.

>>    char* p = m->name;                  //char* name: Zeiger auf array im >Flash
>
> char* ist ein Zeiger auf den RAM. Für Flash gibt es extra PGM_P, siehe
> doku der libc. Aber auch hier gibt es wahrscheinlich ein
> Dereferenzierungsproblem.

Das wusste ich nicht genau und hab dann einfach einen char* Zeiger 
genommen. Solange ich den über pgm_read_byte aufrufe dürfte das doch 
trotzdem passen, oder nicht?


Ein paar 0x00 sind übrigens doch in der Hex-Datei. Ich dachte der 
Hexreader unterstützt .hex Dateien. Das er die nicht unterstützt merkte 
ich daran, dass die Code angeblich 4,6KB groß sei.

von Stefan E. (sternst)


Lesenswert?

Falk Brunner schrieb:
> char* ist ein Zeiger auf den RAM. Für Flash gibt es extra PGM_P,

Zeiger ist Zeiger. PGM_P ist in erster Linie was fürs Auge.

So auf die Schnelle:
1
void do_menu(Menu *m)
2
...
3
    write_entry(1,(MenuEntry*)pgm_read_word(&(m->entry[0])));
und
1
char* p = (char*)pgm_read_word(&(m->name));

PS:
Und was die Warnungen angeht: da fehlen diverse "const" in den 
Struct-Definitionen.

von Sam .. (sam1994)


Lesenswert?

Ich hab den Code ein bisschen umgeändert und kam mit dem auf 6 falsche 
Zeichen zwischen "1 " und "Test:"
1
void do_menu(Menu m)
2
{
3
    lcd_clear();
4
    lcd_setcursor(0,1);
5
    write_entry(1, (MenuEntry*) pgm_read_word(m.entry[0]));
6
}
7
8
void write_entry(uint8_t i, MenuEntry* m)
9
{
10
    char c;
11
    lcd_putc('0' + i);
12
    lcd_putc(' ');
13
    uint8_t len = 12;                   //Länge des Lcds - "i [...]: "
14
                                        //          16      -2     -2 = 12
15
                                        //Len ist wichtig um Data später rechtsbündig darzustellen
16
    char* p = pgm_read_word(m->name);                  //char* name: Zeiger auf array im Flash
17
    while(c = pgm_read_byte(p++))       //nächtes Byte lesen und Zeiger um 1 erhöhen
18
    {
19
        len--;
20
        lcd_putc(c);
21
    }
22
    lcd_putc(':');
23
    lcd_putc(' ');
24
}

@Stefan
Deine Vorschläge machen es auch besser, allerdings erscheint das Test: 
in der 2. Zeile. Also sind >40 falsche Zeichen dazwischen.

Entry ist übrigens schon ein Pointer, siehe Definition. Da muss das "&" 
dann weg.

Irgendwie glaub ich grad, dass Assembler hier unkomplizierter wäre, da 
man nicht auf so ein Pointer pgm Zeug aufpassen muss.

Stefan Ernst schrieb:
> PS:
> Und was die Warnungen angeht: da fehlen diverse "const" in den
> Struct-Definitionen.

Hmm, ich hab alle PROGMEM mit einem const ausgestattet. Google meint 
auch was mit const, aber ich wüsste nicht wohin damit.

Nochmal langsam:
write_entry(1, *);
Hier muss man einen Zeiger MenuEntry* übergeben.
m.entry[0] ist der Zeiger auf den Flash
man liest also pgm_read_byte(m.entry[0])
dann bekommt man das 1. Byte vom MenuEntry
davon den Zeiger ergibt die Startadresse:
1
write_entry(1,&((MenuEntry) pgm_read_byte(m.entry[0])));
Das funktioniert aber nicht:
1
14 conversion to non-scalar type requested

Edit: Danke euch allen. Ich hab das Menu* Zeiger in Stefans Code 
übersehen. Es klappt wenn man den nutzt.

von Sam .. (sam1994)


Lesenswert?

Ich hatte eine falsche Vorstellung der Flashsyntax.
Wenn man eine Konstante als PROGMEM deklariert ist sie immer ein Zeiger. 
Den man auch erst auslesen muss. Erst danach hat sie den "deklarierten" 
Wert.

PS: Der Rest des Programms funktioniert jetzt auch. Danke nochmals.


Was noch offen bleibt wäre wie ich die Warnungen entferne. Wäre nett 
wenn mir jemand kurz sagt wo noch ein const hinmuss.
Und warum man Menu als Zeiger übergeben muss.

Was mich noch wundert ist, dass obwohl
1
33 [Warning] passing argument 1 of 'do_menu' discards qualifiers from pointer target type
diese Warnung ausgespuckt wird, es trotzdem funktioniert.
In do_menu gebe ich &optionen an.

von Stefan E. (sternst)


Lesenswert?

Samuel K. schrieb:
> Wenn man eine Konstante als PROGMEM deklariert ist sie immer ein Zeiger.

Nein, es ist genau das, als was man es deklariert hat. Aber die 
Funktionen zum Auslesen dieses "etwas" aus dem Flash brauchen natürlich 
die Adresse von diesem "etwas", daher immer das & bei den 
Funktionsargumenten.

Samuel K. schrieb:
> Wäre nett
> wenn mir jemand kurz sagt wo noch ein const hinmuss.

Na überall dort, wo du dann auch "const-Sachen" einträgst. Beispiel: 
wenn die Menü-Einträge alle im Flash liegen, dann sollte entry in 
Menu doch wohl ein Array aus "const MenuEntry*" sein.

von Sam .. (sam1994)


Lesenswert?

So, das ganze funktioniert bisher ganz gut. Allerdings will die Union 
nicht so richtig:
1
#define MENU_TYPE_INT 0
2
#define MENU_TYPE_STR 1
3
#define MENU_TYPE_MENU 2
4
5
typedef union 
6
{
7
    char* str;
8
    uint8_t uint;
9
    void* menu;   
10
}Data;
11
12
typedef struct 
13
{
14
    char* name;
15
    uint8_t type;
16
    uint8_t* current;
17
    uint8_t len;
18
    Data value[];
19
}MenuEntry;
20
21
typedef struct 
22
{
23
    uint8_t len;
24
    MenuEntry* entry[];
25
}Menu;
26
27
/**************************Menü - Texte**************************/
28
29
uint8_t Vchannels = 0;
30
31
const char channels[] PROGMEM = "Kanäle";
32
33
//Kanäle
34
const uint8_t _1 PROGMEM = 1;
35
const uint8_t _2 PROGMEM = 2;
36
const uint8_t _3 PROGMEM = 3;
37
const uint8_t _4 PROGMEM = 4;
38
39
const MenuEntry Echannels PROGMEM = {channels, MENU_TYPE_INT, &Vchannels, 4, 
40
    {{.uint=_1},{.uint=_2},{.uint=_3},{.uint=_4}}};
Aber der Compiler sagt je 4x:
1
54 (near initialization for 'Echannels.value[x].uint') 
2
54 initializer element is not constant

Initialisiere ich die Union falsch?


Die anderen Warnungen kommmen übirgens immer noch, obwohl ich vor jedem 
Menu und Entry ein const stehen habe.

von Stefan E. (sternst)


Lesenswert?

Samuel K. schrieb:
> Initialisiere ich die Union falsch?

Bei MenuEntry.len schreibst du bei der Initialisierung doch auch direkt 
eine 4 rein. Warum glaubst du, bei der Union die Zahlen vorher irgendwie 
im Flash ablegen zu müssen?

Samuel K. schrieb:
> Die anderen Warnungen kommmen übirgens immer noch, obwohl ich vor jedem
> Menu und Entry ein const stehen habe.

1) Und warum sehe ich in dem geposteten Code davon nichts?

2) Du hast ja auch noch mehr Konstanten im Flash, z.B. die Strings.

von Sam (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Samuel K. schrieb:
>> Initialisiere ich die Union falsch?
>
> Bei MenuEntry.len schreibst du bei der Initialisierung doch auch direkt
> eine 4 rein. Warum glaubst du, bei der Union die Zahlen vorher irgendwie
> im Flash ablegen zu müssen?
Stimmt daran hab ich irgendwie nicht gedacht.

> Samuel K. schrieb:
>> Die anderen Warnungen kommmen übirgens immer noch, obwohl ich vor jedem
>> Menu und Entry ein const stehen habe.
>
> 1) Und warum sehe ich in dem geposteten Code davon nichts?
Die Warnungen kommen bei den MenuEntry und Menu Definitionen. Die sind 
aber schon const. Kann es sein, dass man bei manchen Angaben für die 
Struktur ein (const) davor schreiben muss?

> 2) Du hast ja auch noch mehr Konstanten im Flash, z.B. die Strings.

Bei den Strings steht doch immer ein const davor. Verstehe ich dich da 
irgendwie falsch?

von Stefan E. (sternst)


Lesenswert?

Sam schrieb:
> Kann es sein, dass man bei manchen Angaben für die
> Struktur ein (const) davor schreiben muss?

Natürlich. Ich dachte eigentlich, das längst geschrieben zu haben:

Stefan Ernst schrieb:
> Beispiel:
> wenn die Menü-Einträge alle im Flash liegen, dann sollte entry in
> Menu doch wohl ein Array aus "const MenuEntry*" sein.

von Falk B. (falk)


Lesenswert?

Das const hat auf die Sache mit dem Flash keinerlei Einfluss beim AVR 
GCC.

von Stefan E. (sternst)


Lesenswert?

Falk Brunner schrieb:
> Das const hat auf die Sache mit dem Flash keinerlei Einfluss beim AVR
> GCC.

Hat ja auch keiner behauptet. Es geht um das Entfernen von Warnungen.

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.