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?
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.
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!"};
... 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" ....
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
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:
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.
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.
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:
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.
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.
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.
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.