Nur ein Bit ändern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
kiaralle
User
Beiträge: 182
Registriert: Donnerstag 19. August 2021, 19:11

Hallo,

wie kann ich bei einem 8bit das 7. bit ändern?

Aufbau bit 7 ....... bit 0

Ist 10100110
Soll 00100110

Gruß Ralf
Sirius3
User
Beiträge: 18375
Registriert: Sonntag 21. Oktober 2012, 17:20

Dafür gibt es bit-Operationen:

Code: Alles auswählen

ist = 0b10100110
soll = ist & ~(1 << 7)
kiaralle
User
Beiträge: 182
Registriert: Donnerstag 19. August 2021, 19:11

Danke, ich hatte auch was gefunden aber das war nicht so schön erklärt.
nezzcarth
User
Beiträge: 1799
Registriert: Samstag 16. April 2011, 12:47

kiaralle hat geschrieben: Sonntag 22. Februar 2026, 08:56 Danke, ich hatte auch was gefunden aber das war nicht so schön erklärt.
"Bitmaske" ist hier der Fachbegriff für die weitere Suche.
kiaralle
User
Beiträge: 182
Registriert: Donnerstag 19. August 2021, 19:11

Was möchte ich überhaupt machen.

Ich bekomme ein Hex der zum Beispiel "C9" ist
Diesen kann ich wenn nötig direkt auf Int umwandeln ... wäre 201.

Diese beiden Ergebnisse stellen dann eigentlich meine Binärzahl 11001001 dar, welche den Aufbau und Funktion eines Datenfeldes im Speicher wiedergeben.

Möchte ich das Datenfeld aus dem Speicher löschen, so wird nur das 7.bit auf 0 gesetzt. der Rest bleibt im Speicher als "unsichtbar" bestehen.
Alles andere ist erst einmal unwichtig.

Sehe ich es richtig, das ein reines 11001001 nicht funktioniert? Es benötigt immer das 0b vor meiner Binärzahl bei bin() benötigt?

Code: Alles auswählen


df_status_int = 201


df_status_bin = bin(df_status_int)#[2:].zfill(8)

print(df_status_bin)

df_status_bin = df_status_bin & ~(1 << 7)

print(df_status_bin)


Error: unsupported operand type(s) for &: 'str' and 'int'
Benutzeravatar
__blackjack__
User
Beiträge: 14336
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kiaralle: Ein ”reines” 11001001 ist die Dezimalzahl elf Millionen und eintausend und eins. 0b11001001 ist die Dezimalzahl 201. `bin()` liefert eine Zeichenkette und keine Zahl und mit Zeichenketten kann keine Bitoperationen durchführen. Das macht also keinen Sinn die 201 in eine Zeichenkette in Binärdarstellung zu wandeln. Rechnen und Bitoperationen macht man mit Zahlen, nicht mit Zeichenketten. In eine Zeichenkette würde man das vielleicht zur Anzeige umwandeln wenn der Benutzer oder der Programmierer das gerne mal in Binärdarstellung sehen möchte. Ansonsten ist das 201 und nach dem löschen des Bits 73 dezimal (oder C9 und 49 hexadezimal, oder 311 und 111 oktal, …). Das sind ja alles nur verschiedene Darstellungen der selben beiden Zahlen als Zeichenketten.
„Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.“ — Brian W. Kernighan
kiaralle
User
Beiträge: 182
Registriert: Donnerstag 19. August 2021, 19:11

So, meine doch etwas andere Lösung. Warum nicht gleich mit Hex rechnen.

Ich bekomme den Status meines Datenfeldes gemeldet..... Hex C8
Ich weiß , dass ich immer 10.000.000 abziehen muss um das Datenfeld abzuschalten..... Hex 80

C8 = 11001000
- 80 = 10000000
48 = 01001000

Also rechnen wir,

Code: Alles auswählen


hex_a ="C8" 
hex_b = "80"
hex_c = hex(int(hex_a,16) - int(hex_b,16))[2:]
print(hex_c)

Ergebniss = 48
Damit kann ich den Datensatz löschen.

Jetzt muss ich an das erstellen des neuen Datenfeldes gehen, den ich wieder als Hex schreiben kann.


bit
7 Datenfeld aktiv
6 beschreibbar
5/4 Passwort 0-3
3 Passwort aktiv
2/1/0 Speichergröße 16/32/64.....

Es bleibt wie immer spannend :lol:

