Autor |
Verständnisproblem beim AVR GCC |
|
|
|
|
BID = 743060
wulf Schreibmaschine
Beiträge: 2246 Wohnort: Bozen
|
|
Habe immer noch recht wenig erfahrung mit dem GCC Compiler und folgendes Problem:
Der Datenspeicher ist immer voll.
Ich habe eine Interaktion zwischen Benutzer und µC per serieller Schnittstelle. Der Benutzer schickt Befehle in ASCII und der µC muss antworten.
Bin dann draufgekommen, dass wenn ich die Strings direkt meiner UART-Sendefunktion übergebe, der Datenspeicher übermäßig beansprucht wird.
Wenn die die Textblöcke als const char * vordefiniert werden wird das im Programmspeicher abgelegt.
Blöderweise ist der Datenspeicher wieder belastet wenn ich diese Strings übergebe
Kann mich jemand über die inneren Abläufe im GCC aufklären?
Hier die relevanten Codestücke. (Sorry wenns noch chaotisch aussieht, ist alles noch beim Entstehen)
Code : |
//initialize the rs485 interface
void init_uart(void) {
UBRRL = 23; //set baud rate generator (UBR) to 23 [UBR = ( 3686400Hz / (16 * 9600Baud) ) -1 ]
//UBRRH doesnt need to be touched and needs special procedure to write (shares with UCSRC)
//UCSRC is OK as default UMSEL = 0 (async mode), UPM0...1 = 0 (no parity), USBS = 0 (1 stop bit), UCSZ0..1 = 1 (8 bit data)
UCSRB |= (1<<TXEN) | (1<<RXEN); //enable TXD and RXD; UCSZ2 = 0 (8 bit data)
enable_uart_interrupt (); //enable interrupt on RX complete
_delay_ms (10); //short delay to let UART initialize properly (otherwise the first character gets lost)
}
//sends one character (on rs485)
void send_char (uint8_t character) {
while ( !( UCSRA & (1<<UDRE)) );
UDR = character;
}
//sends the content of string until it reaches '\0' (on rs485)
void send_string (char *string) {
//bring up the driver enable and wait the for the line to settle
PORTD |= 0b00000100;
_delay_us(LINE_SETTLETIME);
//write out the characters
uint8_t i;
for (i=0; string[i] != '\0'; i++ ) { send_char(string[i]); }
//wait for the last char to be written out before taking down the driver
_delay_ms(2);
//take down the line again
PORTD &= 0x11111011;
}
|
|
Danke für die Hilfe
Grüße
Simon
Edit: Hoppla, war falscher Codeausschnitt
[ Diese Nachricht wurde geändert von: wulf am 23 Jan 2011 18:58 ] |
|
BID = 743165
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
|
Moin Simon,
Das Programm sieht schon OK aus.
Das Problem wird das Nutzen der Makros _delay_ms und _delay_us sein.
Lass mich raten: du hast keine Optimierung eingestellt, oder?
Wenn keine Optimierung angeschaltet ist, wird für genannte Makros der Teil der libc hinzugelinkt, der für Float-Berechnungen zuständig ist... Dadurch bläht sich der Kode so auf.
Rufe den Compiler mit der Option -Os oder -O2 auf, dann schrumpft deine Programmgröße .
P.S.: für konstante Strings kannst du noch avr/pgmspace.h inkludieren.
Ein konstanter String wird nicht ins RAM kopiert und nimmt daher nur Platz im ROM weg. Der Datentyp lautet dann:
prog_char const* mein_String = "...";
prog_char const mein_String[] = "...";
Lesen geht dann mit
byte = pgm_read_byte(pointer);
Edit:
und zufällig gesehen:
...
PORTD &= 0x11111011;
...
_________________
[ Diese Nachricht wurde geändert von: DonComi am 23 Jan 2011 23:30 ]
[ Diese Nachricht wurde geändert von: DonComi am 24 Jan 2011 23:13 ] |
|
BID = 743205
wulf Schreibmaschine
Beiträge: 2246 Wohnort: Bozen
|
Hallo
die Optimierung -O2 war schon aktiv (im Makefile), Habe noch -Os versucht. Keine Änderung. RAM immer noch zu 206% belegt.
Habe das mit pgmspace versucht, aber jetzt gibt der Compiler schon beim Deklarieren massenhaft fehler aus.
Jetzt geht mir leider die Zeit aus, muss zur Vorlesung.
Werde später noch rumprobieren.
Grüße
Simon
_________________
Simon
IW3BWH
|
BID = 743213
Rambodischien Schreibmaschine
Beiträge: 1341 Wohnort: Österreich
|
Ich habe mir wegen den Delays eine eigene Funktion geschrieben.
Code : |
void delay_us(char x)
{
_delay_us(x);
}
void delay_ms(char x)
{
for(i=0;i<4*x;i++)
{
delay_us(250);
}
}
|
|
Damit hats bei mir ziemlich gut geklappt mit dem Speicher. Ich glaube mit der for-Schleife wird das Timing nicht perfekt, das kann man sicher verbessern, aber für meine Anwendungen hat es immer gerreicht.
Hoffe konnte helfen
_________________
Mfg Rambodischien
|
BID = 743338
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Nunja, mit großer Wahrscheinlichkeit liegt es aber nicht an diesem Ausschnitt.
Wenn gewünscht, gebe ich dir meine Mailadresse, dann kannst du mir die Sachen schicken und ich gucke, was da los ist.
Ansonsten kann man natürlich wesentlich besser helfen, wenn man nicht nur weiß, dass der Compiler meckert sondern auch, was er genau zu bemängeln hat!
@Rambodieschen:
Ist zwar nett, bringt aber nix, sogar eher das Gegenteil.
Diese Funktionen sind schon sehr optimiert, viel mehr kann man selbst nicht dran ändern. Also, daran liegt es es nicht.
_________________
[ Diese Nachricht wurde geändert von: DonComi am 24 Jan 2011 18:11 ]
|
BID = 743343
wulf Schreibmaschine
Beiträge: 2246 Wohnort: Bozen
|
Hallo,
ist ja nicht so dass der Code geheim wäre =) ich poste ihn einfach hier.
Hier der Output von AVR-Studio 4:
Code : |
Build started 24.1.2011 at 18:25:39
-------- begin --------
avr-gcc (WinAVR 20100110) 4.3.3
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Size before:
AVR Memory Usage
----------------
Device: atmega8535
Program: 2400 bytes (29.3% Full)
(.text + .data + .bootloader)
Data: 1055 bytes (206.1% Full)
(.data + .bss + .noinit)
Compiling C: testmodul1.c
avr-gcc -c -mmcu=atmega8535 -I. -gdwarf-2 -DF_CPU=3686400UL -O2 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=./testmodul1.lst -std=gnu99 -MMD -MP -MF .dep/testmodul1.o.d testmodul1.c -o testmodul1.
o
testmodul1.c: In function 'main':
testmodul1.c:153: warning: passing argument 1 of 'send_string' discards qualifiers from pointer target type
testmodul1.c:154: warning: passing argument 1 of 'send_string' discards qualifiers from pointer target type
testmodul1.c:155: warning: passing argument 1 of 'send_string' discards qualifiers from pointer target type
testmodul1.c:156: warning: passing argument 1 of 'send_string' discards qualifiers from pointer target type
testmodul1.c:157: warning: passing argument 1 of 'send_string' discards qualifiers from pointer target type
testmodul1.c:158: warning: passing argument 1 of 'send_string' discards qualifiers from pointer target type
testmodul1.c:159: warning: passing argument 1 of 'send_string' discards qualifiers from pointer target type
testmodul1.c:160: warning: passing argument 1 of 'send_string' discards qualifiers from pointer target type
Linking: testmodul1.elf
avr-gcc -mmcu=atmega8535 -I. -gdwarf-2 -DF_CPU=3686400UL -O2 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=testmodul1.o -std=gnu99 -MMD -MP -MF .dep/testmodul1.elf.d testmodul1.o --output testmodul1
.elf -Wl,-Map=testmodul1.map,--cref -lm
Creating load file for Flash: testmodul1.hex
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock testmodul1.elf testmodul1.hex
Creating load file for EEPROM: testmodul1.eep
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \
--change-section-lma .eeprom=0 --no-change-warnings -O ihex testmodul1.elf testmodul1.eep || exit 0
Creating Extended Listing: testmodul1.lss
avr-objdump -h -S -z testmodul1.elf > testmodul1.lss
Creating Symbol Table: testmodul1.sym
avr-nm -n testmodul1.elf > testmodul1.sym
Size after:
AVR Memory Usage
----------------
Device: atmega8535
Program: 2400 bytes (29.3% Full)
(.text + .data + .bootloader)
Data: 1055 bytes (206.1% Full)
(.data + .bss + .noinit)
-------- end --------
Build succeeded with 8 Warnings...
|
|
Und hier der Code:
Danke weiterhin für die Hilfe.
Edit: Das ist der ursprüngliche Code ohne deinen Tipp mit <avr/pgmspace.h>.
Das mit <avr/pgmspace.h> versuche ich jetzt nochmal und teste das genauer, da hatte ich noch keine Zeit dazu.
Sollte ich da dann nicht weiterkommen melde ich mich nochmal mit genaueren Daten.
Was mir aber aufgefallen ist, dass "const char" die Daten bereits in den Programmspeicher legt, und nicht mehr in den Datenspeicher. Nur mit der Variablenübergabe scheint noch einiges im Argen zu liegen.
Ich müsste mal versuchen statt der Arrays einen Pointer darauf zu übergeben und erst innerhalb der Funktion zu dereferenzieren.
Und wenn du dich beim Code wunderst warum so viele ähnliche Strings immer separat daklariert werden, keine Angst, später werde ich sie dann zerstückeln und dann vom Programm bauen lassen.
[ Diese Nachricht wurde geändert von: wulf am 24 Jan 2011 18:39 ]
|
BID = 743349
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Nene, das kann nicht klappen.
Der µC hat 512 Byte RAM, du hast einfach zu viele Daten, und zwar praktisch 100% Strings davon.
Das Problem bei den AVRs ist, dass sie eine Harvard-Architektur besitzen!
Der GCC kann nicht unterscheiden, ob nun ein übergebener Pointer ins RAM oder ins ROM zeigt.
Auch konstante Daten werden daher in den Arbeitsspeicher kopiert, damit sowas wie putc(char const* string) funktioniert.
Aber sie belegen Platz, auch die anonymen Strings, die ja erzeugt werden, wenn man direkt eine Zeichenkette einer Funktion übergibt, also putc("Hallo").
Es ist also nichts besonderes, dass das nicht hinhaut.
Lösung wie angedeutet:
#include <avr/pgmspace.h>
Edit
prog_char const* string1 = "Hallo Welt";
prog_char const string1[] = "Hallo Welt";
Belegt dann keine 11 Bytes im RAM sondern nur im ROM.
Die Stringfunktionen wie z.B. strcmp gibt es auch für konstante Strings, sie haben alle am Ende ein _P angehängt, also z.B. strcpy_P oder strcmp_P.
Verketten ist auch schlecht. Besser ist es, gleich alles auf die UART zu packen.
Dazu habe ich mir mal eine schlanke printf programmiert, mit eigenen Datentypen (z.B. für Debug %b für duale Darstellung).
Edit:
Zitat :
| ist, dass "const char" die Daten bereits in den Programmspeicher legt, |
Natürlich - die Daten müssen ja irgendwo herkommen. Dazu legt der Compiler sie im ROM ab - kopiert sie aber dann in einer der Init-Sections in den RAM. Nach dieser Section sind die Verweise, also die Pointer, auf diese Strings auch erst gültig.
Schau dir am besten den Unterschied zwischen der AVR-Achitektur und x86 an - dann siehst du den Unterschied.
char rxstring[82]; //String containing the received rs485 characters
char workstring[82]; //String for String text constructions and general purpose
Das allein belegt schon 32% des Gesamtarbeitsspeichers.
Ein µC, schon gar kein AVR mit 512 Byte ist kein PC, die Ressourcen sind sehr knapp.
_________________
[ Diese Nachricht wurde geändert von: DonComi am 24 Jan 2011 18:54 ]
[ Diese Nachricht wurde geändert von: DonComi am 24 Jan 2011 19:00 ]
[ Diese Nachricht wurde geändert von: DonComi am 24 Jan 2011 23:12 ]
|
BID = 743358
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
/* Funktion zum Zeichensenden */
(extern ) void uart_putc(char c);
#include <avr/pgmspace.h>
/* Sende (wirklich) konstanten String: */
void uart_puts_P(prog_char const* str)
{
register char c;
while( (c=pgm_read_byte(str++)) != '\0')
{
(void) uart_putc(c);
}
}
/* Aufrug irgendwo im Programm: */
.
.
.
uart_puts_P(PSTR("Dies ist ein wirklich konstanter String\n"));
.
.
.
oder für den „Luxus“:
#define uart_puts(str) uart_puts_P(PSTR(str))
.
.
.
uart_puts("Dies ist ein wirklich konstanter String\n");
.
.
.
_________________
|
BID = 743367
wulf Schreibmaschine
Beiträge: 2246 Wohnort: Bozen
|
Hallo,
sehr interessant deine Ausführung.
Habe mal fast alles auskommentiert und meine Strings als prog_char const* deklariert.
Blöderweise scheinen die trotzdem Ram zu fressen.
Wenn ich diesen Block einfüge:
prog_char const* id000 = "id000\n\0";
prog_char const* id001 = "id001\n\0";
prog_char const* id002 = "id002\n\0";
prog_char const* id003 = "id003\n\0";
prog_char const* id004 = "id004\n\0";
prog_char const* id005 = "id005\n\0";
prog_char const* id006 = "id006\n\0";
prog_char const* id007 = "id007\n\0";
prog_char const* id_message000 = "Supply Voltage Sensor\n\0";
prog_char const* id_message001 = "Input of ADC integrated ADC. Currently no Sensor an ADC Channel 1\n\0";
prog_char const* id_message002 = "Input of ADC integrated ADC. Currently no Sensor an ADC Channel 2\n\0";
prog_char const* id_message003 = "Input of ADC integrated ADC. Currently no Sensor an ADC Channel 3\n\0";
prog_char const* id_message004 = "Input of ADC integrated ADC. Currently no Sensor an ADC Channel 4\n\0";
prog_char const* id_message005 = "Input of ADC integrated ADC. Currently no Sensor an ADC Channel 5\n\0";
prog_char const* id_message006 = "Input of ADC integrated ADC. Currently no Sensor an ADC Channel 6\n\0";
prog_char const* id_message007 = "Input of ADC integrated ADC. Currently no Sensor an ADC Channel 7\n\0";
komme ich auf 788Bytes RAM
Ohne diesen Block nur 153Bytes.
Und nein. Ich übergebe oder benutze diese Strings nirgens. Ist alles auskommentiert. Sie werden nur deklariert.
Also dieser Code:
_________________
Simon
IW3BWH
|
BID = 743460
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Ja, hier zieht wieder die Tatsache, dass C und Harvard einfach nicht zusammenpassen, wenn man nicht massiv schummelt;
Deklariere es als Vektor bzw. Array von prog_char, dann klappts .
Also
prog_char const * id_message004 = "Input of ADC integrated ADC. Currently no Sensor an ADC Channel 4\n\0";
-->
prog_char const id_message004 [] = "Input of ADC integrated ADC. Currently no Sensor an ADC Channel 4\n\0";
Weil sonst wieder genau das passiert, was ich oben meinte. Die erste Zuweisung weißt dem prog_char* einen char* zu, was problemlos geht. Es wird also wieder Platz für den String im RAM reserviert, und der Zeiger auf vermeintlich konstante Strings zeigt mit einem Mal in den RAM und nicht ins ROM. Wenn du auf diesen Zeigern operieren würdest, würdest du irgendwas lesen, aber nicht den String .
C kann da aber nicht trennen, Zeiger ist halt Zeiger. Nur blöd, dass es zwei Adressbusse gibt, quasi zwei Adressräume ...
Offtopic :
|
Hatte heute ziemlich lange Uni (sind auch bald Klausuren) und war vorhin auch nicht mehr in der Lage, halbwegs verständlich das Problem zu erläutern :D |
_________________
|
BID = 743570
Rambodischien Schreibmaschine
Beiträge: 1341 Wohnort: Österreich
|
Offtopic :
|
@ DonComi:
Dieser Programmteil hat mir ziemlich viel Speicher erspart. Also muss die Funktion nicht so gut optimiert. Oder sehe ich das falsch?
|
Offtopic :
|
So wie es aussieht seit ihr beide sehr gute Studenten. Darf man fragen was ihr genau studiert? (Natürlich nur wenn ihr wollt ) Studiere selber auch (Informationselektronik) und ich finde euer Wissen auf dem Gebiet extrem.
|
_________________
Mfg Rambodischien
|
BID = 743653
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Zitat :
|
@ DonComi:
Dieser Programmteil hat mir ziemlich viel Speicher erspart. Also muss die Funktion nicht so gut optimiert. Oder sehe ich das falsch?
|
Jein - der erzeugte Code ist schon sehr gut, aber wird „geinlined“, also der erzeugte Kode landet nicht in einer Funktion, die per call gerufen wird, sondern eben bei aktiver Optimierung dort, wo sie aufgerufen wird.
Du hast sie jetzt in einer Funktion gekapselt, sodass die einzelne Funktion inkl. der call-Instruktionen beim Aufruf kleiner sind, also alle Inline-„Funktionen“ zusammen.
Macht also durchaus Sinn, aber nicht immer.
Offtopic :
| Ich studiere Elektrotechnik an der Uni Bremen. |
_________________
|
BID = 745968
wulf Schreibmaschine
Beiträge: 2246 Wohnort: Bozen
|
Hallo,
jetzt war es leider einig Tage stiller, hab einige Prüfungen gemacht und komme erst jetzt dazu mich wieder mit dem Problem zu beschäftigen.
Habe schon kurz mal deinen Tipp (DonComi) probiert. Funktioniert soweit.
Jetzt werde ich noch eine Funktion bauen die den String Zeichen für Zeichen ausgibt.
Offtopic :
|
@Rambo: Ich studiere an der Fachhochschule in Klagenfurt am Wörthersee, Netzwerktechnik und Kommunikation. Habe vorher 7 Semester (leider ohne Abschluß) in Trient Nachrichtentechnik studiert.
|
Werde mich dann melden wenn es Neuigkeiten gibt.
Grüße
Simon
[ Diese Nachricht wurde geändert von: wulf am 4 Feb 2011 21:25 ]
|
BID = 748145
wulf Schreibmaschine
Beiträge: 2246 Wohnort: Bozen
|
So, bis jetzt hat alles ganz gut geklappt ... aber:
Hab das ganze neue Programm mal in die Hardware geladen und musste mit schrecken feststellen, dass der (eigentlich unangetastet gelassene) Teil, der den Empfangsteil beim UART erledigt, nicht mehr richtig funktioniert.
Hab jetzt lange rumgekopft woram das liegen könnte, kamm aber nicht dahinter.
Genau genommen kommt das Programm nie mehr in den Bereich hinein wo abgefragt wird ob was neues Empfangen wurde (Zeile 123).
Vielleicht fällt ja euch etwas ein.
Grüße
Simon
[ Diese Nachricht wurde geändert von: wulf am 13 Feb 2011 19:37 ]
|
BID = 748154
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Hab grad wenig Zeit, könnte aber daran liegen, dass string_received nicht als volatile deklariert wird.
Mache mal
volatile uint8_t string_received = 0; //Variable to show the main program that a string was received
Daraus.
Die Speicherklasse volatile gibt in diesem Fall an, dass diese Variable durch Hardware verändert werden kann (in diesem Fall durch die ISR) und daher bei jedem Zugriff komplett neu gelesen wird (normalerweise lädt der GCC den Inhalt sonst in ein temp. Register und schreibt am Ende das Ergebnis zurück).
Außerdem stimmt hier was nicht:
void disable_uart_interrupt (void) {
UCSRB &= (0<<RXCIE);
}
Damit löscht das UCSRB komplett.
_________________
|