Forum: Mikrocontroller und Digitale Elektronik C jedes zweite Bit verwerfen


von Werner (Gast)


Lesenswert?

Hallo,

ich habe einen Bitstrom (uint_8 bindata[8]), von welchem jedes 2. Bit 
verworfen werden soll, weil es redundant ist (aber das Vorgängerbit 
invertiert). Im Moment sieht mein Code so aus:
1
uint8_t bindata[8];
2
uint8_t netdata[4];
3
4
for (i=0;i<64;i+=2)
5
  netdata[i / 16] |= ((bindata[i / 8] >> (i % 8 + 1)) & 0x01) << ((i / 2) % 8);

Problem ist im Moment dass das jeweils erste Nibble der Rohdaten im 
jeweils unteren Nibble der Ausgangsdaten landen, und es müsste doch 
überhaupt viel einfacher gehen? Das Ergebnis soll quasi so sein als ob 
ich die Daten über eine UART schicke, und jedes 2. Bit verloren ginge...

von holger (Gast)


Lesenswert?

>Im Moment sieht mein Code so aus:
>
>  netdata[i / 16] |= ((bindata[i / 8] >> (i % 8 + 1)) & 0x01) << ((i / 2) >% 8);

Für solche Codezeilen sollte Teeren und Federn wieder eingeführt 
werden;)

von Joe F. (easylife)


Lesenswert?

wie wäre es damit (zwar nicht kürzer, aber logischer):
1
uint8_t bindata[8];
2
uint8_t netdata[4];
3
4
uint8_t j;
5
6
for (i=0;i<4;i++)
7
{
8
  j = i*2;
9
  netdata[i]  =  bindata[j] & 0x01;
10
  netdata[i] |= (bindata[j] & 0x04) >> 1;
11
  netdata[i] |= (bindata[j] & 0x10) >> 2;
12
  netdata[i] |= (bindata[j] & 0x40) >> 3;
13
14
  j++;
15
  netdata[i] |= (bindata[j] & 0x01) << 4;
16
  netdata[i] |= (bindata[j] & 0x04) << 3;
17
  netdata[i] |= (bindata[j] & 0x10) << 2;
18
  netdata[i] |= (bindata[j] & 0x40) << 1;
19
}

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@Werner (Gast)

>überhaupt viel einfacher gehen? Das Ergebnis soll quasi so sein als ob
>ich die Daten über eine UART schicke, und jedes 2. Bit verloren ginge...

Du willst ein Byte auf ein Nibble zusammenschieben. Das kann man 
entweder mit einer Tabelle mit 256 Einträgen (sehr schnell aber hoher 
Speicherbedarf) oder einer Berechnungsfunktion (langsam, geringer 
Speicherbedarf) erledigen, siehe Bitmanipulation. Auf dem avr gcc 
gibt es eine Spezialfunktion, die kann die Bits in einem Byte beliebig 
verwürfeln.

http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/AVR-Built_002din-Functions.html


Das hier ist Standard-C, das kann jeder Compiler.
1
uint8_t squeeze(uint8_t data) {
2
  uint8_t tmp=0;
3
  
4
  if (data & (1<<7)) tmp |= (1<<3);
5
  if (data & (1<<5)) tmp |= (1<<2);
6
  if (data & (1<<3)) tmp |= (1<<1);
7
  if (data & (1<<1)) tmp |= (1<<0);
8
9
  return tmp;  
10
}
11
12
uint8_t bindata[8];
13
uint8_t netdata[4];
14
15
for (i=0; i<4; i++) {
16
  netdata[i] = (squeeze(bindata[2*i]) << 4) | squeeze(bindata[2*i+1]);
17
}

von Joe F. (easylife)


Lesenswert?

Falk B. schrieb:
(...)

Hehe, gleicher Ansatz, 2 Implementierungen.

Geht auch viel schneller als TOs Code (keine Divisionen, Modulos und 
Diesdas).

von Falk B. (falk)


Lesenswert?

