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!
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
@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.
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?
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.
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.
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
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.
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...
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?
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.
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.