Forum: Mikrocontroller und Digitale Elektronik LSB <----> MSB tauschen in c


von seacrash (Gast)


Lesenswert?

Hallo,

ich muß eine vorhandene Variable x folgendermaßen umschreiben:

x = 0b  MSB    Bit6    Bit5    Bit4    Bit3    Bit2    Bit1    LSB

in

Y = 0b  LSB    Bit1    Bit2    Bit3    Bit4    Bit5    Bit6    Bit7

in asm kein Problem

lsr x
rol y

und das 8 mal -> einfache schnelle Sache.
Aber wie mache ich das schnell und einfach in C?
(mir fallen leider nur komplexe Lösungen ein)

Dank und Gruß

von Marcel (Gast)


Lesenswert?

der IAR C-Compiler kennt den Befehl : _SWAP_BYTES(x);
Ansonsten Bitschubsen in einer Schleife oder?

Gruß Marcel

von Profi (Gast)


Lesenswert?


von TravelRec. (Gast)


Lesenswert?

@Marcel
IAR nutze ich leider nicht :(

@Profi
Danke für die Links, aber auch die bieten keinen nutzbaren
Lösungsansatz in C, es sei denn man hat Flashspeicher übrig wie Sand am
Meer.

Der Profi == Profi?

Grüße

von seacrash (Gast)


Lesenswert?

sorry, vorhergehender Name war noch eingetragen "TravelRec." das bin
nicht ich, sondern mein Kollege.

 Gruß

von Detlef _. (detlef_a)


Lesenswert?

http://www.mikrocontroller.net/forum/read-1-304028.html#304197

Elegant und schnell wenn man gut schieben kann:

byte=((byte&0xaa)>>1)|((byte&0x55)<<1);
byte=((byte&0xcc)>>2)|((byte&0x33)<<2);
// Das folgende ist der AVR Befehl 'swap'
byte=((byte&0xf0)>>4)|((byte&0x0f)<<4);

Der AVR schiebt ja so schlecht. Da ist es vermutlich optimal, die
beiden Nibbles des Byte über ne 16ner Tabelle zu spiegeln und dann die
Hälften mit 'swap' zu tauschen.

Cheers
Detlef

von Mark (Gast)


Lesenswert?

Hab hier mal ein Beispielprogramm.
Wenn du eine Schleife daraus machst, wird's kürzer.

int main(void)
{
  unsigned char byte_1,byte_2,temp;

  byte_1 = 0x0f;
  byte_2 = 0;


  {
    temp   |= (byte_1 & 0x80);
    byte_2 |= (temp >> 7);

    temp   |= (byte_1 & 0x40);
    byte_2 |= (temp >> 5);

    temp   |= (byte_1 & 0x20);
    byte_2 |= (temp >> 3);

    temp   |= (byte_1 & 0x10);
    byte_2 |= (temp >> 1);

    temp   |= (byte_1 & 0x08);
    byte_2 |= (temp << 1);

    temp   |= (byte_1 & 0x04);
    byte_2 |= (temp << 3);

    temp   |= (byte_1 & 0x02);
    byte_2 |= (temp << 5);

    temp   |= (byte_1 & 0x01);
    byte_2 |= (temp << 7);

  }

    return 0;
}

von Marko B. (Gast)


Lesenswert?

Spontan fällt mir dazu ein:
1
unsigned char in = 0x55;
2
unsigned char out = 0;
3
4
char i;
5
6
for(i=0;i<8;i++)
7
    out |= (in & 0x80>>i) ? 1<<i : 0 ;

oder für CPUs ohne barrel shifter:
1
unsigned char in = 0x55;
2
unsigned char out = 0;
3
unsigned char mask = 0x80;
4
5
char i;
6
7
for(i=0;i<8;i++) {
8
    out |= (in & mask) ? 1 : 0;
9
    out<<=1;
10
    mask>>=1;
11
}

von seacrash (Gast)


Lesenswert?

Hallo Mark,

hab´s ähnlich realisiert

uchar c=0;
for(h=1; h ; h<<1){
      y |= (x & h) << (7 - c++);
}


Grüße

von Detlef _. (detlef_a)


Lesenswert?

>>>Aber wie mache ich das schnell und einfach in C?

Dein Code ist weder das eine noch das andere, seufz.

Cheers
Detlef

von seacrash (Gast)


Lesenswert?

@Detlef _A

so ist´s,
bessere Idee? Nur her damit!! :)

Gruß

von Marko B. (Gast)


Lesenswert?

Also meine zweite Version ist doch gar nicht schlecht, mein Compiler
macht da 45 Bytes draus. Wer bietet weniger? =)

Detlef: Deine Version benötigt kompiliert 76 Bytes.

von Marko B. (Gast)


Lesenswert?

Hmm, jetzt habe ich die Optimierung (auf Größe wohlgemerkt) im Compiler
aktiviert, und plötzlich braucht mein Code 50 Bytes und Detlefs 42.

Aber ich habe festgestellt, daß mein Code unnötig umständlich war,
diese Version benötigt nur 29 Bytes bei aktivierter Optimierung:
1
char i,out;
2
3
for(i=0;i<8;i++) {
4
    out >>= 1;
5
    out |= (byte & 0x80);
6
    byte <<= 1;
7
}

von Detlef _. (detlef_a)


Lesenswert?

Hallo Marko,

Du hast die Initialisierung von 'out' nicht drin. Der Code sollte
schnell sein, hatte ich verstanden. Hast Du mal die benötigten
Prozessorcycles für die beiden Versionen ausgezählt, das wäre
interessant!?

