WINAVR Strings im Flash

Hallo,

irgendwie komm ich hier nicht klar. Hab mir einige Beispiele aus dem Netz gesucht, aber irgendwie funktioniert das nicht. Momentan hab ich den Eindruck, dass einige Leute Codeschnipsel veröffentlichen, ohne sie zu testen:

Ich will in einem Projekt konstante Texte im Flash ablegen, um sie bei Bedarf auf einem LCD-Display anzeigen zu können. Dazu habe ich im Web gesucht, wie man das macht, ohne RAM zu verschwenden.

Dazu habe ich unter anderem das hier gefunden:

siehe auch:

formatting link

------------------------------------------------------------ #include const prog_char str3[] = "Hallo Welt!"; unsigned int strlen_P (const prog_char *str) { unsigned int len = 0; while (1) { char c = (char) pgm_read_byte (str); if ('\0' == c) return len; len++; str++; } }

-------------------------------------------------------------- daraus hab ich dann folgendes gemacht:

void LCD_printtxtfromflash(char line, const prog_char *FlashSTR) { char c; do { c = pgm_read_byte (FlashSTR++); if (c) LCD_printchar(line++,c); } while (c); }

const prog_char FS_1D[] = "Bitte geben Sie den"; LCD_printtxtfromflash(0,FS_1D);

----------------------------------------------------------

Die Funktion LCD_printchar schreibt ein einzelnes Zeichen auf ein LCD-Display und funktioniert ansonsten einwandfrei.

Hier bekomme ich aber nur Unsinn auf das Display geschrieben. Hat da jemand einen Tipp für mich?

Gruß

Stefan DF9BI

Reply to
Stefan Brröring
Loading thread data ...

korrekt, line ist die Zeichenposition 0-79 auf einem 4x20 Display. Die betreffende Funktion LCD_printchar funktioniert übrigens einwandfrei, z.B. das hier:

---------------------------------------------------------

void LCD_printtxt(char line, char *z) { uint8_t b; while( *z ) { b = *z++; // null-terminierter String, Abbruch wenn *z=0 LCD_printchar(line++,b); if (line >= 80) line = 0; } }

LCD_printtxt(0,"Hello World");

-------------------------------------------------------------

Stimmt die Länge des ausgegebenen Unsinns?

nein

Gruß

Stefan

Reply to
Stefan Brröring

Stefan Brröring schrieb:

Ich glaub zwar nicht dass es da dran liegt, aber mach mal den expliziten Typecast nach char mit rein.

--
Gruesse
Stephan
Reply to
S.Urban

Stefan Brröring schrieb:

Ist line tatsächlich die Zeile? Beginnend mit 0? Warum wird die innerhalb der Funktion bei jedem Zeichen um eins erhöht? Oder soll line die Zeichenposition angeben? Stimmt die Länge des ausgegebenen Unsinns?

Bernd

Reply to
Bernd Laengerich

Stefan Brröring hat uns dieses gebracht :

Mal so probiert wie im reference manual?

formatting link
Seite 200

const char FS_1D[] PROGMEM = "Bitte geben Sie den";

prog_char is nämlich typedef char PROGMEM prog_char

was aus const prog_char FS_1D[] const char PROGMEM FS_1D[] macht, und nicht const char FS_1D[] PROGMEM wie im manual

ist bei manchen compilern von Belang.

LG Andy

Reply to
Andreas Nebenfuehr

Stefan Brröring fragte :

formatting link
könnte auch interessant sein, wenn noch nicht bekannt.

LG Andy

Reply to
Andreas Nebenfuehr

Stefan Brröring schrieb:

OK, dann sollte die Variable eher startPos oder so heißen :)

Hmm, dann scheint ja pgm_read_byte() nicht korrekt zu funktionieren. Die Methode gibt es als pgm_read_byte_far() und _near(), pgm_read_byte() ist die _near-Variante, vielleicht liegt es daran? Einschränkungen/Bedingungen in avr/pgmspace.h gelesen?

Bernd

Reply to
Bernd Laengerich

Stefan Brröringschrieb: "

Kenne WinAVR nicht, aber nur so ein paar Hinweise.

Bist Du sicher, dass deine Speichqualifizierer stimmen, also near und far usw. Kann der Compiler damit richtig umgehen. Evtl. liegt der Flash-Bereich nicht mehr im near und dann werden die oberen Adressbytes abgeschnitten. Hast du alle Compiler-Warnungen eingeschaltet?!!!

Am einfachsten kann man das debuggen, wenn man den Debugger/Emulator in den mixed-Mode schaltet, also C-Code und dazugehörigen Assembler angezeigt bekommt. Dann kann man durch den Assembler steppen und sich ansehen, was in die Register geschrieben wird. Passiert ja nicht viel in den paar Zeilen, so dass man sofort sieht, wo es klemmt.

Dirk

Reply to
Dirk Ruth

Zwischen pgm_read_byte_far() und _near() bzw. pg,_read_byte() habe ich keinen Unterschied feststellen können.

Ich habe jetzt folgendes herausgefunden:

char PROGMEM FS_1D[] = "TESTCODETESTCODE";

liefert mir einen Zeiger auf 0010BB

Im Flash steht mein String aber bei 003F47

wenn ich den Zeiger FS_1D in eine 32 Bit Variable umkoopiere:

w32 = &FS_1D[0];

und den an pgm_read_byte(w32) übergebe, erhalte ich mit c = pgm_read_byte(w32) den Inhalt der Speicherstelle 0010BB.

wenn ich aber schreibe:

c = pgm_read_byte(FS_1D[0]);

erhalte ich etwas anderes.

Reply to
Stefan Brröring

Puh, so ein Scheiß...

Ich hatte die Deklaration meines Strings innerhalb einer function. Hab die jetzt rausgenommen und außerhalb der function deklariert und siehe da, jetzt funktionierts.

Danke an alle für die Hinweise.

Gruß

Stefan DF9BI

Reply to
Stefan Brröring

Würde auch in der Funktion mit "static" gehen.

Jetzt musst du nur noch drüber nachdenken, /warum/ das so sein muss. (Hint: /wo/ wird der Speicherplatz von "auto"-Variablen angelegt?)

Fazit: bitte vollständigen Code posten, am besten in compilierfähiger Form.

--
cheers, J"org               .-.-.   --... ...--   -.. .  DL8DTL

http://www.sax.de/~joerg/                        NIC: JW11-RIPE
Never trust an operating system you don't have sources for. ;-)
Reply to
Joerg Wunsch

Joerg Wunsch schrieb:

Trotzdem ist das wohl eigentlich ein Fehler. Einen Heap im Flash gibt es vermutlich nicht. Also sollte der Compiler/Linker eine im Flash abgelegte Variable (PROGMEM) auch korrekt adressieren. Das die sich nicht außerhalb der Funktion im Flash auflösen kann, ist klar. Eigentlich sollte PROGMEM daher immer automatisch static sein.

Sowas ist immer gut :)

