Seite 1 von 2

Bytes to int mit Bitwise Operators

Verfasst: Freitag 20. Januar 2023, 22:10
von Dennis89
Guten Abend,

ich habe gerade folgenden Code im Netz gefunden:
https://raw.githubusercontent.com/adamj ... mpu6050.py

da gibt es eine Funktion:

Code: Alles auswählen

    def bytes_toint(self, firstbyte, secondbyte):
        if not firstbyte & 0x80:
            return firstbyte << 8 | secondbyte
        return - (((firstbyte ^ 255) << 8) | (secondbyte ^ 255) + 1)
Ich begreife nicht so ganz was da gemacht wird, bzw. wieso das so gemacht wird. Ich beschreibe mal was ich denke was bis zum "ersten" return passiert.
Mit '&' wird von 'firstbyte' und '0x80' jedes Bit verglichen. Wenn die gleich sind, dann ist das Ergebnis an der Stelle eine eins, ansonsten eine 0. Dann wird da abgefragt ob 'firstbyte' gleich 0x80 ist. Wenn 'firstbyte' nicht 0x80 ist, dann kommt das eingerückte 'return'.
Wenn ich mir 'firstbyte' als Binärzahl vorstelle dann werden jetzt von rechts 8 Nullen aufgefüllt und links fallen 8 Bits weg. Dass ist das Gleiche wie wenn ich 'firstbyte' als Dezimalzahl mit 256 multipliziere. Nach der Rechnung wird das Ergebnis mit 'secondbyte' verglichen, wieder jedes einzelne Bit, nur dieses mal wird eine 1 geschrieben wenn eins von beiden Bits eine 1 ist, ansonsten eine 0.
Das ist mein Erkenntnisstand nach dem ich den Code soweit gelesen habe, aber mir fehlt das wieso. Und zwar in allen Bereichen. Wenn ich die Umrechnung verstehe, dann verstehe ich vielleicht auch, wieso die 'if'-Abfrage notwendig ist, bzw. wieso es zwei verschiedene Umrechnungen gibt.

Wir hatten schon mal so ein ähnliches Thema mit den Bitwise Operatoren bei der RGB656-Umrechnung, aber irgendwie steige ich da nicht durch.

Der Code ist dazu da um ein MPU5060 auszulesen und ich habe ihn aus diesem Tutorial:
https://microcontrollerslab.com/micropy ... 2-esp8266/

Könnt ihr mir bitte erklären, was das genau geschieht oder mir Hinweise geben, wie ich selbst drauf komme?

Vielen Dank und Grüße
Dennis

Re: Bytes to int mit Bitwise Operators

Verfasst: Freitag 20. Januar 2023, 22:44
von grubenfox
Eigentlich hätte ich in Sachen Hinweise hier ja print() geschrieben, aber da ich hier in den letzten Tagen zum ersten mal von snoop gelesen hatte, werfe ich jetzt eben mal snoop in den Raum.

https://pypi.org/project/snoop/#basic-snoop-usage

Das Beispiel hier drauf angepasst:

Code: Alles auswählen

import snoop

@snoop
def bytes_toint(self, firstbyte, secondbyte):
        if not firstbyte & 0x80:
            return firstbyte << 8 | secondbyte
        return - (((firstbyte ^ 255) << 8) | (secondbyte ^ 255) + 1)

bytes_toint(127,127)
bytes_toint(127,128)

bytes_toint(128,127)
bytes_toint(128,128)
Ich habe snoop hier nicht installiert, kann daher nichts über die Debugausgaben sagen. Bis eben hatte ich da noch andere Aufrufe von bytes_toint stehen, aber bei den Werten, die jetzt da stehen, passiert hoffnungsweise genügend interessantes.

Re: Bytes to int mit Bitwise Operators

Verfasst: Freitag 20. Januar 2023, 23:33
von Dennis89
Danke für die Antwort.

Erst mal habe ich raus gefunden, das meine Annahme mit dem &-Operator falsch war. Also der Bit-Vergleich wie beschrieben müsste schon stimmen, aber der Rest was ich darüber geschrieben habe passt so nicht ganz. Der liefert nur False, wenn kein Bit mit dem anderen übereinstimmt. Aber warum wird das abgefragt? Welche Eigenschaften haben die zwei Zahlen, dass die gesondert behandelt werden müssen?

'snoop' habe ich hier neulich auch entdeckt.
Ich habe mir auch schon einiges ausgeben lassen, aber es wurde mir nicht klar, wieso man da 8 Bits verschieben muss und wieso der Oder-Vergleich notwendig ist.
Vielleicht muss ich auch noch mal das Datenblatt genau studieren, was denn die Bytes aussagen.

Grüße
Dennis

Re: Bytes to int mit Bitwise Operators

