Hallo,
ich habe einen mega8 und mein Programm ist leider fast 8kb groß. Nun
versuche ich das ganze etwas kleiner zu machen. Dazu eine
Verständnisfrage.
Was verbraucht weniger Speicher
A
was hindert dich daran, andere Teile deines Programmes temporär
rauszuwerfen, dann beide oben genannten Varianten einzubinden, und zu
sehen was größer wird?
Hast du die Optimierung eingeschaltet? (-os)
Die kürzeste Version müsste A) sein, weil der Compiler daraus ein sbi =
1 Word machen sollte. Kürzer geht nicht.
Christian F. schrieb:> Nun> versuche ich das ganze etwas kleiner zu machen.
Dann suche erstmal die Routinen, die viel Flash belegen.
Ein simples SBI kanns ja auf keinen Fall sein.
Ich hoffe mal nicht, daß Du Spaghettikode schreibst mit 1000-mal SBI:
1
PORTC|=(1<<PC5);
Dann wäre nämlich Dein Programmkonzept Dein größter Feind.
Peter
Christian F. schrieb:> A oder B?
Ein halbwegs gut optimierender Compiler wird für beide Varianten den
selben Code erzeugen. Schau dir besser mal das Listing an und versuche
abzuschätzen, wo du die grössten Speicherfresser hast. Verwendest du
printf? Arbeitest du mit Fliesskomma-Zahlen?
Im Mapfile finden sich vielleicht auch ein paar Funktionen, die man
eigentlich überhaupt nicht haben wollte, die aber aus einem dummen
Zufall heraus doch am Hals hat (32/64bit Rechnung, Fliesskomma, ...).
Vielen Dank für die Antworten!
Ich kann meinen Code gerne mal zeigen :) Zur Erklärung: Es handelt sich
um einen Schattenbahnhofsteuerung für die Modellbahn, die aber BEI
WEITEM noch nicht fertig + optimiert ist. Außerdem ist sie sehr sparsam
kommentiert, USART fehlt noch und vor allem ist sie erstmal
unverständlich. Trotzdem kann ich den Code gerne mal posten.
In den nächsten Tagen, wenn ich den MSA hinter mir habe, werde ich mich
mal dransetzen und alles kommentieren. Man beachte, dass ich da
(natürlich mit Pausen) seit 1 Jahr dran sitze und die Syntax deshalb
noch nicht wirklich toll ist.
Edit: Ja die Variablennamen sind nicht die besten...
Aus dem Bauch heraus können das aber keine 8KB sein. Bist du sicher,
dass du die Optimierung eingeschaltet hast?
Was mir aufgefallen ist: Du hast einige Variablen, in einer Funktion
sogar haufenweise davon, die du alle als 'int' definiert hast. int hat
auf deinem µC 16 Bit, d.h. du wingst deinem µC 16-Bit Aithmtik auf, die
du gar nicht brauchst! Auch das ist
a) Code und
b) Laufzeit
die du unnötig verschenkst.
Als Faustregel.
Verwende auf einem AVR nicht einfach nur int.
Wenn du
* einen kleinen Ganzzahltyp brauchst, ohne Vorzeichen, so dass
du mit dem Wertebereich 0 bis 255 locker über die Runden kommst,
dann nimm uint8_t als Datentyp
* dasselbe, nur diesmal mit Vorzeichen, dann ist int8_t der
Datentyp deiner Wahl
* Wenn deine Zahlen dann doch mal größer werden, dann nimmst du
int16_t (mit Vorzeichen, also signed) bzw. uint16_t (ohne
Vorzeichen, also unsigned).
* und wenns noch größer wird, dann eben int32_t bzw. uint32_t
Alleine dadurch, dass du die 16-Bit Arithmetik erst mal auf 8 Bit
zurückstutzt, und unsigned benutzt wo du nur kannst (also einen uint8_t
benutzt), wird schon einiges an Code wegfallen.
Edith: zu langsam
Karl Heinz Buchegger schrieb:> Aus dem Bauch heraus können das aber keine 8KB sein. Bist du sicher,> dass du die Optimierung eingeschaltet hast?
Optimierung ist eingeschaltet (-Os). Ok, es waren nicht ganz 8kB.
Christian F. schrieb:> In den nächsten Tagen, wenn ich den MSA hinter mir habe, werde ich mich> mal dransetzen und alles kommentieren.
Ganz ehrlich?
So wie der Code aussieht:
Setz dich hin, nimm alles zusammen, was du in diesem Jahr gelernt hast
(auch über das Problem gelernt) und mach dir ein neues Konzept auf dem
Papier. Dann schreib ein neues Programm. Wirst sehen, das dauert auch
nicht viel länger als wenn du das Vorhandene umarbeitest und dafür hast
du dann etwas tragfähiges für die nächste Zeit.
Das dein erstes Programm nicht wirklich das Nonplusultra ist, ist schon
klar. Das wäre ein Wunder, wenn es so wäre. Es braucht viel Übung, bis
man auf Anhieb ein gutes Programmkonzept findet, welches auch Bestand
hat. Von daher ist es nicht so schlimm, wenn man die Erstversion
verwirft und mit dem Gelernten an eine neue Version geht, auch wenn es
auf den ersten Blick schmerzt.
Christian F. schrieb:> Optimierung ist eingeschaltet (-Os). Ok, es waren nicht ganz 8kB.
Also bei mir sind das ohne Optimierung:
Program: 6786 bytes (82.8% Full)
(.text + .data + .bootloader)
Data: 18 bytes (1.8% Full)
(.data + .bss + .noinit)
und mit -0s:
Program: 2246 bytes (27.4% Full)
(.text + .data + .bootloader)
Data: 10 bytes (1.0% Full)
(.data + .bss + .noinit)
mfg.
@ Christian F. (cmf)
>ich habe einen mega8 und mein Programm ist leider fast 8kb groß.> * main.c (12,5 KB, 6 Downloads) | Codeansicht
Wo sollen DAS 8kB werden? Niemals. Oder ist dein .hex File fast 8kB
groß? Das wäre real eher 3kB, denn .hex ist Intel-HEX, da wird alles per
ASCII +bissel Adressierung dargestellt, und ist ~2,5mal so groß wie die
echten, binären Daten.
>Nun versuche ich das ganze etwas kleiner zu machen.
Dazu sollte man andere Konzepte nutzen, und zwar allgemein als auch
bezüglich C. Siehe statemachine. Damit schrumpft der
Speicherverbrauch ganz ordentlich, ausserdem muss man nicht soviel
tippen.
MFG
Falk
Falk Brunner schrieb:> @ Christian F. (cmf)>>>ich habe einen mega8 und mein Programm ist leider fast 8kb groß.>>> * main.c (12,5 KB, 6 Downloads) | Codeansicht>> Wo sollen DAS 8kB werden? Niemals. Oder ist dein .hex File fast 8kB> groß? Das wäre real eher 3kB, denn .hex ist Intel-HEX, da wird alles per> ASCII +bissel Adressierung dargestellt, und ist ~2,5mal so groß wie die> echten, binären Daten.
Ups. Ich habe natürlich immer das HEX File angesehen. Danke! (Woher
sollte man das wissen?)
>>Nun versuche ich das ganze etwas kleiner zu machen.>> Dazu sollte man andere Konzepte nutzen, und zwar allgemein als auch> bezüglich C. Siehe statemachine. Damit schrumpft der> Speicherverbrauch ganz ordentlich, ausserdem muss man nicht soviel> tippen.>> MFG> Falk
Habe ich mal kruz überflogen, mache ich das nicht in etwa so? Ich habe
eine while Schleife, darin 3 große Programmteile (1ANFANG, 1ENDE, ...)
Die haben jeweils 3 bis 4 Zustände, also wie bei der Ampel rot, gelb,
grün, rot, gelb ... Den aktuellen Zustand speichere ich in einer
Variable, damit er in der While Schleife nicht "verloren geht". So ist
das doch auch bei der Statemachine, oder?
>> P S Aha, Problem gefunden.>>
1
>sleep(schaltzeitWeiche);
2
>
>> Macht man so nicht. Siehe>> http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Warteschleifen_.28delay.h.29>> Ändere es ab und staune.
Ja, den Teil habe ich schon gelesen und das gehört natürlich nicht in
eine Statemachine, da das ganze so nicht mehr paralel läuft. Aber ich
hatte eben noch keine Lust, mich deshalb mit Interrupts (oder?)
rumzuschlagen. Aber ich sehe schon, dass sollte man mal machen.
@ Christian F. (cmf)
>Ups. Ich habe natürlich immer das HEX File angesehen. Danke! (Woher>sollte man das wissen?)
Grundlagen. Ausserdem zeigt dir AVR-Studio nach dem Compilieren den
Speicherbedarf direkt an.
>Habe ich mal kruz überflogen, mache ich das nicht in etwa so?
Nur in etwa ;-)
>> P S Aha, Problem gefunden.>> sleep(schaltzeitWeiche);>> Macht man so nicht. Siehe>> http://www.mikrocontroller.net/articles/AVR-GCC-Tu...
Falsche Fährte. schaltzeitWeiche ist ein #define Konstante und damit
unkritisch. ich dachte erst, es wäre eine echte Variable. Dennoch sind
dein Dutzenden Sleeps nicht so doll.
MFG
Falk
Nun mal eine genauere Erkärung, wieso der Code so lang ist. Ich habe im
Hauptteil 3 Abschnitte, die praktisch alle fast das selbe tun, nämlich
Züge von Abschnitt A nach B bringen. Wieso sind sie dann so
unterschiedlich lang? Das liegt daran, weil mein Gleisplan in etwa so
aussieht:
-->--ehs------->----->--egl---\
\->--egm------->---egg--->--ees------>----
/------>--egr---/
/
Beispielhaft mal der Code von egg nach ees.
1
//1ANFANG
2
/*
3
Dieser Programmteil sorgt dafür, dass Züge vom Abschnitt egg zum Abschnitt ees fahren
4
5
In egg steht 0, wenn der Abschnitt frei ist, genauso in ees, wenn ees frei ist
6
7
---->--egg---->---->-----ees----->----->
8
9
Mittels GG schalte ich den Abschnitt egg ein, mittels GGOFF aus.
10
11
*/
12
13
14
15
if(ees==0)// 1: Zielabschnitt besetzt
16
{
17
dummy=zufall(3);//Dient dazu, den Zufall, der weiter unten benutzt wird, etwas "zufälliger" zu machen
18
GGOFF;
19
gleiswendelausfahrt=0;//Nächster Schritt
20
}
21
if((((ees!=0)&&(gleiswendelausfahrt==0))&&(egg==0)))// 2: Zielabschnitt freigeworden + Zug in Abschnitt egg
22
{
23
GG;//Lasse Zug aus egg ausfahren
24
gleiswendelausfahrt=1;//Nächster Schritt
25
dummy=zufall(3);
26
}
27
if((egg!=0)&&(gleiswendelausfahrt==1))
28
GGOFF;// 3 Zug aus Abschnitt ausgefahren, deshalb Abschnitt abschalten
29
//1ENDE
Das ist ja gar nicht so lang. Aber es wird eben bedeutend länger, wenn
die Züge von egl/egm/egr nach egg müssen. Dann muss geprüft werde, wo
ein Zug steht und zufällig einer ausgewählt werden der dann losgeschickt
wird.
Danke nochmals!
Christian F. schrieb:> Das ist ja gar nicht so lang. Aber es wird eben bedeutend länger, wenn> die Züge von egl/egm/egr nach egg müssen.
Denk mal darüber nach, ob man das alles nicht auch durch Daten (zb in
einem oder mehreren Arrays) beschreiben kann.
Naiv wie ich bin, besteht doch eine Fahrstrasse immer aus diesen
Komponenten:
einem Anfangsgleis, einem Endgleis und Zwischenstücken.
Eine Fahrstrasse kann nur dann befahren werden, wenn das Endgleis samt
allen Zwischenstücken frei ist. Oder so ähnlich.
Diese allgemeinen Gedanken würd ich mir erst mal machen und versuchen
die in ein allgemeines Schema zu pressen, welches rein nur durch Daten
beschrieben werden kann, die variabel sind und auf deine spezielle
Situation im Schattenbahnhof zurechtgeschnitten sind.
Karl Heinz Buchegger schrieb:> Christian F. schrieb:>>> Das ist ja gar nicht so lang. Aber es wird eben bedeutend länger, wenn>> die Züge von egl/egm/egr nach egg müssen.>> Denk mal darüber nach, ob man das alles nicht auch durch Daten (zb in> einem oder mehreren Arrays) beschreiben kann.>> Naiv wie ich bin, besteht doch eine Fahrstrasse immer aus diesen> Komponenten:>> einem Anfangsgleis, einem Endgleis und Zwischenstücken.> Eine Fahrstrasse kann nur dann befahren werden, wenn das Endgleis samt> allen Zwischenstücken frei ist. Oder so ähnlich.
Ja, fast richtig. Nur bei mir gibt es keine Zwischenstücke, das wäre zu
aufwendig. Deshalb muss ich mir in einer Variablen (im Beispiel
"gleiswendelausfahrt") merken, dass sich ein Zug zwischen den beiden
Abschnitten befindet.
> Diese allgemeinen Gedanken würd ich mir erst mal machen und versuchen> die in ein allgemeines Schema zu pressen, welches rein nur durch Daten> beschrieben werden kann, die variabel sind und auf deine spezielle> Situation im Schattenbahnhof zurechtgeschnitten sind.
Eigentlich lautet mein Schema, dass ich mir dazu überlegt habe so:
// 1: Warte bis Zielabschnitt besetzt (also Zug nicht auf freier Strecke
liegengeblieben ist)
//Nächster Schritt
// 2: Warte bis Zielabschnitt freigeworden + Zug in Startabschnitt steht
//Lasse Zug aus Startabschnitt ausfahren
//Nächster Schritt
// 3 Wenn Zug aus Startabschnitt ausgefahren ist
//Startabschnitt abschalten
//Wieder von vorne
Ich denke, ich werde mal versuchen, dafür eine allgemeine Funktion zu
schreiben. Mein Problem dabei war, dass ich als Startgleis in einem Fall
nur 1 Variable übergeben müsste (von egg -> ees) und im anderen Fall 3
Variablen (von egl/egr/egm -> egg). Mir kam nicht der Gedanke, Arrays zu
verwenden. Leider kenne ich mich mit den Dingern nicht so gut aus, habe
dafür aber schon so einige Erfahrung unter PHP mit Arrays. Also gut, ich
werde es in den nächsten Tagen mal umbauen.
Hallo,
ich bin gerade dabei, das Programm zu überarbeiten. Dabei stoße ich auf
folgendes Problem:
Wenn ich das Folgende einkommentiere, stürzt das Programm beim Aufruf
ab.
Danke erstmal für die Antwort. Nach einem Austausch sämtlicher uart_puts
durch uart_puts_p stürzt das Programm immer noch ab, außerdem erhalte
ich nur noch kryptische Zeichen.
Hä? Verstehe ich gerade nicht. Im Anhang mal der Code mit uart_puts_p.
(Rekursion heißt ja, eine Funktion ruft sich immer wieder selbst auf. Wo
tue ich das denn?)
Edit: Jetzt ist der Beitrag weg? Wars also doch Quatsch?
Sorry, war ein Schnellschuss, basierend auf dem Namen.
Aber was uart_puts_p angeht: Die Strings müssen zum Parameter passend in
ROM gelegt werden. PSTR("xxx") statt "xxx" könnte notwendig sein.
Manchmal gibt es Versionen _p ohne und _P mit automatischem PSTR. Doku
lesen.
A. K. schrieb:> uart_puts_p in uart_puti ist natürlich Käse, denn da gehts ja nicht um> einen String im ROM, sondern um Daten im RAM.
Ja ist es. Danke.
So funktioniert es jetzt:
1
uart_puts_p(PSTR("bla"));
uart_puts_P gibt es nicht (Also mit automatischem PSTR). Das wollte ich
mir jetzt selber schreiben. Aber wie geht das?
So sieht uart_puts_p aus