Forum: Mikrocontroller und Digitale Elektronik Bitmanipulation optimieren


von Bitmanipulation (Gast)


Lesenswert?

Ich steuere eine LED-Matrix im Multiplex-Betrieb. Soweit funktioniert 
das auch, allerdings besteht da sicherlich noch Optimierungsbedarf und 
ich würde euch gerne um entsprechende Tipps bitten.

Konkret geht es um Folgendes: Ich arbeite hier 74HC237 (3-to-8 line 
decoder with latches). Alles wichtige hängt an PORTC, nämlich PC2, PC3 
und PC4 sind die Adresspins (A0 - A2). PC5 ist das Latch Enable (LE) 
Signal.

Mir geht es nun darum wie ich die Adresspins (PC2-PC4) effizient setzen 
kann. Ich habe in der Variable namens layer einen Zählerstand (0-7) und 
würde diesen gerne an die Pins PC2 bis PC4 anlegen. Zur Zeit mache ich 
das so, dass ich zunächst die entsprechenden Adresspins lösche und in 
einem zweiten Schritt dann über einen Bitshift die layer Variable mit 
hinein odere. Das sieht in etwa so aus:
1
    PORTC &= ~(_BV(PC2) | _BV(PC3) | _BV(PC4));
2
    PORTC |= (layer << 2);
3
4
    PORTC |= _BV(PC5);

Lässt sich das Ganze denn (auch unter zu Hilfenahme von Assembler) 
optimieren? Mir ist klar, dass das wohl eher unter "Mikrooptimierung" 
fällt. Dennoch dürfte es ganz interessant bzw. lehrreich sein.

Vielen Dank!

von Ingo (Gast)


Lesenswert?

Wenn der Kompiler Optimierungspotential sieht, wird er es vermutlich 
ausnutzen... So sehe ich jetzt erstmal nichts, bis auf das man die 
letzte und vorletzte Zeile zusammenschreiben kann. Dann haste n 
Zweizeiler, was willste da noch sparen? Weiss nicht obs in Assembler 
noch einfacher geht, denke aber nicht...



Ingo

von Falk B. (falk)


Lesenswert?

@Bitmanipulation (Gast)

>Ich steuere eine LED-Matrix im Multiplex-Betrieb. Soweit funktioniert
>das auch, allerdings besteht da sicherlich noch Optimierungsbedarf

Warum glaubst du das? Reicht der Flash nicht? Ist das Programm zu 
langsam?

>    PORTC &= ~(_BV(PC2) | _BV(PC3) | _BV(PC4));
>    PORTC |= (layer << 2);

>    PORTC |= _BV(PC5);

Vollkommen OK. Wenig bis kein Optimierungspotential.

>Lässt sich das Ganze denn (auch unter zu Hilfenahme von Assembler)
>optimieren?

Nein. Der Compiler macht aus deinen Bitbefehlen schon optimalen 
Assembler. Schau dir die .lss Datei an.

> Mir ist klar, dass das wohl eher unter "Mikrooptimierung"
>fällt.

Ja.

> Dennoch dürfte es ganz interessant bzw. lehrreich sein.

Nein. Es ist Zeitverschwendung und ein großer Irrtum.

http://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung

Die einzige Mikrooptimierung ohne nennenswerten Aufwand ist eine direkte 
Zählung mit Schrittweite 4. Das spart aber gerade mal zwei Shiftbefehle 
und damit 4 Bytes Flash und 2 Takte. Nicht der Rede wert.
1
    layer += 4;
2
    PORTC &= ~(_BV(PC2) | _BV(PC3) | _BV(PC4));
3
    PORTC |= layer;
4
5
    PORTC |= _BV(PC5);

von Ingo (Gast)


Lesenswert?

Falk Brunner schrieb:
> layer += 4;
>     PORTC &= ~(_BV(PC2) | _BV(PC3) | _BV(PC4));
>     PORTC |= layer;
>
>     PORTC |= _BV(PC5);

ergänzend:
1
    PORTC |= layer | _BV(PC5) ;
somit:
1
    layer += 4;
2
    PORTC &= ~(_BV(PC2) | _BV(PC3) | _BV(PC4));
3
    PORTC |= layer  BV(PC5);

Ich denke mehr ist nicht drinn...


Ingo

von Karl H. (kbuchegg)


Lesenswert?