Verfasst: Freitag 20. Januar 2023, 23:43
von __blackjack__
@Dennis89: Also erst einmal vergleichen ``&`` und ``|`` nichts, das sind bitweise Verknüpfungen, das heisst die Bits der beiden Operanden werden „und“- oder „oder“-verknüpft. Bei ``&`` ist das Ergebnisbit 1 an einer Stelle 1 wenn beide Operanden an der Stelle 1 sind. Und bei ``|`` reicht es wenn mindestens einer der beiden Operanden eine 1 an der jeweiligen Stelle hat.

0x80 in binär ist 0b10000000, also das oberste Bit bei einem Byte. Und eine „und“-Veknpüfung mit einem anderen Bytewert ist 0 falls das oberste Bit in diesem Byte 0 ist und 0x80 falls das oberste Bit 1 ist, denn nur dann sind ja bei beiden Operanden die Bits dort 1. Und weil alle anderen Bits in 0x80 den Wert 0 haben, sind die auch im Ergebnis immer 0. Da wird also nicht mit 0x80 verglichen, sondern geprüft ob das oberste Bit gesetzt ist oder nicht. Falls es *nicht* gesetzt ist wird der erste Bytewert um 8 Bits nach rechts verschoben, wodurch die untersten 8 Bit 0 werden. Links fallen allerdings keine 8 bits weg. Das ist ein Python-Integer, die können beliebig gross werden. Und wenn da 8 Bits wegfallen würden, dann wären das ja die 8 die man gerade nach links geschoben hätte und der Teilausdruck wäre immer 0, was ein bisschen sinnfrei wäre.

Mit dem ``|`` werden dann die unteren 8 Bit auf den Wert gesetzt den `secondbyte` hat. Denn durch das verschieben sind die beim ersten Operanden alle garantiert 0 und somit können nur noch die gesetzten Bits in `secondbyte` etwas zum Ergebnis der Verknüpfung beitragen.

Falls das oberste Bit gesetzt ist, interpretiert der Code bei zweiten ``return`` die beiden Bytes als negative 16 Bit-Zahl im Zweierkomplement. Und rechnet das entsprechend um.

Ich hätte es wahrscheinlich eher so ausgedrückt:

Code: Alles auswählen

def bytes_to_int(high_byte, low_byte):
    result = high_byte << 8 | low_byte
    return -((result ^ 0xFFFF) + 1) if result & 0x8000 else result
Alternativ:

Code: Alles auswählen

def bytes_to_int(high_byte, low_byte):
    result = high_byte << 8 | low_byte
    return ~result + 0xffff if result & 0x8000 else result

Re: Bytes to int mit Bitwise Operators

Verfasst: Samstag 21. Januar 2023, 00:25
von __deets__
Das ist grober Unfug. Da wird voellig unnoetig das Zweierkomplement selbst ausgerechnet. Dafuer benutzt man einfach http://docs.micropython.org/en/v1.8/pyb ... uct.unpack, mit '<h' also Format.

Re: Bytes to int mit Bitwise Operators

Verfasst: Samstag 21. Januar 2023, 12:11
von Dennis89
Guten Morgen,

vielen Dank für die Erklärungen.
Im Datenblatt auf Seite 29 steht, dass die Werte die ich da auslesen will 16 Bit-Zweierkomplement-Werte sind.
Zweierkomplement bedeutet dass das Bit das ganz links ist, das Vorzeichen angibt, weil ein '-' - Zeichen ja nur für uns Menschen lesbar/verwertbar ist.
Damit ich meinen 16-Bit-Wert bekomme, muss ich die zwei Bytes miteinander verknüpfen. Wenn das Bit ganz links eine 1 ist, dann bekomme ich eine negative Zahl und wenn es eine 0 ist, dann ist die Zahl positiv. Das heißt auch, das ich 15 Bits habe um eine Dezimalzahl darzustellen.
Ich hoffe mal dass das soweit passt?

Ich muss dass dann noch mal mit Zahlen durchspielen und dann auch die Codebeispiele von __blackjack__ testen und wenn ich verstanden habe was da gemacht wird, dann ersetze ich das gegen die fertige Funktion.


Grüße
Dennis

Edit: Ich meine mittlerweile "Guten Mittag" ich habe mit dem Beitrag heute früh gestartet, dann kam aber noch was dazwischen :)

Re: Bytes to int mit Bitwise Operators

Verfasst: Samstag 21. Januar 2023, 12:21
von __blackjack__
@Dennis89: Das stimmt nicht so ganz. Du hast nicht nur 15 Bit für die Dezimalzahl. Zweierkomplement bedeutet nicht, dass das oberste Bit das Vorzeichen darstellt und die restlichen die Zahl. Dann hätte man ja zweimal die 0, einmal +0 und einmal -0. Die 0 gibt's im Zweierkomplement aber nur einmal.

