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
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
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
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.
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".)
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.