Guten Morgen zusammen,
ich hatte vor einiger Zeit ein Projekt gehabt und dort habe ich eine
Funktion verwendet die mir meine LCD Strings ins Flash legt. Diese habe
ich nicht selber geschrieben deshalb verstehe ich auch den Fehler im
neuen AVR Studio 6.2 nicht.
In Version 4.1 tritt der Fehler nicht auf.
In meiner LCD.h gibt es diese Funktion:
void lcd_string_P(const prog_char *pdata);
Diese verwende ich in meiner LCD.c:
void lcd_data_P(const prog_char *ptemp1)
{
. . . .
}
Wie muss ich die Funktion abändern damit es auch im Studio mit der
Version 6.2 Funktioniert? Ich blicke da leider nicht ganz durch :/
Viele Grüße
Sebastian
Ooops ;).
Nochmal neu:
Ich verwende 2 Funktionen
Das steht in meiner LCD.h
void lcd_data_P(const prog_char *ptemp1 );
void lcd_string_P(const prog_char *pdata );
und das in meiner LCD.c
void lcd_data_P(const prog_char *ptemp1 )
{
. . . .
}
void lcd_string_P( PGM_P data ) {
{
. . . .
}
Dort bekomme ich folgende Fehlermeldungen:
Error 2 unknown type name 'prog_char'
\LCD_Routines_p.h 30 1
Error 5 conflicting types for 'lcd_string_P' \LCD_Routines_P.c 164
6
Im AVR Studio 4.1 kann ich es Compilieren.
Sebastian schrieb:> Wie muss ich die Funktion abändern damit es auch im Studio mit der> Version 6.2 Funktioniert? Ich blicke da leider nicht ganz durch :/
Ich blicke bei der Fragestellung noch nicht durch. Poste doch bitte
zusätzlich zu den Funktionsrümpfen der aufzurufenden Funktionen auch:
- ein bis zwei Deklarationen von Konstanten, mit denen Du arbeitest
- einen beispielhaften Programmaufruf wie Du die Funktion dann aufrufst
Hier der gesamt auszug aus dem Programm.
Ich habe nicht alles gepostet weil es doch vermutlich zuviel wäre.
Ich bedanke mich schon recht herzlich für die Hilfe
Aus der Header Datei
Oliver S. schrieb:> Nun ja, die Fragestellung ist klar und das Problem bekannt. Erläuterung> dazu und Abhilfe wurde z.B. schon mal hier diskutiert:>> Beitrag "Alter Code mit prog_char und neue avr-libc">> Oliver
Den Beitrag habe ich gefunden aber ich bin aus der Lösung nicht schlau
geworden. ich weiß nicht an welcher stelle ich was ändern muss bzw. in
was ich es ändern soll damit es funktioniert. :(
Sebastian schrieb:> Den Beitrag habe ich gefunden aber ich bin aus der Lösung nicht schlau> geworden. ich weiß nicht an welcher stelle ich was ändern muss bzw. in> was ich es ändern soll damit es funktioniert. :(
Ich kann da irgendwie auch nichts erkennen, was nach "Lösung" aussieht.
Allerdings dürfte das dort beschriebene "Problem" so ziemlich genau das
sein, das Du hast.
Und ich habe auch noch Probleme mit Deiner Problembeschreibung.
Der Aufruf von "lcd_string_P( PSTR("Wetterstation") );" funktioniert
völlig einwandfrei. Die Funktion "void lcd_string_P( PGM_P data )" läßt
sich einwandfrei kompilieren, auch mit neuen Compilerversionen.
In dieser Funktion ist ein Aufruf von "lcd_data(tmp);" enthalten, aber
ob es damit ein Problem gibt, kann man ja so nicht sagen: Den Code der
Funktion zeigst Du nicht vor.
Falls es auch um die Compilierbarkeit der Funktion "lcd_data(tmp);" geht
ohne das _P, dann zeige auch den Code dieser Funktion!
Allerdings zeigst Du den Code einer fast namensgleichen Funktion
"lcd_data_P" vor, die am Namen ein "_P" dranhängen hat. Diese andere
Funktion läßt sich so nur mit älteren GCC-Versionen kompilieren, aber
mit der neuesten Version des GCC-Compilers NICHT. Hier muss die
Funktionsdeklaration leicht geändert werden und die Deklaration der
Variablen, mit denen die Funktion aufgerufen wird.
Falls es Dir insbesondere um diese Funktion "lcd_data_P" geht, zeige
bitte mal beispielhaft vor, wie Du hierfür Variablen als Parameter
deklarierst und diese Funktion ("lcd_data_P") aufrufst!
Jürgen S. schrieb:> Ich kann da irgendwie auch nichts erkennen, was nach "Lösung" aussieht.
Na ja, als quick and dirty fix könnte man das tun, was in der Header
Datei (und auch in der avrlibc) zum Thema prog_char dokumentiert ist:
das "__PROG_TYPES_COMPAT__ " - Define benutzen. Danach kennt die lib
prog_char wieder, und beide Fehler sollten verschwinden.
Alternativ kann man auch alle Vorkommen von prog_char durch const char
ersetzen. Steht aber auch im verlinkte Thread:
Beitrag "Re: Alter Code mit prog_char und neue avr-libc"
Oliver
Oliver S. schrieb:> Alternativ kann man auch alle Vorkommen von prog_char durch const char> ersetzen. Steht aber auch im verlinkte Thread:> Beitrag "Re: Alter Code mit prog_char und neue avr-libc"
Wenn das Verwenden bzw. Ersetzen von prog_char die Lösung für das
Problem sein soll, dürfte das besprochene Problem nicht das Problem
sein, um das es hier geht.
Wenn ich mal raten soll, geht es hier um das, was auf
http://www.nongnu.org/avr-libc/user-manual/pgmspace.html unter der
Überschrift "Storing and Retrieving Strings in the Program Space"
beschrieben ist: Das Ablegen von String-Tabellen im PROGMEM. Und zwar
nicht nur das Ablegen der Strings selbst im PROGMEM, sondern auch das
Ablegen einer String-Pointertabelle im PROGMEM, so dass man
tabellenmäßig darauf zugreifen kann, ohne dass die Strings selbst und
auch ohne dass die String-Pointertabelle im RAM gehalten werden müssen.
Der im User Manual gegebene Vorschlag lautet, die verschiedenen Strings
so im PROGMEM unterzubringen:
1
charstring_1[]PROGMEM="String 1";
2
charstring_2[]PROGMEM="String 2";
3
charstring_3[]PROGMEM="String 3";
4
charstring_4[]PROGMEM="String 4";
5
charstring_5[]PROGMEM="String 5";
Und die Pointer-Tabelle auf die Strings auf diese Art:
1
PGM_Pstring_table[]PROGMEM=
2
{
3
string_1,
4
string_2,
5
string_3,
6
string_4,
7
string_5
8
};
Wobei das Maktro PGM_P eine Ersatzschreibweise für "const char *" ist.
Alte GCC-Compilerversionen fressen das anstandslos, neue
Compilerversionen nicht.
"prog_char" kommt dabei gar nicht vor, kann also auch nicht das Problem
sein.
Das Problem dieser "String-Tabellen komplett im Flash-Speicher" sind die
Pointer: Der neue GCC-Compiler kann es absolut nicht leiden, wenn
Pointer konstant im Flash definiert werden. Und zwar nicht einmal
konstante Pointer auf Textkonstanten im Flash.
Zur Herstellung der Kompatibilität mit neuen GCC-Compilerversionen sind
nun zwei Schritte notwendig. Als erstes ist zusammen mit dem
PROGMEM-Schlüsselwort nun auch immer das Schlüsselwort "const" zwingend
erforderlich. Definition der Strings also einfach:
1
constcharstring_1[]PROGMEM="String 1";
2
constcharstring_2[]PROGMEM="String 2";
3
constcharstring_3[]PROGMEM="String 3";
4
constcharstring_4[]PROGMEM="String 4";
5
constcharstring_5[]PROGMEM="String 5";
Das ist ein Selbstgänger.
Bleibt noch die Stringtabelle, die auf keinen Fall als Array von
char-Pointern deklariert sein darf. Hier muss man sich einfach nur
klarmachen, dass auf den 8-Bit AVRs ein Array von char-Pointern
eigentlich nichts anderes ist als Array von Integer-Werten. Also legt
man anstelle eines Arrays von char-Pointern seine Stringtabelle als
Integer-Array an:
1
constintstring_table[]PROGMEM=
2
{
3
(int)string_1,
4
(int)string_2,
5
(int)string_3,
6
(int)string_4,
7
(int)string_5
8
};
Diese Konstruktion frißt der neue Compiler problemlos, da ein gecasteter
Integer-Wert, der einen Zeiger auf eine Flash-Konstante enthält, für ihn
offenbar tatsächlich konstant sein kann, was er rein bei einem Zeiger
auf eine Flash-Konstante seit einem Compiler-Update nicht mehr für
möglich hält.
Anyway. Damit sind die Strings als auch die String-Pointer als Tabelle
im Flash untergebracht. Und es läßt sich genau so wie mit älteren
Compilerversionen darauf zugreifen.
Ausgabefunktion für die so definierten Strings z.B. auf LCD:
1
voidprintOnLcd(intstring)
2
{
3
chartemp=pgm_read_byte(string);
4
inti=0;
5
while(temp!='\0')
6
{
7
// add your code here: Display char 'temp' on LCD
8
i++;
9
temp=pgm_read_byte(string+i);
10
}
11
}
Aufrufmöglichkeit für entweder den PROGMEM-String direkt, mit Casten des
String-Pointers zu einem int:
1
printOnLcd((int)string_2);
Oder über den Index in der String-Tabelle:
1
printOnLcd(string_table[1]);
Ich vermute mal, das mit den Stringtabellen im PROGMEM geht in die
Richtung des Problems, das der Fragesteller hier fragmentarisch
vorgetragen hat.
Wenn string_table im Flash liegt, kannst du nicht einfach auf den
Arrayinhalt so zugreifen
1
printOnLcd(pgm_read_word(string_table[1]));
Obiges compiliert zwar, aber es funktioniert nur dann, wenn der Compiler
den Zugriff aufs Array durch den ihm bekannten Wert ersetzt.
> Ich vermute mal, das mit den Stringtabellen im PROGMEM geht in die> Richtung des Problems, das der Fragesteller hier fragmentarisch> vorgetragen hat.
Ich würde interessieren, wie die Fehlermeldung lautet, wenn du einen
1
constchar*conststring_table[]PROGMEM=
2
{
3
string_1,
4
string_2,
5
string_3,
6
string_4,
7
string_5
8
};
machst.
(Ich benutz keinen der neueren Gcc. Mir langt der alte. Neuerer Compiler
bedeutet nur: neuere Fehler, die ich nicht kenne)
Karl Heinz schrieb:> Ich würde interessieren, wie die Fehlermeldung lautet, wenn du einen>
1
>constchar*conststring_table[]PROGMEM=
2
>{
3
>string_1,
4
>string_2,
5
>string_3,
6
>string_4,
7
>string_5
8
>};
9
>
> machst.
Sehr gute Idee das mit "const char* const"!
Keine Fehlermeldung, das compiliert fehlerfrei mit der neuen
Compilerversion.
Damit wären mit Stringtabellen im Flash-Speicher Ausgabefunktionen
möglich, die als Parameter "const char*" an die Funktion übergeben:
1
// define some strings in flash memory
2
constcharstring_1[]PROGMEM="String 1";
3
constcharstring_2[]PROGMEM="String 2";
4
constcharstring_3[]PROGMEM="String 3";
5
constcharstring_4[]PROGMEM="String 4";
6
constcharstring_5[]PROGMEM="String 5";
7
8
// define a string table in flash memory
9
constchar*conststring_table[]PROGMEM=
10
{
11
string_1,
12
string_2,
13
string_3,
14
string_4,
15
string_5
16
};
17
18
voidprintOnLcd(constchar*string)
19
// print one string from flash memory to an output device
20
{
21
chartemp=pgm_read_byte(string);
22
inti=0;
23
while(temp!='\0')
24
{
25
// add your code here: Display char 'temp' on LCD
26
i++;
27
temp=pgm_read_byte(string+i);
28
}
29
}
Beispielaufruf zur Ausgabe einer Stringkonstanten:
1
printOnLcd(string_3);
Beispielaufruf zur Ausgabe per Index aus der Stringtabelle:
Es funktioniert also auch mit konstanten Flash-Pointern, um damit
Stringtabellen zu definieren und die Flash-Strings an eigene
Ausgabefunktionen zu übergeben.
Zum Vergleich dazu die funktionsgleiche Version, bei der Flash-Pointer
als Integer verarbeitet werden (muss wohl unsigned sein, da das
PROGMEM-Segment bis zu 64 KB gross sein kann):
1
// define some strings in flash memory
2
constcharstring_1[]PROGMEM="String 1";
3
constcharstring_2[]PROGMEM="String 2";
4
constcharstring_3[]PROGMEM="String 3";
5
constcharstring_4[]PROGMEM="String 4";
6
constcharstring_5[]PROGMEM="String 5";
7
8
// define a string table in flash memory
9
constintstring_table[]PROGMEM=
10
{
11
(uint16_t)string_1,
12
(uint16_t)string_2,
13
(uint16_t)string_3,
14
(uint16_t)string_4,
15
(uint16_t)string_5
16
};
17
18
voidprintOnLcd(uint16_tstring)
19
// print one string from flash memory to an output device
20
{
21
chartemp=pgm_read_byte(string);
22
inti=0;
23
while(temp!='\0')
24
{
25
// add your code here: Display char 'temp' on LCD
26
i++;
27
temp=pgm_read_byte(string+i);
28
}
29
}
Beispielaufruf zur Ausgabe einer Stringkonstanten:
1
printOnLcd((uint16_t)string_3);
Beispielaufruf zur Ausgabe per Index aus der Stringtabelle:
1
inti=3;
2
printOnLcd(pgm_read_word(&string_table[i]));
Keine so gewaltigen Unterschiede zwischen beiden Versionen.
Und in der Arduino-IDE kompilieren beide Codes auf exakt dieselbe
Dateigröße.
Jürgen S. schrieb:> Sehr gute Idee das mit "const char* const"!
Na, ja. Wenn man const verstanden hat und auch, worum es den
Compilerbauern hier ging, eigentlich recht naheliegend.
> Keine Fehlermeldung, das compiliert fehlerfrei mit der> neuen Compilerversion.
Genau deswegen hätte mich das jetzt auch gewundert, wenn es da eine
Warnung oder einen Fehler gegeben hätte.
> Keine so gewaltigen Unterschiede zwischen beiden Versionen.
Das war so auch nicht zu erwarten. Vor allen Dingen nicht zur Laufzeit.
Mich stört nur dieses Gleichsetzen von int mit Pointern aufgrund der
gleichen sizeof und dann rumcasten.
Mich stört zb auch der Cast hier
Die sauberste Lösung, die mir dazu einfällt, wäre eine neue Funktion
pgm_read_char_ptr, in der der Cast zumindest in der Funktion gekapselt
ist. Mein Grundprinzip ist es immer, dem Compiler so wenig wie möglich
im Programm durch Casts in die Suppe zu spucken. Ich WILL dem Compiler
die Möglichkeit geben, die Datentyen für mich zu überprüfen. Da
passieren immer noch Fehler genug, wenn man nicht aufpasst. Daher: so
wenig Casts wie nur irgendwie möglich. Bei den pgm.... Funktionen und
Pointern geht das nicht, weil es keine geeignete Basisfunktion für
Pointer gibt. Aber, was nicht ist kann ja noch werden, in dem ich genau
diese Funktionalität 'einen Pointer aus dem Flash lesen' in eine
Funktion verbanne, in der der Cast passiert (könnte auch ein Makro
sein). Mach ich die Funktion als inline Funktion (oder als Makro), dann
zahl ich auch keinen Runtime Penalty dafür, ich ermögliche aber dem
Compiler die Typprüfungen und zwar ohne dass ich ihn mit einem Cast an
den verwendenden Stellen ruhig stellen müsste. Hier ist zwar alles in
Ordnung, aber generell: wenn man Pointer Datenytpen umcasten muss, dann
stimmt sehr oft etwas nicht.
Genau deswegen wäre ein
1
printOnLcd(pgm_read_char_ptr(&string_table[i]));
programmiertaktisch die um einiges bessere Lösung. Da es die gewünschte
Funktion so standardmässig nicht gibt, muss man sich die eben machen.
Du bist mir da etwas zu freizügig mit Castings.
Casts sind Waffen! Du willst sie niemals leichtsinnig benutzen und nur
dann wen es überhaupt nicht anders geht. Aber selbst dann willst du den
Cast so kapseln, dass du ihn auf jeden Fall unter Kontrolle hast. In
einer eigenen Funktion hast du ihn insofern unter Kontrolle, als es nur
eine einzige Stelle (die Funktion) mit dem Cast gibt und alle anderen
dann darauf aufbauenden weiterführend verwendenden Stellen Cast-frei
sind.
> Und in der Arduino-IDE kompilieren beide Codes auf exakt dieselbe Dateigröße.
Logisch, da sizeof(int) == sizeof( const char*)
Der Unterschied besteht darin, ob du die Typprüfung des Compilers
komplett aushebelst oder nicht. Solange nichts passiert, kann man auch
ein Atomkraftwerk mit abgeschalteten Sicherheitssystemen betreiben oder
mit dem PKW von München nach Hamburg mit 300km/h ohne Sicherheitsgurt
fahren.