Forum: Compiler & IDEs Strings im Flash am LCD ausgeben


von Wolfgang K. (Gast)


Lesenswert?

Hallo!

Ich möchte mit meinem Atmel 4433er ein Menü am LCD erzeugen. Das 
funktioniert auch alles einwandfrei.
Mein Problem ist, dass mein RAM durch die Menütexte voll ist. In Eurem 
C-Tutorial hab ich gelesen, dass man diese Texte im Flash ablegen kann.
Gesagt getan mit solchen Zeilen:

const char Optionen[] PROGMEM = "  Optionen";

So, das funktioniert alles einwandfrei laut Compiler.

Wenn ich die Texte jetzt aber mit

LCD_Out (Optionen);

ausgeben will, bekomme ich folgende Warnung:

main.c:444: warning: passing arg 1 of `LCD_Out' discards qualifiers from 
pointer target type

Funktioniert die direkte Ausgabe eines Strings vom Flash ans LCD anders 
als so?

Vielen Dank jetzt schon mal
Wolfgang

von Karl H. (kbuchegg)


Lesenswert?

Wolfgang K. wrote:

> const char Optionen[] PROGMEM = "  Optionen";
>
> So, das funktioniert alles einwandfrei laut Compiler.
>
> Wenn ich die Texte jetzt aber mit
>
> LCD_Out (Optionen);
>
> ausgeben will, bekomme ich folgende Warnung:
>
> main.c:444: warning: passing arg 1 of `LCD_Out' discards qualifiers from
> pointer target type

Der Compiler teilt dir erst mal mit, dass die Funktion LCD_Out einen
char Pointer annimmt und keinen const char Pointer.
Da davon ausgegangen werden kann, dass LCD_Out den übergebenen
String nicht verändern wird, ist das kein Problem. Allerdings
wäre es in den Fall dann besser, wenn LCD_Out das auch dokumentieren
würde und so definiert würde

   void LCD_Out( const char* Text )
   {
      ...
   }


>
> Funktioniert die direkte Ausgabe eines Strings vom Flash ans LCD anders
> als so?

Ja: Die Funktion kann nicht unterscheiden, ob der Pointer den sie
bekommt ins SRAM oder ins Flash zeigt. Wenn der Pointer aber ins
Flash zeigt, dann muessen die Character anders gelesen werden
als wie wenn der Pointer ins SRAM zeigt. Ist aber im Tutorial
beschrieben.
Diese Thematik hat aber nichts mit der Warnung des Compilers zu tun.
Dem Compiler geht es einzig und alleine um das 'const', welches
beim Aufruf auf der Strecke bleiben würde.

von Wolfgang K. (Gast)


Lesenswert?

Ändert sich durch das Hinzufügen des const bei LCD_Out(const ...) dann 
etwas an der Ausgabe von normalen Text wie z.b.

LCD_Out ("Hallo");

oder funktioniert das trotzdem noch ganz normal wie vorher?

von Wolfgang K. (Gast)


Lesenswert?

Und ist es egal ob ich const char oder static char schreibe?

mfg Wolfgang

von Karl H. (kbuchegg)


Lesenswert?

Wolfgang K. wrote:
> Ändert sich durch das Hinzufügen des const bei LCD_Out(const ...) dann
> etwas an der Ausgabe von normalen Text wie z.b.
>
> LCD_Out ("Hallo");
>

Nein.
Ein String Literal, wie "Hallo", ist sogar per Definition
eine Konstante.

> oder funktioniert das trotzdem noch ganz normal wie vorher?

Funktioniert wie eh und je.

Du brauchst dich hier nicht um den Aufrufer kümmern. Es geht
nur darum, was die Funktion mit dem übergebenen Argument macht.
Verändert sie es, dann kann das kein const char* Pointer sein.
Verändert sie es nicht, dann sollte es ein const char* sein
um auch nach aussen zu dokumentieren, dass die Funktion den
übergebenen String nicht verändern wird.

Wenn eine Funktion einen String nicht verändern wird, dann
kann ich ja durchaus einen veränderbaren (also einen nicht
konstanten) String übergeben. Da die Funktion noch nicht mal
den Versuch unternimmt den String zu verändern, wird nichts
abartiges passieren.
Wenn auf der anderen Seite eine Funktion versucht einen String
zu verändern (also einen nicht-const Pointer übernimmt) und
ich übergebe dieser Funktion einen String der eigentlich nicht
verändert werden darf, dann habe ich ein Problem.

