mikrocontroller.net

Forum: Compiler & IDEs Bitte um Hilfe bei Code-Optimierung


Autor: Santiago m. H. (mausschubser)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

habe einen simplen Timer für meinen Belichter gebaut, der noch ein paar 
weiße LEDs spendiert bekam, um auch als Leuchtpult dienen zu können.
Als Display kommt eine Segmentanzeige von Pollin zum Einsatz (D120150), 
bei der die Segmente "gemultiplext" werden müssen (Ansteuerung über 3 
kaskadierte Schieberegister 74HC595 - ich habe den Schaltplan dem Archiv 
beigepackt).

Die Bedienung habe ich auf einem Mega mit LCD-Anzeige ausprobiert und 
wollte das Ganze jetzt auf einen ATtiny2313 bringen. Leider mit diesem 
Ergebnis:
AVR Memory Usage
----------------
Device: attiny2313

Program:    2104 bytes (102.7% Full)
(.text + .data + .bootloader)

Data:         30 bytes (23.4% Full)
(.data + .bss + .noinit)

Ich meine, schon speichersparend gearbeitet zu haben, aber vielleicht 
gibt es ja noch etwas Einsparpotential (hoffe ich mal).

Bin für jeden Tip dankbar.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie müsste ich das builden?

Wenn ich nach nix gehe und make mache, ist das .elf leer.

Autor: 123 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tip: lass dir ein map file ausgeben. Dann weißt du was, wieviel an Flash 
benötigt.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hier der log

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, hatte die falsche Toolchain (hasr Du nicht dazugeschrieben).

Die sdGenDisplay() ist sehr aufwändig durch die vielen case, die jedoch 
alle praktisch den gleichen Code enthalten. Und natürlich kostet 
32-Bit-Arithmetik.

Hier mal versuchen, die OR-Konstanten in ein Array zu packen und per 
Index (h0,h1,m0,m1) drauf zuzugreifen.

Was machen die switches? Bastelst Du dort die Segmente einer 
7-Segment-Anzeige zusammen?

In main() greifst Du sehr oft auf status zu. Da GCC die Adresse kennt, 
macht er direkte Zugriffe, von denen jeder 4 Byte kostet.

Schreib das mal indirekt, also
typeof (status) * s = & status;
...
s->work.mode = ...

GCC ist wahrscheinlich überschlau, trickst sich selber aus und macht 
dennoch direkte Zugriffe (er kennt die Adresse immer noch!). Also: Im 
Assembler-Out kontrollieren!

Falls da immer noch direkte Zugriffe stehen, kommt nach der Deklaration 
ein
asm ("":"+r"(s));
und der direkte Zugriff ist gegessen.

Ausserdem: oft verwendete Variablen wie .work.mode in lokalen Variablen 
halten und Lesen/Schreiben vor/nach der Änder-Sequenz.

Am besten stückchenweise vortasten.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Santiago m. H. wrote:
> Ich meine, schon speichersparend gearbeitet zu haben, aber vielleicht
> gibt es ja noch etwas Einsparpotential (hoffe ich mal).

Ja, die 7-Segment Ausgabe enthält noch viel Einsparpotential:

- Die 32Bit-Zugriffe sind nicht so der Brüller, da werden dann auch 
Bytes angefaßt, die garnicht verändert werden.
Besser ist ein Array aus 3 Byte.

- Die switch-Anweisung kann auch nicht gut optimieren.
Besser die Segmentmuster mit Index aus ner Tabelle holen.
Hier mal ein Beispiel:

Beitrag "ADC mit Multiplexanzeige"


Danach sollte alles reinpassen.


Peter

Autor: Santiago m. H. (mausschubser)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

herzlichen Dank für Eure Aufmerksamkeit und Unterstützung!

> Ok, hatte die falsche Toolchain (hasr Du nicht dazugeschrieben).

Sorry, ich kenne nur eine (avr-gcc). Was hätte ich dazu schreiben 
sollen?

> Hier mal versuchen, die OR-Konstanten in ein Array zu packen und per
> Index (h0,h1,m0,m1) drauf zuzugreifen.

Das war auch meine erste Idee. Habe die Funktion doppelt geschrieben und 
es stellte sich heraus, dass die Array-Variante über 200 Bytes mehr 
brauchte, abgesehen davon, dass der Speicher für das Array nicht 
ausreichte.