Edit: 0x0000 ist 0 und 0x8000, also gesetztes oberstes Bit und sonst 0en, ist -32768.

Re: Bytes to int mit Bitwise Operators

Verfasst: Samstag 21. Januar 2023, 12:38
von __deets__
@Dennis89: hast du meinen Beitrag gesehen? Man kann einfach ustruct verwenden, und gut ist.

Code: Alles auswählen

12:09 $ python3
Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import struct
>>> z = -12345
>>> struct.pack("<h", z)
b'\xc7\xcf'
>>> struct.unpack("<h", b'\xc7\xcf')
(-12345,)

Re: Bytes to int mit Bitwise Operators

Verfasst: Samstag 21. Januar 2023, 12:50
von __deets__
Und noch ein Nachtrag zum 2er-Komplement: wenn man im obersten Bit eine 1 vorfindet, die Zahl also negativ ist, dann erhaelt man die entsprechende positive Zahl durch

- invertierung der Bits
- Addition von 1

Also die -1, dargestellt in 2er-Komplement als $ffff, wird dann

- $0000
- $0001

Und genau diese Formel benutzt er da auch, und negiert dann wieder das Ergebnis.

Re: Bytes to int mit Bitwise Operators

Verfasst: Samstag 21. Januar 2023, 13:05
von Sirius3
Der Code ist ziemlich umständlich. Die Rechnungen macht man nicht selbst, sondern nutzt das fertige struct-Modul.

Code: Alles auswählen

import machine
import struct

class Accelerator():
    def __init__(self, i2c, addr=0x68):
        self.i2c = i2c
        self.addr = addr
        self.buffer = bytearray(14)
        self.i2c.start()
        self.i2c.writeto(self.addr, b"\x6b\0")
        self.i2c.stop()

    def fill_buffer(self):
        self.i2c.start()
        self.iic.readfrom_mem_into(self.addr, 0x3B, self.buffer)
        self.i2c.stop()

    def get_values(self):
        self.fill_buffer()
        return dict(zip(
            ["AcX", "AcY", "AcZ", "Tmp", "GyX", "GyY", "GyZ"],
            struct.unpack('<7h', self.buffer)))

Re: Bytes to int mit Bitwise Operators

Verfasst: Samstag 21. Januar 2023, 15:17
von Dennis89
Danke für eure weiteren Antworten.

@__blackjack__ und @__deets__ Die Rechnung habe ich jetzt mit euren Beispielen und Rechenvorgänge verstanden. Parallel dazu habe ich auch noch über das Zweierkomplement gelesen und habe gesehen das es noch eine Schreibweise gibt: Least Significant Bit. Da nimmt man das rechte Bit als Vorzeichen. Woher weis ich jetzt wenn ich zum Beispiel 0b10000111 sehe was das ist. Das könnte ja 135 sein oder -121 oder noch was anderes wenn man die Least Significant Bit - Schreibweise verwendet.
Wenn ich jetzt irgendwoher ein paar Zahlen bekomme, dann muss irgendwie mitgeteilt werden, dass ich die Zahl umrechnen muss oder nicht? Wenn ich die Zahl 135 brauche, dann ist das erste Bit eine 1, daran kann ich ja nichts ändern? Kann man verstehen welche Gedanken in meinem Kopf schwirren?

Die Beispielzahl habe ich so umgerechnet:

Code: Alles auswählen