Deine Funktion
  void LCD_Out( char* Text );
dokumentiert mit seiner Schnittstelle, dass sie versuchen
wird, den String zu verändern. Das sie das in Wirklichkeit
nicht tun wird (weil das Ausgeben eines Textes auf einem
LCD nun mal keine Veränderung des Strings bedingt) ist ja
nur mit dieser Schnittstellenbeschreibung nicht sichtbar.

Schreibst du die Funktion aber so:
  void LCD_Out( const char* Text );
so dokumentierst du auch auf der Schnittstelle: Lieber Aufrufer,
du kannst nach Belieben jeden String da hineinstecken, die Funktion
wird nicht versuchen ihn zu verändern.

von Wolfgang K. (Gast)


Lesenswert?

So, hab das jetzt mal ausprobiert, aber das LCD zeigt den Text vom Flash 
nicht an, sondern nur ein paar komische Zeichen. Der Text vom RAM wird 
aber korrekt angezeigt. Hab ich noch was vergessen?

von Wolfgang K. (Gast)


Lesenswert?

ich habe anscheinend das problem mit der adresse, wo sie hin zeigt (ram 
oder flash), wie du es gesagt hast.

habe jetzt ein paar varianten durchprobiert, aber es klappt nicht. 
kannst du mir erklären was ich genau machen muss?

von Karl H. (kbuchegg)


Lesenswert?

Wolfgang K. wrote:
> ich habe anscheinend das problem mit der adresse, wo sie hin zeigt (ram
> oder flash), wie du es gesagt hast.
>
> habe jetzt ein paar varianten durchprobiert, aber es klappt nicht.
> kannst du mir erklären was ich genau machen muss?

Schau ins GCC Tutorial
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmspeicher_.28Flash.29
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Vereinfachung_f.C3.BCr_Zeichenketten_.28Strings.29_im_Flash

von Wolfgang K. (Gast)


Lesenswert?

Hab die Sachen ausprobiert wie den String vorher mit strcpy in RAM 
kopieren und dann ausgeben, aber funktioniert nicht.
Ich will nur ganz simpel einen Text im Flash speichern und den Text vom 
Flash wieder ausgeben, da ja der RAM zu klein wäre für diese Textmengen.
Ich mach anscheinend irgendwo einen Fehler...

const char Optionen[] PROGMEM = "TEXT IM FLASH";

LCD_Out (Optionen);


oder


const char Optionen[] PROGMEM = "TEXT IM FLASH";
char temp;

strcpy (temp, Optionen);
// diese funktion in allen varianten mit pgm_read_byte usw. probiert

LCD_Out (temp);

Kannst du mir die paar Zeilen die funktionieren für mein vorhaben bitte 
reinschreiben?

mfg

von Karl H. (kbuchegg)


Lesenswert?

Wolfgang K. wrote:
> Hab die Sachen ausprobiert wie den String vorher mit strcpy in RAM
> kopieren und dann ausgeben, aber funktioniert nicht.

Dan zeig endlich mal etwas Programmcode. Im speziellen:
Wie hast du denn das LCD_Out programmiert?

>
> Kannst du mir die paar Zeilen die funktionieren für mein vorhaben bitte
> reinschreiben?

Im Tutorial ist doch eine funktionierende Ausgabefunktion
enthalten.

Und falls das noch nicht klar geworden ist:
Du brauchst 2 Funktionen!
Eine für Strings im SRAM
Eine für Strings im Flash

von Karl H. (kbuchegg)


Lesenswert?

Wolfgang K. wrote:

>
> const char Optionen[] PROGMEM = "TEXT IM FLASH";
> char temp;
>
> strcpy (temp, Optionen);
> // diese funktion in allen varianten mit pgm_read_byte usw. probiert

Weil ichs gerade sehe.
Du musst auch noch über Stringverarbeitung nachlesen.
Ist zwar nur ein grober Überblick, aber vielleicht hilfts.
http://www.mikrocontroller.net/articles/FAQ#Wie_funktioniert_String-Verarbeitung_in_C.3F

