Hallo, im Atmel Studio 7 habe ich ein Problem mit "__flash". Im aktuellen Projekt arbeite ich damit statt mit dem "alten" PROGMEM. Ich lege ein Array einer Struktur an, die u.a. einen Pointer enthält, der auf einen vorher definierten String im Flash zeigt. Deklariert man diesen mit __flash geht das nicht, testweise mit PROGMEM (1. Zeile) schon: const __flash char shortKeyTextStim[MENU_LCD_MENUTEXTLEN] = "Stim"; // hier der String im Flash const __flash Menu_t menu_selFunc[] = {{...,...,...,...,shortKeyTextStim,...,...},... // <--- ERROR initializer element is not computable at load time Ändert man die erste Deklaration auf const char shortKeyTextStim[MENU_LCD_MENUTEXTLEN] PROGMEM = "Stim"; dann geht's. Der Parameter ist dabei eigentlich ein pointer auf eine uint8_t RAM-Variable, der hier aber zweckentfremdet wird als Pointer ins flash (Auszug aus der typedef von Menu_t : ... uint8_t * pVar; // <--- normalerweise Pointer auf uint8 im RAM, aber hier eben ins Flash ... ) Ursprünglich hatte ich deswegen auch "shortKeyTextStim" in der array-initialisierung als (uint8_t *) gecastet, um die Warnung zu vermeiden, die man erwarten würde. Dann aber kommt "initializer element is not constant" als Fehler. Was ist falsch bei __flash ?
:
Bearbeitet durch User
Uwe G. schrieb: > (Auszug aus der typedef von Menu_t : > ... > uint8_t * pVar; // <--- normalerweise Pointer auf uint8 im RAM, aber > hier eben ins Flash > ...
1 | const __flash uint8_t *pVar; |
Uwe G. schrieb: > Ändert man die erste Deklaration auf > > const char shortKeyTextStim[MENU_LCD_MENUTEXTLEN] PROGMEM = "Stim"; > > dann geht's. Sollte aber mindestens eine Warnung geben, denn die richtige Deklaration von Menu_t.pVar ist in dem Fall:
1 | const uint8_t *pVar; |
Ohne das const Zeiger-Target von pVar könnte man schreibend auf *pVar zugreifen => Undefined Behaviour. Das const bei menu_selFunc[] bedeutet nur dass dieses Objekte / Array read-only ist (also auch alle Komponenten), aber auf das Ziel von pVar hat das keinen Einfluss.
:
Bearbeitet durch User
...und nicht uint8_t sondern char, weil shortKeyTextStim[] ein char-Array bzw. String ist.
Danke für die Antwort! Die Definition der Struktur kann ich kaum ändern. pVar wird vielfach verwendet - meistens als Pointer auf eine Variable (nicht Konstante), daher deklariere ich das als *pVar, das const wäre also hinderlich ;). Aber diesen 16-Bit-Platz benutze ich auch als simple Integer-Zahl, z.B. als typecast in der Form: {...,...,...,...,(uint8_t *) MIDI_CHANNEL_2,...,...} Das geht problemlos! Aber in meinem Problemfall mit __flash kommt die 1. Fehlermeldung und der Typecast gibt die genannte andere Fehlermeldung. Programmtechnisch ist mehrfach sichergestellt, dass der "Pointer" pVar immer richtig interpretiert wird, das ist nicht das eigentliche Problem. Sauberer wäre es natürlich, 1) für jeden möglichen Parameter ein eigenes Element in der Struktur zu definieren (Aber Speicherplatz, Tippaufwand) 2) eine union zu definieren (Aber: hier schwer als Konstante zu initialisieren). Ist schon klar, dass "const __flash Menu_t menu_selFunc" lediglich sagt, dass das Array im Flash liegt, nicht das die Pointer-Ziele konstant sind - wie gesagt, dass sollen sie z.T. auch nicht sein ;)
Johann L. schrieb: > ...und nicht uint8_t sondern char, weil shortKeyTextStim[] ein > char-Array bzw. String ist. doch, schon (uint8_t*), weil nach der Deklaration der Struktur hier ja ein Pointer auf uint8_t erwartet wird ;) Dass shortKeyTextStim ein Pointer auf ein char-Array ist, sieht der Compiler. Mit dem Typecast sage ich ihm: "Tu mal so, als ob dass ein (uint8_t*) wäre (wie du erwartest), ich weiß schon, dass der angegebene Ausdruck einen anderen Typ ergibt". Bei der Verarbeitung des Pointers steht dann meinerseits ein weiterer typecast als (const __flash char*) (soft_Key[i].pSelMenu->pVar) drin, damit der Compiler weiß, dass es sich (in diesem Fall) bei pVar um einen Pointer auf einen string im Flash handelt (der an der Stelle dann auch benötigt wird)
Mit einer Union sehe ich da überhaupt kein Problem, etwa:
1 | typedef struct |
2 | {
|
3 | int i; |
4 | union
|
5 | {
|
6 | const __flash char *pf; |
7 | char *pc; |
8 | } u; |
9 | } S; |
10 | |
11 | extern char c[]; |
12 | extern const __flash char f[]; |
13 | |
14 | const __flash S s1 = { 1, { .pc = c } }; |
15 | const __flash S s2 = { 2, { .pf = f } }; |
16 | |
17 | void func (void) |
18 | {
|
19 | s1.u.pc[0] = s2.u.pf[0]; |
20 | }
|
Du verwensest ja eh (GNU-)C99+, hast also auch designated Initializers.
Johann L. schrieb: ... > union > { > const __flash char *pf; > char *pc; > } u; ... > const __flash S s1 = { 1, { .pc = c } }; > const __flash S s2 = { 2, { .pf = f } }; Ohne Fehler geht allerdings nur const __flash S s1 = { 1, u = { .pc = c } }... leider müsste ich dann hunderte von Initialisierungen ändern. Und letztlich erklärt es beim ursprünglichen Problem nicht, warum bei der Deklaration die Stringkonstante: const __flash char foo[] = "abc" "foo" an der Stelle des "uint8_t * pVar" bei der Initialisierung der Struktur nicht akzeptiert wird, aber schon, wenn man das so deklariert: const char foo[] PROGMEM = "abc" obwohl beide Deklarationen ja äquivalent sein sollten. bei letzter kommt zwar zurecht ein Warnung, da ein Pointer auf diesen String an einer Stelle verwendet, wo in der Struktur als "pVar" ein (uint8_t *) erwartet wird, das "const" des Ziels nicht mehr garantiert wird - aber es gibt keinen Fehler beim Compilieren.
Uwe G. schrieb: > Johann L. schrieb: > ... >> union >> { >> const __flash char *pf; >> char *pc; >> } u; > ... >> const __flash S s1 = { 1, { .pc = c } }; >> const __flash S s2 = { 2, { .pf = f } }; > > Ohne Fehler geht allerdings nur > > const __flash S s1 = { 1, u = { .pc = c } }... Nope. Der von mir gezeigte Code compiliert problemlos mit v8, v7, v6, v5, v4.9 und v4.7. Wenn überhaupt müsste es ".u=" heißen und nicht "u=". > leider müsste ich dann hunderte von Initialisierungen ändern. Klingt nach "Wasch mich, aber mach mich nicht nass!" > Und letztlich erklärt es beim ursprünglichen Problem nicht, warum bei > der Deklaration die Stringkonstante: > > const __flash char foo[] = "abc" > > "foo" an der Stelle des "uint8_t * pVar" bei der Initialisierung der > Struktur nicht akzeptiert wird, Wegen der unterschiedlichen Address-Space Qualifier. > obwohl beide Deklarationen ja äquivalent sein sollten. Sind sie nicht.
:
Bearbeitet durch User
Johann L. schrieb: > Uwe G. schrieb: >> Johann L. schrieb: >> ... >>> union >>> { >>> const __flash char *pf; >>> char *pc; >>> } u; >> ... >>> const __flash S s1 = { 1, { .pc = c } }; >>> const __flash S s2 = { 2, { .pf = f } }; >> >> Ohne Fehler geht allerdings nur >> >> const __flash S s1 = { 1, u = { .pc = c } }... > > Nope. Der von mir gezeigte Code compiliert problemlos mit v8, v7, v6, > v5, v4.9 und v4.7. Wenn überhaupt müsste es ".u=" heißen und nicht "u=". Komisch, gestern hab ich mich scheinbar vertippt, da ging's nur mit ".u=" (ja, der "." war natürlich im Quelltext) >> leider müsste ich dann hunderte von Initialisierungen ändern. > Klingt nach "Wasch mich, aber mach mich nicht nass!" Frage hierzu: im älteren C-Standard konnte man nur das erste Element einer union initialisieren. Gilt das heute weiterhin, wenn ich nichts (".xyz = ...) angebe? Muss ich dann trotzdem ein "{}" um die Konstante (hier "&(eineVariable)" ) schreiben? Ich bekomme nach Einführung der union nämlich hunderte Warnings(keine Fehler!), dass die braces bei der Intialsierung fehlen. > Wegen der unterschiedlichen Address-Space Qualifier. Worauf die Fehlermeldung aber in keinster Weise hindeutet >> obwohl beide Deklarationen ja äquivalent sein sollten. > Sind sie nicht. OK, dann sollte aber wenigstens ein typecast gehen, der aber eine genauso dumme Fehlermeldung ausspuckt
Uwe G. schrieb: > im älteren C-Standard konnte man nur das erste Element einer > union initialisieren. Gilt das heute weiterhin, wenn ich nichts > (".xyz = ...) angebe? Für eine belastbare Antwort müsste ich jetzt ebenfalls im Standard kramen; aber üblicherweise sind Erweiterungen kompatibel mit den Vorgängerversionen. > Muss ich dann trotzdem ein "{}" um die Konstante > (hier "&(eineVariable)" ) schreiben? Ich bekomme nach Einführung der > union nämlich hunderte Warnings(keine Fehler!), dass die braces bei > der Intialsierung fehlen. GNU-C kennt sowas wie Anonymous Unions, d.h. die Union braucht keinen Name und kann ohne solchen zugegriffen und initialisiert werden, also ohne das .u. Voraussetzung ist, dass keine Namenskonflikte zwischen Union-Komponenten und höherrangigen Composite-Elementen bestehen:
1 | typedef struct |
2 | {
|
3 | int i; |
4 | union
|
5 | {
|
6 | const __flash char *pf; |
7 | char *pc; |
8 | }; // anonymous |
9 | } S; |
10 | |
11 | extern char c[]; |
12 | extern const __flash char f[]; |
13 | |
14 | const __flash S s1 = { 1, .pc = c }; |
15 | const __flash S s2 = { 2, .pf = f }; |
16 | |
17 | void func (void) |
18 | {
|
19 | s1.pc[0] = s2.pf[0]; |
20 | }
|
Die Initialisierung von .pf könnte man auch schreiben als "{ f }" oder "{ .pf = f }", aber nur "f" geht nicht. Und falls man f nicht sonstwo braucht, kann man es auch ohne f schreiben:
1 | #define FSTR(X) (const __flash char[]) { X }
|
2 | |
3 | const __flash S s2 = { 2, .pf = FSTR ("Hallo") }; |
>> Wegen der unterschiedlichen Address-Space Qualifier. > Worauf die Fehlermeldung aber in keinster Weise hindeutet Ja stimmt. Die Meldung reflektiert eher Interna des Compilers. >>> obwohl beide Deklarationen ja äquivalent sein sollten. >> Sind sie nicht. > OK, dann sollte aber wenigstens ein typecast gehen, der aber eine > genauso dumme Fehlermeldung ausspuckt Problem ist der erzeugte Code. Ein Cast übersetzt in Zwischencode, und bei einem Initializer geht das eben nicht, auch wenn das im Ende keinen asm-Code erzeugt. Wäre zwar auch anders denkbar — wenn z.B. die Darstellung nicht identisch ist wie etwa bei __memx, dann könnte das ein Backend per RELOC nach Lokazierung umsetzen — aber bislang hat das wohl niemand gebraucht oder war nicht wichtig genug, als dass jemand das Feature zum GCC hätte beitragen wollen. Die wenigen Targets, die Address-Spaces implementieren, benutzen dies i.d.R für Memory Mapped I/O, haben also nicht die Anforderung, Objekte in und Zeiger auf ASes beschreiben zu müssen. Erschwerend kommt hinzu, dass ISO/IEC DTR 18037 da nicht zuende gedacht ist. Per default liegt alles im AS Generic, also auch Literal "Hallo" in
1 | const __flash char *pf = "Hallo"; |
Der einzige Weg, auf dieses Literal zuzugreifen, ist jedoch via pf, ergo ist der einzige sinnvolle AS für das Literal __flash. Immerhin kann man ein Compound Literal draus machen so dass es funktioniert, aber toll ist das nicht, erschwert Portierung und blöderweise geht es nicht lokal:
1 | void func (void) |
2 | {
|
3 | const __flash char *pf = FSTR ("Hallo"); |
4 | }
|
5 | error: compound literal qualified by address-space qualifier |
Die anonyme union war eine gute Idee, die ich umgesetzt habe. Der Tippaufwand hielt sich damit in Grenzen und jetzt sind die Typen klar definiert und müssen nicht gecastet werden. Und Danke auch für die Ausführungen zum besseren Verständnis der Internas von __flash !
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.