Danke noch mal in die Runde.
Sirius3
User
Beiträge: 18375
Registriert: Sonntag 21. Oktober 2012, 17:20

@kiaralle: Wenn Du Zahlen hast, dann solltest Du auch mit Zahlen rechnen und nicht immer alles in Strings umwandeln. Natürlich kann man dann für die Ausgabe ein passendes Ausgabeformat wählen.
Auch den Input wirst Du ja in irgendeiner binären Form erhalten und nicht als Hexadezimaldarstellung.
Minus zu benutzen hat das Problem, falls das Bit gar nicht gesetzt war, dass dann ein komisches Ergebnis rauskommt. Das ist beim binären UND nicht der Fall.

Code: Alles auswählen

eingabe = 0xC8
ergebnis = eingabe & 0x7F
print(f"{ergebnis:02x}")
Besser man benutzt statt kryptischer Zahlen IntFlags:

Code: Alles auswählen

import enum
class AccessBits(enum.IntFlag):
    ACTIVE = 0x80
    WRITEABLE = 0x40
    PASSWORD = 0x30
    PASSWORD_ENABLED = 0x08
    SIZE = 0x07

value = 0xC8
value &= ~AccessBits.ACTIVE
kiaralle
User
Beiträge: 182
Registriert: Donnerstag 19. August 2021, 19:11

Danke für deine Hilfe und Hinweise.
Ich erhalte den Input wirklich als Hex.
Deshalb war es für mich logisch und mit meine Wissensstand kann ich nur so vorzugehen. :D

Hier habe ich vorher die Anzahl der Datenfelder abgefragt.
Dann lese ich den Aufbau der Datenfelder aus.
Ich wandle den response der als Hex kommt in Bin um und kann so schön die Info der einzelnen Positionen auswerten.
Das funktioniert. Deinen Weg muss ich erst verstehen und werde es dann versuchen umzusetzen.

Code: Alles auswählen

i= 0
        while i <= number_of_datafields -1:

            df = "{:02x}".format(i)

            
            data = bytearray.fromhex("FF 4C" + df)
            data.append(ChecksumXor8.calc(data))
            try:
                serial_interface.reset_input_buffer()
                serial_interface.reset_output_buffer()#flush output buffer, aborting current output 
                time.sleep(0.010)  #give the serial port sometime to receive the data
                serial_interface.write(data)
                time.sleep(0.01)
                response = serial_interface.read(7)
            
                if response.hex()[2:4] == "50" or  ChecksumXor8.calc(response) != 0:
                    self.info_error["text"] = sick_error.srm_errors(response.hex()[4:6])
                else:
                    datenfeld_int=int.from_bytes(response[3:4],'big',signed=False)
                    datenfeld_bin =bin(datenfeld_int)[2:].zfill(8)
                
                    df_mem =str((int(datenfeld_bin[5:])+1) *16)
                
                    df_nummer = "\n" + str(i) + " \t"
                    i+=1
                    if int(datenfeld_bin[0:1]) == 0:
                        df_enable = "nein\t" 
                    else: 
                        df_enable = "ja\t"

                    if int(datenfeld_bin[1:2]) == 0:
                        df_writable = "nein\t" 
                    else: 
                        df_writable = "ja\t"
                
                    df_password = str(int(datenfeld_bin[2:4])) +"\t"

                    if int(datenfeld_bin[4:5]) == 0:
                        df_password_aktiv = "nein\t" 
                    else: 
                        df_password_aktiv = "ja\t"
                    data = df_nummer + df_enable + df_writable +  df_password + df_password_aktiv + df_mem
                    self.datenfeld_info.insert('end',data)

            except Exception as e1:
                print ("error communicating...: " + str(e1))

        self.datenfeld_info.config(state='disabled')

Sirius3
User
Beiträge: 18375
Registriert: Sonntag 21. Oktober 2012, 17:20

