Forum: Mikrocontroller und Digitale Elektronik Bits aus einem unsigned char array extrahieren


von Neb N. (bluemorph)


Lesenswert?

Hallo Leute,

ich habe eine Funktion geschrieben, der ich ein unsigned char feld, ein 
Startbit, eine Länge und ein Zielpointer übergebe. Diese Funktion soll 
nun aus dem unsigned char Feld, ab dem Startbit für die übergebene 
Länge, alle Bits an die Adresse des Zielpointers kopieren.

Der Funktionsheader sieht wie folgt aus:
1
void f_extract_data (t_uchar *W_sourcefield, t_uint W_start_bit, t_uint W_len, t_uchar *W_destination);

Folgende Fälle habe ich schon abgedeckt:
1.) W_len <= 8 und die Bits sind in einem Byte in W_sourcefield
2.) W_len <= 8 und die Bits sind über zwei Bytes in W_sourcefield 
verteilt
3.) W_len > 8 und die Bits sind über zwei Bytes in W_sourcefield 
verteilt

Nun fehlt mir also noch der vierte Fall! Wenn W_len nun Länger als 8 ist 
und die Bits die ich auslesen möchte zum Beispiel über 3 Bytes verteilt 
sind.

Dazu folgendes Beispiel:

Byte:         1           2           3           4
Bit:      0000 1 010 | 1010 0000 | 1 111 0001 | 0000 0000

W_start_bit = 6,
W_len       = 12

In dem Fett markierten Bereich steckt nun also meine Information. Diese 
möchte ich nun an meinen Zielpointer W_destination schreiben, wobei 
W_destination immer die Adresse des niederwertigsten Bytes enthält.

Der Inhalt von W_destination sollte dann so aussehen:

W_destination[0] = 0100 0001
W_destination[1] = 0000 0101

Ich komme mit meinen Überlegungen einfach nicht so recht weiter. Hat 
jemand eine Idee von euch, wie man das realisieren kann?

MfG BlueMorph

von Falk B. (falk)


Lesenswert?

@Benny Nestler (bluemorph)

>Ich komme mit meinen Überlegungen einfach nicht so recht weiter. Hat
>jemand eine Idee von euch, wie man das realisieren kann?

Ja. Man muss eigentlich keine deiner Fallunterscheidungen machen, das 
geht auch mit einem allgemeinen Ablauf, IMO sogar besser.
1
void f_extract_data (t_uchar *W_sourcefield, t_uint W_start_bit, t_uint W_len, t_uchar *W_destination) {
2
    uint8_t mask_in;
3
    uint8_t mask_out=1;
4
    uint8_t cnt_in;
5
    uint8_t data_out=0;
6
    uint8_t data_in;
7
8
    W_sourcefield+= W_start_bit / 8;
9
    data_in = *W_sourcefield;
10
    mask_in = 1<<(W_start_bit % 8);
11
    cnt_in  = 8-(W_start_bit  % 8);
12
13
    for (; W_len > 0; W_Len--) {
14
        if (data_in & mask_in) data_out |= mask_out;
15
        mask_in <<= 1;
16
        mask_out <<= 1;
17
        cnt_in--;
18
        if (cnt_in==0) {
19
            cnt_in=8;
20
            mask_in = 1;
21
            W_sourcefield--;
22
            data_in = *W_sourcefield;
23
        }
24
        if (mask_out==0) {
25
            mask_out = 1;
26
            *W_destination = data_out;
27
            W_destination++;
28
            data_out=0;
29
        }
30
    }    
31
}

MfG
Falk

von Neb N. (bluemorph)


Lesenswert?

Hallo Falk,

danke für deine schnelle Antwort!
Leider habe ich bedenken, dass deine Funktion, so wie sie ist 
funktioniert.

Folgendes Beispiel:

Byte:               0              1
W_sourcefield = 0000 0011 | 0101 0011