Das strcpy() an dieser Stelle nicht funktionieren kann, sollte
doch nun wirklich klar sein. Interessant sind daher all die
Varianten die du ausprobiert hast, und die nicht funktioniert
haben.

von Karl H. (kbuchegg)


Lesenswert?

Wolfgang K. wrote:
> Hab die Sachen ausprobiert wie den String vorher mit strcpy in RAM
> kopieren und dann ausgeben, aber funktioniert nicht.

Das kann man so machen, ist aber eine schlechte Idee. (*)
Besser ist es allemal eine eigene LCD_Out_p Funktion für
die Ausgabe von Strings aus dem Flash zu schreiben.

Alles was du dazu tun musst:
Schau dir im Tutorial die Ausgabe Funktion für die Ausgabe
auf den UART an. Die übernimmst du und anstelle das einzelne
Zeichen auf den UART zu schicken, gibst du das einzelne Zeichen
an das LCD aus. Du hast doch eine Einzelzeichen Ausgabe Funktion
für das LCD?

(*) Das Problem dabei ist, dass du nicht vorhersehen kannst
wieviel Speicher du im RAM reservieren musst um den String
vom Flash in den RAM umzukopieren.
Die direkte Ausgabe vermeidet dieses Problem, indem es einfach
nur Zeichen für Zeichen des Strings aus dem Flash holt und jedes
Zeichen gleich an die LCD ausgibt. Dadurch muss der String nicht
komplett im RAM zwischengespeichert werden.

von Wolfgang K. (Gast)


Lesenswert?

Meine LCD_Out-Funktion sieht so aus, die hab ich von diesem Forum und 
funktioniert auch einwandfrei bei normaler Ausgabe:

void LCD_Out (const char *s)
{
    while (*s)
    {
        LCD_Data(*s);
        s++;
    }
}

Und die wäre die neue für Text aus Flash lt. der Ausgabe bei dem 
Beispiel:

LCD_Out_p (const char *text)
{
  char zeichen;

  while (zeichen = pgm_read_byte(text))
    {
      LCD_Out(zeichen);
      text++;
    }
}

Würde dann LCD_Out_p(Optionen) funktionieren?

von Karl H. (kbuchegg)


Lesenswert?

Wolfgang K. wrote:

> Und die wäre die neue für Text aus Flash lt. der Ausgabe bei dem
> Beispiel:
>
> LCD_Out_p (const char *text)
> {
>   char zeichen;
>
>   while (zeichen = pgm_read_byte(text))
>     {
>       LCD_Out(zeichen);
>       text++;
>     }
> }

Sieht doch schon gut aus :-)
Noch ein void vor die Funktionsdefinition

void LCD_Out_p( const char *text)
{
   ...

und wenn du dann noch innerhalb der Funktion LCD_Data()
mit dem Zeichen aufrufst und nicht LCD_Out(), dann
bist du auch schon fertig.

>
> Würde dann LCD_Out_p(Optionen) funktionieren?

Na, aber sicher doch!

von Wolfgang K. (Gast)


Lesenswert?

Hab das void nicht mitmarkiert... ;-)

Funktioniert!!!! Vielen Dank!

Hab nur noch eine Frage: Beim Compilieren kommt jetzt diese Meldung:

main.c:408: warning: suggest parentheses around assignment used as truth 
value

und zeigt auf diese Zeile

->  while (zeichen = pgm_read_byte(text))
    {
        ...
    }

von Wolfgang K. (Gast)


Lesenswert?

aja, und diese meldung kommt nur beim ersten mal compilieren von 
verändertem code, d.h. beim danach gleich nochmal compilieren ist sie 
weg.

von Johannes M. (johnny-m)


Lesenswert?

Der Vergleichsoperator in C heißt "==" und nicht "=". "=" macht eine 
Zuweisung!

von Wolfgang K. (Gast)


Lesenswert?

das stimmt, aber das soll es hier ja auch machen denke ich. dem char 
zeichen die zeichen von text zuweisen, ist dann ja wie

while(zeichen)
{
  ...
}

also so lange etwas is zeichen ist, so auf die art.

von Karl H. (kbuchegg)


Lesenswert?

Wolfgang K. wrote:

> main.c:408: warning: suggest parentheses around assignment used as truth
> value
>
> und zeigt auf diese Zeile
>
> ->  while (zeichen = pgm_read_byte(text))
>     {
>         ...
>     }

