Forum: Compiler & IDEs Problem mit Strings im Ram und Rom


von topsoft (Gast)


Lesenswert?

Hallo,

Ich habe ein Problem mit avr-gcc. Ich habe folgenden auszug aus dem 
Programmcode :

#include <stdio.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <string.h>

void uart_putc(unsigned char c)
{
    while (!(UCSRA & (1<<UDRE)));
    UDR = c;
}

void uart_puts (char *s)
{
    while (*s)
    {
        uart_putc(*s);
        s++;
    }
}

char Text_Init0[] PROGMEM = "Init "; // im Flash
char Text_Init1[]  = "Init "; // im Ram

int main(void)
{
  uart_puts (Text_Init0);
  uart_puts (Text_Init1);
}

Beim Senden des Strings im Ram ist alles klar. Aber das Senden des 
Strings im Flash führt zu einer Warnung des Compilers: "main.c:26: 
warning: passing arg 1 of `uart_puts' discards qualifiers from pointer 
target type".

Weiss einer was ich falsch mache?

Gruß Topsoft

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Dein uart_puts erwartet einen Pointer aufs RAM, Du aber übergibst beim 
ersten Aufruf einen Pointer aufs ROM. Anhand des Pointers selbst kann 
aber nicht unterschieden werden, worauf er zeigt, so daß Du Dir eine 
spezielle uart_puts_rom-Funktion schreiben musst, in der der uart_putc 
übergebene char-Wert vorher mit pgm_read_byte aus dem ROM gelesen wird.

Mehr zum Thema hier:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Programmspeicher_.28Flash.29


von topsoft (Gast)


Lesenswert?

Hi,

danke für die schnelle Antwort. Wenn ich einen Pointer auf das ROM 
übergebe dann sollte es doch so gehen:

void uart_puts_rom (char *s)
{
    while (*s)
    {
        uart_putc(pgm_read_byte(*s));
        s++;
    }
}

Das Problem mit dem Rom und Ram ist mir jetzt klar, liegt halt an der 
Prozessorarchtektur. Wenn ich die Funktion "pgm_read_byte" benutze 
bekomme ich ja das Byte aus dem Flash auf das der Pointer zeigt. Aber 
ich brauche ja eigentlich einen Pointer auf das gelesene Byte. Denke ich 
?!

Daraus folgt nun:

"main.c:35: warning: passing arg 1 of `uart_puts_rom' discards 
qualifiers from pointer target type"

Gruß Topsoft

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das ist zwar besser als die vorhergehende Variante, geht aber immer noch 
in die Hose:
1
    while (*s)
solltest Du ersetzen durch
1
    while (pgm_read_byte(s))

Du musst auch
1
       uart_putc(pgm_read_byte(*s));
durch
1
       uart_putc(pgm_read_byte(s));
ersetzen.

Die Funktion uart_putc will einzelne Zeichen übergeben bekommen, die 
liefert pgm_read_byte. Als Argument muss dieser Funktion ein Pointer 
übergeben werden, und nicht ein dereferenzierter Pointer.

Gewissermaßen ist

   pgm_read_byte(s)

das Flash-ROM-Äquivalent der Dereferenzierung

  *s

Wie die genaue Syntax lautet, um die Warnung wegzubekommen, entzieht 
sich jetzt meiner Kenntnis (habe hier gerade kein avrgcc) - sieh Dir mal 
in pgmspace.h die Deklaration von pgm_read_byte an, so wie dort das 
Argument spezifiziert wird, musst Du es in Deiner Funktion auch tun.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Das zweifache LPM (in Form von pgm_read_byte()) kann man durch Cachen
in einer Variablen (effektiv in einem CPU-Register) vermeiden:
1
void uart_puts_rom (const char *s)
2
{
3
    char c;
4
5
    while ((c = pgm_read_byte(s)) != '\0')
6
    {
7
        uart_putc(c);
8
        s++;
9
    }
10
}

Zeigt auch das fehlende `const' in der Parameterliste, das zur
entsprechenden Warnung geführt hat.  (Hinweis dazu: ein ``type
qualifier'' im C-Standard ist entweder "const" oder "volatile",
neuerdings auch noch "restricted".)

von Stefan K. (_sk_)


Lesenswert?

Noch eine kleine Anmerkung:

das pgm_read_byte(ptr) macht keine Typüberprüfung des Pointers. Du musst 
also selber aufpassen, dass hier ein Pointer übergeben wird. Dein Aufruf

pgmRead_byte(*s)

erzeugt   -leider-   keinen Fehler oder Warnung!

Viele Grüße, Stefan

von topsoft (Gast)


Lesenswert?

Hallo,

vielen Dank für die geleistete Hilfe. Ich habe es jetzt so gemacht:

void uart_put_string_rom (const char *s){
    char c;
    while ((c = pgm_read_byte(s)) != '\0'){
        while (!(UCSRA & (1<<UDRE)));
  UDR = c;
        s++;
    }
}

Der erzeugte Assemblercode sieht doch Spitze aus:

MOVW    R30,R24          Copy register pair
LPM     R24,Z            Load program memory
TST     R24              Test for Zero or Minus
BREQ    PC+0x06          Branch if equal
SBIS    0x0B,5           Skip if bit in I/O register set
RJMP    PC-0x0001        Relative jump
OUT     0x0C,R24         Out to I/O location
ADIW    R30,0x01         Add immediate to word
RJMP    PC-0x0007        Relative jump
RET                      Subroutine return

Für Strings im Ram gehts noch etwas kürzer:

void uart_put_string_ram (const char *s){
    char c;
  while ((c = *s++) != '\0')
    {
        while (!(UCSRA & (1<<UDRE)));
    UDR = c;
    }
}

Und noch der Assemblercode:

MOVW    R30,R24          Copy register pair
LD      R24,Z+           Load indirect and postincrement
TST     R24              Test for Zero or Minus
BREQ    PC+0x05          Branch if equal
SBIS    0x0B,5           Skip if bit in I/O register set
RJMP    PC-0x0001        Relative jump
OUT     0x0C,R24         Out to I/O location
RJMP    PC-0x0006        Relative jump
RET                      Subroutine return

Gruß Topsoft

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.