Hi,
Ich benutze den ATtiny2313 zusammen mit 3 Schieberegistern (74HC164) um
24 LEDs anzusteuern. Dabei soll das laden der Bits in das
Schieberegister natuerlich so schnell wie moeglich vonstatten gehen, da
man sonst auch die ausgeschalteten LEDs kurz aufblinken sieht.
Seht ihr noch optimierungsmoeglichkeiten?
leds.h
1
#ifndef LEDS_H
2
#define LEDS_H
3
4
#include<avr/io.h>
5
6
#define LEDS_PORT PORTB
7
#define LEDS_CLK PB0 // clock pin for all three 74HC164 registers
Hi,
> Warum nimmst Du denn keinen 74HC595 ?
Oh.. den kannte ich noch nicht. Beim naechsten mal nehme ich natuerlich
den. Das mit dem kurzen Aufblinken ist auch nicht so schlimm, ich wollte
nur wissen, ob man nicht vielleicht noch einbisschen optimieren
koennte..
Gruss,
Stefan
@ Stefan Noack (stefan_n)
>den. Das mit dem kurzen Aufblinken ist auch nicht so schlimm, ich wollte>nur wissen, ob man nicht vielleicht noch einbisschen optimieren>koennte..
Ja, siehe unten. Der direkte Zugriff auf den Port ist volatile, das ist
so in den Include Files definiert. Mit einer Hilfsvariable gehts noch
etwas schneller.
Falk Brunner schrieb:> Ja, siehe unten. Der direkte Zugriff auf den Port ist volatile, das ist> so in den Include Files definiert. Mit einer Hilfsvariable gehts noch> etwas schneller.
Nicht ratsam, weil dadurch data setup time = 0, muss aber mehr sein.
Wenn schon dann sowas wie
1
unsignedchartemp=PORTB&~...;
2
if(row0&bit)
3
temp|=_BV(...);
4
if(row1&bit)
5
temp|=_BV(...);
6
if(row2&bit)
7
temp|=_BV(...);
8
PORTB=temp;
9
PORTB|=_BV(...);
Bringt aber zumindest in 4.3.4 ziemlich wenig.
Was mehr bringen dürfte: -O3. Aber auch mehr Platz, weil vollständiges
unrolling.
Das mit der Hilfsvariable verstehe ich ehrlich gesagt nicht.
Laut der Doku von avr-libc wird aus
1
PORTB|=_BV(...)
einfach eine set-bit instruction:
>Access to the AVR single bit set and clear instructions are provided via>the standard C bit manipulation commands. The sbi and cbi macros are no>longer directly supported. sbi (sfr,bit) can be replaced by sfr |= BV(bit)>>i.e.: sbi(PORTB, PB1); is now PORTB |= _BV(PB1);>>This actually is more flexible than having sbi directly, as the optimizer>will use a hardware sbi if appropriate, or a read/or/write operation if>not appropriate. You do not need to keep track of which registers sbi/cbi>will operate on.>>Likewise, cbi (sfr,bit) is now sfr &= ~(_BV(bit));
Mit der Zwischenvariable habe ich doch dann anstatt der SBI-instructions
erstmal variablenzuweisungen und dann noch zusaetzlich einmal eine
OUT-instruction, oder?
Stefan Noack schrieb:> Laut der Doku von avr-libc wird aus> ...> einfach eine set-bit instruction:
Das schon, aber die braucht 2 Takte auf Port und nur 1 Takt auf
Register. Ausserdem gilt das nicht wenn es mehrere Bits sind. Da die
Maskierung folglich als load/and/store ausgeführt werden muss kann man
das auch explizit codieren und das Tempregister weiterverwenden.
> erstmal variablenzuweisungen
Diese Variable sitzt allerdings in einem Register.
@ Stefan Noack (stefan_n)
>Laut der Doku von avr-libc wird aus>PORTB |= _BV(...)>einfach eine set-bit instruction:
Ach so stimmt. Ist mir irgendwie entfallen :-0
Gilt aber in erster Näherung für den AVR und andere Prozessoren, die das
so schnell können.
Moment, SBI dauert aber 2 Takte, ein einfaches ORI nur 1. Der Vergleich
ist der selbe. Also ist meine Version doch schneller ;-)
Hmm, nöö, doch nicht, am Ende ist es wieder gleich, siehe Anhang.
>Mit der Zwischenvariable habe ich doch dann anstatt der SBI-instructions>erstmal variablenzuweisungen
Die wird aber in ein CPU-Register gelegt und ist damit maximal schnell.
> und dann noch zusaetzlich einmal eine OUT-instruction, oder?
Ja, aber die dauert nur 1 Takt. Aber vorhwr muss man laden und ODERn.
MfG
Falk
>> Laut der Doku von avr-libc wird aus>> ...>> einfach eine set-bit instruction:>> Das schon, aber die braucht 2 Takte auf Port und nur 1 Takt auf> Register.
Ah.. stimmt! (Datenblatt lesen hilft...)
> Ausserdem gilt das nicht wenn es mehrere Bits sind. Da die> Maskierung folglich als load/and/store ausgeführt werden muss kann man> das auch explizit codieren und das Tempregister weiterverwenden.
Stimmt.. Ich maskiere sowieso, d.h. ich habe sowieso einen
load/and/store-zyklus, den kann ich natuerlich gleich aufweiten und
zwischendrin direkt mit dem register arbeiten, was doppelt so schnell
ist.
>>> erstmal variablenzuweisungen>> Diese Variable sitzt allerdings in einem Register.
..und das ist schneller.. habe kapiert.
Es hat auch was gebracht. Mit der Variable ist das aufblitzen kaum noch
zu sehen. Mit -O3 sieht man es dann nur im dunkeln und bei ganz scharfem
hingucken.
Naja naechstes mal nehme ich gleich den 595er..
Danke!
Originalversion 24-27 Takte (datenabhängig) pro Iteration, die
modifizierte Version 23 Takte (gcc 4.3.4). Nicht wirklich der Brüller.
Ergibt zusammengerechnet also an die 200 Takte.
Mit -O3 sind es jedoch für alle 8 Iterationen insgesamt nur knapp 90
Takte.
Moin,
Falk Brunner schrieb:> void set_leds(uint8_t row0, uint8_t row1, uint8_t row2) {> uint8_t tmp;
wenn man tmp durch eines der 3 GPIOR ersetzt, ist vielleicht noch was
rauszukitzeln.
MfG
@ Sauger (Gast)
>wenn man tmp durch eines der 3 GPIOR ersetzt, ist vielleicht noch was>rauszukitzeln.
Nö, das Setzen der Bits in tmp als CPU-Register ist schon maximal
schnell, ori dauert 1 Takt.
Aber SBI dauert nur 2 Takte. Die drei zusätzlichen Takte durch 3x sbi
werden in der tmp-Variante am Ende wieder kompensiert, wo der Port
gelesen, modifiziert und wieder geschrieben wird.
Wie gewonnen, so zerronnen. ;-)
Die tmp-Variante ist dann schneller, wenn man auf variable Bits per
Bitmasken zugreifen muss, dann geht kein sbi/cbi.
http://www.mikrocontroller.net/articles/Bitmanipulation#Siehe_auch
MfG
Falk
Falk Brunner schrieb:> Die tmp-Variante ist dann schneller, wenn man auf variable Bits per> Bitmasken zugreifen muss, dann geht kein sbi/cbi.
Die I/O-Bits liegen fest, was fehlt ist die passende Operation zum Test
ob gesetzt. Auch ein TEST Befehl, d.h. ein AND ohne Resultat, wäre
hilfreich.
In der -O3 Variante ändert insbesondere diese Aspekt, denn bei
vollständigem unrolling liegen die Bits fest und damit wird die
Testoperation von MOV/AND/BREQ zu SBIC.
Was spricht gegen USI, wenn's dann ganz schnell gehen muß?
Laut Atmel Doku "SPI Master Operation Example" (der zweite Teil mit
"enrolled loop") schaft man damit F_CPU/2.
Gruß,
Jürgen
Moin,
A. K. schrieb:> Du meinst, ein I/O-Port sei effizienter als ein Prozessorregister??
unter umständen schon. Betrachte die GPIOR's als globale Variablen die
in einen Register liegen. Dieses dürfte für den Compiler/Optimizer ein
gefundenes Fressen sein, wenn der Funktionsaufruf von set_leds(...) und
was davor kommt, inline wird. Ist aber zugegebenermaßen stark vom
Programmfluss abhängig.
MfG
A. K. schrieb:> jw schrieb:>>> Was spricht gegen USI, wenn's dann ganz schnell gehen muß?>> Vermutlich der Umstand, dass er 3 davon bräuchte ;-).
Und sobald es 4 werden wird es langsamer als die Variante mit der
Variable.
Sauger schrieb:> unter umständen schon. Betrachte die GPIOR's als globale Variablen die> in einen Register liegen. Dieses dürfte für den Compiler/Optimizer ein> gefundenes Fressen sein, wenn der Funktionsaufruf von set_leds(...) und> was davor kommt, inline wird. Ist aber zugegebenermaßen stark vom> Programmfluss abhängig.
Der aufruf an set_leds() ist vollkommen unkritisch (davor und danach
wird eh 250ms gepennt). Es geht nur darum, die bits so schnell wie
möglich in das Register reinzukrachen, damit man es nicht sieht, dass
ich zu blöd war, ein register mit Output Latches zu verwenden.
jw schrieb:> Naja, deshalb doch seriell, oder?> (164(Q7) an (in)164(Q7) an (in)164, paralleler Takt)
Nein, hat jeder einen eigenen pin (bleiben eh genug übrig). Clock ist
gemeinsam.
Egal.. Ich löte jetzt Kondensatoren an die LEDs und dann hat sichs...
;-)
Sauger schrieb:> unter umständen schon. Betrachte die GPIOR's als globale Variablen die> in einen Register liegen.
In einem I/O-Register, ja.
> Dieses dürfte für den Compiler/Optimizer ein> gefundenes Fressen sein, wenn der Funktionsaufruf von set_leds(...) und> was davor kommt, inline wird.
Inwieweit es sinnvoll sein könnte, ein Prozessorregister durch ein
GPIORx zu ersetzen, das kann ich absolut nicht erkennen. Mit ist kein
einziger Befehl bekannt, der mit GPIORx kürzer und/oder schneller ginge
als mit einem Prozessorregister.
Die GPIORs sind genau dann sehr praktisch wenn man globale(!) Flagbits
irgendwo unterbringen will, weil man dann die CBI/SBI/SBIC/SBIS-Befehle
an Stelle LDS/.../STS verwenden kann. Gegenüber Byte-Variablen sind die
Lade/Speicheroperationen immerhin ein bissel schneller. Beide Fälle sind
hier nicht gegeben.
Stefan Noack schrieb:> jw schrieb:>> Naja, deshalb doch seriell, oder?>> (164(Q7) an (in)164(Q7) an (in)164, paralleler Takt)>> Nein, hat jeder einen eigenen pin (bleiben eh genug übrig). Clock ist> gemeinsam.>> Egal.. Ich löte jetzt Kondensatoren an die LEDs und dann hat sichs...> ;-)
Und das nächste mal Schieberegister mit getrenntem Ausgangsregister.
Dann stellt sich das Problem erst gar nicht.
Karl heinz Buchegger schrieb:> Stefan Noack schrieb:>> jw schrieb:>>> Naja, deshalb doch seriell, oder?>>> (164(Q7) an (in)164(Q7) an (in)164, paralleler Takt)>>>> Nein, hat jeder einen eigenen pin (bleiben eh genug übrig). Clock ist>> gemeinsam.>>>> Egal.. Ich löte jetzt Kondensatoren an die LEDs und dann hat sichs...>> ;-)>> Und das nächste mal Schieberegister mit getrenntem Ausgangsregister.> Dann stellt sich das Problem erst gar nicht.
Ich habs ja verstanden...
Ich glaube nun ist alles gesagt, was hierzu gesagt werden kann.
Mahlzeit,
A. K. schrieb:> Die GPIORs sind genau dann sehr praktisch wenn man globale(!) Flagbits> irgendwo unterbringen will, weil man dann die CBI/SBI/SBIC/SBIS-Befehle> an Stelle LDS/.../STS verwenden kann. Gegenüber Byte-Variablen sind die> Lade/Speicheroperationen immerhin ein bissel schneller. Beide Fälle sind> hier nicht gegeben.
Wenn man die Flagbits zwecks Visualisierung auf Leuchtidoten legen
möchte schon :-). Wird aber OT dem TE wurde geholfen, damit kann das
Thema knitterfrei abgeschlossen werden.
MfG
Peter Dannegger schrieb:> Wenns schnell schieben soll, bastele zuerst die 8 Portbytes zusammen und> gebe dann aus>> Das Schieben dauert dann nur 24 Zyklen.>
Passiert das bei -O3 nicht sowieso?
EDIT:
oh.. und wenn man die mit CLK geODERten auch noch vorher ausrechnet sind
es nur noch 16 takte :D Ich denke, ich werde das mal probieren...
2nd EDIT:
Könnte man die eingabewete auch als Array übergeben, ohne dass es
langsamer wird?
Stefan Noack schrieb:> Passiert das bei -O3 nicht sowieso?
Der Compiler ist doch kein Hellseher, er weiß nicht auf was es Dir
ankommt.
Ein Compiler wird immer so wenig wie möglich Variablen verwenden.
Das Aufblasen von 3 auf 8 Register-Variablen mußt Du erzwingen.
Es kann durchaus sein, daß Du fast_out() noch als noinline definieren
mußt, damit es nicht wieder zurückoptimiert wird.
Peter
Stefan Noack schrieb:> verliere ich etwas, wenn ich ein array verwende?
Ich denke, ja.
Schau mal ins Listing.
Die 8 Variablen müssen in Registern vorliegen, sonst gewinnst Du nichts.
Peter
Du darfst natürlich keine Wunder erwarten. Das Auge ist logarithmisch.
Wenn die Schiebezeit halbiert wird, hast Du nicht den Eindruck, daß das
Aufblitzen der ausgeschalteten LEDs auch nur halb so hell ist.
Sinnvoll ist auch, alle 100Hz eine neue Ausgabe zu machen. Dann sieht
man das Aufblitzen nicht mehr.
Peter