Hallo Leute,
hier gehts darum den vorhandenen Code zu Optimieren oder andere
möglichkeiten aufzugreifen. Der Code funktioniert!
Ich habe ein Atmega 8 bei dem ich den Status der Portregister zu einem
Char zusammenfügen möchte um es dann mit einem PC Programm auszuwerten.
dazu verwende ich momentan
char B2 = 0b00000000;
B2 |= (((PIND & (1<<5))>>5) | ((PIND & (1<<6))>>5));
hier wird das Bit PIND5 um 5 stellen nach rechtsverschoben und liegt
dann in der var B2 genauso bei PIND6
aus 0b00100000 und 0b01000000
wird nun 0b00000011
Kann man das auch anders machen?
Gruß
Mathias Feld schrieb:> char B2 = 0b00000000;
Hallo!
Bei Bitmanipulationen verwende ich zur Sicherheit normalerweise
"unsigned char", damit es bei negativen Werten (gesetztes Bit 7) keine
Überraschungen gibt.
> B2 |= (((PIND & (1<<5))>>5) | ((PIND & (1<<6))>>5));
Gleichbedeutend mit
1
B2|=(PIND&1<<5)>>5|(PIND&1<<6)>>5;
falls ich mich nicht irre. Manchmal sind weniger Klammern besser
(zumindest übersichtlicher) als mehr. Zuerst könntest du das ">>5"
rausziehen:
1
B2|=((PIND&1<<5)|(PIND&1<<6))>>5;
Dann wird einmal weniger geschoben. Die beiden zu lesenden Bits lassen
sich natürlich auch auf einmal lesen:
1
B2|=(PIND&(1<<5|1<<6))>>5;
Ein guter Compiler optimiert das aber möglcherweise selbst. Den Rest
hast du bei C nicht mehr in der Hand, du bist auf den Compiler
angewiesen. Wenn der Compiler schlau ist, schaut der erzeugte Code
ungefähr so aus:
1
in temp,PIND
2
andi temp,0b01100000
3
swap temp
4
lsr temp
5
or B2,temp
Braucht 5 CPU-Takte. Es gibt noch einen universelleren, komplett anderen
Weg, der aber ebenfalls 5 Takte braucht.
Markus Weber schrieb:> Die beiden zu lesenden Bits lassen> sich natürlich auch auf einmal lesen:>>
1
B2|=(PIND&(1<<5|1<<6))>>5;
>> Ein guter Compiler optimiert das aber möglcherweise selbst.
Um da einzuhaken:
Nein das darf er nicht. (Also die Zusammenfassung der beiden
Maskieroperationen)
Der Grund dafür ist, dass *PIND als volatile markiert ist, d.h. jeder
Zugriff muss genau so ausgeführt werden, wie er da steht.
Und da macht es nun mal einen Unterschied, ob man 2 mal kurz
hintereinander auf den PIND zugreift (in der Zwischenzeit könnten sich
ja auch Bits verändert haben) oder ob man nur einen einzigen Zugriff
hat.
Es geht nur um die Anzahl der Zugriffe! Denn: Die Reihenfolge, in der
die Zugriffe auf das ausmaskieren von Bit 5 bzw. Bit 6 erfolgen, hat man
damit nicht in der Hand. Die kann der Compiler wieder machen wie er
will. Nur weil man schreibt
i = a() + b()
bedeutet das nicht, dass die zuerst die Funktion a aufgerufen wird und
dann b. Genau das ist gemeint, wenn man sagt: Operatoren Reihenfolge ist
etwas anderes wie Auswertungsreihenfolge.
Markus Weber schrieb:> B2 |= ( (PIND & 1<<5) | (PIND & 1<<6) )>>5;> Dann wird einmal weniger geschoben. Die beiden zu lesenden Bits lassen> sich natürlich auch auf einmal lesen:> B2 |= ( PIND & (1<<5 | 1<<6) )>>5;
was aber nicht mehr das gleiche ist, denn PIND ist volatile und muss bei
jedem Zugriff gelesen werden. Hier darf er nicht weiter optimieren.
Markus Weber schrieb:> Ein guter Compiler optimiert das aber möglcherweise selbst.
Karl Heinz und Peter: Ihr habt natürlich Recht, PIND ist keine "normale"
Variable, der Compiler hat in diesem Fall weniger Freiheiten bei der
Optimierung. Mein Fehler. Bleibt also nur die Vor-Optimierung von Hand.
Würde mich trotzdem interessieren, welchen Assembler-Code der Compiler
dann draus macht. Mathias, falls du das Programm übersetzt, magst du mal
nachschauen?
>PIND ist keine "normale" Variable>Bleibt also nur die Vor-Optimierung von Hand.
Du musst eben selbst erst überlegen, wann (wie oft) diese
Spezialregister explizit gelesen bzw. geschrieben werden soll oder muß.
Einzelne "rd" bzw. "wr" Zugriffe sollten zwecks Optimierung
grundsätzlich in eine oder ggf. mehrere "temp" Zwischenvariable(n)
erfolgen.
Die Zwischenvariable(n) kannst du dann ruhig beliegig weiterverwursteln,
dann kann der Compiler diese tatsächlich optimieren.
Bei schrittweiser Bearbeitung ruhig mehrere Zwischenvariablen temp1,
temp2 usw. verwenden, auf jede nur einmalig zuweisen. Das macht den Code
lesbarer und letztendlich optimiert der Compiler alles Zwischenvariablen
weg (bei geeigneter Optimierungseinstellung und bei einem uC der
ausreichend Arbeitsregister hat).
Und mit mehreren dann eher kurzen Anweisungszeilen kann man auch noch
ordentliche Kommentare hinschreiben.
Gruss
@Εrnst B✶ (ernst)
>Wenn's mehrere Bits zum umsortieren sind:>uint8_t vermischt=__builtin_avr_insert_bits (0x04217563, abc, 0);>http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/AVR-Bu...
Ich hab hier die letzte offizielle Version von WinAVR von 2010. Läuft
für meine Zwecke problemlos. Aber da ist nur GCC 4.3.3 drin. Dieses
Makro ist dort noch nicht enthalten. Gibt es irgendwo eine fertig
nutzbare, aktuelle GCC Version, die ich einfach mit WinAVR 2010 nutzen
kann? Nein, ich möchte den Compiler und das Ganze Geraffel nicht selber
bauen!
Edit: (was ich sagen wollte)
__builtin_avr_insert_bits ist ja mal was interessantes, benutzt "load
bit" und "store bit" Befehle.
In diesem Spezialfall ist aber "herkömmliches" Bitschieben effektiver,
insbesondere die Variante: erst schieben, dann maskieren.
*ARGL* was soll das denn???
Jörg Esser schrieb:> Nachfolgend das Howto:> [...]> Nun alle Verzeichnisse vom alten WinAVR2010 sichern.> Bei mir sind das folgende:> avr> bin> lib> share> Ich habe sie einfach umbenannt in:> avr.2010> bin.2010> lib.2010> share.2010>> Jetz alle Unterverzeichnisse aus avr-gcc-4.8_2013-03-06_mingw32> in das WinAVR-20100110 kopieren.> Dann noch avrdude.conf,avrdude.exe undlibusb0.dll> von bin.2010 nach bin kopieren>> Damit sollte nun alles wieder funktionieren allerdings mit> neuestem avr-gcc
Es genügt doch PATH zu setzen!
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
@Johann L. (gjlayde) Benutzerseite
>Es genügt doch PATH zu setzen!
Und wie genau? Ihc hab die beiden Einträhe in Path verändert, geht aber
nicht, im AVR Studio sind noch die alten EInträge vom Pfad. Wenn man die
mauell verbiegt (und vorher das Verzeichnis util, wo make drin ist
kopiert), läuft es scheinbar, aber es fehlt am Ende die Auflistung des
Speicherverbrauchs.
Mit AVR-Studio kann ich nicht weiterhelfen, das war (und ist) mit
einfach zu fett...
Oder meinst du den avr-size Hack von Atmel, der nicht in die Binutils
aufgenommen wird?
@falk
Hatte ich doch auch geschrieben welche Datei man für den
speicherverbrauch ändern muss?
@Johann
Hat bei meinem komplexen Haufen nicht funktioniert und ich hatte kein
Bock alles neu zu installieren. Also habe ich minimal "Invasiv"
gearbeitet :)
Gruß
Jörg
Wie gesagt: Eine Möglichkeit ist die Installation in einen anderen Pfad
und dann Auswahl der gewünschten Toolchain z.B. im Makefile.
So einfach kann das Leben sein — es sei denn, man verwendet Atmel Studio
:-P
Ich hab über ein duzend Toolchains installiert, wenn ich da immer
kopieren müsste, ne ne ne