Forum: Compiler & IDEs const prog_char in AVR Studio 6.2


von Sebastian (Gast)


Lesenswert?

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

von Peter II (Gast)


Lesenswert?

Sebastian schrieb:
> In Version 4.1 tritt der Fehler nicht auf.

ja wenn man jetzt noch wüsste welcher Fehler auftritt...

von Sebastian (Gast)


Lesenswert?

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.

von Ingo L. (corrtexx)


Lesenswert?

Sebastian schrieb:
> const prog_char
Ersetze es mal gegen
1
const char

Ingo

von Jürgen S. (jurs)


Lesenswert?

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

von Oliver S. (oliverso)


Lesenswert?

Jürgen S. schrieb:
> Ich blicke bei der Fragestellung noch nicht durch.

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

von Sebastian (Gast)


Lesenswert?

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
1
void lcd_string_P(const char *pdata );
Aus der C Datei
1
void lcd_data_P(const char *ptemp1 )
2
{
3
  unsigned char temp2 = (unsigned char) pgm_read_byte(ptemp1);
4
  unsigned char temp3 = (unsigned char) pgm_read_byte(ptemp1);
5
  
6
     LCD_PORT_CONTROL |= (1<<LCD_RS);  // RS auf 1 setzen
7
 
8
    temp3 = temp3 >> 4;          // oberes Nibble holen und
9
  temp3 = temp3 & 0x0F;              // maskieren
10
   LCD_PORT_DATA &= 0xF0;
11
  LCD_PORT_DATA |= temp3;             // setzen
12
  lcd_enable();
13
14
  
15
  temp2 = temp2 & 0x0F;              // unteres Nibble holen und maskieren
16
  LCD_PORT_DATA &= 0xF0;
17
  LCD_PORT_DATA |= temp2;              // setzen
18
  lcd_enable();
19
   
20
   _delay_us(50);
21
}


Aufruf aus der main.c
1
lcd_init();
2
lcd_clear();
3
sei();
4
set_cursor(0,1);
5
lcd_string_P( PSTR("Wetterstation") );

von Sebastian (Gast)


Lesenswert?

Das steht noch in der LCD.c
1
void lcd_string_P( PGM_P data ) {
2
    uint8_t tmp;
3
 
4
    tmp = pgm_read_byte( data );
5
    while( tmp != '\0' ) {        
6
        lcd_data( tmp );
7
        data++;
8
        tmp = pgm_read_byte( data );
9
    }
10
}

von Sebastian (Gast)


Lesenswert?

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. :(

von Jürgen S. (jurs)


Lesenswert?

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!

von Sebastian (Gast)


Lesenswert?

Ich muss jetzt erst ein mal in die Schule ich melde mich entweder Heute 
abend oder morgen früh noch mal.

Viele Grüße

von Oliver S. (oliverso)


Lesenswert?

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

von Jürgen S. (jurs)


Lesenswert?

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
char string_1[] PROGMEM = "String 1";
2
char string_2[] PROGMEM = "String 2";
3
char string_3[] PROGMEM = "String 3";
4
char string_4[] PROGMEM = "String 4";
5
char string_5[] PROGMEM = "String 5";
Und die Pointer-Tabelle auf die Strings auf diese Art:
1
PGM_P string_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
const char string_1[] PROGMEM = "String 1";
2
const char string_2[] PROGMEM = "String 2";
3
const char string_3[] PROGMEM = "String 3";
4
const char string_4[] PROGMEM = "String 4";
5
const char string_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
const int string_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
void printOnLcd(int string)
2
{
3
  char temp= pgm_read_byte(string);
4
  int i=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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Jürgen S. schrieb:

> Oder über den Index in der String-Tabelle:
>
1
>   printOnLcd(string_table[1]);
2
>

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
const char* const string_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)

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Jürgen S. (jurs)

Schöner Beitrag. Sollte man im GCC Tutorial verlinken.

von Jürgen S. (jurs)


Lesenswert?

Karl Heinz schrieb:
> Ich würde interessieren, wie die Fehlermeldung lautet, wenn du einen
>
1
> const char* const string_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
const char string_1[] PROGMEM = "String 1";
3
const char string_2[] PROGMEM = "String 2";
4
const char string_3[] PROGMEM = "String 3";
5
const char string_4[] PROGMEM = "String 4";
6
const char string_5[] PROGMEM = "String 5";
7
8
// define a string table in flash memory
9
const char* const string_table[] PROGMEM =
10
{
11
  string_1,
12
  string_2,
13
  string_3,
14
  string_4,
15
  string_5
16
};
17
18
void printOnLcd(const char* string)
19
// print one string from flash memory to an output device
20
{
21
  char temp= pgm_read_byte(string);
22
  int i=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:
1
int i=3;
2
printOnLcd((const char*)pgm_read_word(&string_table[i]));

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
const char string_1[] PROGMEM = "String 1";
3
const char string_2[] PROGMEM = "String 2";
4
const char string_3[] PROGMEM = "String 3";
5
const char string_4[] PROGMEM = "String 4";
6
const char string_5[] PROGMEM = "String 5";
7
8
// define a string table in flash memory
9
const int string_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
void printOnLcd(uint16_t string)
19
// print one string from flash memory to an output device
20
{
21
  char temp= pgm_read_byte(string);
22
  int i=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
int i=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.

von Karl H. (kbuchegg)


Lesenswert?

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
1
  printOnLcd((const char*)pgm_read_word(&string_table[i]));

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.

: Bearbeitet durch User
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.