Das einzige was ich tun würde:
Anstatt alle OPerationen direkt am Port zu machen, würde ich die 
komplette Bitmanipulation auf einer Variablen machen und dann erst das 
Endergebnis wieder auf den Port schreiben.

Aber auch das: im Grunde ziemlich uninteressant.
Du machst genau das, was man beim Optimieren nicht tut. Du suchst dir 
auf gut Glück irgendein Detail heraus und versuchst es zu optimieren. 
Hast du dir denn schon angesehen, was denn der Compiler aus deinen 3 
Zeilen macht? Hättest du das getan, hättest du gesehen, dass da sowieso 
nichts übrig bleibt und es da auch kein großartiges 
Optimierungspotential gibt.



Was machst du mit der 'anderen Seite' der Matrix?

von Peter D. (peda)


Lesenswert?

Ich würde die HW optimieren, statt dem umständlichen 74HC237 ganz 
einfach ein 74HC164.
Mit jedem Taktimpuls wird eins weiter geschaltet. Und bei layer == 0 
wird eine 1 eingeschrieben, sonst 0.
Und spart obendrein 2 IO-Pins.

von anderes ich (Gast)


Lesenswert?

Vieleicht solltest du erst mal erwähnen welchen Controller du einsetzt. 
xmegas z.B. haben da noch mehr Möglichkeiten als ein megaxyz

von Bitmanipulation (Gast)


Lesenswert?

Ingo schrieb:
> Wenn der Kompiler Optimierungspotential sieht, wird er es vermutlich
> ausnutzen...

Wobei ich sagen muss, dass da avr-gcc sicherlich weit weg von "optimal" 
ist.

Ingo schrieb:
> So sehe ich jetzt erstmal nichts, bis auf das man die
> letzte und vorletzte Zeile zusammenschreiben kann.

Die habe ich deswegen getrennt, damit die Zustände an den Adress-Pins 
"stabil" sind, bevor sie gelatcht werden. Um den Compiler das klar zu 
machen, sollte ich wohl noch ein nop dazwischen setzen. Ich habe das 
jetzt nicht durchgerechnet und glaube nicht, dass das etwas ausmacht 
(das propagation delay liegt ja irgendwo im ns Bereich), aber meiner 
Meinung nach dürfte das dennoch sauberer sein, oder?

Falk Brunner schrieb:
> Warum glaubst du das? Reicht der Flash nicht? Ist das Programm zu
> langsam?

Nein, einfach aus Interesse ;).

Karl Heinz Buchegger schrieb:
> Das einzige was ich tun würde:
> Anstatt alle OPerationen direkt am Port zu machen, würde ich die
> komplette Bitmanipulation auf einer Variablen machen und dann erst das
> Endergebnis wieder auf den Port schreiben.

S.o.. Ich hätte halt gerne erst stabile Zustände an den Adress-Pins, 
bevor ich sie übernehme.

Karl Heinz Buchegger schrieb:
> Was machst du mit der 'anderen Seite' der Matrix?

Da "fülle" hintereinander weg 8 74HC574er, welche jeweils auch durch 
einen 74HC237 "ausgewählt" werden.

Peter Dannegger schrieb:
> Ich würde die HW optimieren, statt dem umständlichen 74HC237 ganz
> einfach ein 74HC164.

Wieso denn umständlich? Ich empfand das eigentlich als recht einfach und 
sauber. Beim 74HC164 gefällt mir außerdem das Pin-Out nicht. Das ist auf 
Streifenplatine nicht schön zu "routen". Aber ja, damit würden sich wohl 
Pins sparen lassen. Muss ich mir mal ansehen.

anderes ich schrieb:
> Vieleicht solltest du erst mal erwähnen welchen Controller du einsetzt.
> xmegas z.B. haben da noch mehr Möglichkeiten als ein megaxyz

Zur Zeit ist ein ATmega32 im Einsatz.

von Karl H. (kbuchegg)


Lesenswert?

Bitmanipulation schrieb:
> Ingo schrieb:
>> Wenn der Kompiler Optimierungspotential sieht, wird er es vermutlich
>> ausnutzen...
>
> Wobei ich sagen muss, dass da avr-gcc sicherlich weit weg von "optimal"
> ist.

Na ja.
Bei einem

    PORTC &= ~(_BV(PC2) | _BV(PC3) | _BV(PC4));

ist aber auch nicht viel Potential vorhanden. Was soll er bei

    in    Register, PORTC
    andi  Register, Konstante
    out   PORTC, Register