Die avr gcc Version wäre diese hier.
1
uint8_t tmp;
2
3
for (i=0; i<4; i++) {
4
  tmp = __builtin_avr_insert_bits(0x7531FFFF, bindata[2*i], 0);
5
  netdata[i] = __builtin_avr_insert_bits(0xFFFF7531, bindata[2*i+1], tmp);
6
}

von Frank B. (frank501)


Lesenswert?

Falk B. schrieb:
> mit einer Tabelle mit 256 Einträgen

reichen da nicht 16 Einträge die mit dem zu prüfenden Byte verodert 
werden?

edit:

Nee, nur verodern reicht nicht.
Man müsste die relevanten Bits mit einer Maske aus Einsen und-verknüpfen 
und kann dann mit einer 16 Byte großen Tabelle vergleichen.

: Bearbeitet durch User
von Ralf D. (doeblitz)


Lesenswert?

Frank B. schrieb:
> Falk B. schrieb:
>> mit einer Tabelle mit 256 Einträgen
>
> reichen da nicht 16 Einträge die mit dem zu prüfenden Byte verodert
> werden?

Du brauchst zwar nur 16 Einträge, die sind aber über die halbe Tabelle 
verteilt (sparse table), weshalb sie eben trotzdem mindestens 128 
Einträge groß ist (wenn man vorher die irrelevanten Bits wegmaskiert).

Wenn du anfängst irgendwie rumzurechnen, um mit einer kleinen Tabelle zu 
vergleichen, dann kannst du gleich vollständig rechnen, das macht vom 
Aufwand her auch kaum noch was aus.

von Carl D. (jcw2)


Lesenswert?

Wenn's nur für AVR sein soll:
Inline ASM,
 8 x BLD/BLS,
 2 Byte rein, 1 Byte raus, macht 3 Register, in denen ICE Werte eh schon 
stehen.
 16 Takte, 16 Byte Flash
Und gut is,

von Werner (Gast)


Lesenswert?

Danke für die schönen Lösungen!

Werner

von Klaus (Gast)


Lesenswert?

Ralf D. schrieb:
> Du brauchst zwar nur 16 Einträge, die sind aber über die halbe Tabelle
> verteilt (sparse table), weshalb sie eben trotzdem mindestens 128
> Einträge groß ist (wenn man vorher die irrelevanten Bits wegmaskiert).

Na eigentlich 17.

Werner schrieb:
> von welchem jedes 2. Bit
> verworfen werden soll, weil es redundant ist (aber das Vorgängerbit
> invertiert)

Es gibt ja noch den Fall, daß das Bit nicht das invertierte Vorgängerbit 
ist. Der Code wäre dann fehlerhaft, Fall 17.

MfG Klaus

von Jupp (Gast)


Lesenswert?

Werner schrieb:
> von welchem jedes 2. Bit
> verworfen werden soll, weil es redundant ist (aber das Vorgängerbit
> invertiert)

Warum prüfst du nicht auf Verfälschung, wenn du schon die Möglichkeit 
hast?

von Sebastian (Gast)


Lesenswert?

Jupp schrieb:
> Warum prüfst du nicht auf Verfälschung, wenn du schon die Möglichkeit
> hast?

Tut er vielleicht, war aber zu trivial um danach zu fragen (um 1 
schieben, XOR)?