W_start_bit = 4 und W_len = 4.
1
void f_extract_data (t_uchar *W_sourcefield, t_uint W_start_bit, t_uint W_len, t_uchar *W_destination) {
2
    uint8_t mask_in;
3
    uint8_t mask_out=1;
4
    uint8_t cnt_in;
5
    uint8_t data_out=0;
6
    uint8_t data_in;
7
8
    W_sourcefield+= W_start_bit / 8; 
9
    data_in = *W_sourcefield;          // data_in = 0000 0011
10
    mask_in = 1<<(W_start_bit % 8);    // mask_in = 0010 0000
11
    cnt_in  = 8-(W_start_bit  % 8);    // cnt_in  = 4
12
13
    for (; W_len > 0; W_Len--) {       //W_len = 4
14
        if (data_in & mask_in)         // 0000 0011 & 0010 0000 = 0
15
             data_out |= mask_out;     // wird also nichts gemacht
16
        mask_in <<= 1;                 // mask_in  = 0100 0000
17
        mask_out <<= 1;                // mask_out = 0000 0010
18
        cnt_in--;                      // cnt_in   = 3
19
        if (cnt_in==0) {
20
            cnt_in=8;
21
            mask_in = 1;
22
            W_sourcefield--;
23
            data_in = *W_sourcefield;
24
        }
25
        if (mask_out==0) {
26
            mask_out = 1;
27
            *W_destination = data_out;
28
            W_destination++;
29
            data_out=0;
30
        }
31
    }    
32
}

Ich hab jetzt nur den ersten for - Schleifendurchlauf gemacht!
Warum setzt du die mask_in mit
1
 1 << (W_start_bit % 8 )
auf?

MfG BlueMorph

von Falk B. (falk)


Lesenswert?

@Benny Nestler (bluemorph)

>Leider habe ich bedenken, dass deine Funktion, so wie sie ist
>funktioniert.

Kann schon sein, ist NICHT getestet, sollte im Wesentlichen nur das 
Prinzip darstellen.

OK, hier die debuggte Version, auch wenn das pädagogisch unklug ist.
1
#include <stdint.h>
2
3
// global
4
5
    uint8_t source[3] = {0b00010101, 0b00110011, 0b10101010};
6
    uint8_t target[3];
7
8
void f_extract_data (uint8_t *W_sourcefield, uint8_t W_start_bit, uint8_t W_len, uint8_t *W_destination) {
9
    uint8_t mask_in;
10
    uint8_t mask_out=1;
11
    uint8_t cnt_in;
12
    uint8_t data_out=0;
13
    uint8_t data_in;
14
15
    W_sourcefield+= (W_start_bit+W_len-1) / 8;
16
    data_in = *W_sourcefield;
17
    mask_in = 1<<(7-((W_start_bit+W_len-1) % 8));
18
    cnt_in  = 1 + ((W_start_bit+W_len-1)  % 8);
19
20
    for (; W_len > 0; W_len--) {
21
        if (data_in & mask_in) data_out |= mask_out;
22
        mask_in <<= 1;
23
        mask_out <<= 1;
24
        cnt_in--;
25
        if (cnt_in==0) {
26
            cnt_in=8;
27
            mask_in = 1;
28
            W_sourcefield--;
29
            data_in = *W_sourcefield;
30
        }
31
        if (mask_out==0) {
32
            mask_out = 1;
33
            *W_destination = data_out;
34
            W_destination++;
35
            data_out=0;
36
        }
37
    }
38
    if (mask_out != 1) *W_destination = data_out;
39
}
40
41
int main(void) {
42
43
    f_extract_data (source, 3, 9, target); 
44
    return 0;
45
};

>Warum setzt du die mask_in mit

> 1 << (W_start_bit % 8 )

>auf?

Weil damit die Postion des ersten, niederwertigsten Bits markiert wird.

MFG
Falk

von Neb N. (bluemorph)


Lesenswert?

Okay, du hast natürlich recht, der pädagogische Effekt ist jetzt weg. 
Aber sag mal, wie bist du denn vorgegangen, um die Formeln für mask_in 
und cnt_in zu erstellen?

