Hallo!
Controller: ATMEGA128
Umgebung: WinAVR
Ich schreibe hier das Programm für einen Prüfplatz mit 85
Prüfschritten.
Dazu muß ich u.a. 136 Relais und 210 LED's ansteuern. Dies geschieht
logischerweise seriell mittels mehreren Allegro A6818.
Ich habe nun eine Funktion geschrieben, die die Daten ausgibt. Die
daten liegen in einem 20Byte un einem 28Byte "Char-Array". Deses
Array habe ich mittels "volatile unsigned char def_led[28]" und
"unsigned char def_relay[20]" definiert.
Bisher alles ok! :)
Nun das Problem:
Wenn ich nun 85 Prüf-Funktionen anlege und jede davon greift auf das
mit volatile definierte Char zu, so benötigt der Compiler alleine
hierfür 85* (20+28) Byte -> 4080Byte RAM zum verwalten! :(
Wie könnte ich dies eleganter lösen?
Ich hoffe, ich habe mich verständlich ausgedrückt..
Im Vorraus schonmal einen herzlichen Dank!
Hi,
ohne Kenntnis der in diesen Arrays kann man nicht sagen, dass man diese
Ersetzen kann. Würde es einen einfachen Algoritmus geben, hättest du ihn
sicherlich verwendet :)
Oder benutzt hier jede der 85 Prüfroutinen das gleiche/selbe Feld? Dann
ist eine Vervielfachung natürlich Quatsch. Kann man das Feld nicht,
sagen wir mal global machen? Warum eigentlich volatile? Ich verstehe
das so, dass aus diesem Array nur Werte zur Ausgabe geholt werden, dann
ändern sich diese Felder doch aber nicht?
Hallo Stromspannung!
Im Prinzip ist das Array nur die Vorlange für das auszugebende
Bitmuster. Da sowohl das Hauptprogramm, als auch die einzelnen
prüffunktionen aud die Relais und die LEDs zugreifen können müssen,
habe ich die Variablen mit volatile definiert.
Um das ändern der Zustände schön übersichtlich zu machen, habe ich eine
Header-Datei erstellt, wo alle Aktionen per defines vorgegeben sind.
z.B.
#define RELAY_K100_ON def_relay[10] |= 0x10
#define RELAY_K100_OFF def_relay[10] &= ~0x10
usw..
Nachdem ich dann die Zusände wie gewünscht angepasst habe, schiebe ich
die daten mittels einer Funktion "Send_RELAY_data();" raus.
Verstehst du, was ich meine?
Gruß,
Techniker
Hmm..
D.h. wenn eine normale Funktion die Variable ändert, bekommt das die
Hauptfunktion mit und umgekehrt? :)
Braucht man volatile wirklich nur in Verbindung mit Interrupts?
> Nun das Problem:> Wenn ich nun 85 Prüf-Funktionen anlege und jede davon greift auf> das mit volatile definierte Char zu, so benötigt der Compiler> alleine hierfür 85* (20+28) Byte -> 4080Byte RAM zum verwalten! :(
?
Was hast du denn fuer eine perverse Rechnerei.
Die Arrays sind doch wohl global, existieren also
nur ein einziges Mal, bzw. sie existieren in der main()
> Hmm..> D.h. wenn eine normale Funktion die Variable ändert, bekommt> das die Hauptfunktion mit und umgekehrt? :)
Wie wäre es mit einem C-Buch?
Sieht aus als ob du das nötig hättest.
Das Verständnis von 'Argument Passing' bzw. den 'Scope-Regeln'
ist eigentlich Grundvoraussetzung für professionelle Software-
Entwicklung (selbst wenn man sie nur im Hobby Bereich betreibt).
@Karl:
Die Rechnerei ist nicht pervers, sondern genau dass, was mir WinAVR
Ausgibt!
Klammere ich nämlich jegliche Zugriffe auf das Array aus einer
Prüffunktion aus und kompiliere das Programm neu, so zeigt mir gcc
genau 48Byte weniger RAM-Verbrauch. Wenn das nicht irgendein Zufall
ist...
Und funktionieren mein bisheriges Speichermodell gut, da ich es mit 3
prüfschritten getestet habe. Nur für das komplette Projekt reichen mir
so die 4kB nicht.
Was ich möchte ist genau das, was du schilderst, nämlich EINMAL 20Byte
bzw. 28Byte Array und dies Global. Was ich bei meinem problem eben
nicht verstehe, warum GCC bei jeder Unterfunktion genau diese Größe
nocheinmal braucht?
Und noch eine Bitte: Bevor du solche Beiträge verteilst, wie "perverse
Rechnerei" und "Wie wäre es mit einem C-Buch?" gibt doch konstruktive
Beispiele bzw. Hilfe oder lass es komplett bleiben! - Danke!
Schöne Grüße!
Zeig mal dein Program.
Irgendwas hast du da gaenzlich daneben programmiert
oder ich versteh noch nicht, bzw. hab noch nicht richtig
erraten wie dein Pgm-Aufbau ist.
> konstruktive> Beispiele bzw. Hilfe oder lass es komplett bleiben! - Danke!
Ich denke ich hab schon mehr als einmal bewiesen, dass ich
sehr wohl konstruktiv arbeiten kann und das nicht mal
so schlecht.
Hingegen hast du in diesem Beitrag bewiesen, dass du immer
noch nicht gelernt hast, dass man wenn man ein Problem in
einem Programm hat, man dieses Problem immer mit realem
Code illustriert.
@Ich kenn dich nicht persönlich und kenn (jedenfalls bewusst) auch
keinen anderen konstruktiven beitrag von Dir. Ich habe nur deinen
Beitrag hier gelesen und dazu passend geantwortet. Aber nix für ungut..
;)
Den kompletten Code zu Posten wäre ein bischen viel.
Ich poste hier mal die relevanten Stellen für LED-Ansteuerung:
main.h:
1
unsigned char def_led[28]; //Puffer für LED-Status
Nun rufe ich hald in einer ganz normalen Funktion z.B.
1
LED_D001_RT;
2
A6818_LED_set(def_led);
auf - sonst nix!
Und mit jeder zusätzlichen Funktion steigert sich reproduzierbar der
RAM-Verbrauch um genau 48Byte (20Byte für Relais, 28Byte für die LEDs)
Gruß,
Techniker
externunsignedchardef_led[28];//Puffer für LED-Status
schreiben und den Speicher in exakt einem Source-File definieren
Bei Deiner Schreibweise wird der Speicher bei jedem Einbinden von
main.h erneut definiert (und nicht nur deklariert), was eigentlich auch
zu einer Linkerfehlermeldung führen sollte.
Hallo,
also wenn ich das richtig sehe, reservierst du in einem Header (main.h)
Speicher, so etwas tut man nicht, dieser Speicher wird nämlich jedes Mal
reserviert, wenn du den Header irgendwo einbindest, zusätzlich wird
jedes Mal ein Symbol erzeugt, der Linker müsste dir also auf die Füße
treten und dir sagen, dass da ein Symbol mehrfach definiert ist.
Zu Lösung des Problems:
main.h
1
extern unsigned char def_led[28];
main.c
1
unsigned char def_led[28];
ein Header legt nur die Schnittstelle fest, alles was später irgendwie
zu einem Symbol werden könnte, hat in einem Header nichts verloren!
Ciao, Fabian
@Fabian:
Danke für deinen Tipp!
Habs jetzt so gemacht, wie du es geschrieben hast. Lässt sich ohne
Fehler kompilieren und funktioniert auch. Nur am RAM-Verbrauch ändert
sich garnix.. :(
> jedes Mal ein Symbol erzeugt, der Linker müsste dir also auf die> Füße treten und dir sagen, dass da ein Symbol mehrfach definiert> ist
Hat nicht der gcc-Linker eine Erweiterung, die genau dieses
verhindert. Ich kann mich dunkel erinnern, dass der automatisch
mehrfach-Definitionen zu einer zusammen fasst.
@Techniker
Mit RAM: meinst du da SRAM oder Flash?
Bei SRAM: das kann nicht sein. Das Array existiert
jetzt (mit der extern Erweiterung) mit Sicherheit nur
ein einziges mal. Da muss noch was anderes sein.
@Stefan:
Hab mir beide Files angeschaut, kann damit aber nicht wirklich was
anfangen.. ;)
Welcher bereicht ist denn im MAP-File interessant, dann poste ich den
mal.
Gruß,
Techniker
Ich habe deinen Beispielcode soweit nachgestellt, bis er kompiliert
werden kann. Und dabei die Maßnahmen der anderen Antworten
berücksichtigt.
Zum Kompilieren wurde die Toolchain WinAVR 4.1.0 verwendet. Natürlich
kommt kein lauffähiges Programm heraus, aber darum geht es ja nicht.
Bei einer oder drei "LED-Funktionen" kommt kein erhöhter
Speicherbedarf zustande. Es werden immer 28 Bytes im BSS Bereich
angelegt (s. Ausgabe beim Make) und exakt ein Symbol def_led (s.
main.sym)
Allerdings habe ich einige Makros nur als Dummy drin, weil du dafür
nichts angegeben hast. Es kann mit geringer Wahrscheinlichkeit sein,
dass in den Makros Speicher angelegt wird
Ich würde vorschlagen, dass du dieses Projekt soweit in Richtung deines
Codes abänderst, bis der Fehler reproduzierbar ist.
@Stefan:
Danke, werde ich heute Nachmittag mal ausprobieren.
Nochwas: Warum ist in der main.c nicht die main.h include'd?
Ist das bei WinAVR 4.1.0 nicht mehr nötig? (arbeite noch mit 3.4.3)
Gruß,
Techniker
Bei diesem Projekt ist es egal, ob dir main.h in main.c included wird
oder nicht.
Ich habe es rausgelassen, weil darin nur das externe Array def_led
bekannt gemacht wird und main.c dieses Symbol schon kennt.
Was hat er nicht gebracht?
Das Array def_led in deinem Programm ist exakt 0x1c = 28 Bytes gross.
Es ist genauso wie in meinem Code nicht der Übeltäter.
Fetten DATA Speicher belegt pruefschritte.o. Dort würde ich die Suche
beginnen. Leider sind keine Symbole von diesem File erkennbar.
Möglicherweise handelt es sich um initialisierte static Variablen.
Hallo Stefan!
O-Dateien kann man nicht leserlich öffen, oder? ;)
Als Static ist darin auch nix definiert. Kann ich irgendwie rausfinden,
wer da soviel Speicher frisst?
In der Datei sind hald die ganzen Prüfschriit-Funktionen, die jede auf
die beiden Arrays zugreifen. Kommentiere ich in einer Funktion die
Aufrufe, welche mit den Arrays arbeiten aus, so verkleinert sich der
RAM-Verbrauch um jeweils 48Byte..?!? :-o
Irgendwas läuft da extrem schief.. :-/
Sowas hatte ich bisher auch noch nicht! Und dazu funktioniert
komischerweise auch alles bestens! (Nur der RAM geht mir hald aus..)
Ja, dann mach doch endlich mal Butter bei die Fische und zeige, was eine
typ. Prüfroutine macht oder checke das selbst ab, wenn du den Code nicht
öffentlich zeigen willst.
Das Beispiel, dass du bisher gegeben hast, zeigt das Verhalten nicht,
ist allerdings auch nicht vollständig siehe meine Dummy-Defines. Daher
auch mein Vorschlag nach und nach, deinen Code in mein
unproblematisches Beispiel einzubauen.
Für mich bleibt nach allem der Schluss, dass die echten Defines einen
Murks bauen.
Nachtrag:
Du kannst *.o Dateien selbstverständlich disassemblieren. Ich glaube
OBJDUMP aus der GNU Toolchain macht das. Das ist allerdings in deinem
Stadium ziemlich heavy, es ist wesentlich einfacher den C-Quellcode zu
lesen.
@ der wahre
zuerst vorab, Karl kenne ich von der deutschen NG
und er hilft gerne weiter, vorausgesetzt das Problem
ist klar geschildert. Da dieses "problemschildern"
schwer ist (mir auch manchmal), so ist ab besten
den Code mitangeben (zuerst sinnvoll reduzieren)
und jetzt zu Fragen
nie Variablen in .h Datei deklarieren
wenn diese in zig .c Datien includiert wird, so wird
in jeder .o Datei Speicher dafür angelegt(oder vorgesehen).
extern char feld[];
zweitens, volatile braucht man, wenn man verhindern möchte
dass der Variablewert aus dem (CPU lokalem)Cache benutzt wird ..
gleichzeitig wo asnychron (zB aus Interrupt oder von anderer CPU)
die Variable im eigentlichem Variablen RAM verändert wurde.
wenn du dein Problem debuggen willst, so schaue dir
nm und "objdump -d" an
ansonsten kann ich mir nicht vorstellen, dass der Zugriff
alleine zur Speicherduplikation führen soll
Gruss, Daniel
@kbucheg:
> Hat nicht der gcc-Linker eine Erweiterung, die genau dieses> verhindert. Ich kann mich dunkel erinnern, dass der automatisch> mehrfach-Definitionen zu einer zusammen fasst.
das macht der Linker aber nur für sog. weak-Symbole - solche Symbole
erzeugt der Compiler, wenn er C++-Templates instantiiert, für normale
Symbole gilt dies nicht, wenn das so wäre - auweia, in 2 Modulen wird
versehentlich dieeselbe globale Variable deklariert, welche Version
verwendet man den nun gerade => das muss einen Fehler geben
Ciao, Fabian
Ich halte das für einen philosophischen Fehler im Compiler. Dadurch wird
schlampiger und schlechter Programmierstil unterstützt und die diversen
Anfänger kapieren nie, was der Unterschied zwischen Deklaration und
Definition von Variablen ist.
Aber philosophische Diskussionen sind müßig - genauso wie die über die
auf Windows-Systemen fehlerhafte (sprich: nicht zulässige)
Unterscheidung zwischen Groß- und Kleinschreibung von Dateinamen durch
make oder ähnliche Tools.
@OldBug,kbucheg:
sapperlot, das hätte ich jetzt nicht gedacht, vielleicht bin ich dafür
einfach zu gut erzogen ... ;-)
@Rufus:
da hast du wohl leider recht
@ Fabian Scheler
die weak Symbole sind mir schon früher begegnet
ich hab grob (ganz grog) kapiert was sie bewirken
Du scheinst Dich mehr damit beschäftigt zu haben
Hast du paar Geheimquellen die Du vielleicht als Link
angeben könntest?
nm ist mir viel zu knapp in der Beschreibung
ausserdem fehlen da jegliche Beispiele (für Programmierer)
Wäre super nett.
Gruss, Daniel
@-daniel-:
sorry, aber einen besseren Link als die nm-manpage bzw. die gcc und ld
info-Page kann ich dir leider auch nicht geben. In der gcc info-Page
findet man gewissermaßen noch einen Verwendungshinweis: das
weak-Attribut kann verwendet werden, wenn der Benutzer
Bibliotheksfunktionen überschreiben können soll, vielleicht erhellt das
den Sachverhalt etwas?
Wo wünscht du dir in der nm-manpage noch mehr Info? Vielleicht kann ich
ja eine gezielte Frage beantworten?
Ciao, Fabian
Ja, weak ist dafür da, um bestimmte Symbole in einer Bibliothek
vorgeben zu können, sie dennoch vom Nutzer überschreibbar zu halten.
Ein typischer Fall dafür bei der avr-libc ist die Interrupt-Vektor-
Tabelle: der Startup-Code definiert eine vollständige Tabelle, in der
als Ziele __vector_1 ... __vector_N definiert sind und mittels .weak
jeweils auf das Symbol __bad_interrupt gemappt werden.
Wenn nun ein Nutzer seinen eigenen Vektor einbinden will, definiert er
selbst das Symbol __vector_M. Da das in der Bibliothek als "weak"
markiert ist, gibt es keinen Linkerfehler über eine doppelte
Symboldefinition, die es ohne .weak geben würde.
***
Das Überlagern der Common-Blöcke ist übrigens ausdrücklich im
C-Standard als eine der Möglichkeiten zur Behandlung globaler
Variablen genannt (wenn ich mich recht entsinne, wird es als das
ursprünglich von K&R implementierte Konzept beschrieben). Wenn man es
im GCC abschalten will, kann man das mit -fno-common tun.
Wollt bloß schnell berichten, dass ich nun den Code nach Stefan's
Hinweise umgeschrieben habe und nun ist der Fehler weg! :-D
-> Speicherverbrauch (RAM) unter 5%! ;)
(Flash ist zu 78% ausgelastst)
(Wobei mich immer noch interessieren würde, was es genau war.. :-/ )
Vielen Dank an alle! :)