Hier ist noch eine Lösung für CPUs mit 64 bit Registern. Zugegeben, dort 
sind dann meistens auch die anderen Varianten schnell genug :-)
1
#include <stdio.h>
2
#include <assert.h>
3
#include <stdint.h>
4
5
// Falks Version zum Vergleich im Unittest:
6
uint8_t squeeze8(uint8_t data)
7
{
8
    uint8_t tmp=0;
9
10
    if (data & (1<<7)) tmp |= (1<<3);
11
    if (data & (1<<5)) tmp |= (1<<2);
12
    if (data & (1<<3)) tmp |= (1<<1);
13
    if (data & (1<<1)) tmp |= (1<<0);
14
15
    return tmp;  
16
}
17
// Erweitert auf 16 bit
18
uint8_t squeeze16(uint16_t data)
19
{
20
    return squeeze8(data) | (squeeze8(data>>8) << 4);
21
}
22
23
24
uint8_t squeeze(uint16_t data)
25
{    
26
    data &= 0b1010101010101010;
27
    // data = A B C D E F G H
28
    
29
    uint64_t tmp = data * 0x3000300030003;
30
    // 0x3000300030003 = 1<<0 | 1<<1 | 1<<16 | 1<<17 | 1<<32 | 1<<33 | 1<<48 | 1<<49
31
    // 7654321076543210765432107654321076543210765432107654321076543210
32
    //                                                 A B C D E F G H   0
33
    //                                                A B C D E F G H    1
34
    //                                 A B C D E F G H                  16
35
    //                                A B C D E F G H                   17
36
    //                 A B C D E F G H                                  32
37
    //                A B C D E F G H                                   33
38
    // A B C D E F G H                                                  48
39
    //  B C D E F G H                                                   49
40
    
41
    tmp &= 0xC0000C0000C0000C;
42
    // 0xC0000C0000C0000C = 3<<2 | 3<<22 | 3<<42 | 3<<62
43
    //                48              32              16
44
    // 7654321076543210765432107654321076543210765432107654321076543210
45
    // AB                  CD                  EF                  GH
46
    // 0                   18                  36                  54
47
    
48
    tmp *= 0x40001000040001;
49
    // 0x40001000040001 = 1<<0 | 1<<18 | 1<<36 | 1<<54
50
    
51
    return tmp >> 56;
52
}
53
54
55
int main(int argc, const char* argv[])
56
{
57
    // Stichprobe:
58
    //                 _ _ _ _ _ _ _ _ 
59
    assert(squeeze16(0b1001100101011001) == 0b10100010);
60
    assert(squeeze(0b1001100101011001) == 0b10100010);
61
    // Decke alles ab, mit Vergleichs-Funktion:
62
    for(uint32_t i = 0; i < 0x10000; ++i)
63
    {
64
        assert  (squeeze(i) == squeeze16(i));
65
    }
66
    printf("Done.\n");
67
    return 0;
68
}

von Werner (Gast)


Lesenswert?

>Warum prüfst du nicht auf Verfälschung, wenn du schon die Möglichkeit
>hast?

Ja, mache ich, allerdings mit einer Schleife die ähnlich unleserlich 
aussieht wie die im Eröffnungspost...

Werner

von Daniel A. (daniel-a)


Lesenswert?

ungetestet, prüffunktion:
1
bool isOk(uint8_t x){
2
  return !(x&(x>>1)&0x55);
3
}

von Sebastian (Gast)


Lesenswert?

Daniel A. schrieb:
> ungetestet

und falsch. Gegenbeispiel: x=0.

Mein Vorschlag:
1
((x ^ (x>>1)) & 0x55) == 0x55

von Klaus (Gast)


Lesenswert?

Werner schrieb:
>>Warum prüfst du nicht auf Verfälschung, wenn du schon die Möglichkeit
>>hast?
>
> Ja, mache ich, allerdings mit einer Schleife die ähnlich unleserlich
> aussieht wie die im Eröffnungspost...

Dann wäre doch eine Tabelle von 256 Werten, wie

Falk B. schrieb:
> Du willst ein Byte auf ein Nibble zusammenschieben. Das kann man
> entweder mit einer Tabelle mit 256 Einträgen (sehr schnell aber hoher
> Speicherbedarf)

das Richtige. Da geht man mit dem Originalbyte rein und bekommt 
Ergebniss oder Fehler heraus. Check und Umsetzung in einem Rutsch. Und 
256 Byte halte ich heutzutage nicht wirklich für einen "hohen" 
Speicherbedarf.

MfG Klaus

von chris_ (Gast)


Lesenswert?

Man kann auch 2 Tabellen mit 16 Werten nehmen und vorher den 
Eingangswert in 2 Nibble teilen.

von Falk B. (falk)


Lesenswert?

@  chris_ (Gast)