0b10000111 -> invertieren:
0b01111000 + 0b00000001
0b01111001
=  -121
@Dennis89: hast du meinen Beitrag gesehen? Man kann einfach ustruct verwenden, und gut ist.
Ja habe ich gesehen und mit meinem Satz:
...wenn ich verstanden habe was da gemacht wird, dann ersetze ich das gegen die fertige Funktion.[/quote

habe ich die fertigen Funktionen aus deinem Link gemeint. Da das was wir gerade durchkauen ja in der fertigen Funktion auch gemacht wird, würde ich es gerne verstehen, damit ich auch weis warum ich die Funktion anwenden muss.

@Sirius Danke für das Umschreiben. Das war war mein langfristiges Ziel. Also ich wollte verstehen was da gemacht wird und schauen ob es etwas gibt, das ich mit meinem Wissen verbessern könnte. Wenn ich soweit bin kann ich ihn jetzt mit deiner Musterlösung vergleichen.

Grüße
Dennis

Re: Bytes to int mit Bitwise Operators

Verfasst: Samstag 21. Januar 2023, 15:35
von __deets__
Ich habe noch nicht einmal in meinem Leben eine LSB Implementierung des 2er-Komplement gesehen. Damit musst du dich nicht beschaeftigen. Was man wissen muss ist, welche Byte-order deine Daten haben. Da gibt es little endian und big endian. Das beschreibt, in welcher Reihenfolge mehr-byte Zahlen abgelegt werden. Kommt das LSB zuerst (little endian), oder das MSB (big endian). Ja, LSB und MSB ist aergerlicherweise auch fuer Bytes gleich benannt wie fuer Bits.

Re: Bytes to int mit Bitwise Operators

Verfasst: Samstag 21. Januar 2023, 15:39
von __blackjack__
@Dennis89: Was die Bits bedeuten musst Du wissen. Das ist ja das schöne an Bytes: Man kann damit alles mögliche darstellen. Zahlen, Zeichen, Farben, Töne, …

Was die Bits in einem Byte bedeuten und ob das Byte überhaupt für sich alleine steht oder oder noch Bits aus anderen Bytes braucht um einen sinnvollen Wert zu ergeben, muss irgendwo dokumentiert sein. Den Bytes/Bits selbst sieht man das nicht an.

Re: Bytes to int mit Bitwise Operators

Verfasst: Samstag 21. Januar 2023, 15:44
von __deets__
Das entnimmt man in diesem Fall der Dokumentation der IMU https://invensense.tdk.com/wp-content/u ... r-Map1.pdf auf Seite 31, da stehen die Register. Das ist dann MSB first, also Big Endian. Sieht man dem Code ja aber auch an.

Re: Bytes to int mit Bitwise Operators

Verfasst: Samstag 21. Januar 2023, 16:39
von Dennis89
Okay, vielen Dank.

Vorerst habe ich keine Fragen mehr. Ich beschäftige mich jetzt mal etwas mit dem Datenblatt und schau ob ich den vollständigen Code als Text ausfindig machen kann. Ich bin schon lang daran interessiert, wie man anhand eines Datenblattes einen Code schreibt. Vielleicht ist der recht übersichtliche Code hier ein guter Einstieg.

Viele Grüße
Dennis

Re: Bytes to int mit Bitwise Operators

Verfasst: Samstag 21. Januar 2023, 23:23
von Dennis89
Ohje ich verstehe das nicht wirklich.
Der originale Code startet so

Code: Alles auswählen

    def __init__(self, i2c, address=0x68):
        self.i2c = i2c
        self.address = address
        self.i2c.start()
        self.i2c.writeto(self.address, bytearray([107, 0]))
Setze ich hier im Register 107, CKLSEL auf 0? Seite 40. Oder was mache ich da? Ich habe halt in dem Zusammenhang nur die Zahlen 107 und 0 gefunden.

Dann hier

Code: Alles auswählen

    def get_raw_values(self):
        self.i2c.start()
        values = self.i2c.readfrom_mem(self.address, 0x3B, 14)
        self.i2c.stop()
        return values
Wird das Register 59 ausgelesen, aber das gibt nur X_OUT (Seite 29) raus, was ist denn mit den anderen Achsen, die haben andere Register und werden im Code nicht angesprochen. Das ist für mich alles irgendwie noch ganz undurchsichtig.

Vielleicht kommt auch über Nacht die Erleuchtung :D

Danke im voraus für euer Gedult und Mühe.

Grüße
Dennis

Re: Bytes to int mit Bitwise Operators

Verfasst: Sonntag 22. Januar 2023, 11:07
von Sirius3
Das Register 107 besteht ja aus vielen Bits. Entscheidend ist hier, dass nach dem Start das Register den Wert 0x40 hat, also das SLEEP-Bit gesetzt ist, und das wird auf 0 gesetzt.

Beim Lesen von Speichern, wird automatisch zur nächsten Speicherstelle weitergegangen, hier also Register 59 bis 72.

Re: Bytes to int mit Bitwise Operators

Verfasst: Sonntag 22. Januar 2023, 14:43
von Dennis89
Danke für die Antwort.

Okay, wenn ich eine 0 sende ist dass gleich mit 0b0000000. Wenn ich jetzt zum Beispiel den CYCLE (nur als zufälliges Beispiel) aktivieren wollen würde, dann könnte ich 0b00100000 oder 0x20 senden.

Das mit lesen verstehe ich jetzt auch, es werden ja 14 Bytes gelesen und nicht 14 Bits.

Während ich versucht habe das zu verstehen, ist mir aufgefallen, dass du in deinem Code einen Buffer eingebaut hast, den der originale Code nicht hat. Welchen Vorteil habe ich damit?

Grüße
Dennis

Re: Bytes to int mit Bitwise Operators

Verfasst: Sonntag 22. Januar 2023, 14:59
von __deets__
Dr buffer vermindert die Menge an allocations. Verbraucht also etwas weniger CPU und macht das System deterministischer.

Re: Bytes to int mit Bitwise Operators

Verfasst: Sonntag 22. Januar 2023, 15:13
von Dennis89
Super, vielen Dank.

War mal wieder sehr interessant 👍🏼

Grüße
Dennis