@seacrash: Wer lesen kann ist klar im Vorteil!

Cheers
Detlef

von Läubi (Gast)


Lesenswert?

Inline Assembler? wäre jezt nur sone Idee :)

von seacrash (Gast)


Lesenswert?

@Marko B.
Dein Code ist für meinen Zweck ausreichend schnell und klein.
Danke.


@Detlef_A
>Wer lesen kann ist klar im Vorteil!
??

Gruß

von Marko B. (Gast)


Angehängte Dateien:

Lesenswert?

Hi Detlef,

hast Recht. Das kam daher, daß ich den Code in GCC teste, aber in NC30
kompiliere um den Platzbedarf auf dem Controller zu bestimmen.
Witzigerweise braucht meine Funktion mit der Initialisierung nur 25
Byte.

Hier der genaue Code, den ich verglichen habe:
1
unsigned char mirror(unsigned char byte)
2
{
3
    char out = 0, i;
4
5
    for(i=0;i<8;i++) {
6
        out >>= 1;
7
        out |= (byte & 0x80);
8
        byte <<= 1;
9
    }
10
    return out;
11
}

und Deine Version:
1
unsigned char mirror(unsigned char byte)
2
{
3
    byte=((byte&0xaa)>>1)|((byte&0x55)<<1);
4
    byte=((byte&0xcc)>>2)|((byte&0x33)<<2);
5
    byte=((byte&0xf0)>>4)|((byte&0x0f)<<4); 
6
7
    return byte;
8
}

Ich habe die Taktzyklen nicht genau gezählt, schätze aber, daß deine
Version ca. um den Faktor 2-3 schneller ist (Listing ist im Anhang).
Das aber nur, weil der R8C einen Barrel Shifter hat. Auf einem AVR
dürfte meine Version gleichauf oder sogar schneller sein. Vielleicht
kann ja mal jemand den Code für AVR compilieren und vergleichen, ich
hab gerade keinen Compiler zur Hand.

Läubi: logisch, in Assembler geht das viel einfacher und schneller, da
kann man einfach durchs Carry shiften. Ist dann aber nicht portabel.

von Detlef _. (detlef_a)


Lesenswert?

Hallo Marko,

ja, sowas Schönes wie barrel-shifter hat der AVR nicht. Die letzte
C-Zeile von mir ist allerdings auf dem AVR ein einziger Befehl, das
erwähnte 'swap'. Für den AVR sollte das mit swap und einer Tabelle
aus 8  oder 16 chars schneller geht: Halbbyte ist zweimal die Adresse
in die Tabelle, 16 Einträge zu ja halbem Byte macht 8 Byte. Naja, ist
was für nen Novembersonntag.

Cheers
Detlef

von Profi (Gast)


Lesenswert?

@seacrash:
Wenn das noch nicht genug "profimassig" war, wie wärs dann mit:
bit=0x01;out=0;
for (mask=0x80;mask;mask/=2){
  if (x&mask){y|=bit;}
  bit*=2;}

Oder ohne Schleife:
  y=x&128?1:0 | x&64?2:0 | x&32?4:0 | x&16?8:0 |
    x& 8?16:0 | x&4?32:0 | x&2?64:0 | x&1?128:0;
Inwieweit der Compiler das elegant löst??

Oder brute force:
  if(x&128){y=1;}else{y=0;}
  if(x& 64){y|=  2;}
  if(x& 32){y|=  4;}
  if(x& 16){y|=  8;}
  if(x&  8){y|= 16;}
  if(x&  4){y|= 32;}
  if(x&  2){y|= 64;}
  if(x&  1){y|=128;}


Außerdem:
for(h=1; h ; h<<1){    dürfte nicht die gewünschte Wirkung haben...

for(h=1; h ; h<<=1){   oder
for(h=1; h ; h*=2){

von Marko B. (Gast)


Lesenswert?

Profi: alles etwas umständlich, aber es hat mich auf eine Idee gebracht,
man braucht die Zählvariable ja gar nicht:
1
unsigned char mirror(unsigned char byte)
2
{
3
  char out = 0;
4
5
  for(;byte;byte<<=1) {
6
    out >>= 1;
7
    out |= (byte & 0x80);
8
  }
9
10
  return out;
11
}

Nur noch 21 Bytes. =)

von Marko B. (Gast)


Lesenswert?

Halt, stopp, Kommando zurück, das funktioniert ja gar nicht. Die
Schleife kann dann ja weniger als 8 mal durchlaufen.

von Profi (Gast)


Lesenswert?

in meiner 3. Zeile muss es statt out=0 natürlich y=0 heißen.

oder funktioniert's doch, denn bei x=0 kann die Schleife beendet
werden, falls das Ergebnis y nicht geschoben werden muss:

  j=128;y=0;
  do{
    if(x&1){y|=j;}
    j/=2;}
  while(x/=2);    //kann eine Warnung geben, ist so beabsichtigt

oder als for-Schleife geschrieben:
  for(j=128,y=0;x;x/=2){
    if(x&1){y|=j;}
    j/=2;}

evtl. kann es günstiger sein, andersherum zu schieben (falls die
Schleife wg. der Bitmuster so schneller beendet wird):
  for(j=1,y=0;x;x*=2){
    if(x&128){y|=j;}
    j*=2;}

Naj, gut für heute mit dem Geschiebe ;-)

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.