Forum: Mikrocontroller und Digitale Elektronik Code Reduzieren


von Sascha H. (Gast)


Lesenswert?

Hi @ll,

ich bin grade dabei für mein Praktikum (Studium) den letzten C164
Versuch zu programmieren. Leider haben wir von Keil µVision nur
Evaluations Versionen und diese haben eine Beschränkung der Codelänge
auf 8KB.

Daher die Frage, wo kann man effektiv den Code verkürzen. Was sich
schon gut gemacht hat, war alle strncpy durch for-Schleifen zu
ersetzen. Außerdem lasse ich den Compiler schon auf Größe optimieren
[#pragma ot(7, size)].

Hat noch jemand Ideen für typische Konstrukte die man verkürzen kann.

Ich will den ganzen Code jetzt nicht posten, der wäre etwas zu lang.

Sascha

von Thorsten (Gast)


Lesenswert?

hmm deinen Code zu kürzen ohne den zu kennen ist natürlich etwas
schwierig.
Du kannst alles kürzen was du kürzer schreiben kannst!

von Sascha H. (Gast)


Lesenswert?

Was auch noch ganz gut klappt, möglichst viele Variablen in den
near-Speierbereich legen. Um in far zu schreiben/lesen sind ja ein paar
Maschienenbefehle mehr nötig.

von Richard (Gast)


Lesenswert?

Hinter vorgehaltener Hand: Wenn genug RAM, dann statt lokaler Variablen
möglichst globale Variablen verwenden, denn dann entfällt das gepushe
und gepoppe.

von Sascha H. (Gast)


Lesenswert?

Die Idee mit den globalen Variablen ist auch gut. Ich denke da muss man
keine Hand vorhalten.

Außerdem habe ich bemerkt, dass die Variante A mehr Code braucht, als
die Variante B:

A:
UmdrehTime = UmdrehTime + ( (float) drehBuffUeber[i] * ( (float) 0xFFFF
) + (float) drehBuff[i] )* T8SETING;

B:
UmdrehTime = UmdrehTime + (float) drehBuffUeber[i] * ( (float) 0xFFFF )
* T8SETING;
UmdrehTime = UmdrehTime + (float) drehBuff[i] * T8SETING;

von Gernot F. (gernotfrisch)


Lesenswert?

Variablen wo's geht wiederverwenden.
Funktionen statt Makros.
Überlegen, ob eine Funktion über ein zusätzliches "Steuerbit" nicht 2
oder 3 Funktionen ersetzen kann usw...
Wenn man z.B. über ein Feld von XY immer wider verschiedene Funktionen
aufruft, kann man auch eine Funktion:
void ForEach(XY* pFirst, XY* pEnd, void (*foo)(XY* pA, XY* pB) );
machen, die dann die Schleife aufruft und dabei jedes Element mit
seinem Nachbarn einsetzt.
Beispiel (ungetestet)
1
void Drucken(XY* pA)
2
{
3
   LCD_out(pA->text);
4
}
5
6
void ForEach(XY* pFirst, XY* pEnd, void (*foo)(XY* pA) )
7
{
8
 XY* pA;
9
 for(pA=pFirst; pA!=pEnd; ++pA)
10
 {
11
      foo(pA);
12
 }
13
}
14
15
int main(int, char**)
16
{
17
  ...
18
  // pEnd = 1 nach dem letzen Element
19
  ForEach(sXY, sXY+xycount, Drucken);
20
}

von Ronny (Gast)


Lesenswert?

Keine Format-Strings für printf verwenden und wenn´s doch nich anders
geht wenigstens auf Fliesskomma dabei verzichten.Man kann z.B auch eine
Fliesskommazahl in 2 Integer verwandeln und die dann formatiert
ausgeben.Bei einigen Compilern kann man das in den Compiler/Linker
Einstellungen angeben,das z.B keine Fliesskomma-Unterstützung erwünscht
ist.

von Peter D. (peda)


Lesenswert?

"Die Idee mit den globalen Variablen ist auch gut."

Genau das Gegenteil ist der Fall !

Lokale Variablen können oft in Registern gehalten werden und sind daher
viel effizienter, sowohl vom Code-Verbrauch, als auch von der
Geschwindigkeit.

Ich kenne den C164 nicht, aber dann muß der schon sehr vekorkst sein,
wenns bei dem wirklich andersrum wäre.


Sehr viel Code kann man auch sparen, wenn man statt float, int oder
long nimmt. Da sind durchaus 1..2kB Ersparnis drin.


Peter

von HAL9000 (Gast)


Lesenswert?

In Funktionsaufrufen kann man manchmal sparen (zumindest beim 8051)
wenn ne funktion wenig Inhalt, aber viele Parameter hat, lieber den
inhalt direkt an der Aufrufstelle reinschreiben, als eine funktion zu
benutzen. Das Stack-geschiebe braucht schließlich viel platz.

Ist unsauber, ich weiß, (genau wie global RAM geschichte) aber wenn mal
tatsächlich die paar wenige bytes speicher fehlen, um das programm
raufzubraten, macht mans lieber.

von Sascha H. (Gast)


Lesenswert?

Also das mit dem NEAR scheint nur in manchen Fällen was zu bringen.
Manchmal wächst der Code dadurch auch. In den meisten Fällen bleibt er
scheinbar gleich. Wobei ich das jetzt nur durch rumprobieren getestet
habe, den Assembler zu zerlegen war mir jetzt zu aufwändig.

Auf jeden Fall passt es jetzt. Ich glaube die beste Lösung war doch,
dass mit den globalen Variablen. Damit kann man ohne großen Aufwand
schon mal einiges an Code gut machen. Auf jedenfall bin ich jetzt klein
genug zum Kompilieren.

Sascha

von Stefan (Gast)


Lesenswert?

Wie ist die Beschränkung auf Codegrösse realisiert - über "Abzählen"
oder über eine Beschränkung des Adressraums oder über einen Offset der
Codeposition im Speicherraum...?

Ersteres ist nicht so kritisch und kann bei der Entwicklung
berücksichtigt werden. d.h. vielleicht kannst du grössere Funktionen
zusammenfassen und als getrenntes Stück in den µC laden...

Dazu musst du rausfinden, wie du eine Funktion an eine von dir
gewünschte Position im Speicher legst und den Linker überredest dafür
zu relozieren.

Im abgespeckten Restcode verwendest du die ausgelagerten Funktionen
über Funktionspointer.

Du solltest Ausschau nach Funktionen halten, die wenige bis keine
Libraryfunktionen benutzen. Ich würde mir eine Initfunktion in dem
ausgelagerten Block schreiben, die mir eine Tabelle mit den benötigten
Funktionspointern zusammenbaut.

Mit den Aufrufen von Libraryfunktionen im abgetrennten Codestück
verfährst du im Prinzip gleich. Du baust dir eine Tabelle mit den
benötigten Funktionspointern im Hauptprogramm.

von Wolfram (Gast)


Lesenswert?

Wenn das ganze ein Praktikum ist und davon die letzte Aufgabe wundert es
mich schon ein wenig, wenn du 8Kb sprengst. Denn dann haben ja wohl auch
andere Studenten das Problem bestimmt schon gehabt und den Prof.
informiert.
Wenn du die Aufgabe nicht nur lösen willst, sondern dabei auch was
lernen willst: Mach dir Gedanken über deinen Algorithmus und deine
Datenstrukturen, denn dies bestimmt zum größtenteil wie effektiv oder
auch klein dein Programm wird.
Ich finde es sehr verwunderlich das ein ersetzen von allen strncpy
durch for-Schleifen einen kleineren Code ergibt.
Lass dir mal die Mapping Datei ausgeben, da siehst du welche Funktionen
den grössten Platzbedarf haben.

von Mike (Gast)


Lesenswert?

Ab und zu ist das sogar Absicht mit der Codegröße. Bei unserem letzten
Praktikum war es auch nur ganz knapp möglich alle gewünschten
Funktionen zu realisieren bevor einem der Speicher ausgegangen ist. War
sozusagen eine Qualitätskontrolle :-)

