Forum: Mikrocontroller und Digitale Elektronik Bitmanipulation, verschieben von Bits, optimierung


von Mathias F. (savag)


Lesenswert?

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ß

von Peter II (Gast)


Lesenswert?

Mathias Feld schrieb:
> char B2 = 0b00000000;
>   B2 |= (((PIND & (1<<5))>>5) | ((PIND & (1<<6))>>5));

diese kann durchaus schneller sein

char B2 = 0b00000000;
if ( (PIND & (1<<5) )
   B2 |= 1;


if ( (PIND & (1<<6) )
   B2 |= 2;


müsste man mal den asm code vergleichen.

von Stefan E. (sternst)


Lesenswert?

Alternative:
1
uint8_t B2 = (PIND >> 5) & 0x03;

von Εrnst B. (ernst)


Lesenswert?

Wenn's mehrere Bits zum umsortieren sind:
1
uint8_t  vermischt=__builtin_avr_insert_bits (0x04217563, abc, 0);
http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/AVR-Built_002din-Functions.html

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

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?

von Εrnst B. (ernst)


Lesenswert?

und vergleich's mit dem ASM-Code, den __builtin_avr_insert_bits 
ausspucken würde.

von Erich (Gast)


Lesenswert?

>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

von Falk B. (falk)


Lesenswert?

@Ε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!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Für welches OS denn? Von Atmel gibt es builds der 4.7.2 für Windows und 
für Linux. Für 4.7.2 MinGW32 gibt's auch

http://lists.gnu.org/archive/html/avr-gcc-list/2012-09/msg00024.html

von Falk B. (falk)


Lesenswert?

WinXP

von Leo C. (rapid)


Angehängte Dateien:

Lesenswert?

Anhänge mit folgendem Compiler:
1
leo@cb:~/src/avr/tmp$ avr-gcc -v
2
Using built-in specs.
3
COLLECT_GCC=avr-gcc
4
COLLECT_LTO_WRAPPER=/usr/lib/gcc/avr/4.7.2/lto-wrapper
5
Target: avr
6
Configured with: ../src/configure -v --enable-languages=c,c++ --prefix=/usr/lib --infodir=/usr/share/info --mandir=/usr/share/man --bindir=/usr/bin --libexecdir=/usr/lib --libdir=/usr/lib --enable-shared --with-system-zlib --enable-long-long --enable-nls --without-included-gettext --disable-libssp --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=avr
7
Thread model: single
8
gcc version 4.7.2 (GCC)

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Für nur 2 Bits braucht es doch keine Wumme wie 
__builtin_avr_insert_bits...

von Jörg E. (jackfritt)


Lesenswert?


von Johann L. (gjlayde) Benutzerseite


Lesenswert?

*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.

von Falk B. (falk)


Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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?

von Jörg E. (jackfritt)


Lesenswert?

@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

von Falk B. (falk)


Lesenswert?

Hmm, na gut, so wichtig ist mir der AVR GCC 4.8 nicht, der 2010 läuft 
1A, war nur mal einfach so.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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

von @ Johan (Gast)


Lesenswert?

Was verwendest du denn für einen Editor oder IDE?

von Jörg E. (jackfritt)


Lesenswert?

Eclipse mit avr plugin
http://avr-eclipse.sourceforge.net/wiki/index.php/Plugin_Download
Ziemliches gebastelt mit der letzen eclipse Release ;)
Aber läuft halt.
Deswegen ging das mit dem path nicht
 weil der Rest sich noch auf die alte
Installation bezog.

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.