Weil die Zeile eigentlich so aussehen sollte:

  while( ( zeichen = pgm_read_byte(text) ) != '\0' )

Der Programmierer hat sich aber den Vergleich mit '\0'
gespart, da der sowieso automatisch gemacht wird.

Der 'Trick' besteht darin, dass hier gleichzeitig das
Ergebnis von pgm_read_byte an zeichen zugewiesen wird
und das so zugewiesene Zeichen mit '\0' verglichen wird.

Den Vergleich muss man nicht explizit schreiben. Allerdings
ist es eine beliebte Fehlerquelle bei einem anderen
Vergleich, sagen wir mal

   while( i == 5 )

einen Tippfehler zu machen und stattdessen eine Zuweisung
anstelle eines Vergleiches zu machen

  while( i = 5 )

Viele Compiler warnen an dieser Stelle mit der, oft nicht
unbegründeten, Vermutung, dass es sich dabei um einen Tippfehler
handelt.
Allerdings kommt es manchmal vor, dass man tatsächlich Zuweisung
meint und auch Zuweisung schreibt. Und um dem Compiler mitzuteilen,
dass man die Zuweisung auch ernst meint, hat sich eingebürgert
da noch mal ein ( ) - Paar rundherum zu setzen

   while( (i = 5) )    // das ist in dem Fall natuerlich eine
                       // sinnlose Zuweisung. Es geht nur ums
                       // Prinzip

so wie man das auch machen muesste, wenn man dann noch einen
Vergleich auf ungleich 0 anhaengen wuerde

   while( (i = 5) != 0 )

Ersetze i mit 'Zeichen' und die 5 mit dem Funktionsaufruf
von pgm_readByte und du bist genau auf deiner Form.

von Wolfgang K. (Gast)


Lesenswert?

Ahhh, hab das jetzt umgeschrieben, da mich die Warnung rein optisch 
gestört hat.

Danke für deine ganze Hilfe nochmal!

mfg

PS: Weisst du rein zufällig, auf die schnelle, zu wieviel man den RAM 
und FLASH beim kompilieren anfüllen darf, um nicht in das Risiko des 
Überschreibens zu geraten? (z.b. 90%)
Hatte das schon beim RAM wegen den Strings und dann spielt ja alles 
verrückt.

von Karl H. (kbuchegg)


Lesenswert?

Wolfgang K. wrote:
> PS: Weisst du rein zufällig, auf die schnelle, zu wieviel man den RAM
> und FLASH beim kompilieren anfüllen darf, um nicht in das Risiko des
> Überschreibens zu geraten? (z.b. 90%)
> Hatte das schon beim RAM wegen den Strings und dann spielt ja alles
> verrückt.

Flash ist kein Thema. Das kannst du bis zum letzten Byte
mit deinem Code füllen.

Beim RAM kann man das nicht so pauschal sagen.
Während der Laufzeit wird ja auch RAM verbraucht. Irgendwo
muss ja der Prozessor die Rücksprungadressen bei einem
Funktionsaufruf auch speichern. Und funktionslokale Variablen
wollen auch erzeugt werden etc, etc.

Kurz und gut: Wieviel Speicher dies alles verschlingt, hängt unter
anderem auch vom exakten Programmablauf ab und vom Programm
selber ab.

Sorry. Ich kann dir da jetzt keine Zahl nennen. Es ist ein leichtes
ein Programm zu schreiben, das selbst bei einer angezeigten
Auslastung von 0% nicht mit dem SRAM auskommen wird und es
ist auch nicht weiter schwer ein Programm zu schreiben, welches
bei einer 98% Auslastung ohne Probleme klarkommt. Aber das sind
konstruierte Beispiele und haben mit der Realität nicht viel
zu tun. Irgendwo zwischen diesen beiden Zahlen wird sich bei
deinem speziellen Programm die magische Grenze bewegen. Beim
nächsten Programm wird es wieder so eine Grenze geben aber die
liegt dann an einer ganz anderen %-Marke.

von Johannes M. (johnny-m)


Lesenswert?

Upps, hatte nicht gesehen, dass die Zuweisung an der Stelle gewollt ist. 
Aber der gute Karl Heinz hat ja mal wieder Licht ins Dunkel gebracht...

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
Noch kein Account? Hier anmelden.