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