Problem mit Strings im Ram und Rom

Gast #429834
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
Persönliche Seite #429837
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

Gast #429862
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
Persönliche Seite #429877
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.
Moderator Persönliche Seite #429910
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".)
Gast #430447
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

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