Ich glaube das Prinzip ist mir soweit klar. Mit cnt_in zählst du die 
übrigen, noch nicht geschriebenen Bits im aktuellen W_sourcefield - 
Byte. Ist das richtig? Wenn dann alle Bits geschrieben sind, 
dekrementierst du den Zeiger und er zeigt auf das vorhergende Byte des 
W_sourcefields. Diesmal kann die mask_in bei 1 beginnen, da ja das 
niederwertigste Bit auch das LSB des aktuellen Bytes ist.
Wenn mask_out soweit fortgeschritten ist, dass die 1 aus dem unsigned 
char herausgeschoben wird und damit mask_out wieder 0 ist, muss an das 
nächste W_destination Byte geschrieben werden.
Übergeordnet wird bei jedem Schleifendurchlauf W_len dekrementiert, so 
dass die Schleife entsprechend abgebrochen wird, wenn das Ende der zu 
extrahierende Information erreicht ist. usw.

Vielen Dank dir schonmal!

von Falk B. (falk)


Lesenswert?

@  Benny Nestler (bluemorph)

Deine Erkläsrung stimmt. Jetzt wo ich das alles nochmal sehe, kann man 
cnt_in auch weglassen und die Abfrage wie bei mask_out machen. Macht es 
einen Tick schneller.

MFG
Falk

von Neb N. (bluemorph)


Lesenswert?

Meinst du das so:
1
void f_extract_data (t_uchar *W_sourcefield, t_uint W_start_bit, t_uint W_len, t_uchar *W_destination) {
2
    t_uchar V_mask_in;
3
    t_uchar V_mask_out = 1;
4
    t_uchar V_data_out = 0;
5
    t_uchar V_data_in;
6
7
    W_sourcefield += (W_start_bit + W_len - 1) / 8;        //Setze Zeiger auf das Byte wo das LSB drin steht steht
8
    V_data_in = *W_sourcefield;                  //Kopiere das Byte in eine temp Variable
9
    V_mask_in = 1 << (7 - ((W_start_bit + W_len - 1) % 8));    //Maskiere das LSB des niederwertigsten Bytes
10
11
    for (; W_len > 0; W_len--) {
12
        if (V_data_in & V_mask_in) V_data_out |= V_mask_out;
13
        V_mask_in <<= 1;
14
        V_mask_out <<= 1;
15
        if (V_mask_in == 0) {                  //aktuelles Byte hat keine Bits mehr die geschrieben werden müssen
16
            V_mask_in = 1;                    //Maskiere das LSB des nächsten Bytes, diesmal ist es auch das tatsächliche LSB
17
            W_sourcefield--;                  //dekrementiere den W_Sourcefield - Pointer
18
            V_data_in = *W_sourcefield;              //Kopiere das neue Byte in die temp - Variable
19
        }
20
        if (V_mask_out == 0) {                  //Die temporäre Variable V_data_out ist voll mit Bits
21
            V_mask_out = 1;                    //Setzte die Ausgangsmaske wieder an den Anfang
22
            *W_destination = V_data_out;            //Kopiere V_data_out an den destination Pointer  
23
            W_destination++;                  //Inkrementiere den Destination - Pointer
24
            V_data_out = 0;                    //V_data muss nun wieder mit 0 anfangen
25
        }
26
    }
27
    if (V_mask_out != 1) *W_destination = V_data_out;      //Für den Fall, dass nur wenige Bits in einem Byte geschrieben werden, muss V_data_out noch an die W_destination Speicherstelle geschrieben werden
28
}

Verrätst du mir noch, wie du auf die Formeln für die Maske gekommen 
bist???
War das so eine Überlegung ... von wegen "Ich kenne das Ziel" und "ich 
probiere jetzt aus, wie ich rechnen muss, damit ich mein Ziel erreiche"?

Das Ziel war ja im niederwertigsten Byte das LSB zu markieren. Das war 
auch mein Ziel, allerdings bin ich ganz anders herangegangen und habe 
versucht verschiedene Fälle, die auftreten können zu unterscheiden ... 
bis ich mich verrannt habe und nicht mehr weiter wusst ;-) .