Bernd

Reply to
Bernd Laengerich

GCC ist einfach nie dafür konzipiert worden, auf Harvard-Maschinen angesetzt zu werden. Daher ist das Ganze mit dem __attribute__ ((progmem)) nur von hinten reingestrickt, der Compiler hat keine große Chance, eine unsachgemäße Benutzung desselben (wie die Anwendung auf eine auto-Variable nun einmal darstellt) zu erkennen. Das ist also alles zusammen eher ein Kompromiss, um die Flash-Funktionalität überhaupt auf die Reihe zu bekommen.

Dafür wiederum geht es dann gar nicht so schlecht.

Idealer Weise sollte sich mal jemand bei GCC des Embedded C TRs annehmen:

formatting link
formatting link

Dann hätten wir multiple address spaces auf saubere Weise. Ich fürchte nur, dass man dafür /sehr viel/ im GCC-Code umkrempeln muss. :-(

--
cheers, J"org               .-.-.   --... ...--   -.. .  DL8DTL

http://www.sax.de/~joerg/                        NIC: JW11-RIPE
Never trust an operating system you don't have sources for. ;-)
Reply to
Joerg Wunsch

Was mich etwas verwundert, aber dazu kenne ich vermutlich zuwenig vom Aufbau eines Compilers, dass der Speicherplatz lokaler Variablen nicht wieder freigegeben wird.

Beispiel:

void irgendeinefunktion(void); { tuewasmiteinemstring("bla bla bla"); }

Dass der Compiler hier zunächst Speicherplatz im RAM reservieren muss, um den String zu speichern, um dann einen Zeiger auf die Funktion tuewasmiteinemstring() zu übergaben sehe ich ja noch ein. Aber warum wird der Speicherplatz nicht anschließend einfach wieder freigegeben?

Also eigentlich will ich das gar nicht wissen, aber das scheint mir hier der Knackpunkt zu sein...

Gruß

Stefan DF9BI

Reply to
Stefan Brröring

"Stefan Brröring" schrieb im Newsbeitrag news:49146e48$0$11100$ snipped-for-privacy@newsreader.ewetel.de...

Der string bla bla bla ist auf alle Faelle statisch und muss auch nach dem Aufruf von tuewasmiteinemstring weiterhin an derselben Adresse verfuegbar sein, denn es koennte ja sein, dass sich tuewasmiteinemstring die Adresse als Seiteneffekt merkt:

char *message;

void tuewasmiteinemstring(char *str) { message=str; }

void druckemessage(voids) { printf("%s\n",message); }

void irgendeinefunktion(void); { tuewasmiteinemstring("bla bla bla"); druckemessage(); }

Wenn der einzige Adressbereich, in den char * zeigen durfen, der RAM Bereich ist, dann muss cor Programmstart dieser an den Stellen dioe vorbelegt sein sollen initialisiert werden.

Es wird also nicht "speicher nicht freigegeben", sondern der Speicher wird von woanders her initialisiert, dort ist der zusaetzlich Verbrauch. Das koennte es aber auch durch entZIPpen oder sonstige Kompressionsmethoden passieren, bla bla bla braucht im Flash nicht im Klartext zu stehen, weil es dort sowieso nur durch die Initialisierungsfunktion verwendet wird.

Natuerlich sind beliebige Compileroptimierungen denkbar und sollten von einem guten Compiler auch ausgefuehrt werden.

--
Manfred Winterhoff, reply-to invalid, use mawin at gmx dot net
homepage: http://www.geocities.com/mwinterhoff/
de.sci.electronics FAQ: http://dse-faq.elektronik-kompendium.de/
Read 'Art of Electronics' Horowitz/Hill before you ask.
Lese 'Hohe Schule der Elektronik 1+2' bevor du fragst.
Reply to
MaWin

Stefan Brröring schrieb:

Das ist keine lokale Variable, sondern sie ist per definitionem statisch, auch wenn sie außerhalb dieser Funktion nicht zugreifbar ist und sie nicht vom Programm geändert werden darf.

Referenz: ISO/IEC 9899:TC2, 6.4.5 String literals, Absatz 5:

[...] The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence. [...]

Mithin betrachtet der Compiler das wie jede andere statische Variable. Sie wird beim AVR einmalig während des Programmstarts aus dem ROM initialisiert, und du darfst sie hernach nicht mehr ändern. Der Compiler besitzt das Recht (und macht davon auch Gebrauch), mehrere identische Strings in einer einzigen Variablen zu hinterlegen.

--
cheers, J"org               .-.-.   --... ...--   -.. .  DL8DTL

http://www.sax.de/~joerg/                        NIC: JW11-RIPE
Never trust an operating system you don't have sources for. ;-)
Reply to
Joerg Wunsch

snipped-for-privacy@uriah.heep.sax.de (Joerg Wunsch) schrieb:

p.s.: Der C-Standard ist als sogenannter `as if'-Standard aufzufassen. Eine konforme Implementierung (darunter versteht man das Zusammenspiel des Compilers und der Systembibliothek(en)) muss daher nicht zwingend genau so realisiert sein, wie es im Standard beschrieben ist, sondern sie muss sich nur so verhalten, /als wäre/ sie so realisiert.

Damit gibt es an dieser Stelle in der Tat Optimierungspotenzial, was den RAM-Verbrauch betrifft. Die Implementierung wäre immer noch konform, wenn sie den RAM-Speicherplatz für den innerhalb der Funktion benutzten String gar nicht statisch anlegen würde, sondern als lokale Variable auf dem Stack, und diesen Bereich dann erst beim Funktionseintritt aus dem ROM füllt. Da du nicht das Recht besitzt, den String zu ändern, ist es also OK, eine ggf. doch vorgenommene Änderung beim Verlassen der Funktion zu verwerfen. (Dass überhaupt erst RAM benutzt werden muss hängt damit zusammen, dass der String von Standardfunktionen wie strcpy() etc. benutzt werden können muss, die beim AVR-GCC notwendiger Weise auf dem RAM arbeiten.)

Allerdings braucht das geringfügig mehr ROM (beim AVR-GCC werden Stackframes nur dann eingerichtet, wenn sie wirklich benötigt werden, und übliche Variablen kommen oft genug in Registern unter) und einiges mehr an Rechenzeit (für das Einrichten des Stackframes und das Kopieren bei jedem Eintritt in die Funktion), insofern wäre es kein guter Kandidat für eine Optimierung, die man immer aktiviert. Als Kommandozeilenoption oder __attribute__ könnte ich mir das jedoch vorstellen. Allerdings fürchte ich, dass das im gegenwärtigen GCC-Framework auf Grund der fehlenden Unterstützung für Harvard- Architekturen einfach mal gar nicht unter zu bekommen ist.

--
cheers, J"org               .-.-.   --... ...--   -.. .  DL8DTL

http://www.sax.de/~joerg/                        NIC: JW11-RIPE
Never trust an operating system you don't have sources for. ;-)
Reply to
Joerg Wunsch

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.