> Was machen die switches? Bastelst Du dort die Segmente einer
> 7-Segment-Anzeige zusammen?

Nein, die habe ich in der Header-Datei bereits zusammen gebaut. Das 
Display ist so organisiert, dass es 2 gemeinsame Kathoden-Stränge gibt. 
Dann sind die Anoden mancher Segmente auch zusammen gefasst ...
Mit den Switches baue ich die Bitfolgen für jeden Kathodenstrang 
zusammen, der dann an die Schieberegister übertragen wird.
Da jede Ziffer anders angeschlossen ist, kann ich die Ziffern nicht 
einheitlich verarbeiten.

> In main() greifst Du sehr oft auf status zu. Da GCC die Adresse kennt,
> macht er direkte Zugriffe, von denen jeder 4 Byte kostet.

Irgendwo las ich, dass es effektiver wäre, globale Variablen in einer 
Struktur zusammen zu fassen. Gilt die Aussage nicht für gcc?
Sollte ich besser ohne Struktur arbeiten?

Den Tip mit dem Zeiger statt der globalen Struktur verstehe ich noch 
nicht. Was wird besser, wenn ich einen Zeiger verwende?

> - Die switch-Anweisung kann auch nicht gut optimieren.
> Besser die Segmentmuster mit Index aus ner Tabelle holen.

Wie bereits geschrieben habe ich das ja bereits versucht.
Das ekelhafte ist, dass die Digits nicht gleich angesteuert werden 
können, d.h. ich brauche für jede Ziffer ein Array.
Ich habe die Verdrahtung des Displays mal angehängt.

