21Bit signed Integer -> 32bit signed Integer

Hallo,

edit, Denkfehler, das Problem ist etwas anders als zuvor geschildert:

Ich möchte die Position eines Sensors auswerten. Er sendet seinen Wert als 21bit signed Integer. Leider gibt es meines Wissens auf der Arduino Plattform keinen 21bit Typen.Ich muss den Wert also in eine 32bit Variable stecken. Das Problem ist dass negative Werte, die bei signed Integert ja durch das überlaufen unter die Null ausgedrückt werden in einer 32Bit Variable hohen positiven Werten entsprechen. Kann mir jemand einen Tipp geben wie man elegant und sinnvoll einen 21bit signed Wert in einen 32bit signed Wert konvertieren kann?

Mein erster Gedanke sah etwa so aus:

int32_t value = readSensor();
if (value > 0xFFFFF) value = -(0x1FFFFF - value);

Was mir wie die gröbste Holzhacker Methode vorkam. Das muss doch auch schöner gehen, oder?

Meine Methode funktioniert, aber ich bin immer froh wenn ich etwas neues lernen kann.

Gruß, G-SezZ

Er sendet seinen Wert als 21bit signed Integer.

Noch “eleganter” als mit deiner Lösung kann es nur werden, wenn du verrätst, wie die 21 Bit empfangen werden.

Bitseriell (Vorzeichen+MSB zuerst ?) oder 3 Byte? Haben die übrigen Bit evtl. eine Bedeutung ( Status, Fehler ?)

Sonst kannst du auch das VZ-Bit auf die oberen freien 11 Bit kopieren.

long data = 0x1FFFFF;  // Wie kommen die 21 Bits in die long Variable ?
if ( data & 0x100000 ) data |= 0xFFF00000;
Serial.print(data);
if (data == -1) Serial.println(" OK");

Oder, was passiert bei Bitfeld struct ?

struct { signed data:21;} x;

x.data = 0x1FFFFFL;

Serial.print("sizeof x = "); Serial.println(sizeof x); 

if ( x.data < 0 ) Serial.print("OK : ");
else Serial.print("No sign : ");
Serial.println (x.data);

Kompiliert, aber ich hab grade keinen Arduino da

Was hältst du von der Variante?

if (value & 0x100000L) value |= 0xFFE00000L;

Wenn das Sign Bit an ist, fülle die oberen Bits mit Einsen.

Hallo,

das man einen 21Bit Wert in eine 32Bit Variable speichert ist normal. Man nimmt immer den nächst größeren Datentyp. Es gibt auch keinen LKW mit genau 4,37m Länge für meine 4,37m Schrankwand ... :) :) :)

Ich hatte mal einen Sensor am SPI auch zu Fuß seriell eingelesen. Hatte 23Bit an Rohdaten. Hierzu hatte ich das Vorzeichenbit und andere Statusbits nachdem einlesen vereinzelt. Wenn man den Takt auch noch selbst generiert kann man das gleich beim Bit einlesen sortieren. Nachdem die Statusbits und der eigentliche Meßwert in Rohform einzeln vorlagen, habe ich das Vorzeichenbit geprüft. Wenn das "negativ" war, habe ich vom Meßwert das Zweierkomplement gebildet und mal -1 genommen. Damit hatte ich egal ob positiv oder negativ immer den richtigen Dezimalwert.

Die Frage wäre also, was liefert wie dein Sensor?

Binäre vorzeichenbehaftete Zahlendarstellungen können durch Wiederholen des Vorzeichenbits auf beliebige Länge werterhaltend verlängert werden.

Hallo,

ich kann dir gedanklich aktuell nicht folgen. Du veränderst doch den eigentlichen Wert wenn du wahllos einsen reinkloppst.

Das Vorzeichenbit ist nicht wahllos, sondern wohldefiniert.

Bei einer vorzeichenbehafteten Darstellung ist das oberste Bit das Vorzeichen.

Wenn die Zahl mehr Bits einnehmen soll, muss man die oben mit dem Vorzeichen auffüllen.

-2 = 0xFE (als char) = 0xFFFE (als int) = 0x1FFFFE (als 21 Bit Zahl) = 0xFFFFFFFE (als long)

2 = 0x02 = 0x0002 = 0x000002 = 0x00000002

siehe, das Vorzeichenbit füllt oben auf.

Hallo,

das das höchste Bit das Vorzeichen ist ist mir klar. Mit deinem "auffüllen" komme ich nicht zurecht. Wir reden doch von Bits (0/1), nicht von Bytes (0...FF). ???

Angenommen man hat die Zahl

1000.0011 eingelesen

und du füllst jetzt wahllos die Bits auf zu

1111.1111

dann kommt doch eine andere Zahl raus. So hatte ich das verstanden. Was natürlich nicht sein kann. Deine Methode müßtest du nochmal ausführlicher erklären.

Andersherum: du hast Beispielsweise eine vorzeichenbehaftete 4-bit Zahl: 1001 Die bringst du auf 8-bit durch wiederholen des Vorzeichenbits: 8-bit Zahl 1111 1001 bzw. bei einer positiven Zahl wird die 0 wiederholt: 0111->0000 0111

