Forum: Mikrocontroller und Digitale Elektronik Flash Speicher sparen


von Christian F. (cmf) Benutzerseite


Lesenswert?

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
1
MAIN:
2
...
3
PORTC |= (1 << PC5);
4
...
5
PORTC |= (1 << PC5);
6
...
7
PORTC |= (1 << PC5);
8
...
9
PORTC |= (1 << PC5);

B
1
void schaltePortC(void)
2
{
3
    PORTC |= (1 << PC5);
4
}
5
...
6
MAIN:
7
...
8
schaltePortC();
9
...
10
schaltePortC();
11
...
12
schaltePortC();
13
...
14
schaltePortC();

A oder B?

Danke, cmf

von Wegstaben V. (wegstabenverbuchsler)


Lesenswert?

was hindert dich daran, andere Teile deines Programmes temporär 
rauszuwerfen, dann beide oben genannten Varianten einzubinden, und zu 
sehen was größer wird?

von Detlev T. (detlevt)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

In Mapfile reinsehen und die Klöpse suchen. Dort optimieren. Wenns zu 
aufwendig wird: Pinout/Gehäuse vom Mega8 ist bis 32KB Flash ausbaufähig.

von Helfer (Gast)


Lesenswert?

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?

von (prx) A. K. (prx)


Lesenswert?

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, ...).

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Christian F. schrieb:
> Nun versuche ich das ganze etwas kleiner zu machen.
Zeig doch mal deinen Code...

von Christian F. (cmf) Benutzerseite


Angehängte Dateien:

Lesenswert?

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...

von Marcus O. (marcus6100)


Lesenswert?

1
void Schattenbahnhofsteuerung(void)
2
{
3
  int dreigleiseinfahrt = 0, einfahrtsgleis = 0, gleiswendelausfahrt = 0;
4
  int ees = 0, ehs = 0, egg = 0, egl = 0, egr = 0, egm = 0, egw = 0;
5
  int dreigleisausfahrt = 0, dreigleisausfahrt2 = 0;
6
  int *pointeraufdreigleisausfahrtsgleis = NULL;
7
  int eesold = 1, ehsold = 1, eggold = 1, eglold = 1, egrold = 1, egmold = 1, egwold = 1;
8
  int eescounter = 0, ehscounter = 0, eggcounter = 0, eglcounter = 0, egrcounter = 0, egmcounter = 0, egwcounter = 0;
9
  int signalcounter = 0;

Wenn du mit 7 oder 8 bit auskommst nimm int8_t oder uint8_t anstatt int,
das spart ca 50% flash und ram.

von Christian F. (cmf) Benutzerseite


Lesenswert?

Ja danke, war immer zu faul darauf zu achten. Ich probiere es gleich mal 
aus.

von Karl H. (kbuchegg)


Lesenswert?

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

von Christian F. (cmf) Benutzerseite


Lesenswert?

Ja genial!

Von 6,351 auf 4,698. Ich hatte das einfach unterschätzt. Jetzt passt ja 
die Kommunikation mit dem Computer noch locker rauf! Danke!

von Christian F. (cmf) Benutzerseite


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Thomas E. (thomase)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@  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

von Christian F. (cmf) Benutzerseite


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@  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

von Christian F. (cmf) Benutzerseite


Lesenswert?

Dutzende?? Ich habe 3 Stück im richtigen Programm + 3 Stück in Debug 
Funktionen.

AVR-Studio und Linux vertragen sich nicht so...

von Lukas K. (carrotindustries)


Lesenswert?

Christian F. schrieb:
> AVR-Studio und Linux vertragen sich nicht so...

avr-size main.elf

von Christian F. (cmf) Benutzerseite


Lesenswert?

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!

von Karl H. (kbuchegg)


Lesenswert?

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.

von Christian F. (cmf) Benutzerseite


Lesenswert?

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.

von Christian F. (cmf) Benutzerseite


Angehängte Dateien:

Lesenswert?

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.
1
//DIESE FUNKTION BRINGT DEN µC zum ABSTURZ
2
void uartSendStep(const int teil, const int step)
3
{
4
  uart_puts("\n======Teil "); uart_puti(teil); uart_puts("======\n");
5
  if (teil == 2 || teil == 3)
6
  {
7
    switch (step)
8
    {
9
    case 1:
10
      uart_puts("Zielabschnitt besetzt\n");
11
      uart_puts("   Startabschnitt aus\n");
12
      break;
13
    case 2:
14
      uart_puts("Zielabschnitt freigeworden und Zug in Startabschnitt\n");
15
      uart_puts("   Startabschnitt an\n");
16
      break;
17
    case 3:
18
      uart_puts("Startabschnitt freigeworden\n");
19
      uart_puts("   Startabschnitt aus\n");
20
      break;
21
    default:
22
      break;
23
    }
24
  }
25
  uart_puts("====================\n");
26
}

Daten:
mega8
kein externer Quarz
Baud Rate: 4800
UART Bibliothek von Peter Fleury

Der UART funktioniert sonst wunderbar (siehe z.B. uartSendInit())

von holger (Gast)


Lesenswert?

Nimm mal uart_puts_P() statt uart_puts().

von Christian F. (cmf) Benutzerseite


Lesenswert?

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.

von Christian F. (cmf) Benutzerseite


Angehängte Dateien:

Lesenswert?

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?

von (prx) A. K. (prx)


Lesenswert?

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.

von Christian F. (cmf) Benutzerseite


Lesenswert?

Ich probiers mal aus.

von (prx) A. K. (prx)


Lesenswert?

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.

von Christian F. (cmf) Benutzerseite


Lesenswert?

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
1
void uart_puts_p(const char *progmem_s )
2
{
3
    register char c;
4
    
5
    while ( (c = pgm_read_byte(progmem_s++)) ) 
6
      uart_putc(c);
7
8
}/* uart_puts_p */

von Christian F. (cmf) Benutzerseite


Lesenswert?

Oh. Hätte ich selber drauf kommen können... Nochmal Danke.

von (prx) A. K. (prx)


Lesenswert?

Probiers mal so: #define uart_puts_P(s) uart_puts_p(PSTR(s))
(diesmal wars daneben geklickt...)

von Christian F. (cmf) Benutzerseite


Lesenswert?

Nochmal ne Frage:

Ich habe ja eine Endlosschleife und am Ende ein _delay_ms(), siehe 
Statemachine.
1
MAIN:
2
for(;;)
3
{
4
  (PROGRAMM)
5
  _delay_ms(500);
6
}
Ist es nun schöner, besser, ordentlicher das so zu machen:
1
volatile uint8_t durchlauf = 0;
2
ISR(TIMEROVERFLOW) //Alle 500ms
3
{
4
  durchlauf = 1;
5
}
6
7
MAIN:
8
for(;;)
9
{
10
  if(durchlauf == 1)
11
  {
12
    durchlauf = 0;
13
    (PROGRAMM)
14
  }
15
}

cmf

von Falk B. (falk)


Lesenswert?

@Christian F. (cmf)

>Ist es nun schöner, besser, ordentlicher das so zu machen:

Ja.

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
Noch kein Account? Hier anmelden.