von Sascha Haupt (Gast)


Lesenswert?

Praktikum sieht halt so aus, dass man von Versuch zu Versuch zusätzliche
Funktionen zum Programm hinzufügt. Ich bin auch nicht der einzige der
das Problem hatte.

Zu den globalen Variablen:
Also nach genauerem Testen kann ich sagen, dass man nicht pauschal
sagen  ob das mehr oder weniger Code gibt. Das scheit echt davon
abzuhängen ob er die irgendwann auf den Stack schreibt, oder ob er sie
in einem Register hält. -> Ausprobieren

Wie die Codegröße vom Linker limitiert wird weis ich nicht. Da habe ich
mir auch noch keinen Kopf drum gemacht.

Auf jeden Fall habe ich das ganze auf die Reihe bekommen und jetzt
funktioniert es.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Globale Variablen sollten ebenso wie statische Variablen nicht auf dem
Stack landen, das tun nur automatische Variablen.

von T.Stütz (Gast)


Lesenswert?

zu "Zu den globalen Variablen:
Also nach genauerem Testen kann ich sagen, dass man nicht pauschal
sagen  ob das mehr oder weniger Code gibt. Das scheit echt davon
abzuhängen ob er die irgendwann auf den Stack schreibt, oder ob er sie
in einem Register hält. -> Ausprobieren"

Datentyp "sdata" verwenden (sdata ist im interen RAM des C16x und
wird mit einem Wort-Befehl angesprochen)

Bsp: unsigned int sdata wWort;

Variablen die nur zwei Zustände haben (AN/AUS,WAHR/FALSCH etc) auch als
"bit" deklarieren (Wieder ein Wort-Befehl)

Bsp: bit bSearch = 1;

Innerhalb Prozeduren/Funktionen kann per "register" Variablen im
Register gehalten werden.

Bei Interrupts die viele Register verwenden ist es sinnvoller eine
ganze Registerbank zu verwenden

Bsp: void NMIInt (void) interrupt 0x02 using TRAP_REG {}

Gruss

von Robert Teufel (Gast)


Lesenswert?

Also wenn du mit float rechnest, dann hast du wohl schon verloren mit
der Eval Version. Vergiss LIB Aufrufe, unter keinen Umstaenden PRINTF
benuetzen. All die schoenen Dinge in "C" verbraten eine Unmenge
Speicher und somit Programmgroesse.

Robert

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.