Wir reden doch von Bits (0/1), nicht von Bytes (0...FF)

Hä? In Hexadezimal stellt jede Ziffer vier Bit dar.

Ansonsten schlage mal "Zweierkomplement" nach.

if (value & 0x00000080L) value |= 0xFFFFFF00L;  // für  8 Bit
if (value & 0x00008000L) value |= 0xFFFF0000L;  // für 16 Bit
if (value & 0x00100000L) value |= 0xFFE00000L;  // für 21 Bit

Hallo,

jetzt hab ich das geschnallt wie das gemeint ist. :) Auch eine schöne Methode.

@ Serenifly, du hast das diesmal völlig falsch aufgefasst

Mit Worten konnte ich mich anscheinend nicht verständlich machen. ;)

Ich schubse aber schon 40 Jahre lang Bits durch die Gegend.

Hallo,

ehrlich gesagt hat mich Theseus in die richtige Denkschleife gebracht. Auf Bitebene.
Danach konnte ich dein Bsp. im hex Format nachvollziehen.
Ich denke eben auch etwas anders herum. :slight_smile:
Hauptsache ist, man ist am Ende auf dem gleichen Nenner.

Ich empfinde binäre Zahlen eher als unübersichtlich

0b11111111111000000000000000000000 <> ox0xFFE00000L

Hallo,

ja, kommt auf die Größe der Zahl an und was man darstellen und erklären möchte. In dem Bsp. vom Thread bin ich immer vom einlesen der Zahl ausgegangen. Dies erfolgt in reiner Binärform. Deshalb vielleicht auch das kleine durcheinander. Egal, ist geklärt. Prost.

Danke für die Antworten.
Der Sensor liefert ein Clock und ein Data Signal. Übertragen wird ausschließlich dieser Zahlenwert. Er sendet jeweils die 21bits, beginnend mit dem LSB, dann macht die Clock Pause bis zur nächsten Übertragung. Ich triggere mit der Clock einen Interrupt und schreibe das empfangene Bit in ein Array. Ist das Array bis zur 21 Stelle gefüllt baue ich aus den einzelnen Bits per Bitshift den 21bit Wert in einer 32bit Variable zusammen.
An das auffüllen durch den MSB hatte ich auch schon gedacht, nur hatte ich dabei eine Schleife mit Bitshift im Sinn, was wohl die Krönung aller Umständlichkeit gewesen wäre. :wink:

if ( data & 0x100000 ) data |= 0xFFF00000;

Das gefällt mir sehr gut.

Wenn ich mir jetzt noch überlege wie ich das ganze gegen Übertragungsfehler absichern kann könnte ich eigentlich auch auf das Array verzichten und gleich in die Variable schreiben.

int32_t value = readSensor();
if (value > 0xFFFFF) value = -(0x1FFFFF - value);

Wir hatten ja die Aufgabe, diese elegante Lösung (ehrlich gemeint !) noch zu toppen ;)

Wenn deine Funktion readSensor() ein int32_t zurückliefert, gehört das allerdings, philosophisch betrachtet, in die Funktion hinein ... :P

Hehe, eine Funktion readSensor() gibt es in meinem tatsächlichen Code gar nicht, sollte nur ein Beispiel sein, dass die andere zeile nicht ganz ohne Zusammenhang steht. :wink:

Ich habe auch noch eine Möglichkeit gefunden nicht erst alle Bits in ein array speichern zu müssen, nur um den MSB, der zuletzt übertragen wird, als erstes in die Variable schieben zu können:

void dataReceived() {
 if (micros() - lastBitTime > 200) //Wenn der Abstand zwischen zwei Bits größer als 200µs wurde entweder einer nicht gelesen, oder es handelt sich um einen neuen Wert.{
 tmp = 0;
 bits_received = 0;
 }
 lastBitTime = micros();
 tmp |= !digitalRead(data_pin); //Stecke den empfangenen Bit als LSB in die Variable
 tmp = (tmp >> 1) | (tmp << 31); //rotiere die Variable um einen Bit nach rechts.
 bits_received++; //zähle die Bits mit
 if (bits_received == 21) { //Wenn wir alle 21 zusammen haben,
 tmp >>= 10; //verschiebe die 21Bits  zum LSB der Variable
 if ( tmp & 0x100000 ) tmp |= 0xFFF00000; // Fülle bei negativen Werten die fehlenden Einsen auf.
 position = tmp;
 }
}

Jetzt muss ich nur noch heraus finden weshalb das ganze funktioniert wenn Sensor und Arduino keine direkte Masseverbindung haben (nur über USB Stromversorgung aus dem PC). Und wieso es zu ganz üblen Störungen im Signal kommt wenn ich die beiden Massen direkt verbinde. :frowning:

weshalb das ganze funktioniert wenn Sensor und Arduino keine direkte Masseverbindung haben (nur über USB Stromversorgung aus dem PC). Und wieso es zu ganz üblen Störungen im Signal kommt wenn ich die beiden Massen direkt verbinde

Im ersten Fall haben sie doch eine gemeinsame Masse, die im zweiten Fall eine Induktionsschleife bildet.
Theoretische Elektrotechnik ist schwierig, praktische ist nervig.