>Man kann auch 2 Tabellen mit 16 Werten nehmen und vorher den
>Eingangswert in 2 Nibble teilen.

Kann man, aber dann ist er Vorteil der hohen Geschwindigkeit einer 
Tabelle schon fast wieder weg. Selbst die Einfachversion mit bitweise 
Prüfung und Setzen ist nicht wirklich langsam.

von chris_ (Gast)


Lesenswert?

>Kann man, aber dann ist er Vorteil der hohen Geschwindigkeit einer
>Tabelle schon fast wieder weg.

... fast ... vielleicht ... ist es auch ein Mittelding zwischen Tabelle 
und Bit-Algo. Man müsste es mal probieren ...

von Axel S. (a-za-z0-9)


Lesenswert?

In Assembler ist das simpelst zu programmieren. Einfach das eine 
Register immer zweimal links schieben (oder rotieren). Und dann das 
Carry-Bit in das Zielregister hinein schieben (oder rotieren). 3 
Assemblerinstruktionen pro Bit. Und trivial ausrollbar.

von Carl D. (jcw2)


Lesenswert?

> 3 Assemblerinstruktionen pro Bit. Und trivial ausrollbar.

BLD/BST sind nur 2 pro Bit.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

@chris_ (Gast)

>>Kann man, aber dann ist er Vorteil der hohen Geschwindigkeit einer
>>Tabelle schon fast wieder weg.

>... fast ... vielleicht ... ist es auch ein Mittelding zwischen Tabelle
>und Bit-Algo. Man müsste es mal probieren ...

Siehe Anhang.

Wobei die Behandlung des Bit 7 SEHR merkwürding aussieht. Da hat sich 
der Optimierer selber ins Knie geschossen, auch wenn das Ergebnis stimmt 
;-)
1
uint8_t squeeze(uint8_t data) {
2
  uint8_t tmp=0;
3
  
4
  if (data & (1<<7)) tmp |= (1<<3);
5
  5e:  98 2f         mov  r25, r24
6
  60:  99 1f         adc  r25, r25
7
  62:  99 27         eor  r25, r25
8
  64:  99 1f         adc  r25, r25
9
  66:  99 0f         add  r25, r25
10
  68:  99 0f         add  r25, r25
11
  6a:  99 0f         add  r25, r25
12
  if (data & (1<<5)) tmp |= (1<<2);
13
  6c:  85 fd         sbrc  r24, 5
14
  6e:  94 60         ori  r25, 0x04  ; 4
15
  if (data & (1<<3)) tmp |= (1<<1);
16
  70:  83 fd         sbrc  r24, 3
17
  72:  92 60         ori  r25, 0x02  ; 2
18
  if (data & (1<<1)) tmp |= (1<<0);
19
  74:  81 fd         sbrc  r24, 1
20
  76:  91 60         ori  r25, 0x01  ; 1
21
22
  return tmp;  
23
}
24
  78:  89 2f         mov  r24, r25
25
  7a:  08 95         ret


>  if (data & (1<<7)) tmp |= (1<<3);
>  5e:  98 2f         mov  r25, r24
>  60:  99 1f         adc  r25, r25

*2, MSB wandert ins Carry-Bit

>  62:  99 27         eor  r25, r25

r25 =0

>  64:  99 1f         adc  r25, r25

Carry-Bit ins LSB "kopieren"

>  66:  99 0f         add  r25, r25
>  68:  99 0f         add  r25, r25
>  6a:  99 0f         add  r25, r25

3x *2 -> 3 mal links schieben

Das ist ein guter Kandidat für code obfuscation ;-)
Manchmal ist einfach auch besser. Bei den 3 anderen Bits geht es doch 
auch.

Macht 24 Takte für die normale Funktion incl. call/return. Die muss 2x 
aufgerufen werden. Macht in Summe 275 Takte für die Schleife mit 4 
Zielbytes.

Die 256er Tabelle klappert mit gerade mal 138 Takten durch.

Die 16er Tabelle braucht 293 Takte, sogar mehr als die Version mit der 
squeeze Funktion!

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.