Hallo,
Ich habe einen Eingangspuffer von 15Bytes. Die Informationen sind
bitkodiert in den 15Bytes enthalten. Sie wurden dabei bestmöglich
zusammengepackt.
2 Bit Status
6 Bit Modus
1 Bit Zustand
12 Bit Adresse
....
bis 15*8=120Bits
Ich bekomme einen Pointer auf das erste Byte des Puffers und möchte alle
Informationen nach dem dekodieren in verschiedenen Variablen speichern.
int16_t status
int16_t modus
int16_t zustand
int16_t adresse
....
Das ist jetzt nicht sonderlich schwer aber ich verfalle immer in wildes
Bitgeschubse und der Code wird sehr unübersichtlich. Auf den
Eingangspuffer habe ich keinen Einfluss bezüglich Reihenfolge der
Informationen.
Gibt es für sowas elegante Lösungen?
Gruß
Mike
Mike schrieb:> Gibt es für sowas elegante Lösungen?
Es läuft immer auf Bitwise AND und Bitschieberei hinaus, Du kannst nur
versuchen, es zu optimieren und halbwegs lesbar zu schreiben.
status = buffer[0] >> 6; /* die obersten beiden Bits, daher kein AND */
modus = buffer[0] & 0x3f; /* die unteren 6 Bits, daher keine
Bitschieberei */
Wenn die fraglichen Bits mittendrin sind, evtl. zuerst Bitschieberei,
dann AND, damit Du auf den ersten Blick die Nummer des niederwertigsten
Bits siehst:
foo = (buffer[1] >> 4) & 0x03; /* foo liegt in Bit 4 und 5 */
Mike schrieb:> der Code wird sehr unübersichtlich.
So ist das eben, wenn man sich vorher keine Gedanken macht. Ich hätte
die 21 Bits auf Pakete zu 24 oder 32 Bits erweitert.
Ich kopiere mir immer eine Anzahl von Bytes in eine Variable und
maskiere das dann mit Makros. Das ist denke ich schon ganz gut aber ich
finde es total unübersichtlich. Hin und wieder wird das Protokoll
geändert und ich fange dann jedes Mal von vorne an die Bits abzuzählen
und probiere so lange bis es funktioniert und das nervt. Durch das
memcpy dreht man dann zusätzlich noch um es maximal unwartbar zu machen.
grobes Beispiel
#define STATUS_MASK(x) ((0x18000000 & x) >> 27)
#define MODE_MASK(x) ((0x07E00000 & x) >> 21)
memcpy(&data,buffer,4); //4Bytes aus dem Puffer kopieren
int32_t status = STATUS_MASK(data);
Arduino Fanboy D. schrieb:> Bitfields?> Dann schubst der Kompiler für dich.> struct MeinFeld> {> int16_t Status:2;> int16_t Modus:6;> int16_t Zustand:1;> int16_t Adresse:12;> // ----> };
Das klingt interessant, kannst Du vielleicht ganz kurz erläutern was da
passiert, ich habe es auf die schnelle nicht verstanden.
Wenn es nicht kompatibel sein muss:
Schaue, ob es mit einer Bitstruktur und einem 16/32-Bit typen
einigermaßen passt. Notfalls halt die 2 oder 3 Stukturen, die über die
Grenzen ragen, per SonderCode aufdröseln.
Überlege Dir ine Struktur mit {ptr, startbit, bitsize}, die Du füllst
und dann per kleinem code abarbeitest. So in der Art für uint8 data[15]
und low byte first:
Nur für int16 als Ziel und wohl weder lauffähig noch kompatibel sondern
so runtergetippt. Ich bin mir aber sicher, dass man das in wenigen
Minuten auch richtig machen kann.
Mike schrieb:> Arduino Fanboy D. schrieb:>> Bitfields?
...
>> Das klingt interessant, kannst Du vielleicht ganz kurz erläutern was da> passiert, ich habe es auf die schnelle nicht verstanden.
Dazu sollte man aber anmerken, dass das "implementation dependent" ist.
D.h. bei einem Compilerwechsel (auch Version) kann das schon mal nicht
mehr funktionieren.
leo
Arduino Fanboy D. schrieb:> Bitfields?> Dann schubst der Kompiler für dich.
Ich wusste es gleich als ich die Überschrift dieses Threads gelesen
habe...
Irgendjemand kommt wieder mit Bitfeldern um die Ecke, ohne sie genauer
zu erklären.
Bitfelder sind auf den ersten Blick reizvoll und der Code ist auch
absolut aufgeräumt. ABER: Bitfelder sind abhängig vom Alignment des
Prozessors.
Das beudeutet: Wenn dein Decoder auch auf anderen Prozessoren laufen
soll oder generell mal der Prozessor gewechselt wird funktioniert es
u.U. nicht mehr. Bitfelder sind also nicht portabel.
Was steht denn dazu in den nicht-funktionalen Anforderungen für dein
System?
Niklas G. schrieb:> Serialisierung> https://github.com/Erlkoenig90/uSer kapselt die Bit-Operationen> komfortabel aber standard-komform & portabel.
Deine Lib in allen Ehren... Aber du empfiehlst jetzt nicht einem
Anfänger der wahrscheinlich einen Mini uC hat, der noch nicht Mal
Bitfelder kennt den Einsatz dieser Lib, oder?
Gibt es überhaupt einen Cpp17 Compiler für seinen uC?
Hat er Datenkonstrukte mit denen die Lib umgehen kann?
Du schreibst auf der GitHub Seite selbst dass es Einschränkungen gibt.
Meine Meinung dazu an den TO:
Verwende wie oben geschrieben Bitfelder.
Macht es für meinen Geschmack übersichtlicher als Bitschieberei.
Bei komplexen Strukturen achte darauf ob du packed benötigst.
SOLLTEST du wirklich Mal in die Bedrängnis kommen einen anderen Compiler
verwenden zu müssen (bei allen uC die ich seither verwendet habe gab es
immer einen Gcc und auf allen laufen meine Bitfelder ohne Anpassungen),
mach einen Error rein dass das Alignement nicht stimmt oder dass es
nicht der richtige Compiler ist. Im schlimmsten Fall muss man die
Strukturen und Bitfelder dann einmal drehen und Plattformabhängig
einbinden.
Edit: Typo
N. M. schrieb:> Deine Lib in allen Ehren... Aber du empfiehlst jetzt nicht einem> Anfänger der wahrscheinlich einen Mini uC hat, der noch nicht Mal> Bitfelder kennt den Einsatz dieser Lib, oder?
Warum nicht? Sie ist gut dokumentiert. Wer sagt dass er Anfänger ist?
N. M. schrieb:> Gibt es überhaupt einen Cpp17 Compiler für seinen uC?
Er hat den µC nicht genannt.
N. M. schrieb:> Hat er Datenkonstrukte mit denen die Lib umgehen kann?
Die Lib kann mit einer ganzen Reihe Konstrukte umgehen, inklusive den im
Ausgangspost genannten.
N. M. schrieb:> Du schreibst auf der GitHub Seite selbst dass es Einschränkungen gibt.
Logisch. Aber aus dem Eingangs-Post sind diese nicht ersichtlich. Soll
ich erst langwierig die genauen Umstände erfragen um dann die Library zu
nennen, oder vielleicht einfach den Link posten sodass der TO selbst die
Eignung prüfen kann?
N. M. schrieb:> bei allen uC die ich seither verwendet habe gab es> immer einen Gcc
Wenn es einen GCC gibt, gibt es mit hoher Wahrscheinlichkeit auch C++17.
So könnte man vorsortieren.. allerdings ist das aus dem Kopf, soll es
nur veranschaulichen.. Bitshifting ist lange her, müsste selbst erst
googeln wie es richtig gemacht wird :D
Niklas G. schrieb:> Wenn es einen GCC gibt, gibt es mit hoher Wahrscheinlichkeit auch C++17.
Es wird eingangs ja noch nicht mal eine Sprache genannt.
Wenn ich mir gefallen lassen muss, dass Bitfields problematisch sein
können, dann musst du den Einlauf einstecken, dass dein Zeugs weder in
C, noch auf AVR mit C++17 funktioniert.
Arduino Fanboy D. schrieb:> Wenn ich mir gefallen lassen muss, dass Bitfields problematisch sein> können, dann musst du die Kröte schlucken, dass dein Zeugs weder in C,> noch auf AVR mit C++17 funktioniert.
Es lässt sich in C-Projekte integrieren, wenn man eine Datei mit C++
kompilieren kann. Aber ja, auf AVR funktioniert es nicht, weil der
AVR-GCC keine Standard-Bibliothek mitliefert. Man könnte sich einiges
selber basteln aber das macht wenig Spaß. Aber ob es hier überhaupt um
AVR geht...
Hallo Mike,
kannst du denn schon nähere Einschränkungen zur Kodierung machen?
> 2 Bit Status> 6 Bit Modus> 1 Bit Zustand> 12 Bit Adresse
little endian? Also wenn der Status 1 ist (und alles andere 0), ist das
erste Byte 1? Wenn Adresse 1 ist, (alles andere 0), ist das zweite Byte
2?
Signed oder unsigned oder variabel? (Status 0..3 oder -2..1)
> int16_t status> int16_t modus
nur int16_t oder beliebigee Zeile? (float, int, unsigned, 16 oder 32
Bit)
Mike schrieb:> Gibt es für sowas elegante Lösungen?
Was hälst du denn von dem Code oben? Also der Beschreibung als
{Variable, Bit-Anfang, Bit-Count}
A. S. schrieb:> Nur für int16 als Ziel und wohl weder lauffähig noch kompatibel sondern> so runtergetippt. Ich bin mir aber sicher, dass man das in wenigen> Minuten auch richtig machen kann.
Wenn Du die Fragen beantwortest, kann ich das gerne testen und anpassen.
Niklas G. schrieb:> Warum nicht? Sie ist gut dokumentiert.
Das bezweifle ich nicht. Und ehrlich gesagt schaue ich mir die auch
selbst Mal an ob ich da was gebrauchen kann.
Niklas G. schrieb:> Wer sagt dass er Anfänger ist?
Wenn jemand die einfachen Dinge wie Bitfelder nicht kennt, sorry aber
dann kennt sich derjenige in der Sprache einfach nicht aus. Und genau
das sehe ich dann auch das Problem. Da fehlen Grundlagen.
Niklas G. schrieb:> Soll ich erst langwierig die genauen Umstände erfragen
Nö, garantiert nicht. Davon gibt es schon genug im Forum.
Niklas G. schrieb:> oder vielleicht einfach den Link posten sodass der TO selbst die Eignung> prüfen kann
Wenn das der TO kann, ja. Wenn er aber noch nochmal ein Bitfeld kennt
Stelle ich in Frage ob er das selbst kann.
Niklas G. schrieb:> Wenn es einen GCC gibt, gibt es mit hoher Wahrscheinlichkeit auch C++17.
Das stimmt leider nicht immer. Oft gibt es nur einen 11er. Wenn
überhaupt.
Mike schrieb:> Gibt es für sowas elegante Lösungen?
Ja.
Fasse all deine Bytes in deinem Feld auf als teile eines gemeinsamen
Bitstreams. Also bei dir:
Bits 0..7 in Byte 0,
Bits 8..15 in Byte 1,
usw.
und nun schreibst du dir eine Funktion zum Holen der nächsten n Bits.
Fertig. Ich hatte mir sowas vor Zeiten mal als Teil einer
Dekodierroutine für gepackte Bilder am µC geschrieben.
Hier mal sowas aus dem Stegreif (ohne Gewähr):
1
longBitPos;// wieviel noch im BitBuf steht
2
longBitBuf;// der Schiebepuffer
3
byte*MapBuf;// zeiger auf die empfangenen Bytes
4
5
// Vorbereitung nach Empfang:
6
BitPos=0;
7
BitBuf=0;
8
9
10
wordPxGet(intshift)
11
{longQ,mask;
12
wordresult;
13
14
mask=(1<<shift)-1;
15
if(BitPos<shift)
16
{Q=*MapBuf++;
17
BitBuf=BitBuf|(Q<<BitPos);
18
BitPos+=8;// ist hier ja immer byteweise
19
}
20
result=BitBuf&mask;
21
BitBuf=BitBuf>>shift;
22
BitPos-=shift;
23
returnresult;
24
}
So etwa. Du fängst an bei den niedrigsten Bitpositionen:
// für 2 Bit Status
Status = PxGet(2);
// dann 6 Bit Modus
Modus = PxGet(6);
// und so weiter
Zustand = PxGet(1);
Adresse = Pxget(12);
etc.
So, hoffe mich nicht verschrieben zu haben.
W.S.
W.S. schrieb:> TotoMitHarry schrieb:>> ist übersichtlicher finde ich>> So? Jetzt müßtest du bloß noch erklären, wozu das Ganze gut sein soll.
hat er doch. Übung zum Bitschiften:
TotoMitHarry schrieb:> Bitshifting ist lange her, müsste selbst erst> googeln wie es richtig gemacht wird
Mit der Aufgabe des TO hat es wenig gemeinsam, nur 3 Bitfelder sind frei
daran angelehnt.
Mike schrieb:> 2 Bit Status> 6 Bit Modus> 1 Bit Zustand> 12 Bit Adresse> ....> int16_t status> int16_t modus> int16_t zustand> int16_t adresse
Welchen Sinn soll es denn haben, für Status und Zustand jeweils
Variablen mit einer Größe von 16 Bit vorzusehen, wenn die bei weitem
nicht benötigt werden?
Und warum um alles in der Welt vorzeichenbehaftete Variablen? 🤔
Mark B. schrieb:> Welchen Sinn soll es denn haben, für Status und Zustand jeweils> Variablen mit einer Größe von 16 Bit vorzusehen, wenn die bei weitem> nicht benötigt werden?
weil RAM billig und oft auch alligned ist und eine Sonderbehandlung (8
oder 16 Bit je nach Anzahl der Bits) schlecht wartbar.
> Und warum um alles in der Welt vorzeichenbehaftete Variablen? 🤔
vielleicht weil es gebraucht wird? wenn alles unsigned ist, ist es
einfacher.
W.S. schrieb:> So etwa. Du fängst an bei den niedrigsten Bitpositionen:> // für 2 Bit Status> Status = PxGet(2);> // dann 6 Bit Modus> Modus = PxGet(6);> // und so weiter> Zustand = PxGet(1);> Adresse = Pxget(12);
Gefällt mir am besten von den hier geposteten Antworten.
Es ist übersichtlich, dokumentiert sich quasi selbst und fängt Fehler ab
(wenn es bei/nach der Deserialisierung zu einem Over/Underrun kommt).