Forum: Mikrocontroller und Digitale Elektronik uart progmem: invalid initialiser


von Mike (Gast)


Lesenswert?

Guten Morgen,

ich programmiere einen AVR mit uart Ausgabe und komme bei der Ausgabe 
einer Textvariablen nicht weiter.
Habe schon in den Foren und bei Onkel G.. gesucht, aber ich verstehe die 
Erklärungen nicht oder ich finde die falschen Beiträge.

Ich möchte gerne Textvariablen als String ausgeben. Ich nutze die Libs 
von P.Fleury.
Die uart Ausgabe funktioniert. Ich kann Texte "on the fly" ausgeben.

Folgendes funktioniert:
1
uint8_t SWversion = 0x12;
2
uart1_puts_P("\nNeustart Software: ");
3
uart1_puts  (utoa(SWversion,buffer,16));

Was bei mir nicht funktioniert und mit der Fehlermeldung "invalis 
initialiser" quittiert wird:
1
const uint8_t FW_variable[] PROGMEM = "Firmware 16.12.2015";
2
uart1_puts_P( FW_variable );

Was will mir die Fehlermeldung sagen?
Was muss ich ändern, anpassen, einbinden?

von Amateur (Gast)


Lesenswert?

Mit Konstanten im PROGMEM kann man nicht genauso arbeiten, wie im RAM.

Entweder, Du nutzt eine dafür angepasste Ausgaberoutine, oder Du holst 
den String Temporär ins normale RAM und gibst ihn anschließend aus.

Wenn es nicht zu viel Arbeit macht, schau' doch einfach mal in 
AVR-Tutorials;-)

von Peter II (Gast)


Lesenswert?

Amateur schrieb:
> Entweder, Du nutzt eine dafür angepasste Ausgaberoutine

macht er ja - deswegen der _p an der Funktion.

von Mike (Gast)


Lesenswert?

Amateur schrieb:
> Mit Konstanten im PROGMEM kann man nicht genauso arbeiten, wie im RAM.

Das wird meines Wissens in der Lib fon Peter berücksichtigt. Hier ein 
Auszug:
1
/*************************************************************************
2
Function: uart1_puts()
3
Purpose:  transmit string to UART1
4
Input:    string to be transmitted
5
Returns:  none          
6
**************************************************************************/
7
void uart1_puts(const char *s )
8
{
9
    while (*s) 
10
      uart1_putc(*s++);
11
12
}/* uart1_puts */
13
14
/*************************************************************************
15
Function: uart1_puts_p()
16
Purpose:  transmit string from program memory to UART1
17
Input:    program memory string to be transmitted
18
Returns:  none
19
**************************************************************************/
20
void uart1_puts_p(const char *progmem_s )
21
{
22
    register char c;
23
    
24
    while ( (c = pgm_read_byte(progmem_s++)) ) 
25
      uart1_putc(c);
26
27
}/* uart1_puts_p */

von Mike (Gast)


Lesenswert?

Mike schrieb:
> Was bei mir nicht funktioniert und mit der Fehlermeldung "invalis
> initialiser" quittiert wird:

Die Fehlermeldung lautet natürlich: "invalid initializer"

von Peter II (Gast)


Lesenswert?

Mike schrieb:
> Die Fehlermeldung lautet natürlich: "invalid initializer"

was für eine gcc Version und bei welcher Zeile kommt die Fehlermeldung?

von Amateur (Gast)


Lesenswert?

@Mike
Tut mir leid, dass ich nicht jede, von Dir versteckte, Bibliothek kenne.

Was ich aber (er)kenne ist der Unterschied zwischen:

uart1_puts_P("\nNeustart Software: ");
uart1_puts_P( FW_variable );

Hier wird ein und dieselbe Funktion mit zwei verschiedenen Datentypen 
gefüttert. Wundert mich nicht, wenn der Compiler hierbei 
Verdauungsstörungen bekommt.

Üblicherweise wird bei Atmels eine Konstante wie: "\nNeustart Software: 
" beim Programmstart im RAM abgelegt. Eine explizit im FLASH definierte 
Konstante bleibt aber dort.

von Peter II (Gast)


Lesenswert?

Amateur schrieb:
> Tut mir leid, dass ich nicht jede, von Dir versteckte, Bibliothek kenne.
>
> Was ich aber (er)kenne ist der Unterschied zwischen:
>
> uart1_puts_P("\nNeustart Software: ");
> uart1_puts_P( FW_variable );