Also, Du wandelst ein `i` in Hex um, um es gleich danach wieder über bytearray.fromhex in Bytes umzuwandeln. Dann liest Du sieben Bytes und wandelst diese in Hex um.
Oder `datenfeld_int in eine Binärdarstellung um diese dann als Dezimalzahl zu interpretieren.

Also: Nein, Du bekommst keine Hex- oder Binärdaten, sondern bist selbst dafür verantwortlich, dass das alles so kompliziert ist.

Deine while-Schleife sollte eine for-Schleife sein und statt auf einer String-Repräsentation Deiner Zahlen solltest Du direkt mit den Zahlen selbst arbeiten:

Code: Alles auswählen

        for i in range(number_of_datafields):
            data = bytearray([0xff, 0x4c, i])
            data.append(ChecksumXor8.calc(data))
            try:
                # flush output buffer, aborting current output 
                serial_interface.reset_input_buffer()
                serial_interface.reset_output_buffer()
                time.sleep(0.010) 
                serial_interface.write(data)
                # give the serial port sometime to receive the data
                time.sleep(0.01)
                response = serial_interface.read(7)
            
                if response[0] == 0x50 or ChecksumXor8.calc(response) != 0:
                    self.info_error["text"] = sick_error.srm_errors(hex(response[1]))
                else:
                    datenfeld = response[3]
                    mem = datenfeld & AccessBits.SIZE
                    enabled = "ja" if datenfeld & AccessBits.ACTIVE else "nein"
                    writeable = "ja" if datenfeld & AccessBits.WRITEABLE else "nein"
                    password = datenfeld & AccessBits.PASSWORD
                    password_active = "ja" if datenfeld & AccessBits.PASSWORD_ENABLED else "nein"
                    info = f"\n{i}\t{enabled}\t{writeable}\t{password}\t{password_active}\t{mem}"
                    self.datenfeld_info.insert('end', info)
            except Exception as error:
                print(f"error communicating...: {error}")

        self.datenfeld_info.config(state='disabled')
kiaralle
User
Beiträge: 182
Registriert: Donnerstag 19. August 2021, 19:11

@Sirius

Du zeigst mir gerade neue Wege auf. :D
Das scheint ja wirklich einfacher zu sein als mein Weg.
Mein Code hat jetzt zwar ca. 1000 Zeilen aber ich habe keinen Zeitdruck und ich werde deine Tipps annehmen und den Code umschreiben.
Schön das du mir den Weg zeigst.
Mein Programm funktioniert zu 90% und ich kann es zur Montage von Gebern am Servomotor bereits verwenden.
Aber ich lerne gern was dazu.
Bin halt doch nur ein Schlosser :lol: Aber das wird...

Danke
kiaralle
User
Beiträge: 182
Registriert: Donnerstag 19. August 2021, 19:11

Noch mal ich,

ich habe mal deinen Code als Versuch genommen.
Die Variable "datenfeld" zeigt mir jetzt 200 an.
Damit kann ich ja so nichts anfangen. Ich benötige wieder die 11001000 um zu schauen welches bit wie gesetzt ist.
Oder hab ich es noch nicht ganz verstanden?

Wie kommt man sonnst an AccessBits.SIZE.... ?

Code: Alles auswählen

   
        mem = bin(datenfeld)[2:][0:1]
        print(mem)
1

data = bytearray([0xff, 0x4c, 0x01])
data.append(ChecksumXor8.calc(data))
try:
serial_interface.reset_input_buffer()
serial_interface.reset_output_buffer()
time.sleep(0.010)
serial_interface.write(data)
time.sleep(0.01)
response = serial_interface.read(7)

if response[0] == 0x50 or ChecksumXor8.calc(response) != 0:
print(sick_error.srm_errors(hex(response[1])))
else:
datenfeld = response[3]
"""
mem = datenfeld & AccessBits.SIZE
enabled = "ja" if datenfeld & AccessBits.ACTIVE else "nein"
writeable = "ja" if datenfeld & AccessBits.WRITEABLE else "nein"
password = datenfeld & AccessBits.PASSWORD
password_active = "ja" if datenfeld & AccessBits.PASSWORD_ENABLED else "nein"
"""
print(datenfeld) # 200
except Exception as error:
print(f"error communicating...: {error}")

Benutzeravatar
__blackjack__
User
Beiträge: 14336
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kiaralle: Da kommt man dran in dem man alle anderen Bits ausmaskiert. Du musst Dir wirklich mal diese Operationen anschauen was die machen. Bei 200 hat ”size” den Wert 0:

Code: Alles auswählen

In [448]: 200 & 0b111
Out[448]: 0
„Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.“ — Brian W. Kernighan
Benutzeravatar
DeaD_EyE
User
Beiträge: 1329
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Code: Alles auswählen

def set_bit(data: int, bit: int, value: bool):
    """
    Set or clear a bit of given data and return it.
    Look here: https://graphics.stanford.edu/~seander/bithacks.html#ConditionalSetOrClearBitsWithoutBranching
    """
    return data ^ (-value ^ data) & (1 << bit)
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
kiaralle
User
Beiträge: 182
Registriert: Donnerstag 19. August 2021, 19:11

Mal sehen ob ich es richtig verstanden habe.

Mit 0b10000000 frage ich das 7.bit ab mit mit 0b01000000 das 6.bit?...

enabled = "ja" if datenfeld & 0b10000000 else "nein"..... sagt bei 200 = 11001000 ... ja
Benutzeravatar
DeaD_EyE
User
Beiträge: 1329
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Ja, ich würde es aber so nicht im Quelltext verwenden.

Code: Alles auswählen

BIT6 = 1 << 6
value = 255

if BIT6 & value:
    print("Bit 6 ist gesetzt")
Man muss wissen, dass ein bool(0) immer False ergibt und alle andere Zahlen (auch negativ) ein bool(x) == True.
Die if-Anweisung fragt den boolschen Wert eines Objektes ab und bool ist von int vererbt.

Noch einfacher ist es sich eine Funktion zu schreiben:

Code: Alles auswählen

def get_bit(value: int, bit: int) -> bool:
    return bool(value & 1 << bit)
Und falls du mehrere Bits auf einmal prüfen möchtest:

Code: Alles auswählen

from typing import Iterable

def get_bits(value: int, bits: Iterable[int]) -> list[bool]:
    return [bool(value & 1 << bit) for bit in bits]
oder als Dict zurückgeben:

Code: Alles auswählen

def get_bits(value: int, bits: Iterable[int]) -> list[bool]:
    return {bit: bool(value & 1 << bit) for bit in bits}
Oder wenn z.B. nur ein True zurückgegeben werden soll, wenn mehrere Bits gleichzeitig gesetzt sind.

Code: Alles auswählen

def is_set(value: int, bits: Iterable[int]) -> list[bool]:
    mask = sum(1 << bit for bit in bits)
    return value & mask == mask

PS: Die Typehints sind nicht notwendig. Das kannst auch wieder entfernen, da es keinen Einfluss auf das Programm hat. Das macht nur Sinn, wenn man konsistent Typehints nutzt oder eine Bibliothek schreibt.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Sirius3
User
Beiträge: 18375
Registriert: Sonntag 21. Oktober 2012, 17:20

@DeaD_EyE: oder man nimmt, wie ich ja schon gezeigt habe, ein enum.IntFlag, denn dann hat man sprechende Namen, statt irgendwelcher Bit-Werte.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1329
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Sirius3 hat geschrieben: Montag 23. Februar 2026, 23:01 @DeaD_EyE: oder man nimmt, wie ich ja schon gezeigt habe, ein enum.IntFlag, denn dann hat man sprechende Namen, statt irgendwelcher Bit-Werte.
Das kann man auch machen, aber dann lernt man nichts über Bit-Manipulation und spätestens wenn Datenblätter verstehen muss, hat man ein Problem das Protokoll zu verstehen.


Nehmen wir z.B. das Modul, mit dem ich mich gerade beschäftige. Es geht um die Ansteuerung von Dosierpumpen einer VT-Anlage: Technologiemodul TM Pulse 2x24V

Siemens war so nett, Datentypen bereitzustellen, aber in vielen Fällen muss man das auch selbst machen.
D.h. wenn man z.B. später im Beruf SPS programmieren will, muss man die Bit-Manipulation verstehen. Wenn man jedes Mal Nachlesen muss, kommt man nicht weit.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
kiaralle
User
Beiträge: 182
Registriert: Donnerstag 19. August 2021, 19:11

Ich teste das heute Abend.
Ich sehe jetzt Licht am Ende des Tunnels.

Aber, wie Werte ich zum Beispiel drei Bits aus.
Bit 0-2 zeigen mir die Speichergröße an.

Bit 0-2 = 000 = (1 +bit) x 16 = 16
001 = (1+ bit) x16 = 32
....

Geht das auch auf diesen Wege?
Benutzeravatar
__blackjack__
User
Beiträge: 14336
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kiaralle: Das hatte ich doch schon gezeigt. Wenn man die letzten drei Bits haben möchte, dann muss man die anderen einfach alle ausmaskieren mit ``&`` und 0b111 oder 0x07 oder 0o007 oder 7. Das sind ja alles nur verschiedene Schreibweisen für den gleichen Wert. Also:

Code: Alles auswählen

memory_size = (1 + (byte_value & 0b0000_0111)) * 16
„Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.“ — Brian W. Kernighan
Antworten