mikrocontroller.net

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


Autor: topsoft (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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-Tu...


Autor: topsoft (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

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

Du musst auch
       uart_putc(pgm_read_byte(*s));
durch
       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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das zweifache LPM (in Form von pgm_read_byte()) kann man durch Cachen
in einer Variablen (effektiv in einem CPU-Register) vermeiden:
void uart_puts_rom (const char *s)
{
    char c;

    while ((c = pgm_read_byte(s)) != '\0')
    {
        uart_putc(c);
        s++;
    }
}

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".)

Autor: Stefan Kleinwort (_sk_)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: topsoft (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.