uart progmem: invalid initialiser

Gast #4390698
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?
Gast #4390720
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;-)
Gast #4390728
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 */
Gast #4390742
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.
Gast #4390745
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.
Gast #4390765
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.
Gast #4390784
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.
Gast #4390814
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.
#4391023
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.
Gast #4391131
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren