Verständnisproblem beim AVR GCC

Im Unterforum Microcontroller - Beschreibung: Hardware - Software - Ideen - Projekte

Elektronik Forum Nicht eingeloggt       Einloggen       Registrieren




[Registrieren]      --     [FAQ]      --     [ Einen Link auf Ihrer Homepage zum Forum]      --     [ Themen kostenlos per RSS in ihre Homepage einbauen]      --     [Einloggen]

Suchen


Serverzeit: 25 11 2024  18:49:15      TV   VCR Aufnahme   TFT   CRT-Monitor   Netzteile   LED-FAQ   Osziloskop-Schirmbilder            


Elektronik- und Elektroforum Forum Index   >>   Microcontroller        Microcontroller : Hardware - Software - Ideen - Projekte

Gehe zu Seite ( 1 | 2 Nächste Seite )      


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.

_________________


      Nächste Seite
Gehe zu Seite ( 1 | 2 Nächste Seite )
Zurück zur Seite 1 im Unterforum          Vorheriges Thema Nächstes Thema 


Zum Ersatzteileshop


Bezeichnungen von Produkten, Abbildungen und Logos , die in diesem Forum oder im Shop verwendet werden, sind Eigentum des entsprechenden Herstellers oder Besitzers. Diese dienen lediglich zur Identifikation!
Impressum       Datenschutz       Copyright © Baldur Brock Fernsehtechnik und Versand Ersatzteile in Heilbronn Deutschland       

gerechnet auf die letzten 30 Tage haben wir 20 Beiträge im Durchschnitt pro Tag       heute wurden bisher 11 Beiträge verfasst
© x sparkkelsputz        Besucher : 182398612   Heute : 5809    Gestern : 7548    Online : 589        25.11.2024    18:49
3 Besucher in den letzten 60 Sekunden        alle 20.00 Sekunden ein neuer Besucher ---- logout ----viewtopic ---- logout ----
xcvb ycvb
0.0440089702606