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.
Mike schrieb: > aber ich verfalle immer in wildes > Bitgeschubse und der Code wird sehr unübersichtlich Bitfields? Dann schubst der Kompiler für dich.
1 | struct MeinFeld |
2 | {
|
3 | int16_t Status:2; |
4 | int16_t Modus:6; |
5 | int16_t Zustand:1; |
6 | int16_t Adresse:12; |
7 | // ----
|
8 | };
|
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:
1 | uint8 data[20]; /* davon die ersten 15 gefüllt */ |
2 | struct sDsc {int16 *z; int s; int n;}; |
3 | struct sDsc Dsc[]={{&status, 0,2}, ...,{0,0,0}}; /* Null-terminiert */ |
4 | struct sDsc *p; |
5 | |
6 | for(p=Dsc; p->z; p++) |
7 | {
|
8 | signed long i; |
9 | |
10 | memcpy(i, data + p->s/8, sizeof(i)); |
11 | i<<=sizeof(i)-p->n-p->s%8; |
12 | i>>=sizeof(i)-p->n; |
13 | p->z=(int16) i; |
14 | }
|
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?
Man könnte jedes Datenfeld in einer Schleife in ein 32bit Integer verschieben und dann etwas übersichtlicher maskieren.
Serialisierung https://github.com/Erlkoenig90/uSer kapselt die Bit-Operationen komfortabel aber standard-komform & portabel.
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
1 | int datatemp[10]; |
2 | |
3 | while (i < sizeof(datatp)) { |
4 | datatemp[cnt] = int((unsigned char)(datapt++) << 24 | |
5 | (unsigned char)(datapt++) << 16 | |
6 | (unsigned char)(datapt++) << 8 | |
7 | (unsigned char)(datatp)); |
8 | int check=test*8%21; |
9 | if (test*8%21!=0){ |
10 | cnt++; |
11 | datatemp[cnt](unsigned char)(datatp++)<< check); |
12 | }else cnt++; |
13 | }
|
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 | long BitPos; // wieviel noch im BitBuf steht |
2 | long BitBuf; // der Schiebepuffer |
3 | byte* MapBuf; // zeiger auf die empfangenen Bytes |
4 | |
5 | // Vorbereitung nach Empfang:
|
6 | BitPos = 0; |
7 | BitBuf = 0; |
8 | |
9 | |
10 | word PxGet (int shift) |
11 | { long Q, mask; |
12 | word result; |
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 | return result; |
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.
Mein vorheriges Beispiel war etwas komisch.. Ich finde so könnte man das machen.. ist übersichtlicher finde ich, auf das Bitshifting keine Gewähr :D. [c] // Example program #include <iostream> #include <string> #include <cstring> #include <bitset> int main () { int i = 0; unsigned int datatemp[11]; unsigned int data[] = { 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000 }; unsigned int *pdata = data; char buffer[40]; char *pbuffer = buffer; memcpy (buffer, pdata, 40); unsigned int status[10]; unsigned int modus[10]; while (i < 10) { int start = 15 * i / 8; int shiftalign = 15 * i % 8; std::memcpy (&datatemp[i], buffer + start, 4); datatemp[i] = (datatemp[i] >> (shiftalign)); std::string name; unsigned int dtemp = datatemp[i]; status[i] = (dtemp & 3);//erste 2 bits modus[i] = (modus[i]>>2);//status wegschieben modus[i] =(dtemp&63);//Jetzt erste 6bits kopieren std::cout << shiftalign << std::endl; std::cout << std::bitset < 32 > ((unsigned int) status[i]) << std::endl; std::cout << std::bitset < 32 > ((unsigned int) modus[i]) << std::endl; i++; // } } [c]
TotoMitHarry schrieb: > ist übersichtlicher finde ich So? Jetzt müßtest du bloß noch erklären, wozu das Ganze gut sein soll. 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.
Beitrag #6409363 wurde von einem Moderator gelöscht.
Beitrag #6409374 wurde von einem Moderator gelöscht.
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).
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.