aber dort ist nirgend ein "initialiser" also kommt die Fehlermeldung 
eventuell gar nicht von dort.

von Mike (Gast)


Lesenswert?

Peter II schrieb:
> was für eine gcc Version und bei welcher Zeile kommt die Fehlermeldung?

Wo finde ich die GCC Version?

Die Fehlermeldung kommt in Zeile
1
uart1_puts_P( FW_variable );

von Amateur (Gast)


Lesenswert?

Schau doch bitte mal, wie von mir angeregt, ins "AVR-GCC-Tutorial".
Im Kapitel: "Flash mit PROGMEM und pgm_read" wird auf diese Problematik 
eingegangen.

Bei einem "normalen" Programm wird eine Konstante zwar auch im FLASH 
abgelegt, aber beim Programmstart (Reset) in das RAM kopiert.
Dies ist auch der Grund dafür, dass mit jeder angelegten Konstanten der 
verfügbare Speicher kleiner wird.

Bei der expliziten Anlage im FLASH, greift dieser Mechanismus nicht. 
Spart zwar RAM, aber ist nicht mit 08/15-Mitteln erreichbar. Die Firma 
Atmel hat aber, für den direkten Zugriff auf das FLASH, eigene 
Funktionen implementiert.

von Mike (Gast)


Lesenswert?

Amateur schrieb:
> Schau doch bitte mal, wie von mir angeregt, ins "AVR-GCC-Tutorial".


Ja, ich habe vor Threaderstellung auch das von dir vorgeschlagene 
Tutorial gelesen, aber es hat mir nicht weitergeholfen.
Ich habe mir auch folgendes angeschaut: 
http://www.nongnu.org/avr-libc/user-manual/pgmspace.html

von Amateur (Gast)


Lesenswert?

Irgendwie glaube ich, dass wir aneinander vorbei "reden".

Peters Bibliothek enthält, wie Du ja selber herausgefunden hast zwei 
verschiedene Funktionen:
void uart1_puts(const char *s )
void uart1_puts_p(const char *progmem_s )

Du aber "sagst", Du hättest:
void uart1_puts_p(const char *progmem_s )

einmal mit     "const char *s"
und einmal mit "const char *progmem_s"
gefüttert.

Ent- oder weder. Aber nicht beides.

von chris (Gast)


Lesenswert?

Mike schrieb:
> uart1_puts_P

Wenn ich mich richtig erinnere, ist uart_puts_P als Makro definiert und 
setzt dann irgendwo ein PSTR(x) ein.
D.h. du musst in deiner main.c bzw. dort wo du uart_puts_P aufrufst, 
noch die <avr/progmem.h> includen.

von Amateur (Gast)


Lesenswert?

Nach dem von Mike publizierten Auszug sind beides "stinknormale" 
Funktionen.

Nummer zwei benutzt ja auch den von "pgm_read_byte" zur Verfügung 
gestellten Mechanismus. Dieser will aber explizit einen Zeiger auf eine 
FLASH-Adresse.

Die Doppelfunktion bei der Parameterübergabe funktioniert nach meinem 
Wissen nur in überladenen Funktionen. Dies aber ist ein C++-Mechanismus.

von Amateur (Gast)


Lesenswert?

Ich vergaß:

Wenn mich nicht alles täuscht, kommt die Fehlermeldung bei:
uart1_puts_P("\nNeustart Software: ");

und nicht bei:
uart1_puts_P( FW_variable );

von Karl H. (kbuchegg)


Lesenswert?

WIe ist es wirklich.

In der Fleury Lib gibt es 3 Mechanismen

uart1_puts
uart1_puts_p   (Achtung: kleines p!)
uart1_puts_P   (Achtung: grosses P!)

Wann wird welcher eingesetzt?

uart1_puts
**********

Diese Funktion kommt immer dann zum Zug, wenn ich einen String habe, der 
im SRAM liegt. Also alles was Variablen sind. Zb
1
  char txt[20];
2
  sprintf( txt, "Hallo" );
3
  uart1_puts( txt );

uart1_puts_p  (kleines p)
*************************

Diese Funktion wird immer dann eingesetzt, wenn ich einen 
(notwendigerweise konstanten) String habe, der im Flash liegt.
1
  const char FW_variable[] PROGMEM = "Firmware 16.12.2015";
