Ich bin gerade sehr verwirrt, weil es ja sehr einfach sein sollte, aber
ich finde eine Menge widersprüchlicher Infos im Netz:
Ich möchte einfach nur ein paar konstante "Strings" und auch Wertearrays
(uint16_t und uint32_t) im Flash und nicht im RAM ablegen und über die
UART verschicken.
Was ist jetzt wirklich die "aktuellste" und sichere Variante?
PROGMEM ja oder ist das doch nur kosmetisch?
Was ist mit pgm_read_byte oder pgm_read_byte_near? Wann ist der
Unterschied relevant?
Und wie wird das zu sendende Array im Flash korrekt an die Sendefunktion
übergeben?
Gibt es irgendwelche Sideeffects beim abspeichern im Flash vs. im RAM?
Luky S. schrieb:> Was ist jetzt wirklich die "aktuellste" und sichere Variante?> PROGMEM ja oder ist das doch nur kosmetisch?
Ja, ist immer noch aktuell. Nur musst Du dann alle Funktionen so
schreiben, dass sie damit umgehen können oder Dir die Daten vorher in
ein Clipboard holen im RAM. Alternativ kann print überladen werden mit
__FlashStringHelper*, dazu gibt es auch Beispielcodes, wie progmem.h
verwendet wird.
1
#include<avr/pgmspace.h>
2
conststaticcharPROGMEMtext_A[]="Text A ist hier";
3
conststaticcharPROGMEMtext_B[]="Text B ist hier";
Steht im Flash, kann aber ohne weiteres nicht an Funktionen übergeben
werden.
Thorsten M. schrieb:> Ja, ist immer noch aktuell.
wie in "Funktioniert noch"
Luky S. schrieb:> Was ist jetzt wirklich die "aktuellste" und sichere Variante?
das ist "__flash". Da kümmert sich der Compiler. Zumindest soweit
möglich.
Εrnst B. schrieb:> Luky S. schrieb:>> Was ist jetzt wirklich die "aktuellste" und sichere Variante?>> das ist "__flash". Da kümmert sich der Compiler. Zumindest soweit> möglich.
In C.
Für den Fall, daß da doch noch Arduino als Salamischeibe und damit C++
nachgereicht wird, geht es nur mit PROGMEM.
Oliver
Oliver S. schrieb:> Für den Fall, daß da doch noch Arduino als Salamischeibe und damit C++> nachgereicht wird, geht es nur mit PROGMEM.
Wobei Arduino netter Weise das F() Macro mit bringt, und auch die
"incomplete __FlashStringHelper Class".
Mit F() werden allerdings keine Duplikate erkannt.
Die Print Methoden sind passend dazu ausgestattet.
Hier mal im Beispiel:
1
#include<Streaming.h> // die Lib findest du selber ;-)
2
Print&cout=Serial;// cout Emulation für "Arme"
3
4
usingFlashStr=__FlashStringHelper*;
5
6
7
constcharroman[]PROGMEM{"Hier steht ein Roman im Flash!"};
EAF schrieb:> const char roman[]
... würde ein Mensch aus dem englischen Sprachraum empfinden
als "const char römisch", würde also einen römischen
Roman vermuten. Oder aber auch ein "Array von Römern" ....
SCNR
Es geht tatsächlich um C auf einen ATMega328 ohne Arduino, sondern mit
dem Microchip (ehemals Atmel) Studio.
Zunächst will ich mal Statusinfos ausgeben, also
const __flash char BUILDTIME[] = {__DATE__ " " __TIME__};
und einen Hilfetext
const __flash char HELP[] = {"Für Hilfe siehe ....\r\n"};
über UART verschicken
Luky S. schrieb:> Zunächst will ich mal Statusinfos ausgeben, also> ...> über UART verschicken
Das ist ja auch in C kein Drama!
Oder?
Luky S. schrieb:> pgm_read_byte_near
Die Probleme fangen erst an, wenn der "near" Bereich verlassen wird.
Denn Pointer in AVR C sind nur 16Bit breit.
Luky S. schrieb:> Es sollte ja kein Drama sein, aber irgendwie erzählt jeder was> anderes...
Über 99% der Menschheit hat überhaupt keine Ahnung worüber wir her
reden.
Lass sie erzählen ....
EAF schrieb:> Die Probleme fangen erst an, wenn der "near" Bereich verlassen wird.
Nein, denn es gibt in <pgmspace.h> eine Sammlung von String-
Funktionen die das Arbeiten mit Strings im Flash ermöglichen,
und das auch für den Speicherbereich oberhalb der
16-Bit-Adressierung.
Beispielhaft eine Funktion die für Flash im Far-Bereich arbeitet:
Luky S. schrieb:> sollte print_flashstr(BUILDTIME);> beim ATMega328 und auch bei den größeren Varianten (z.B. ATMega2560)> sicher funktionieren?
Warum probierst du es nicht einfach aus? Hast du keinen
Controller dafür? Sollten wir diesen einfachen Test für dich
durchführen müssen?
Solange du im unteren 16-Bit Adressraum ("near") bleibst ist
das Verhalten für den ATMega2560 gleich. Ansonsten verwende
die vorher von mir bereits erwähnten far-Funktionen fürs Flash.
"richtig" wäre, wenn man sich an den C Standard halten könnte. Dann
würde ein einfaches
const char s[]="Hello World!"
genügen. Bei ARM Controllern ist das so.
Doch leider kommt man nicht umhin, bei AVR eine Sonderlocke zu machen,
weil für den Zugriff auf Flash andere Assembler-Befehle nötig sind als
für Zugriff auf RAM, was in C eigentlich nicht vorgesehen ist. Im
Artikel https://www.nongnu.org/avr-libc/user-manual/pgmspace.html und in
https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html
ist detailliert beschrieben, welcher Workaround in der AVR C Bibliothek
vorgesehen ist. Was im AVR Umfeld richtig ist, steht dort.
Auf die Seite wurde schon in der ersten Antwort hingewiesen. Ich
wiederhole den Hinweis, weil es mir ein Rätsel ist, was es da großartig
zu diskutieren gibt. Das Thema hätte nach der ersten Antwort bereits
beendet sein sollen.
Stefan F. schrieb:> weil es mir ein Rätsel ist, was es da großartig> zu diskutieren gibt. Das Thema hätte nach der ersten Antwort bereits> beendet sein sollen.
Weil:
>> Ab Version 4.7 unterstützt avr-gcc Adress-Spaces gemäß dem Embedded-C Dokument>> ISO/IEC TR18037
damit gibt es schon zwei Methoden, um die AVR-Sonderlocke für
Strings-im-Flash umzusetzen.
Und wo es mehrere Wege zum Ziel gibt, kann man über die Vor- und
Nachteile jedes Weges diskutieren.
Und exakt das war auch in der Fragestellung des TE:
Luky S. schrieb:> Was ist jetzt wirklich die "aktuellste" und sichere Variante?
und "PROGMEM" ist nunmal sicher nicht der "aktuellste" Weg. Vielleicht
der am häufigsten gegangene.
ernst hat es verstanden. Ich war bzw. bin immer noch verwirrt, welche
die "beste" Möglichkeit ist, relativ viel Text RAM-sparend abzulegen und
z.B. über die UART auszugeben (kann auch für eine Displayausgabe sein)
Und wie man das konkret sauber umsetzt.
Stefan F. schrieb:> Doch leider kommt man nicht umhin, bei AVR eine Sonderlocke zu machen,> weil...
Tja, das ist keine 'Sonderlocke', sondern der ganz normale Unterschied
zwischen Harvard (AVR) und v.Neumann (ARM, PC, andere). Eigentlich
sollte das jeder begriffen haben, wenn er mit Programmieren anfängt. Es
sind eben die bei Harvard getrennten Adreßräume zwischen Daten und
Instruktionen. Da kann man nicht mit einem schlichten Zeiger überall
hinzeigen. Und weil C eben sehr zeigerlastig ist, fällt das bei C eben
besonders auf.
W.S.
EAF schrieb:> Mit F() werden allerdings keine Duplikate erkannt.
Wäre ja auch zu einfach gewesen, wenn das Arduino-Framework irgendetwas
richtig machen würde ;-)
Luky S. schrieb:> Wenn der Stringconst __flash char BUILDTIME[] = {__DATE__ " "> __TIME__};> und die Ausgabefunktionvoid send_byte_usart0(u8 c) {> while(!(UCSR2A&_BV(UDRE2))); //Uart not ready> UDR2 = c; //send data> }> void print_flashstr(PGM_P str) {> u8 c;> while((c=pgm_read_byte_near ((u16)str))) {> send_byte_usart0(c);> str++;> }> }> benutzt wird, sollteprint_flashstr(BUILDTIME);> beim ATMega328 und auch bei den größeren Varianten (z.B. ATMega2560)> sicher funktionieren?
Auf dem Atmega328: Ja. Auf den größeren Varianten (1284, 2560) mit >64KB
Flash: Nein, nicht wenn BUILDTIME im oberen Flash-Bereich abgelegt ist.
Dann ist pgm_read_byte_far und dann sind bei Pointern die Handstände mit
dem Z-Register nötig, weil 16 Bit nicht mehr ausreichen.
Was da compilermäßig inzwischen Stand der Kunst ist würde mich auch
interessieren.
LG, Sebastian
Sebastian W. schrieb:> Auf dem ATmega328: Ja. Auf den größeren Varianten (1284, 2560) mit >64KB> Flash: Nein, nicht wenn BUILDTIME im oberen Flash-Bereich abgelegt ist.> Dann ist pgm_read_byte_far und dann sind bei Pointern die Handstände mit> dem Z-Register nötig, weil 16 Bit nicht mehr ausreichen.
For allem braucht man einen eigenen Datentyp und Makros aus
avr/pgmspace.h, und überhaupt an die Adressen zu kommen und diese in
einer Variable zu halten.
> Was da compilermäßig inzwischen Stand der Kunst ist würde mich auch> interessieren.
Er gibt Address-Space __memx. Pointer darauf sind 24-Bit Zeiger. Beim
Zugriff wird RAMPZ passend gesetzt und danach wieder hergestellt.
Objektgröße ist aber wie für "normalen" Code auch auf 32767 Bytes
begrenzt. Und mit 24-Bit pointern zu hantieren ist natürlich
aufwändiger als mit normalen Zeigern. Dafür wird Leser über
Segmentgrenzen hinweg unterstützt.
Daten werden in .progmemx.data abgelegt, was nach ausführbarem Code
(.text) kommt, während normales progmem in .progmem.data vor
ausführbaren Code lokatiert wird.
Und dann gibt's auch noch __flash1 etc., die auch in der obigen
Wiki-Seite beschrieben werden, die aber eine Ergänzung zum Linkerscript
erfordern für Sections .progmem1.data etc.
Dann gibt es nocht Devices wie ATmega4808, die Flash im RAM-Adressraum
sichtbar machen. Und dort wird .rodata dann ins Flash lokatiert und man
braucht weder PROGMEM noch __flash.
Neuere AVR32D Devices haven nur einen Teil den Flashs im RAM Adressraum,
z.b. nur das Ende des Flashs, was aver von avr-gcc momentan nicht
unterstützt wird. Dazu bräuchte es ein eigenes Linkerscript also eine
neue Emulaton (z.B. avrxmega8) für Binutils und Erweiterung im Compiler
und in AVR-Libc.
Wilhelm M. schrieb:> EAF schrieb:>> Mit F() werden allerdings keine Duplikate erkannt.>> Wäre ja auch zu einfach gewesen, wenn das Arduino-Framework irgendetwas> richtig machen würde ;-)
Das hat nix mit Arduino zu tun sondern ist eine Schwäche des Compilers.
Johann L. schrieb:> Wilhelm M. schrieb:>> EAF schrieb:>>> Mit F() werden allerdings keine Duplikate erkannt.>>>> Wäre ja auch zu einfach gewesen, wenn das Arduino-Framework irgendetwas>> richtig machen würde ;-)>> Das hat nix mit Arduino zu tun sondern ist eine Schwäche des Compilers.
Nein.
Man kann es mit Templates so machen, dass Duplikate nur einmal werden.
Johann L. schrieb:> Es gibt nun mal Schwächen im avr-gcc mit Duplikaterkennung, egal ob man> da C oder C++ reinfüttert.
Wie gesagt: mit C++ Templates kann man das elegant lösen, auch wenn es
der avr-gcc selbst nicht kann.
* Wie erwartet werden trailing Strings gemergt. Zum Beispiel ist &gut =
6 + &ngut, d.h. gut[] ist Teil von ngut[].
* Dito für Strings im Flash: &p_gut = &f_gut = 6 + &f_ngut.
PR92606 mischt Strings im Flash mit Strings im RAM, daher muss diese
(Compiler-)Optimierung deaktiviert werden.
Normales String-Merging geschieht hingegen auf Linker-Ebene, indem
passende Sections-Flags gesetzt werden: "S" für String und "M" für
Mergeable.
Johann L. schrieb:> PR92606 mischt Strings im Flash mit Strings im RAM, daher muss diese> (Compiler-)Optimierung deaktiviert werden.
Du meinst den Bug 92606:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92606
Und -fno-ipa-icf-variables ist ein Work-around für diesen Bug. Dein
Patch wurde ja (leider) nicht angenommen.
Welche Optimierungen fallen denn noch weg, wenn -fno-ipa-icf-variables
gesetzt ist?
Wilhelm M. schrieb:> Du meinst den Bug 92606:
Ja. PR steht für "Problem Report", und es gibt eine fortlaufende
Numerierung.
https://gcc.gnu.org/PR92606> Welche Optimierungen fallen denn noch weg, wenn -fno-ipa-icf-variables> gesetzt ist?
Es fällt genau diese Optimierung weg:
1
-fipa-icf-variables Perform Identical Code Folding for variables.
Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.
Wichtige Regeln - erst lesen, dann posten!
Groß- und Kleinschreibung verwenden
Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang