Hallo,
ich brauche einige konstante C-Strings mehrfach in meinem Code und
möchte diese gerne in den Flash als Konstanten ablegen.
Z.B. unsigned char Konstanten lege ich auf diese Weise in den Flash (ich
benutze einen Xmega):
Zum Auslesen benutze ich die Funktion pgm_read_byte. Ich frage mich nun,
wie ich das Ganze mit C-String bewerkstellige. Meine Vorstellung geht in
diese Richtung, was aber natürlich nicht funktioniert:
1
constchar*strings[]PROGMEM={"Hallo","Welt"};
Darüber hinaus ist auch die Frage, wie ich diese an andere Funktionen
übergebe.
Für Hinweise wäre ich sehr dankbar!
Ich glaube, bei dem konkreten Anwendungsfall ist __flash nicht
einfacher, denn man kommt trotzdem nicht umhin, eine separate Variante
zum Senden dieser Strings zu schreiben, die den String aus dem Flash
Speicher liest, statt aus dem RAM.
Stefan U. schrieb:> eine separate Variante> zum Senden dieser Strings zu schreiben, die den String aus dem Flash> Speicher liest
Genau diese Arbeit nimmt dir der Compiler ab, wenn er das Attribut
__flash bei einer Konstanten findet.
> Genau diese Arbeit nimmt dir der Compiler ab
Kannst mal ein Beispiel für eine Funktion zeigen, die sowohl normale
Strings als auch Flash-Strings sendet?
Nils H. schrieb:> aber warum kennt mein aktuelles AVR Studio "__flash" nicht?
Weil du vermutlich AVR Studio 4.19 oder älter verwendest.
Der Compiler der dabei ist "versteht" __flash noch nicht.
Irgendwann mit neuerem Compiler geht's dann ....
Arduinoquäler schrieb:> Der Compiler der dabei ist "versteht" __flash noch nicht.>> Irgendwann mit neuerem Compiler geht's dann ....
Wobei das eine GNU-Erweiterung ist, die es nicht in den Standard
geschafft hat. Daher -std=gnu99 o.ä.
>> Beispiel für eine Funktion> Ist es zu viel verlangt, dass du dir einfach den zitierten Artikel> ansiehst?
Ich habe mir das Beispiel angesehen. Dort sehe ich eine Funktion, die
einen String ausschließlich aus dem Flash senden kann, aber keinen
"normalen" String.
Ich kann das Programm auch nicht compilieren. Was mache ich falsch?
main.c:7:1: error: '__memx' specified for parameter 'character'
5
void sendChr ( const __memx char character )
6
^
7
make: *** [main.o] Error 1
Wie schreibt man denn nun eine sendStr() Funktion, dass sie sowohl für
RAM als auch für PROGMEM geeignet ist? Das Beispiel von Stefan Ernst
konnte ich nicht anwenden, wie man sieht.
Ich würde es gerne verstehen.
Stefan U. schrieb:> Ich kann das Programm auch nicht compilieren. Was mache ich falsch?
void sendChr ( const __memx char character )
->
void sendChr ( char character )
Danke, das ergibt folgendes Assembler-Listing für die main() Funktion:
1
cc: 80 e0 ldi r24, 0x00 ; 0
2
ce: 91 e0 ldi r25, 0x01 ; 1
3
d0: 6c 01 movw r12, r24
4
d2: 20 e8 ldi r18, 0x80 ; 128
5
d4: e2 2e mov r14, r18
6
d6: 8e 2d mov r24, r14
7
d8: b6 01 movw r22, r12
8
da: 0e 94 4b 00 call 0x96 ; 0x96 <sendStr>
9
de: 8e 2d mov r24, r14
10
e0: b6 01 movw r22, r12
11
e2: 0e 94 4b 00 call 0x96 ; 0x96 <sendStr>
Ich finde den Aufruf der sendStr() Funktion seltsam. Bin ich blind, oder
wird sie zweimal mit exakt dem gleichen Pointer (r24 und r22)
aufgerufen? Woher weiss die Funktion dann, dass sie einmal auf RAM und
danach auf PROGMEM zugreiden soll?
Und wenn ich mir die sendStr() Funktion genauer ansehe, vermisse ich
auch hier die Unterscheidung zwischen den beiden Fällen:
1
00000096 <sendStr>:
2
96: cf 93 push r28
3
98: df 93 push r29
4
9a: a8 2f mov r26, r24
5
9c: cb 01 movw r24, r22
6
9e: fc 01 movw r30, r24
7
a0: 24 91 lpm r18, Z
8
a2: a7 fd sbrc r26, 7
9
a4: 20 81 ld r18, Z
10
a6: 22 23 and r18, r18
11
a8: 59 f0 breq .+22 ; 0xc0 <sendStr+0x2a>
12
aa: c6 ec ldi r28, 0xC6 ; 198
13
ac: d0 e0 ldi r29, 0x00 ; 0
14
ae: 01 96 adiw r24, 0x01 ; 1
15
b0: a1 1d adc r26, r1
16
b2: 28 83 st Y, r18
17
b4: fc 01 movw r30, r24
18
b6: 24 91 lpm r18, Z
19
b8: a7 fd sbrc r26, 7
20
ba: 20 81 ld r18, Z
21
bc: 21 11 cpse r18, r1
22
be: f7 cf rjmp .-18 ; 0xae <sendStr+0x18>
23
c0: df 91 pop r29
24
c2: cf 91 pop r28
25
c4: 08 95 ret
Selbst wenn die den Optimizer abschalte (mit -O0) wird zwar geringfügig
anderer Code erzeugt, aber ich sehe immer noch die selben Probleme.
Stefan U. schrieb:> Ich finde den Aufruf der sendStr() Funktion seltsam. Bin ich blind, oder> wird sie zweimal mit exakt dem gleichen Pointer (r24 und r22)> aufgerufen?
Klar, weil du es selber so programmiert hast.
1
sendStr(message1);
2
sendStr(message1);
Stefan U. schrieb:> Und wenn ich mir die sendStr() Funktion genauer ansehe, vermisse ich> auch hier die Unterscheidung zwischen den beiden Fällen:
Die Unterscheidung ist hier:
Ich blicke durch den Assembler Code von SendStr() nicht durch. Ich
verstehe ihn so:
> lpm r18, Z
Liest aus dem Programmspeicher
> sbrc r26, 7> ld r18, Z
Überschribt das mit dem Wert aus dem RAM.
Was für ein quatsch, entweder soll er aus dem PROGMEM lesen oder aus dem
RAM, aber doch nicht beides zusammen!
Jedenfalls scheint also r26 Bit 7 zu bestimmen, was eine Kopie von r24
bit 7 ist. Demnach müsste das Hauptprogramm die Funktion einmal mit
gesetztem Bit 7 und einmal mit gelöschtem Bit 7 aufrufen. Tut sie aber
nicht.
Was übersehe ich da?
Nachtrag:
>> Klar, weil du es selber so programmiert hast.> sendStr(message1);> sendStr(message1);
Oh Mann, wie blöde. Vielen Dank, ich hab's jetzt geschnallt.
Stefan U. schrieb:> Was für ein quatsch, entweder soll er aus dem PROGMEM lesen oder aus dem> RAM, aber doch nicht beides zusammen!
Dann mach doch mal einen Vorschlag, wie man das "entweder oder"
effizienter lösen kann als mit der vorhandenen Befehlsfolge.
Hmm, ich bräuchte dann wenigstens einen Sprung mehr und hätte dann
tatsächlich keine einzigen Takt gewonnen.
Ok, der Compiler hat gewonnen. :-)
Schade, dass die avr-libc dieses Konstrukt nicht benutzt. Jetzt wo ich
ich nachvollziehen kann gefällt es mir nämlich ganz gut.
Nun zurück zum Thema, das __flash funltioniert soweit (natürlich
verwende ich nicht eine Atmel Studio Version vom vorletzten
Jahrhundert).
Ich schaffe es dennoch nicht, einen C-String mit getrennter Header und
Source Datei so zu deklarieren und definieren. Gerne würde ich auch ein
Struct mit mehreren Strings anlegen.
Header (das scheint zu funktionieren):
1
const__flashchar*text;
2
3
conststruct__flash{
4
char*text1;
5
char*text2;
6
}s_Test;
Source:
1
char*text="Hallo Welt";
2
chars_Test.*text="Hallo Welt";
Muss beim Source noch das const und flash auch hin? Ich habe alle
möglichen Kombinationen probiert etc.
Es gibt den Error
1
expected '=', ',', ';', 'asm' or '__attribute__' before '.' token
Nils H. schrieb:> Header (das scheint zu funktionieren):
Es kompiliert vielleicht ohne Fehler, aber von "funktionieren" kann wohl
kaum die Rede sein. Oder willst du tatsächlich nur die Pointer im Flash
haben, die Texte selber aber im RAM?
Und ganz unabhängig von __flash: wieso sind im Header Definitionen und
keine Deklarationen?
.h
1
#define FSTR(X) ((const __flash char[]){X})
2
3
typedefstruct{
4
const__flashchar*text1;
5
const__flashchar*text2;
6
}s_Test_t;
7
8
externconst__flashchartext[];
9
10
externconst__flashs_Test_ts_Test;
.c
1
const__flashchartext[]="Hallo Welt";
2
3
const__flashs_Test_ts_Test={FSTR("Hallo Welt 1"),FSTR("Hallo Welt 2")};