2
  uart1_puts_p( FW_variable );

uart1_puts_P  (grosses P)
*************************

Dieses Makro(!) wird immer dann eingesetzt, wenn ich ebenfalls einen 
(notwendigerweise konstanten) String habe, der im Flash liegt.
Huch!
Was ist dann der Unterschied zu der Funktion uart1_puts_p bzw. warum 
will ich da noch ein Makro haben?
Ganz einfach.
Wenn ich 'on the fly' einen String im Flash haben will, also zb
1
  uart1_puts_p( "ok" );    // der String "ok" soll im Flash liegen
dann geht das klarerweise so nicht. Ich kann den String nicht direkt im 
Funtionsaufruf angeben. Ich müsste also schreiben
1
  const char pgmOk[] PROGMEM = "ok";
2
....
3
  uart1_puts_p( pgmOk );
und das wird auf Dauer mühsam. Vor allen Dingen dann, wenn ich 
zwischendurch immer wieder mal einen neuen Text ins Programm einbauen 
möchte. Dann muss ich jedesmal erst mühsam eine entsprechende PROGMEM 
Variable anlegen, damit ich mich beim Funktionsaufruf darauf beziehen 
kann. Ganz abgesehen davon, dass dann der eigentliche Text von der 
Aufrufstelle entfernt wird. Denn woher weiss ich denn schon, was bei
1
  uart1_puts_p( pgmJuhu );
wirklich für ein Text ausgegeben wird? Ich müsste mir die Definition von 
pgmJuhu suchen
1
  const char pgmJuhu[] PROGMEM = "Ready";
um das feststellen zu können.
Und genau da kommt jetzt die Variante von uart1_puts_P (mit grossem P) 
ins Spiel. Diese Makroversion macht 2 Dinge. Zum einen verbannt sie den 
angegebenen String ins Flash und zum zweiten ruft sie dann mit dem so 
ins Flash verbannten String die Funktion uart_puts_p auf.
uart1_puts_P ist also eine Komfortversion, die mir den Tippaufwand von 
oben erspart. Anstatt eine extra Variable anlegen zu müssen, auf die ich 
mich dann im uart1_puts_p beziehen kann, kann ich gleich einfach
1
  uart1_puts_P( "ok" );
schreiben und das Makro erledigt den Rest. Der String "ok" wird im Flash 
angelegt und die Funktion uart1_puts_p wird darauf aufgerufen.


Was ist daher an
1
  const char FW_variable[] PROGMEM = "Firmware 16.12.2015";
2
  uart1_puts_P( FW_variable );
falsch?
Na ganz einfach. FW_variable liegt ja schon im Flash!
Ich brauch daher nicht die 'gross P' Version, die mir das Argument ins 
Flash verschieben würde, sondern ich brauch die Version mit dem 'kleinen 
p', welche einen Pointer ins Flash annimmt, den ich mit FW_variable ja 
schon habe.
1
  const char FW_variable[] PROGMEM = "Firmware 16.12.2015";
2
  uart1_puts_p( FW_variable );   // die Funktion mit dem kleinen p

und die Fehlermeldung 'invalid initializer' erklärt sich auch ganz 
einfach. Wenn uart1_puts_P eine Hilfsvariable im Flash erzeugt, dann 
will sie das Argument als Initialisierung benutzen. Da wird also 
versucht eine Hilfsvariable im Flash erzeugt, die mit einer bereits im 
Flash liegenden Variablen initialisiert werden soll.
1
  const char strTmp PROGMEM = FW_variable;
und das ist in C nun einmal illegal.

von Mike (Gast)


Lesenswert?

Karl H. schrieb:
> In der Fleury Lib gibt es 3 Mechanismen
>
> uart1_puts
> uart1_puts_p   (Achtung: kleines p!)
> uart1_puts_P   (Achtung: grosses P!)

Oh Mann!!!
Genau das war es, bzw. ist es!

Vielen Dank Karl H.

Ganz besonders auch für die ausführliche Erklärung.
Leider habe ich solch eine Erklärung oder einen Hinweis darauf bei der 
Suche nicht gefunden.
Es wäre schön, wenn solch ein Hinweis mit in das Tutorial aufgenommen 
wird.
Ich vermute, dass ich nicht der einzige bin, der vor diesem Problem 
stand, bzw. steht.

Nochamls vielen Dank für die Auflösung und die Erklärung.

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.