Du wirst lachen, aber ich kann dir ja mal meine Funktion zeigen, die ich 
angefangen habe zu schreiben:
1
void f_extract_data (t_uchar *W_sourcefield, t_uint W_start_bit, t_uint W_len, t_uchar *W_destination){
2
  
3
  t_uchar V_index = 0;
4
  t_uint V_start_byte = 0, V_end_byte = 0;
5
  t_ulong V_mask = 0;
6
  
7
  V_start_byte   = W_start_bit / 8;
8
  V_end_byte    = ((W_start_bit + (W_len - 1)) / 8);
9
    
10
  if (W_len <= 8){
11
    for (V_index = 0; V_index < W_len; V_index++){
12
      V_mask |= (1 << V_index);
13
    }
14
    if (V_start_byte == V_end_byte){
15
      // The data to extract is stored only in one byte
16
      
17
      *W_destination = (W_sourcefield[V_start_byte] >> (8 - (W_len + (W_start_bit - V_start_byte * 8))) ) & V_mask;
18
      return;
19
    }
20
    // The data to extract is stored about two byte
21
    else{
22
      t_uchar V_bits_in_start_byte = 8 - (W_start_bit % 8);
23
      t_uchar V_bits_in_end_byte    = W_len - V_bits_in_start_byte;
24
      
25
      *W_destination = (W_sourcefield[V_end_byte] >> (8 - V_bits_in_end_byte));
26
      *W_destination |= (W_sourcefield[V_start_byte] << (V_bits_in_end_byte)) & V_mask;
27
    }    
28
  }
29
  else{
30
    // The data is bigger than 8 bits 
31
    t_uchar V_bits_in_start_byte = 8 - (W_start_bit % 8);
32
    t_uchar V_bits_in_following_bytes    = W_len - V_bits_in_start_byte;
33
    
34
    
35
    if (V_bits_in_following_bytes <= 8){
36
      *W_destination = W_sourcefield[V_end_byte] >> (8 - V_bits_in_following_bytes);
37
      *W_destination |= W_sourcefield[V_start_byte] << (V_bits_in_following_bytes);
38
      
39
      for (V_index = 0; V_index < (W_len - 8); V_index++){
40
        V_mask |= (1 << V_index);
41
      }
42
43
      *(W_destination + 1) = (W_sourcefield[V_start_byte] >> (8 - V_bits_in_following_bytes)) & V_mask;
44
    }
45
  }
46
}

MfG BlueMorph

von Falk B. (falk)


Lesenswert?

@  Benny Nestler (bluemorph)

>Meinst du das so:

Ja. Noch ein Tip. Verwende die standardisierten Datentypen aus stdint 
und nichts selbstgestricktes. Macht die u.a. Sache leichter lesbar. UN 
Zeilenlänge auf 80...100 Zeichen begrenzen.

>Verrätst du mir noch, wie du auf die Formeln für die Maske gekommen
>bist???

Brain 2.0 ;-)

>War das so eine Überlegung ... von wegen "Ich kenne das Ziel" und "ich
>probiere jetzt aus, wie ich rechnen muss, damit ich mein Ziel erreiche"?

Eigentlich ist es nichts weiter als Bitmanipulation plus ein paar 
Schleifen.

Erste Idee. Ich maskiere die Bits im Quell- und Zielbereich mit einer 
Maske.
Zweite Idee. Ich berechne aus den Parametern eben diese Masken sowie die 
Offsets im Array.
Drittens. Die Daten werden rückwärts gelesen.

Der Rest ergibt sich daraus. Der "Trick" mit Division und Modulo setze 
ich mal als bekannt vorraus, siehe Festkommaarithmetik.

>Du wirst lachen, aber ich kann dir ja mal meine Funktion zeigen, die ich
>angefangen habe zu schreiben:

Naja, dein Ansatz ist erstmal OK, wenn gleich du dich verrannt hast. 
Bitmaske erzeugen, Bits maskieren und schieben, eben halt 
Bitmanipulation.

MfG
Falk

von Neb N. (bluemorph)


Lesenswert?

Dann danke ich dir erstmal für deine Unterstützung Falk!

Schönen Abend noch!

MfG BlueMorph

von name (Gast)


Lesenswert?

12

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.