Es kam hier zwar bestimmt schon ein paarmal vor, aber die Suche brachte
kein Ergebnis.
Wie ändere ich am einfachsten die Bitreihenfolge in C ?
In Assembler ist es klar: Die Bits der Reihe nach nach rechts schieben,
aber mit einem Linksshift wieder auffangen.
Bei dem dummen BMP Format ist alles genau anderst rum: Es wird von
unten nach oben gespeichert.
Mikrocontroller ist ein M16C.
Wenn es schnell gehen soll und 256 Byte Platz vorhanden sind:
konstantes byte-Array[256] anlegen mit den verdrehten Werten:
msb_to_lsb[256] = {0x00,0x80,0x40,0xc0 ...};
Im Programmablauf dann den Wert, der gedreht werden soll, als Index der
Tabelle verwenden:
my_erg = msb_to_lsb[my_byte];
Beim M16C hast Du ja genügend Flash, dass die 256 nicht weh tun, und
zur Laufzeit schneller geht es wohl kaum.
Stefan
Habe die Tabelle, die ich mal benutzt habe, gefunden. Ist zwar in
Assembler, aber nach C müuss nur wenig geändert werden:
spiegeltab
db 0,128, 64,192, 32,160, 96,224
db 16,144, 80,208, 48,176,112,240
db 8,136, 72,200, 40,168,104,232
db 24,152, 88,216, 56,184,120,248
db 4,132, 68,196, 36,164,100,228
db 20,148, 84,212, 52,180,116,244
db 12,140, 76,204, 44,172,108,236
db 28,156, 92,220, 60,188,124,252
db 2,130, 66,194, 34,162, 98,226
db 18,146, 82,210, 50,178,114,242
db 10,138, 74,202, 42,170,106,234
db 26,154, 90,218, 58,186,122,250
db 6,134, 70,198, 38,166,102,230
db 22,150, 86,214, 54,182,118,246
db 14,142, 78,206, 46,174,110,238
db 30,158, 94,222, 62,190,126,254
db 1,129, 65,193, 33,161, 97,225
db 17,145, 81,209, 49,177,113,241
db 9,137, 73,201, 41,169,105,233
db 25,153, 89,217, 57,185,121,249
db 5,133, 69,197, 37,165,101,229
db 21,149, 85,213, 53,181,117,245
db 13,141, 77,205, 45,173,109,237
db 29,157, 93,221, 61,189,125,253
db 3,131, 67,195, 35,163, 99,227
db 19,147, 83,211, 51,179,115,243
db 11,139, 75,203, 43,171,107,235
db 27,155, 91,219, 59,187,123,251
db 7,135, 71,199, 39,167,103,231
db 23,151, 87,215, 55,183,119,247
db 15,143, 79,207, 47,175,111,239
db 31,159, 95,223, 63,191,127,255
Stefan
Danke, die Idee ist gut.
Ich hatte bisher die Idee das ganze Harwaremäßig über den Datenbus zu
machen:
Daten an eine bestimmte Adresse senden, dort befindet sich ein Latch,
dessen Ausgang in umgedrehter Folge mit den den Eingängen verbunden
ist. Liest man den Wert, hat er eine umgedrehte Bitfolge.
Mir ist das auch son passiert:
PortB mit Eagle an 8 Solidstate-Relias, von da auf den Steckverbinder
geroutet und nun war der PortB0 der 8te ausgang.
Das Workaround sah dann ( in FastAVR ) so aus:
Die Variablen sind vorab Global definiert.
Function bitswap(b_in As Byte) As Byte
wrong_in = b_in
For xx= 8 To 1 Step -1
Shift(Left,1,wrong_in)
Shift(Right,1,ok_out)
k = wrong_in And &h0100
If k = &h0100 Then
ok_out =ok_out + 128
End If
k = 0
Next xx
Return ok_out
End Function
Gruß
Axel
Hatte mit ähnlichem Problem auch schon durch einen Layoutfehler zu
kämpfen. Da war aber nur der halbe Datenbus verdreht. Das Workaround
sah bei mir etwa so aus:
neu =
((alt&&128)>>7)||((alt&&64)>>5)||((alt&&32)>>3)||((alt&&16)>>1)||((alt&&
8)<<1)||((alt&&4)<<3)||((alt&&2)<<5)||((alt&&1)<<7)
Codesparend, aber natürlich langsamer.
Gruß,
Markus_8051
Es gibt dafür ein bekanntes Makro (u. a. beim Programm fortune dabei)
mit logarithmischer Zeitkomplexität:
// reverse (mirror) u32 number n
#define mc_REVERSE(n) \
{\
n = ((n >> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa); \
n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc); \
n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0); \
n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00); \
n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000); \
}
Wie man das auf 8 Bit reduziert oder 128 Bit Zahlen ausbaut, ist
offnsichtlich.
Allerdings bringt das Makro (so genial wie's auch ist) bei 8 Bit
praktisch keinen Vorteil mehr gegenüber der expliziten Schleife.
(Been there, done that. Das Thema kommt regelmäßig hoch.)
Markus_8051:
sollte es nicht besser statt
neu =
((alt&&128)>>7)||((alt&&64)>>5)||((alt&&32)>>3)||((alt&&16)>>1)||((alt&&
8)<<1)||((alt&&4)<<3)||((alt&&2)<<5)||((alt&&1)<<7)
neu =
((alt&128)>>7)|((alt&64)>>5)|((alt&32)>>3)|((alt&16)>>1)|((alt&8)<<1)|((
alt&4)<<3)|((alt&2)<<5)|((alt&1)<<7)
heißen?
Hoch lebe der M16C und der M32C!
sieht hier einer durch?...
muss bei dem ganze hin und hergeschiebe nicht die augangsvariable
"alt" irgentwo gesichert werden? wenn die sagen ((alt&128>>7) dann
schiebt er, so habe ich das jetzt nach langem mangala lesen erkannt,
das höchste bit 7 mal nach rechts. was aber, wenn da schon ein bit
steht? ganz hinten steht in der zeile: alt&1<<7rüber. da steht doch
jeztzt das bit drinne, was eben noch gaanz links stand. Ich mein, wenn
ich zwei sachen über Cut and Paste tauschen will, muss ich das
"linke" Teil erstmal wo ablegen, dann das "rechte" Teil dahin
packen, wo das "linke" stand, und erst jetzt kann ich das "linke"
teil aus der Ablage nehmen und zum "rechten" packen. Das machte ihr
hier alles so in einer Zeile auf einen Rutsch?
Kein Wunder, wenn mein Windows sich ständig weghängt.hihi ;-)))
Schönen Feierabend, bis morgen
Axel
alt bleibt immer gleich, weil ihm ja nichts zugewiesen wird.
neu wird über die 8 ORs bitweise zusammengebaut:
(&128)>>7 alt-Bit 7 nach neu-Bit 0
(& 64)>>5 alt-Bit 6 nach neu-Bit 1
(& 32)>>3 alt-Bit 5 nach neu-Bit 2
(& 16)>>1 alt-Bit 4 nach neu-Bit 3
(& 8)<<1 alt-Bit 3 nach neu-Bit 4
(& 4)<<3 alt-Bit 2 nach neu-Bit 5
(& 2)<<5 alt-Bit 1 nach neu-Bit 6
(& 1)<<7 alt-Bit 0 nach neu-Bit 7
wenn ich zwei sachen über Cut and Paste tauschen will
Hier wird ja nichts in-place getauscht.
was aber, wenn da schon ein bit steht?
Da steht nichts, weil nicht neu (und erst recht nicht alt) modifiziert
wird (z.B. alt&=), sondern es wird ein neuer Wert zugewiesen, so wie
bei neu=12+34.
Ist es jetzt klarer?
@Profi:
Gute Frage, das eine war das bitweise, das andere das logische UND.
Hier muß natürlich das bitweise hin. Frag mich aber nicht, ob das jetzt
& oder && war. Gleiches gilt natürlich auch für das ODER.
Noch eine Variante:
Es empfiehlt sich, für alt und neu register-Variablen zu verwenden.
register int alt,neu;
neu = (alt&128) >>1 ; //alt-Bit 7 nach neu-Bit 6
neu |= (alt=<1) &128; //alt-Bit 6 nach neu-Bit 7
neu =>1; //neu-Bit 6&7 nach neu-Bit 5&6
neu |= (alt=<1) &128; //alt-Bit 5 nach neu-Bit 7
neu =>1; //neu-Bit 5-7 nach neu-Bit 4-6
neu |= (alt=<1) &128; //alt-Bit 4 nach neu-Bit 7
neu =>1; //neu-Bit 4-7 nach neu-Bit 3-6
neu |= (alt=<1) &128; //alt-Bit 3 nach neu-Bit 7
neu =>1; //neu-Bit 3-7 nach neu-Bit 2-6
neu |= (alt=<1) &128; //alt-Bit 2 nach neu-Bit 7
neu =>1; //neu-Bit 2-7 nach neu-Bit 1-6
neu |= (alt=<1) &128; //alt-Bit 1 nach neu-Bit 7
neu =>1; //neu-Bit 1-7 nach neu-Bit 0-6
neu |= (alt=<1) &128; //alt-Bit 0 nach neu-Bit 7
Vorteil ist hier, dass immer nur um EIN Bit geschoben wird, und das bei
den meisten uC (außer sie haben einen Barrel-Shifter) schneller geht als
Schieben um mehrere Bits.
Da ich im Urlaub bin und hier keinen Compiler installiert hab, kann ich
es nicht ausprobieren, vor allem ob
alt=<1
nicht als kleiner gleich compiliert wird. Ich meine natürlich alt um
eins nach links schieben.
Hier wird eine Besonderheit von C ausgenutzt, nämlich dass eine
Zuordung selbst als Wert verwendet werden kann.
neu |= (alt=<1) &128; ist eine Kurzschreibweise für
alt=<1;
neu |= alt &128;
Das ist auch der Grund, warum z.B.
if (i=8) {....}
syntaktisch richtig ist. Der Variablen i wird 8 zugewiesen und dann ist
die if-Abfrage
if (8) {....}
natürlich immer true. In diese gemeine Falle ist wohl jeder schonmal
getappt.
Gute Compiler fragen natürlich, ob es nicht besser
if (i==8) {....}
heißen soll.
In Assembler wäre das simples "Loop-Unrolling", statt schleife also
LSR Input
ROL Output
LSR Input
ROL Output
LSR Input
ROL Output
LSR Input
ROL Output
LSR Input
ROL Output
LSR Input
ROL Output
LSR Input
ROL Output
LSR Input
ROL Output
oder eben einfach
LDI Counter, 8
Loop:
LSR Input
ROL Output
DEC Counter
BRNE Loop
Gruß hagen