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 | const static char PROGMEM text_A[] = "Text A ist hier"; |
3 | const static char PROGMEM text_B[] = "Text B ist hier"; |
Steht im Flash, kann aber ohne weiteres nicht an Funktionen übergeben werden.
Luky S. schrieb: > eine Menge widersprüchlicher Infos im Netz Die avrlibc ist sehr gut dokumentiert auf ihrer Homepage. Die Informationen dort kannst du als gültig ansehen. Hier ist der Link: https://www.nongnu.org/avr-libc/user-manual/
:
Bearbeitet durch User
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.
1 | #include <stdint.h> |
2 | #include <avr/pgmspace.h> |
3 | |
4 | extern __flash const uint8_t test_flash[3]; |
5 | extern const uint8_t test_progmem[3] PROGMEM; |
6 | |
7 | |
8 | void access() { |
9 | volatile uint8_t x; |
10 | x=test_flash[2]; |
11 | x=pgm_read_byte(&test_progmem[2]); |
12 | }
|
Kompiliert (mit Optimierung) zu:
1 | ldi r30,lo8(test_flash+2) |
2 | ldi r31,hi8(test_flash+2) |
3 | lpm r24,Z |
4 | std Y+1,r24 |
5 | |
6 | ldi r30,lo8(test_progmem+2) |
7 | ldi r31,hi8(test_progmem+2) |
8 | lpm r30, Z |
9 | std Y+1,r30 |
d.H. identischer ASM-Code, nur ein anderes Register (r30) zum Zwischenspeichern... AVR-GCC-Tutorial: Flash mit flash und Embedded-C
:
Bearbeitet durch User
Ε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
Ein Array aus Strings fester Länge geht. Ein Array aus Pointern auf Strings geht nicht, bzw. nur mit extrem viel Schreibarbeit.
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 | using FlashStr = __FlashStringHelper *; |
5 | |
6 | |
7 | const char roman[] PROGMEM {"Hier steht ein Roman im Flash!"}; |
8 | |
9 | void setup() |
10 | {
|
11 | Serial.begin(9600); |
12 | cout << F("Start: ") << F(__FILE__) << endl; |
13 | cout << FlashStr(roman) << endl; |
14 | |
15 | // alternativ:
|
16 | Serial.print( F("Start: ")); Serial.println(F(__FILE__)); |
17 | Serial.println(FlashStr(roman)); |
18 | Serial.println((FlashStr)roman); |
19 | Serial.println((__FlashStringHelper *)roman); |
20 | }
|
21 | |
22 | void loop() |
23 | {
|
24 | |
25 | }
|
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: > ATMega328 EAF schrieb: > Die Probleme fangen erst an, wenn der "near" Bereich verlassen wird. Das ist eher unwahrscheinlich. Oliver
Es sollte ja kein Drama sein, aber irgendwie erzählt jeder was anderes...
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:
1 | char *strncpy_PF(char *dest, uint_farptr_t src, size_t len); |
Mit diesen Funktionen lässt sich eigentlich alles im "Progmem" und oberhalb von 16-Bit-Speicheradressierung erschlagen.
Wenn der String
1 | const __flash char BUILDTIME[] = {__DATE__ " " __TIME__}; |
und die Ausgabefunktion
1 | void send_byte_usart0(u8 c) { |
2 | while(!(UCSR2A&_BV(UDRE2))); //Uart not ready |
3 | UDR2 = c; //send data |
4 | }
|
5 | |
6 | void print_flashstr(PGM_P str) { |
7 | u8 c; |
8 | while((c=pgm_read_byte_near ((u16)str))) { |
9 | send_byte_usart0(c); |
10 | str++; |
11 | }
|
12 | }
|
benutzt wird, sollte
1 | print_flashstr(BUILDTIME); |
beim ATMega328 und auch bei den größeren Varianten (z.B. ATMega2560) sicher funktionieren?
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.
Funktionieren tut es ja, aber ich bin daran interessiert, es gleich "richtig" zu machen und halt nicht nur irgendwie...
"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.
:
Bearbeitet durch User
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.
Ab hier lesen: https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Flash_mit_PROGMEM_und_pgm_read Und dann mach einfach, was du für richtig hältst. Das ist dann für dich "das Beste". Oliver
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
:
Bearbeitet durch User
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.
:
Bearbeitet durch User
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.
Es gibt nun mal Schwächen im avr-gcc mit Duplikaterkennung, egal ob man da C oder C++ reinfüttert.
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.
...evtl wurde auch nur vergessen, -fmerge-all-constants anzugeben. Hier ein Beispiel mit
1 | avr-gcc -Os foo.c -mmcu=atmega8 -o foo.elf -fmerge-all-constants -fno-ipa-icf-variables && (avr-nm foo.elf | grep gut) |
-fno-ipa-icf-variables wird benötigt wegen PR92606.
1 | #include <string.h> |
2 | #include <avr/pgmspace.h> |
3 | |
4 | const char gut[] = "alles gut."; |
5 | const char ngut[] = "nicht alles gut."; |
6 | |
7 | const __flash char f_gut[] = "alles gut."; |
8 | const __flash char f_ngut[] = "nicht alles gut."; |
9 | |
10 | PROGMEM const char p_gut[] = "alles gut."; |
11 | |
12 | void get_str (char *str, char *nstr) |
13 | { |
14 | strcpy (str, gut); |
15 | strcpy (nstr, ngut); |
16 | } |
17 | |
18 | void get_f_str (char *str, char *nstr, char* pstr) |
19 | { |
20 | strcpy_P (str, (const char*) f_gut); |
21 | strcpy_P (nstr, (const char*) f_ngut); |
22 | strcpy_P (pstr, p_gut); |
23 | } |
24 | |
25 | int main(){} |
*Ausgabe von nm:*
1 | 0000002c T f_gut |
2 | 00000026 T f_ngut |
3 | 00800066 D gut |
4 | 00800060 D ngut |
5 | 0000002c T p_gut |
* 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.
:
Bearbeitet durch User
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. |
Wobei "Code-Folding" eher ein "Merging" bedeutet. Details in ./gcc/ipa-icf.c, Schalter heißt flag_ipa_icf_variables: https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/ipa-icf.cc
:
Bearbeitet durch User
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.