schon groß vergeigen?
Da muss man sich schon extrem anstrengen, wenn man da ineffizient 
arbeiten will.

> Die habe ich deswegen getrennt, damit die Zustände an den Adress-Pins
> "stabil" sind, bevor sie gelatcht werden.

Du hast das falsch verstanden

Das hier
1
    PORTC &= ~(_BV(PC2) | _BV(PC3) | _BV(PC4));
2
    PORTC |= layer;
kann man zusammenfassen
1
    PORTC = ( PORTC & ~(_BV(PC2) | _BV(PC3) | _BV(PC4)) ) | layer;

und spart somit zwei Zugriffe auf PORTC ein.

    in    Register, PORTC
    andi  Register, Konstante
    ori   Register, layer
    out   PORTC, Register

anstelle von

    in    Register, PORTC
    andi  Register, Konstante
    out   PORTC, Register
    in    Register, PORTC
    or    Register, layer
    out   PORTC, Register

(und dasselbe Ergebnis würde ich auch von

    uitn8_t tmp = PORTC;
    tmp &= ~(_BV(PC2) | _BV(PC3) | _BV(PC4));
    tmp |= layer;
    PORTC = layer;

erwarten. Die Variable tmp wird als Registervariable ausgeführt und es 
kommt wieder der kurze Code raus. Die C-Schreibweise ist zwar etwas 
länger aber dafür ist sie auch leichter zu verstehen, weil die Zeilen 
nicht so vollgekramt sind)


Den Latch lässt du so wie er ist.

>
> Nein, einfach aus Interesse ;).
>
> Karl Heinz Buchegger schrieb:
>> Das einzige was ich tun würde:
>> Anstatt alle OPerationen direkt am Port zu machen, würde ich die
>> komplette Bitmanipulation auf einer Variablen machen und dann erst das
>> Endergebnis wieder auf den Port schreiben.
>
> S.o.. Ich hätte halt gerne erst stabile Zustände an den Adress-Pins,
> bevor ich sie übernehme.

Die hast du ja sowieso, denn wenn die Portausgabe einen Propagation 
Delay hat, dann hat auch das abschliessende Setzen das Latch-Pins 
denselben Delay. D.h. bei

   PORTC = .....

   PORTC |= _BV(PC5);

kommen die Signale in der richtigen Reihenfolge.

von Sven P. (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das hier    PORTC &= ~(_BV(PC2) | _BV(PC3) | _BV(PC4));
>     PORTC |= layer;
> kann man zusammenfassen
>     PORTC = ( PORTC & ~(_BV(PC2) | _BV(PC3) | _BV(PC4)) ) | layer;
Das ist ein sehr zweischneidiges Schwert.

PORTC ist volatile, daher ist die Optimierung eigentlich nicht ganz 
legal. Wobei das ohnehin doof ist wegen read-modify-write, ist ohne 
Sperren also nicht Thread- bzw. Interruptfest.

Interessant wird es dann bei einzelnen Bits. Sowas wie
1
PORTC |= _BV(PC1);
kann schon schiefgehen. Wenn der Compiler es als 'sbi' umsetzt (umsetzen 
kann), ist es implizit Interruptfest. Wenn er aber in/or/out generiert, 
hast ein Problem...

von Bitmanipulation (Gast)


Lesenswert?

Sven P. schrieb:
> Wenn er aber in/or/out generiert,
> hast ein Problem...

Andererseits geschieht das sowieso im Kontext einer ISR, und da der AVR 
die nicht verschachteln kann (zumindest ohne weiteres zutun), sollte das 
doch in Ordnung gehen, oder?

von Fabian O. (xfr)


Lesenswert?

Sven P. schrieb:
> PORTC ist volatile, daher ist die Optimierung eigentlich nicht ganz
> legal.

Genau deshalb besteht an der Stelle ja Optimierungspotenzial von Hand: 
Weil der Compiler es nicht von selber darf.

Das Programm macht dann natürlich etwas geringfügig anderes, nämlich die 
Pins nicht für zwei Takte auf Low setzen. Aber das ist in diesem Fall ja 
auch nicht nötig.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Fabian O. schrieb:
> Sven P. schrieb:
>> PORTC ist volatile, daher ist die Optimierung eigentlich nicht ganz
>> legal.
>
> Genau deshalb besteht an der Stelle ja Optimierungspotenzial von Hand:

--> Einfach eine Zwischenvariable verwenden.

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.