Autor: Santiago m. H. (mausschubser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
erster Erfolg:

> Ausserdem: oft verwendete Variablen wie .work.mode in lokalen Variablen
> halten und Lesen/Schreiben vor/nach der Änder-Sequenz.

das hat schonmal 42 Byte (nur für mode) gebracht. Danke!

Autor: Santiago m. H. (mausschubser)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

habe die Array-Variante nochmal überarbeitet, habe sie aber immer noch 
nicht kleiner als die Switch-Version bekommen. Anbei mal die Sauce der 
Array-Variante.

Statistik der Varianten:
1. Switch-Variante:
AVR Memory Usage
----------------
Device: attiny2313

Program:    2062 bytes (100.7% Full)
(.text + .data + .bootloader)

Data:         30 bytes (23.4% Full)
(.data + .bss + .noinit)

2. Array-Variante:
AVR Memory Usage
----------------
Device: attiny2313

Program:    2196 bytes (107.2% Full)
(.text + .data + .bootloader)

Data:         30 bytes (23.4% Full)
(.data + .bss + .noinit)

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Santiago m. H. wrote:
> Hallo,
>
> herzlichen Dank für Eure Aufmerksamkeit und Unterstützung!
>
>> Ok, hatte die falsche Toolchain (hasr Du nicht dazugeschrieben).
>
> Sorry, ich kenne nur eine (avr-gcc). Was hätte ich dazu schreiben
> sollen?

Ich verwende die alte (3.4.6) weil die bei meinen Projekten durchweg 10% 
kleineren Code macht, und hatte erst mal nicht dran gedacht, daß es bei 
der neuen 4.x viele tolle Optionen gibt, welche die alte noch nicht 
kennt. Geht also auf meine Kappe.

>> Hier mal versuchen, die OR-Konstanten in ein Array zu packen und per
>> Index (h0,h1,m0,m1) drauf zuzugreifen.
>
> Das war auch meine erste Idee. Habe die Funktion doppelt geschrieben und
> es stellte sich heraus, dass die Array-Variante über 200 Bytes mehr
> brauchte, abgesehen davon, dass der Speicher für das Array nicht
> ausreichte.

Hmmm mal rechnen. Pro Ziffer braucht man 11 Einträge à 1 Byte: macht 44 
Byte.

-- Byte für Ziffer 1 lesen
-- Byte für Ziffer 2 lesen
-- OR
-- Byte für Ziffer 3 lesen
-- Byte für Ziffer 4 lesen
-- OR
-- Ist wohl ein 2:1 MUX, also je nach MUX-Phase die unerwünschten Bits 
rausUNDen
-- die beiden Bytes zu dem Long zusammenbasteln und weg damit

pro Bytelese-Aktion rechne ich mal 16 Byte Code, insgesamt sollten für 
die Musterbildung 100-120 Bytes an Code reichen (ohne die Divisionen).

>> In main() greifst Du sehr oft auf status zu. Da GCC die Adresse kennt,
>> macht er direkte Zugriffe, von denen jeder 4 Byte kostet.
>
> Irgendwo las ich, dass es effektiver wäre, globale Variablen in einer
> Struktur zusammen zu fassen. Gilt die Aussage nicht für gcc?
> Sollte ich besser ohne Struktur arbeiten?

Nein, das wäre die falsche Richtung. Der Zugriff würde sich zudem nicht 
unterscheiden von einem struct-Zugriff. Ein struct/union hat aber den 
Vorteil, daß die Komponenten im Speicher hintereinander liegen -- 
abgesehen davon, daß man nur eine Variable hat und nicht 1000 
Kleingefuzzel an globalen Variablen.

> Den Tip mit dem Zeiger statt der globalen Struktur verstehe ich noch
> nicht. Was wird besser, wenn ich einen Zeiger verwende?

Für AVR gilt: Ein indirekter Zugriff kostet 2 Byte Code im Gegensatz zu 
den 4 Bytes bei direktem Zugriff. Die Ausführungsgeschwindigkeit ist 
dabei identisch. Allerdings muss die Adresse erst in eines der 
Adress-Register geladen werden, was Code kostet und ein 16-Bit-Register 
belegt. Y- und Z-Register können mit festem Offset adressieren. Bei 
vielen Zugriffen auf die gleiche Struktur kann es daher lohnend sein, 
erst die Startadresse zu laden und dann indirekt zuzugreifen.

Ob das wirklich zu einem Codegewinn führt, muss man austesten. Falls der 
Compiler auf die Idee kommt, die Startadresse zB in Register R15:R14 
anzulegen, dann kopiert er die Adresse vor jedem Zugriff nach Y oder 
(wahrscheinlicher) Z. Damit hätte man wieder 4 Byte Code (oder gar 6, 
wenn kein MOVW geht) bei sogar 4 Ticks :-(

Versuchen kann man folgendes: Indirekte Zugriffe hinschreiben. Der 
Compiler wird das wahrscheinlich zu direkten Zugriffen "optimieren", 
also hat man nix gewonnen oder verloren, ausser etwas Zeitaufwand. Mit 
dem asm kann man dann die "Optimierung" verhindern und schauen, ob es 
einen Code-Schrumpf gibt. Wenn nicht, lässt man es bei den indirekten 
Zugriffen, die der Compiler in direkte umwandelt und hat zumindest nix 
an Code verloren.

Schön ist das nicht, aber schöne Lösungen kosten meist auch Platz.

>> - Die switch-Anweisung kann auch nicht gut optimieren.

Switch erweitert 8-Bit-Argumente zu 16-Bit. Ohne Sprungtabelle hat man 
also für jedes Case einen unnötigen 16-Bit-Vergleich. Auch hier gilt: 
mal eine explizite if-else-Orgie machen und schauen, ob der Code besser 
wird (am besten im asm kontrollieren). Wiederum: der Code wird nicht 
schöner...

Autor: Santiago m. H. (mausschubser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Geschafft :)

> Schreib das mal indirekt, also
typeof (status) * s = & status;
...
s->work.mode = ...

das ergab den Durchbruch, bzw. nochmal 58 Byte Einsparung.
Mit der Switch-Variante bin ich jetzt bei 98% - damit kann ich loslegen 
:)

> Switch erweitert 8-Bit-Argumente zu 16-Bit. Ohne Sprungtabelle hat man
> also für jedes Case einen unnötigen 16-Bit-Vergleich. Auch hier gilt:
> mal eine explizite if-else-Orgie machen und schauen, ob der Code besser
> wird

Ok, werde ich auch nochmal durchprobieren - schließlich lerne ich ja was 
dabei :)

> Ich verwende die alte (3.4.6) weil die bei meinen Projekten durchweg 10%
> kleineren Code macht, und hatte erst mal nicht dran gedacht, daß es bei
> der neuen 4.x viele tolle Optionen gibt, welche die alte noch nicht
> kennt. Geht also auf meine Kappe.

Hm - das hat sich also nicht geändert? Ich habe eine Zeitlang Deinen 
Fred zwecks Compiler-Vergleich mitgelesen, ihn dann aber aus den Augen 
verloren.
Das heißt also, wenn man knackigen Code möchte, sollte man 3.4.6 
verwenden?


