Individuelle Bitmanipulation

Hallo,

ich suche nach einer komfortablen Möglichkeit ein Byte folgendermaßen zu ändern.

Ausgangsbasis:

11101101

Die vier Bits in der Mitte sollen folgende "Werte" annehmen:

0001

Ergebnis:

11000101

Ich möchte praktisch erreichen, dass mehreren Bits gleichzeitig Werte annehmen, unabhängig von deren Ursprungszustand.

Ich kann mir vorstellen, dass man hierfür eine "Bitmaske" verwenden könnte, weiss aber nicht, wie man in Anbetracht der Tatsache, dass ich die neuen Werte unabhängig von den Ursprungswerten geschrieben werden sollen, damit umgeht.

Gruß Chris

Hallo,
zuerst mit AND ausmaskieren, z.B. 11111111 AND 11000011 = 11000011, setzt also die gewünschten Bits auf Null. Dann mit OR die gewünschten Bits wieder einschalten: 11000011 OR 11011011 = 11011011.

Bei AND wird das Bit im Ergebnis nur dann 1, wenn beide Bits in den Operanden auch 1 waren, sonst wird es null:

A B Q

0 0 0
0 1 0
1 0 0
1 1 1 A AND B ergibt Q

Bei Or wird das Bit im Ergebnis 1, wenn eines der beiden Eingangsbits 1 war:

A B Q

0 0 0
0 1 1
1 0 1
1 1 1 A OR B ergibt Q

Gruß,
Ralf

Danke für die ausführliche Antwort.

Mit einem einzelnen Befehl geht es aber nicht, oder?

Ich muss also tatsächlich erstmal alles maskierte "reseten"?

Gruß Chris

Naja, Du könntest natürlich

Q = A AND B OR C;

schreiben. Ich weiß nicht, ob das die Anforderung "einzelner Befehl" erfüllt. Mit einer einzigen Anweisung geht es nicht. Du musst die Bits erst ausmaskieren, ehe Du sie neu setzen kannst.

Gruß,
Ralf

[Edit:] Oben nur zur Verdeutlichung. in C muss es natürlich heißen Q = A & B | C;

In Datenblättern sieht man schonmal Bits mit 3 Zuständen ( 0 1 X ) Das gibts aber in C nicht direkt.
Auch Operatoren mit 3 Eingängen sind eher "ungewöhnlich", und leichter verständlich, wenn man sie direkt als Funktion definiert:

Beispiel:

//Bitwise replace src by input where mask == 1
byte maskedSet( byte src, byte input, byte mask) 
{   return (src & ~ mask) | (input & mask) ;    }

mask hat hier eine andere Bedeutung als B in Ralf Schachmanns Beispiel
Ausserdem werden hier -sicherheitshalber- noch evtl. überzählige Bits in input ausgeblendet.

Verwendung:

const byte M = B00111100;
byte Q = maskedSet(B11101101,B00000100, M); // liefert B11000101

Ob du einen Funktionsaufruf oder einen Ausdruck mit 2 Operatoren bevorzugst, ist eigentlich Geschmackssache.
Die Funktion kannst du auch als #define oder inline definieren, so dass im Endeffekt das Gleiche kompiliert wird.

michael_x:
In Datenblättern sieht man schonmal Bits mit 3 Zuständen ( 0 1 X )

Das X bedeutet IMHO, dass es egal ist, welchen Zustand das Bit hat.

Gruß,
Ralf

Schon klar, Ralf,
was ich meinte war nur: in C ist ein Bit entweder gesetzt oder nicht, für etwas drittes ( unverändert lassen ) muss man 2 Werte nehmen, in diesem Fall eben die zu setzenden Bits und eine Maske.

Auf die Bitmaske ist Chris ja schon von selbst gekommen, und deine Beiträge sind vollkommen richtig.
Die Syntax mit sowas wie

Q &=~ M;

ist zu Beginn etwas abschreckend, daher mein Zusatztip, evtl. eine Funktion zu definieren.

Schachmann:
Hallo,
zuerst mit AND ausmaskieren, z.B. 11111111 AND 11000011 = 11000011, setzt also die gewünschten Bits auf Null. Dann mit OR die gewünschten Bits wieder einschalten: 11000011 OR 11011011 = 11011011.

Kann es sein, dass bei OR folgendes Byte falsch ist: 11011011

Müsste es nicht 00011000 heissen?

Hier mein Code, der mich auf diese Vermutung brachte:

// Dieser Sketch soll ein Byte mit dem Wert 11101101 in 11000101 ändern. Es werden die mittleren vier Bits unabhängig von Ihrem Ausgangszustand geändert.

byte buffer = 0b11101101;
byte nullungsmaske = 0b11000011;
byte bitsetzer = 0b00000100;

void setup()
{
  Serial.begin(9600);
  delay(1000);
  buffer=buffer&nullungsmaske;
  Serial.print("Nach UND-Operator (Maske): ");
  Serial.println(buffer,BIN);
  delay(1000);
  buffer=buffer|bitsetzer;
  Serial.print("Nach ODER-Operator: ");
  Serial.println(buffer,BIN);  
}

void loop()
{
}

Gruß Chris

Chris72622:
Müsste es nicht 00011000 heissen?

Ja. Er hat wahrscheinlich angenommen, dass die äußeren Bits immer 1 sind (so wie in deinem Beispiel ganz am Anfang). In dem Fall wäre es egal. Aber wenn sie auch den Wert 0 annehmen sollen, dann müssen sie in der Maske auch 0 sein.

Wenn die Maske bestimmen soll, welche Bits von wo kommen, wird meist der eine Eingang mit der Maske, der andere mit der invertierten Maske maskiert (UND) und das Ergebnis zusammengeODERt.

(src & ~ mask) | (input & mask)

Hallo,

ich komme nun soweit gut zurecht, habe aber doch noch einmal eine Frage.

Ich möchte folgendes erreichen:

byte buffer = 0b10100010;

void setup()
{
  if("buffer == XXX00010")
  {
  }
}

Die Bits an den Stellen, an denen ich ein X geschrieben habe, sollen für das Erfüllen der if-Bedingung nicht berücksichtigt werden.

Wie schreibt man diesen Pseudocode unter Verwendung einer Maske, also ohne bitRead zu benutzen, um?

Gruß Chris

in dem du die ersten drei Bits ausmaskierst:

Der Inhalt von buffer wird tempbuffer kopiert, dabei die ersten 3 Bit zu Null gesetzt, der Rest bleibt unverändert.

tempbuffer = buffer & 0b00011111;  // erste 3 bit zu Null

if(tempbuffer == 00000010)

Dann in der Abfrage die ersten 3 Bits ebenfalls auf Null "prüfen", was auf alle Wahr ist, weil du ja diese vorher zu Null gesetzt hast.

edit: natürlich ohne Anführungszeichen.

Oder kürzer, weil 0b00011111 = 0x1F,

if((buffer & 0x1F) == wert)

Gruß,
Ralf