Nochmals danke an alle die mitgeholfen haben.

Gruß Santi

Autor: Santiago m. H. (mausschubser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

das Umformulieren von swith in if...else ergab folgendes:
Der Switch vom mode auf die enum-Werte ergab 4 Byte Einsparung bei 
if...else, der Switch von z.B. h0 auf 12 mögliche Werte ergab einen 
Mehrverbrauch von 8 Byte.
Am Meisten brachte wieder die Verwendung eines Zeigers für den Zugriff 
auf die Struktur (nachmal fast 20 Byte).

Mein Fazit:
Also die Verwendung von if...else macht somit nur in Einzelfällen Sinn 
und die Einsparung ist nicht so groß, dass sich die "Verunstaltung" des 
Codes generell rechtfertigen ließe. Im Einzelfall gilt es bei Bedarf zu 
prüfen.
Ein Zeiger auf die globale Struktur macht dagegen generell Sinn.

Somit habe ich wieder viel dazu gelernt. Danke!

Autor: egberto (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du kannst ja mal testen, ob du den Code mit -mint8 zum laufen 
bekommst(siehe Artikel AVR-Codeoptimierung).
Hat bei mir ziemlich viel rausgeholt (ich glaube, das waren fast 300 
Byte).

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Santiago m. H. wrote:

>> Ich verwende die alte (3.4.6) weil die bei meinen Projekten durchweg 10%
>> kleineren Code macht, und hatte erst mal nicht dran gedacht, daß es bei
>> der neuen 4.x viele tolle Optionen gibt, welche die alte noch nicht
>> kennt. Geht also auf meine Kappe.
>
> Hm - das hat sich also nicht geändert? Ich habe eine Zeitlang Deinen
> Fred zwecks Compiler-Vergleich mitgelesen, ihn dann aber aus den Augen
> verloren.
> Das heißt also, wenn man knackigen Code möchte, sollte man 3.4.6
> verwenden?

Kann man so allgemein nicht sagen. Mein C-Code kommt nicht von nem 
32-Bit-µC oder gar Host-Rechner, sondern ist direkt für AVR geschrieben. 
Ausserdem kenn ich die avr-gcc-Interne bzw. GCC-Interna recht gut und 
weiß, was dem Compiler schmeckt und was nicht.

Leider fehlt gcc ne Option wie -fsmart-code oder -fbraindead-code, womit 
man einstellen könnte, wie geschickt programmiert wurde.

Ein C-Compiler will natürlich unabhängig sein zB von der speziellen 
algebraischen Formulierung einzelner Ausdrücke. Man will ja portierbaren 
Code und nicht für jede Plattform an der Quelle schnitzen.

Für Code aus nem Code-Generator (-fbraindead-code) würde ich mal 
schätzen, daß 4.x besseren Code mach. Bei für avr-gcc 3.4.x mungerecht 
geschriebenem C-Code ist das aber leider nicht so.

Einen neuen Anlauf, avr-gcc 4.3.x zu testen hab ich nicht gemacht. Weder 
unter Win32 noch unter Linux. Wobei reizen würd es je schon, ein 
bisschen die Quellen zu tunen...

Santiago m. H. wrote:
> Hallo,
>
> das Umformulieren von swith in if...else ergab folgendes:
> [...]
> Mein Fazit:
> Also die Verwendung von if...else macht somit nur in Einzelfällen Sinn
> und die Einsparung ist nicht so groß, dass sich die "Verunstaltung" des
> Codes generell rechtfertigen ließe. Im Einzelfall gilt es bei Bedarf zu
> prüfen.

In Deinem Fall liegen die case-Werte schön hintereinander, optimal für 
jump-tables. Liegen die case verstreut, ist mit einer Tabelle nicht viel 
zu wollen und die Vergleiche würden ausgetextet. Der Compiler hat dazu 
intern Heuristiken, aber die GCC-Entwickler streuben sich leider 
dagegen, switch auch für 8 Bit einzubauen, so daß bei expliziten 
Vergleichen auf int-Ebene verglichen wird...

> Ein Zeiger auf die globale Struktur macht dagegen generell Sinn.

Wie gesagt, nicht generell. Aus hier gibt es zahlreiche Ausnahmen, zB 
wenn viele Register gebraucht werden, oder gleichzeitig auf mehrere 
solcher Strukturen zugegriffen werden soll. Leider hat AVR nur 2 
wirklich brauchbare Adress-Register, von denen eines von avr-gcc schon 
für den Frame-Pointer reserviert ist, also nur dann für "normalen" Code 
allokierbar ist, wenn kein Framepointer gebraucht wird.

egberto wrote:
> Du kannst ja mal testen, ob du den Code mit -mint8 zum laufen
> bekommst(siehe Artikel AVR-Codeoptimierung).

Nicht wundern, wenn dann keine 32-Bit-Arithmetik mehr geht...

Autor: egberto (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Nicht wundern, wenn dann keine 32-Bit-Arithmetik mehr geht...

Deswegen habe ich ja auch extra geschrieben "testen, ob du den Code mit 
-mint8 zum laufen bekommst" und auf den passenden Artikel verwiesen.

Ob Aufwand und nutzen hier vernünftig sind, muß man dann halt 
abschätzen.
Aber das Programm pass ja wohl schon in den Chip....

Schönes (Bastel)WE

Autor: Santiago m. H. (mausschubser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Johann,

> Kann man so allgemein nicht sagen. Mein C-Code kommt nicht von nem
> 32-Bit-µC oder gar Host-Rechner, sondern ist direkt für AVR geschrieben.

Wie meinst Du das?

Ich habe auch versucht, den C-Code direkt für den AVR zu schreiben. 
Generiert ist da nix und 32-Bit µCs habe ich auch nicht - aber klar, ich 
bin noch Anfänger im µC-Umfeld, von daher muss ich noch viel der 
Eigenheiten lernen.

Also wenn Du noch einen Tip hättest, was ich besser machen könnte/sollte 
- bin dankbarer Abnehmer :)

Wie sieht das denn mit App-Notes von Atmel bzgl. C-Programmierung aus?
Sind die übertragbar auf GCC, oder gelten die "nur" für den Intel 
Compiler?


Gruß Santi

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Santiago m. H. wrote:
> Hallo Johann,
>
>> Kann man so allgemein nicht sagen. Mein C-Code kommt nicht von nem
>> 32-Bit-µC oder gar Host-Rechner, sondern ist direkt für AVR geschrieben.
>
> Wie meinst Du das?
>
> Ich habe auch versucht, den C-Code direkt für den AVR zu schreiben.
> Generiert ist da nix und 32-Bit µCs habe ich auch nicht - aber klar, ich
> bin noch Anfänger im µC-Umfeld, von daher muss ich noch viel der
> Eigenheiten lernen.

Die Großzahl der Optimierungsalgorithmen in GCC ist maschinenunabhängig, 
was Optimierung aber in Wirklichkeit fast nie ist. GCC zielt eben eher 
auf 32-Bit-Architekturen und versucht näher an die Konkurrenten wie 
Intel C-Compiler ranzukommen. Naja, und solche Optimierungen führen für 
AVR dann manchmal zu schlechterem Code als ältere Compiler-Versionen. 
Ein bisschen hatte ich auch im von Dir genannten Thema dazu geschrieben. 
Aber GCC lernt bestimmt noch dazu. Das Potential an mächtigen 
Optimierungen hat er zweifellos, es fehlt aber etwas an deren 
zielgenauem Einsatz...

> Also wenn Du noch einen Tip hättest, was ich besser machen könnte/sollte
> - bin dankbarer Abnehmer :)
>
> Gruß Santi

Sehr hilfreich finde ich, hin und wieder einen Blick ins erzeugte 
Assembler zu werfen. Dazu gibt es mehrer Wege

http://www.roboternetz.de/wissen/index.php/Assembl...

Weit verbreitet ist, den Assembler ein List-File miterzeugen zu lassen. 
Ich bevorzuge jedoch, direkt in die Compiler-Ausgabe zu schauen. Während 
die avr-as-Ausgabe schon Maschinencode enthält, ist das bei der 
Compiler-Ausgabe noch nicht der Fall. Dafür kann man andere 
Informationen bekommen über die verwendeten Variablen oder welches 
Pattern eine gewisse Befehlsfolge erzeugt hat

foo-gcc -save-temps -dp -fverbose-asm ...

-save-temps legt die .s-Datei im aktuellen Verzeichniss an anstatt als 
tmp-Datei.

-dp ist recht speziell, die Zusatzinfos dienen eigentlich zum Debuggen 
des Compilers... Ausserdem fin ich -g (Debug-Info) recht lästig. Ich 
verwende die Option nicht, weil ich eh nicht debugge und -g die .s groß 
und schlecht lesbar macht.

Die herkömmlich erzeigten lst/lss-Files sind aber auch ganz nett und 
enthalten Quell-Info, so daß der Code zuortenbar bleibt.

Meistens genügt ein Blick ins List, um zu beurteilen, was gcc zu treibt. 
Macht er viel überflüssiges Zeug, geht das idR auf Codegröße und auf 
Speicherplatz.

Ein Kandidat wäre zB eines der ersten swich-Statements in der Ausgabe, 
das 2x die Sequenz
h0 = h / 10;
h1 = h % 10;
m0 = m / 10;
m1 = m % 10;
enthält.

Wenn gcc gut ist, erkennt er das und legt den Code nur 1x an und 
verzweigt so, wie man es händisch mit goto machen würde.

Wenn gcc den Code 2x anlegt, was bei -Os unerwünscht ist, kann man sich 
unterschiedliche Strategien überlegen, das loszuwerden.

Noch was: Die Arrays S1Dig0[] ect. sind 32 Bit breit. Pro Ziffer reichen 
aber doch 1 Byte. Es codiert für Bit 6..13 (Dig 1,2) bzw. für Bit 15..21 
(Dig 3,4). Zusammenbasteln kann man das später. Das spart Platz in den 
Arrays und beim Lesen!

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Santiago m. H. wrote:
> habe die Array-Variante nochmal überarbeitet, habe sie aber immer noch
> nicht kleiner als die Switch-Version bekommen.

Kein Wunder, sie ist ja immer noch 32-Bittig und das mag der GCC nicht 
(gut optimieren).
Und da Du nur 24Bit brauchst, sinds schonmal 25% toter Code.

Du hast 4 Digits a 7 Segmente.
Also mach erstmal ein Array 4*7 für jedes Segment ein Byte (=28Byte 
Flashverbrauch).
Das Byte enthält einmal die Bitnummer 0..23 (=5Bits) und die 
Multiplexnummer 0..1 (= nochn Bit), die 6 Bits passen also in ein Byte.

Dann die nächste Tabelle mit 10 Einträgen für den 7-Segmentcode.

Dann ein Array (Display-RAM) im SRAM von 6 Byte (je 24 Bit für MUX0 und 
MUX1).

So und nun ne Funktion, die in den Display-RAM ne Ziffer einstellt.
Diese Funktion setzt/löscht dann die Bits einer Ziffer auf einem Digit, 
also 2 Argumente:
void digit_out( uint8_t digit, uint8_t number );
Am besten  als Schleife Bit für Bit, damit der Code klein bleibt.
Dauert zwar etwas länger als tausend Switches, aber der Mensch kann eh 
nicht so schnell lesen.
Man sollte nicht schneller als 2..5 Werte je Sekunde ausgeben, damits 
ergonomisch ist.

Und fertisch.


Peter

Autor: Santiago m. H. (mausschubser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

@Johann L.
Danke für die ausführlichen Erklärungen. Schätze, ich muss mich doch mit 
Assembler auseinander setzen. Hatte mich bislang davor gedrückt.

@Peter
Also Deine Tips reißen mal wieder die Mundwinkel bis zu den Ohrläppchen.
Das muss ich mir langsam auf der Zunge zergehen lassen.
Also erstmal nen Kaffee holen und dann versuchen umzusetzen.

So wie es aussieht habbich wohl die Tina verärgert 
(Beitrag "zum Abschied rauch ich eine"). Kann die Tips also 
(vorerst) nur virtuell testen.

Nochmals ein Dankeschön Euch allen und ein angenehmes WE.

Gruß Santi

Autor: Peter Dannegger (peda)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Santiago m. H. wrote:
> @Peter
> Also Deine Tips reißen mal wieder die Mundwinkel bis zu den Ohrläppchen.
> Das muss ich mir langsam auf der Zunge zergehen lassen.
> Also erstmal nen Kaffee holen und dann versuchen umzusetzen.

Im Anhang hab ichs mal schnell geproggt (ohne Gewähr).

Die konstanten Daten kann man noch nach PGM verschieben, ich habs 
erstmal im SRAM gemacht.
Das Array SEGMENT_TAB mußt Du noch mit den richtigen Anschlüssen 
belegen.


Peter

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Im Anhang hab ichs mal schnell geproggt (ohne Gewähr).

Wie nicht anders zu erwarten war:

Sehr kurz das Programm und effektiv ;-)

Autor: Peter Dannegger (peda)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
So, schon nen Fehler gefunden, das
digit <<= 2;  
ist natürlich quatsch.

Und wenn man sauber programmieren will, sollte man immer abtesten, ob 
man nicht außerhalb eines Arrays schreibt.


Peter

Autor: Santiago m. H. (mausschubser)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Peter,

als ich las, dass Du es "mal schnell geproggt" hast, war ich erst 
enttäuscht und dachte: wie langweilig.
Aber dann sagte ich mir, du musst es ja nicht gleich anschauen.

Also habe ich erst versucht, es selbst umzusetzen und dann erst Deinen 
code angeschaut.

So knackig habe ich es natürlich nicht hinbekommen :) - war ja klar.

Aber die Ergebnisse meines Codes haben mich auch überrascht.
Zuerst habe ich glatt runtergeschrieben und den Display-Puffer als 
32Bit-Werte gelassen.
Nach dem Übersetzen zeigte sich, dass die Array-Variante ca 40Byte mehr 
an Code produzierte, als die Switch-Variante.

Dann habe ich den Display-Puffer in ein Char-Array umgewandelt und den 
Code angepasst.

Ergebnis: 310 Byte Einsparung gegenüber der 32-Bit-Variante, bzw. 270 
Byte weniger als die Switch-Variante. Das ist doch mal nicht schlecht.
Allerdings denke ich, dass ich die Codegröße gegen schlechtere Laufzeit 
eintauscht habe - insbesondere beim Tiny2313 der keinen Multiplikator 
hat.

Was meinst Du?

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Santiago m. H. wrote:
> Allerdings denke ich, dass ich die Codegröße gegen schlechtere Laufzeit
> eintauscht habe - insbesondere beim Tiny2313 der keinen Multiplikator
> hat.

Der MC kann über 100-mal schneller Zahlen ausgeben, als der Mensch 
ablesen kann. Daher hast Du keinerlei merkbare Laufzeiteinbuße.


Peter

Autor: Santiago m. H. (mausschubser)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Peter,

habe inzwischen auch Deinen Vorschlag umgesetzt und was soll ich sagen:
nochmals 140 Byte Einsparung.

Ich muss gestehen, so richtig habe ich es erst beim Abschreiben 
verstanden.
Schon genial, Rechenoperationen in die Arraybelegung mit zu nehmen.
Schätze derlei Genialitäten lernt man nur, wenn man wirklich mit 
begrenztem Speicher umgehen muss.
Ein Anwendungsentwickler, der sich nicht um die Größe des 
Auslagerungsspeichers kümmern muss, würde nie auf so eine Idee kommen.
Da habe ich wohl noch viel zu lernen.

Danke für Deine Unterstützung.

Angenehmes WE wünscht

Santi

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab nochn Fehler in meinem Code gefunden:
Es sind ja 7 Segmente, also darf die Schleife auch nur 7 Durchläufe 
machen und nicht 8.


Noch Tip:
for (; i < 8; i++) {
   j = 1 << i;

Schieben um ne Variable ist aufm AVR umständlich, das muß der Compiler 
noch mit ner extra Zählschleife machen.

Besser also gleich die Schleife nehmen:
for ( j = 1; i < 8; i++, j<<=1 ) {

Könnte kürzeren Code ergeben, die Laufzeit verkürzt es auf jeden Fall.


Peter

Autor: Santiago m. H. (mausschubser)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Peter,

> Ich hab nochn Fehler in meinem Code gefunden
Das finde ich sehr großzügig, dass Du meine Fehler auf Dir nimmst ;)
So wie der Typ in dem alten Schinken: "den Pups der Dame nehm ik auf 
mir..."

Schätze Deine Variante, den Zähler runter zu zählen und die 
Abbruchbedingung gegen 0 zu prüfen dürfte auch noch schneller sein.

Auf jeden Fall danke ich Dir für die extra Tips. Jetzt verstehe ich 
auch, warum  meine Variante so schlecht ist. Da sind jede Menge variable 
Schiebeoperationen drin.

Anbei mal die vollständige Datei nach Deiner Vorlage.

Gruß Santi

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.