Forum: Mikrocontroller und Digitale Elektronik Byteorder PIC32?


von Gowron (Gast)


Lesenswert?

Nach dem ich mich jetzt endgültig in der Dokumentation verheddert habe 
und es momentan auch leider nicht einfach mal ausprobieren kann, frage 
ich mal ganz doof hier nach:

Ich habe einen PIC32, der per MPLAB-IDE programmiert wird. Dort habe ich 
folgende Struktur definiert:
1
struct raw_data
2
{
3
   union
4
   {
5
      unsigned char bData[4];
6
      unsigned int  iData;
7
   } d;
8
};

Wen ich in iData jetzt z.B. 0xDEADBEEF reinschreibe - was steht dann in 
bData[0], bData[1], bData[2] und bData[3] drin?

von Mr.T (Gast)


Lesenswert?

Erste Antwort hier https://www.microchip.com/forums/m998923.aspx geht 
von little endian aus.

von Peter D. (peda)


Lesenswert?

Schreib den Code sauber, dann spielt das keine Rolle.
1
uint32_t byte2long(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3)
2
{
3
  return b0 | b1<<8 | (uint32_t)b2<<16 | (uint32_t)b2<<24;
4
}

von Gowron (Gast)


Lesenswert?

Peter D. schrieb:
> Schreib den Code sauber, dann spielt das keine Rolle.

Schön dass du eine Frage beantwortest, die nie gestellt wurde...

von Wilhelm M. (wimalopaan)


Lesenswert?

Gowron schrieb:
> Peter D. schrieb:
>> Schreib den Code sauber, dann spielt das keine Rolle.
>
> Schön dass du eine Frage beantwortest, die nie gestellt wurde...

Genau die Frage hast Du gestellt ;-)

Über welche Sprache reden wir denn hier?

von Gowron (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Über welche Sprache reden wir denn hier?

Es ist C/C++, ich wüsste nicht, dass die bereits erwähnte MPLAB-IDE was 
anderes kann. Und ich habe auch klar und deutlich die Frage nach der 
verwendeten Byteorder gestellt, und nicht danach, wie ich das 
Byteorder-Problem umgehen kann.

Also Danke an Mr.T und ansonsten Danke für nichts!

von Wilhelm M. (wimalopaan)


Lesenswert?

Gowron schrieb:
> Es ist C/C++

In C++ ist das UB, wenn Du es als type-punning betreiben möchtest.

von Peter D. (peda)


Lesenswert?

Gowron schrieb:
> Schön dass du eine Frage beantwortest, die nie gestellt wurde...

Stimmt, mit steigender Programmiererfahrung kommst Du irgendwann auch 
von selber darauf. Ich wollte Dir halt nur die Mühe ersparen.

von Gowron (Gast)


Lesenswert?

Peter D. schrieb:
> Gowron schrieb:
>> Schön dass du eine Frage beantwortest, die nie gestellt wurde...
>
> Stimmt, mit steigender Programmiererfahrung kommst Du irgendwann auch
> von selber darauf. Ich wollte Dir halt nur die Mühe ersparen.

Na dann erkläre es mir doch mal mit deiner fantastischen 
Programmiererfahrung: Ich habe als Eingangsdatum den 32-Bit-Wert und 
möchte diesen byteweise in einer definierten Reihenfolge auf eine 
serielle Leitung schicken. Was genau hilft mir dein komischer Code 
dabei?

Genau: gar nichts!

von Peter D. (peda)


Lesenswert?

Gowron schrieb:
> Was genau hilft mir dein komischer Code
> dabei?

Er erklärt das Prinzip, wie es unabhängig von Compiler und Target immer 
richtig ist.
Natürlich mußt Du für die Byteausgabe das Prinzip umdrehen:
1
void long2byte(uint8_t* b, uint32_t val)
2
{
3
  b[0] = val     & 0xFF;
4
  b[1] = val>>8  & 0xFF;
5
  b[2] = val>>16 & 0xFF;
6
  b[3] = val>>24 & 0xFF;
7
}

von morph1 (Gast)


Lesenswert?

mhm...

es gibt ja eigentlich 2 möglichkeiten und im zweifelsfalle probiert man 
es einfach aus :)

aber beim PIC ist LE (soweit mir bekannt) bei allen Prozessoren üblich. 
Der PIC32 als MIPS-Derivat macht hier keine Ausnahme.

von Wilhelm M. (wimalopaan)


Lesenswert?

morph1 schrieb:
> es gibt ja eigentlich 2 möglichkeiten und im zweifelsfalle probiert man
> es einfach aus :)

Der TO hat das Problem und/oder die Lösung dazu allerdings wohl gar 
nicht verstanden.

von (prx) A. K. (prx)


Lesenswert?

Gowron schrieb:
> Ich habe als Eingangsdatum den 32-Bit-Wert und
> möchte diesen byteweise in einer definierten Reihenfolge auf eine
> serielle Leitung schicken.

Dafür leihe ich mir, wenn verfügbar, Funktionen wie htonl/ntohl aus der 
BSD Socket Lib aus. Dann kann mir die Byteorder egal sein.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Die PIC32 heißen doch nurnoch PIC und nutzen MIPS Kerne.
Die sind normalerweise Big Endian.

von Peter C. (peter_c49)


Lesenswert?

>Die sind normalerweise Big Endian.

PIC32MX Family Reference Manual (DS61113C-page 2-44)
 CONFIG: Register; CP0 Register
 bit 15 BE: Big Endian bit
  Indicates the Endian mode in which the processor is running, PIC32MX 
is always little endian.
  0 = Little endian
  1 = Big enidan      <== das ist natürlich ein rechtschreibfehler im 
Referenz Manual, bleibt aber LE.

;-)

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Ouch! Jedenfalls so viel zu "normalerweise"
Aber MIPS in den little endian Modus zwingen is schon "nett".

von Wilhelm M. (wimalopaan)


Lesenswert?

Wenn es denn C++ sein soll, kann man es (in Zukunft, oder jetzt schon im 
gcc) so feststellen:

https://en.cppreference.com/w/cpp/types/endian

von Peter C. (peter_c49)


Lesenswert?

#if !defined(_BIG_ENDIAN_)
    printf("little endian\n\r");
#endif

das kann der xc32 eigentlich immer.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter C. schrieb:
> das kann der xc32 eigentlich immer.

Der Gcc auch:

https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html#Common-Predefined-Macros

Aber ich wollte auf einen Standard-konformen Weg in C++ hinweisen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Wer structured-bindings in C++ mag, kann auch mit dem tuple-Interface 
folgendes schreiben:
1
template<typename C, typename T>
2
requires (std::is_same_v<typename C::value_type, std::byte>) 
3
static inline void long2byte(C& b, T val) {
4
    static_assert(b.size() == size0f(T));
5
    [&]<auto... II>(std::index_sequence<II...>) {
6
        ((b[II] = std::byte(val >> (8 * II))), ...);  
7
    }(std::make_index_sequence<b.size()>{});    
8
}
9
10
volatile auto r = 0x04030201;
11
12
int main() {
13
    auto [lsb, b1, b2, msb] = bytesOf(r);
14
    return (int)lsb;
15
}

Leider sind trailing-requires-clauses noch nicht so ganz richtig im gcc 
implementiert. Sonst könnte man das static_assert() auch noch 
sinnvollerweise ersetzen.

von Dirk B. (dirkb2)


Lesenswert?

A. K. schrieb:
> Dafür leihe ich mir, wenn verfügbar, Funktionen wie htonl/ntohl aus der
> BSD Socket Lib aus.

Die nützen auf Big Endian Systemen aber nichts.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Dirk B. schrieb:
> A. K. schrieb:
> Dafür leihe ich mir, wenn verfügbar, Funktionen wie htonl/ntohl aus der
> BSD Socket Lib aus.
>
> Die nützen auf Big Endian Systemen aber nichts.

Doch. Man hat immer eine definierte Bytereihenfolge für Ein-/Ausgabe - 
egal, ob das System Little- oder Big-Endian ist.

Glaubst Du tatsächlich, dass Big-Endian-Systeme nicht am Internet 
teilnehmen können?

: Bearbeitet durch Moderator
von morph1 (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Der Gcc auch:

heute kann ich das wort derivat ungewöhnlich häufig verwenden :)

der XC32 ist ja auch ein GCC derivat =)

von Dirk B. (dirkb2)


Lesenswert?

Frank M. schrieb:
> Doch. Man hat immer eine definierte Bytereihenfolge für Ein-/Ausgabe -
> egal, ob das System Little- oder Big-Endian ist.
>
> Glaubst Du tatsächlich, dass Big-Endian-Systeme nicht am Internet
> teilnehmen können?

Internet ist, wenn ich mich recht erinnere, Big Endian.
Diese Routinen (ntoh und hton) machen auf BE-Systemen also Nichts.

Damit kommst du auch nicht auf LE.

Der TO wollte auf LE konvertieren.

von leo (Gast)


Lesenswert?

Dirk B. schrieb:
> A. K. schrieb:
>> Dafür leihe ich mir, wenn verfügbar, Funktionen wie htonl/ntohl aus der
>> BSD Socket Lib aus.
>
> Die nützen auf Big Endian Systemen aber nichts.

Um eine dedizierte Byteorder (nicht nur BE) zu erreichen bietet sich
1
$ man 3 endian
an, wenn man die Funktionen denn hat.

leo

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Dirk B. schrieb:
> Der TO wollte auf LE konvertieren.

Wo steht das?

Ich finde nur das:

Gowron schrieb:
> Ich habe als Eingangsdatum den 32-Bit-Wert und möchte diesen byteweise
> in einer definierten Reihenfolge auf eine serielle Leitung schicken.

Er braucht eine definierte Reihenfolge. Von LE schreibt er nichts.

Beitrag #6077502 wurde vom Autor gelöscht.
von Heinz (Gast)


Lesenswert?

Gowron schrieb:
> Ich habe als Eingangsdatum den 32-Bit-Wert und
> möchte diesen byteweise in einer definierten Reihenfolge auf eine
> serielle Leitung schicken.

Das union-Konstrukt aus uint32 und byte[4] ist für diese Anwendung
nur scheinbar der richtige Weg, denn es ist von Hause aus schon 
alignment-abhängig und legt die Bytes daher nur unter bestimmten 
Rand-Bedingungen in der richtigen Reihenfolge auf die serielle Leitung.

Du brauchst eine alignment-unabhängige Serialisierung und das ginge in 
etwa wie folgt:

struct x
{
  uint32 a;
  uint32 b;
  uint16 c;
}myStruct

byte  buffer[64];

CopyUint32ToBuffer(myStruct.a, &buffer[0]);
CopyUint32ToBuffer(myStruct.b, &buffer[4]);
CopyUint16ToBuffer(myStruct.c, &buffer[8]);



void CopyUint32ToBuffer(uint32 value, byte* p)
{
  #if BIG_ENDIAN
    CopyUint32ToBufferBE
  #else
   CopyUint32ToBufferLE
}

void CopyUint32ToBufferBE(uint32 value, byte* p)
{
  p[0] = (byte)((value&0xFF000000) >> 24);
  p[1] = (byte)((value&0x00FF0000) >> 16);
  p[2] = (byte)((value&0x0000FF00) >> 8);
  p[3] = (byte)((value&0x000000FF));
}

void CopyUint32ToBufferLE(uint32 value, byte* p)
{
  p[3] = (byte)((value&0xFF000000) >> 24);
  p[2] = (byte)((value&0x00FF0000) >> 16);
  p[1] = (byte)((value&0x0000FF00) >> 8);
  p[0] = (byte)((value&0x000000FF));
}

...für UInt16, Int16, Int32, Float entsprechend im gleichen Muster.

Es gibt auch Libs, welche das Serialisieren und Deserialisieren
eleganter kapseln, als hier gezeigt. Das Beispiel oben illustriert 
jedenfalls das Prinzip ganz anschaulich wie ich finde und obiger Code 
läuft bei mir seit Jahren auf verschiedensten Prozessoren unauffällig.

von 900ss (900ss)


Lesenswert?

Heinz schrieb:
> Das union-Konstrukt aus uint32 und byte[4] ist für diese Anwendung
> nur scheinbar der richtige Weg

Ja, ich stimme dir vol zu. Leider wird das immer wieder verwendet. Wie 
es endianess unabhängig geht, wurde ja mehrfach gezeigt.

Beruflich habe ich immer wieder mit beiden Endianess-Varianten zu tun. 
Und weil kaum jemand der Kollegen dieses berücksichtigt hat, ist das 
wiederverwenden von SW oft ein riesen Aufwand, nur um den ganzen Krempel 
aufzuräumen. :( Zumal natürlich auch nicht berücksichtigt wird, einmal 
beim Empfang der Daten und beim Versenden anzupassen sondern das 
schleppt sich durch die ganze SW-Struktur.

von Wilhelm M. (wimalopaan)


Lesenswert?

900ss D. schrieb:
> Ja, ich stimme dir vol zu. Leider wird das immer wieder verwendet. Wie
> es endianess unabhängig geht, wurde ja mehrfach gezeigt.

Und leider antworten viele Leute dann in dieser Art darauf:

Gowron schrieb:
> Genau: gar nichts!

von Dirk B. (dirkb2)


Lesenswert?

Frank M. schrieb:
> Er braucht eine definierte Reihenfolge. Von LE schreibt er nichts.

Oh, ja.
Dann habe ich da was vermixt.

von Dirk B. (dirkb2)


Lesenswert?

Heinz schrieb:
> Du brauchst eine alignment-unabhängige Serialisierung und das ginge in
> etwa wie folgt:

Mittlerweile erkennt der Compiler solche Konstrukte und optimiert die 
auch.
Da wird nicht mehr geschoben, sondern die Bytes direkt getauscht (wenn 
der Prozessor das unterstützt).

von Sven L. (svenl)


Lesenswert?

Hallo zusammen,

Das Problem des TE ist folgendes: Er versteht nicht, dass ein "int" 
nicht zwangsläufig 32 bit groß sein muss. - Er kann je nach Architektur 
8, 16, 32 oder was weiß ich wie viele Bits haben.

Wenn man etwas Erfahrung in C hat, dann weicht man automatisch auf die 
entsprechenden Datentypen wie zum Beispiel "uint32_t" aus. Dann wird das 
auch portabler Code, der auf jeglicher Architektur funktioniert. - Dies 
ist es, was mit "Code ordentlich schreiben" gemeint wurde...

Nun zur eigentlichen Frage: Das Ergebnis ist von der Architektur 
abhängig, ob sie little endian oder big endian ist.

Wenn die Dokumentation zum Controller nichts hergibt (was ich nicht 
glaube), dann hilft ausprobieren.

Wenn Du zur Laufzeit die Endianess feststellen musst, dann kannst Du als 
kleine Fingerübung eine Funktion schreiben, welche Dir mit statischen 
Werten des eine oder das andere erwartete Ergebnis liefert.

Viele Grüße!

Sven

von STK500-Besittzer (Gast)


Lesenswert?

Sven L. schrieb:
> Er kann je nach Architektur

Die durch die Angabe "PIC32" vorgegeben ist.

Sven L. schrieb:
> Dann wird das
> auch portabler Code, der auf jeglicher Architektur funktioniert.

Ist hier irrelevant, oder?!
Das hatte Peter D. aber schon geschrieben.

Sven L. schrieb:
> Wenn die Dokumentation zum Controller nichts hergibt (was ich nicht
> glaube), dann hilft ausprobieren.

Oder den Post von Peter D. lesen.

Zum Thema:
Wenn auf beiden Seite der Leitung die gleichen Systeme (Compile, 
Controller) verwendet werden, ist die Byteorder doch egal.

Die Methode eine Union zu verwenden finde ich praktisch, wenn es sich 
bei den Daten um eine struct handelt, und ich sie (lokal) in einem 
EEPROM o.dergl speichere - da ist mir die Byteorder völlig egal; 
notfalls halt als packed struct.

von Dirk B. (dirkb2)


Lesenswert?

Sven L. schrieb:
> Er versteht nicht, dass ein "int"
> nicht zwangsläufig 32 bit groß sein muss. - Er kann je nach Architektur
> 8, 16, 32 oder was weiß ich wie viele Bits haben.

8 Bit sind nicht vom Standard gedeckt, darum braucht man die nicht 
betrachten.
Man kann Compiler (für Mikrocontroller) wohl dazu zwingen, aber dann 
kann man char nehmen.

> Wenn man etwas Erfahrung in C hat, dann weicht man automatisch auf die
> entsprechenden Datentypen wie zum Beispiel "uint32_t" aus. Dann wird das
> auch portabler Code, der auf jeglicher Architektur funktioniert.

Dann wird er unportabler (wegen "was weiß ich wie viele Bits haben").
Historisch sind 9 Bit pro Byte oder bei DSP auch 24 Bit pro Byte.

Es gibt aber noch uint32_least_t oder uint32_fast_t, die sind dafür 
besser geeignet.

Wenn es auf die genaue Anzahl Bits nicht ankommt, bleibt man bei int

von Bernd K. (prof7bit)


Lesenswert?

Er wills doch überhaupt nicht portabel haben denn er hat explizit 
gefragt wie es speziell auf dem PIC32 ist. Er hat nicht gefragt wie es 
überall ist oder wie man irgendwas portabel macht. Da wirds wohl ein 
offizielles ABI-Dokument geben und da wird dann schwarz auf weiß drin 
stehen wie groß dort ein Byte ist, wie die Byte-Order ist, die 
Alignment-Regeln und ob und wie sein union funktionieren wird.

Man kann nämlich auch mal die Kirche im Dorf lassen! Man kann auch 
absichtlich Code schreiben für nur genau eine einzige Hardware die 
niemals irgendwo anders laufen soll und sich dann einen ganzen Haufen 
Extrawürste oder kleinste gemeinsame Nenner sparen und stattdessen 
pragmatische Abkürzungen nehmen, solange man vorher die richtigen 
Fragen stellt und dann in Erfahrung bringt was genau einem auf dieser 
einen speziellen Plattform schriftlich garantiert wird und was nicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Bernd K. schrieb:
> Er wills doch überhaupt nicht portabel haben denn er hat explizit
> gefragt wie es speziell auf dem PIC32 ist. Er hat nicht gefragt wie es
> überall ist oder wie man irgendwas portabel macht.

Das mag sein.

Allerdings ist die portable und immer korrekte Lösung per Bit-Shift im 
späteren Code ja gar nicht anders gegenüber den speziellen Lösungen für 
eine Architektur. Denn Bit-Shifts mit Vielfachen von der Bytegröße durch 
einen entsprechenden Register / Memory-Zugriff zu ersetzen, gehört so 
ziemlich zu den ältesten Compileroptimierungen. Vertraue Deinem 
Compiler.

Hier meine universelle C++ Lösung für alle Fälle (ja, ich weiß ... er 
hat aber ausdrücklich C/C++ geschrieben):
1
template<auto E = std::endian::little, typename T = void>
2
inline constexpr auto bytesOf(T value) {
3
    std::array<std::byte, sizeof(T)> data;
4
    if constexpr(E == std::endian::little) {
5
        [&]<auto... II>(std::index_sequence<II...>) {
6
            ((data[II] = std::byte(value >> ((8 * II)))), ...);  
7
        }(std::make_index_sequence<sizeof(T)>{});
8
    }
9
    else if constexpr (E == std::endian::big){
10
        [&]<auto... II>(std::index_sequence<II...>) {
11
            ((data[sizeof(T) - 1 - II] = std::byte(value >> ((8 * II)))), ...);  
12
        }(std::make_index_sequence<sizeof(T)>{});
13
    }
14
    else {
15
        static_assert(std::false_v<T>);
16
    }
17
    return data;
18
}
19
20
volatile auto r = 0x04030201;
21
22
int main() {
23
    auto [b1] = bytesOf(uint8_t{0x01});
24
    auto [lsb2, msb2] = bytesOf(0x0201);
25
    auto [lsb3, b31, b32, msb3] = bytesOf<std::endian::big>(r);
26
    auto [lsb4, b41, b42, msb4] = bytesOf(0x04030201);
27
    auto [lsb5, b51, b52, b53, b54, b55, b56, msb5] = bytesOf(0x0403020104030201UL);
28
         
29
    return (int)b1;
30
}

von Bernd K. (prof7bit)


Lesenswert?

Wilhelm M. schrieb:
> Denn Bit-Shifts mit Vielfachen von der Bytegröße durch
> einen entsprechenden Register / Memory-Zugriff zu ersetzen, gehört so
> ziemlich zu den ältesten Compileroptimierungen. Vertraue Deinem
> Compiler.

Manchmal will man auch kompakten Quelltext da stehen haben und keine 
Shift-Orgie nur um die Dogmatiker zu befriedigen, da wird dann halt mal 
ein großes struct in einem Rutsch als Blob gesendet oder empfangen und 
spart sich das mit einer shift-Orgie jedes Element einzeln zu 
serialisieren nur um am Ende die exakt selben Daten zu haben.

Lieber plaziert man davor 2 oder 3 static asserts die die kritischen 
Fragen auf dieser Plattform (Endianness, sizeof, alignment) abklären und 
wenn die alle nicht anschlagen dann ist der darauf folgende Code 
garantiert korrekt und nur einen Bruchteil so groß wie die 
allgemeingültige Variante.

Die gesparte Lebenszeit steckt man dann in wichtigere Dinge.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

An dieser Stelle sei den interessierten Lesern der Artikel 
Serialisierung empfohlen. Dieser behandelt die verschiedenen 
Möglichkeiten zu diesem Thema.

von Joachim B. (jar)


Lesenswert?

Bernd K. schrieb:
> Er wills doch überhaupt nicht portabel haben denn er hat explizit
> gefragt wie es speziell auf dem PIC32 ist.

er ist aber auch nicht besonders kooperativ.
Nun gut statt hier rumzumaulen weil für ihn ungenügende Antworten kommen 
wegen der "komischen" Aufgabenstellung hätte er locker einige Variablen 
in ein Struct schreiben können und sich durch byteweises Auslesen die 
Frage selber beantworten können.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Bernd K. schrieb:
> Die gesparte Lebenszeit steckt man dann in wichtigere Dinge.

Genau.
Man schreibt einmal eine saubere Funktion dafür und muß sich dann nie 
wieder darum kümmern. Man ruft fürderhin nur die entsprechende Funktion 
auf.

Zeitverschwendung wäre, wenn man bei neuen Projekten mit anderen 
Compilern und Targets jedesmal neu darüber nachdenken müßte und den Code 
ändern muß.

von Peter D. (peda)


Lesenswert?

Bernd K. schrieb:
> Man kann nämlich auch mal die Kirche im Dorf lassen! Man kann auch
> absichtlich Code schreiben für nur genau eine einzige Hardware die
> niemals irgendwo anders laufen soll

Sicher niemals?
Die Versuchung ist nämlich groß, einmal geschriebenen Code in anderen 
Projekten nachnutzen zu wollen, statt immer wieder vom Urschlein an 
alles neu zu schreiben.
In der Regel hat man dann alle Fallgruben und dirty hacks vergessen und 
fällt auf die Nase.

von Joachim B. (jar)


Lesenswert?

Peter D. schrieb:
> Die Versuchung ist nämlich groß, einmal geschriebenen Code in anderen
> Projekten nachnutzen zu wollen, statt immer wieder vom Urschlein an
> alles neu zu schreiben.

ich hätte nie gedacht das ich puren C-Code aus meiner 
Prüfgeräteentwicklungszeit von DOS über Atari nun zum Atmel und sogar zu 
Arduino(sorry) bringe, aber man soll nie nie sagen und manchmal ist es 
sogar einfach toll das es möglich ist und leichter als Pascal, 6502.asm 
oder Basic weiterzunutzen.

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Peter D. schrieb:
> In der Regel hat man dann alle Fallgruben und dirty hacks vergessen und
> fällt auf die Nase

Dann macht man darauf ein static assert und fertig. Ich finde es absurd, 
ein unsigned char nicht bei 255 nach 0 überlaufen zu lassen, nur weil es 
auch 9 Bit haben kann. Oder die int32-fetischisten, die glauben, damit 
portabel zu sein, ohne an integral Promotion zu denken oder 
Formatstrings portabel zu verunstalten.

Und ja, strukturierte Telegramme sind einfach lesbarer als hunderte von 
1-4 Byte kopierorgien. Auch da reichen asserts, um sicherzustellen, dass 
die Strukturen so gepackt sind wie erwartet.

Oder die Registerzugriffe mit bequemen structs des Herstellers: ich habe 
die bitfetischisten nie verstanden, die statt reg0.xy=5 lieber ihre 
schiebe und maskierformeln wollen. Ja, nicht portabel, aber welches 
HW-register ist das schon?

Wobei das Beispiel im Threadstart natürlich Blödsinn ist und auf einen 
Anfänger deutet, der erstmals serialisiert.

von Wilhelm M. (wimalopaan)


Lesenswert?

A. S. schrieb:
> Oder die Registerzugriffe mit bequemen structs des Herstellers: ich habe
> die bitfetischisten nie verstanden, die statt reg0.xy=5 lieber ihre
> schiebe und maskierformeln wollen. Ja, nicht portabel, aber welches
> HW-register ist das schon?

In diesem Forum gab es mal einen Thread zu folgendem Thema: es ging 
darum, die von Atmel/MicroChip für AVR zur Verfügung gestellten Header 
mit den SFR-Definition zu modifizierten Header umzuschreiben, bei denen 
Bitfield-Typen für die SFRs verwendet werden. Und das ist grober Unfung.

Denn bei einem Register r (< 0x20) wird aus
1
r.bit0 = 1;

ein sbi (korrekt) und bei einem anderen Register (>= 0x20) wird daraus 
ein RMW-Zyklus (ld, ori, st). Neben der Nicht-Atomarität ist das größere 
Problem hier die Flag-Register, deren Flags durch das Schreiben einer 1 
ins jeweilige Bit zurückgesetzt werden. Damit setzt man ggf. alle 
gesetzten Flags im Flag-Register zurück, und nicht nur das gewünschte.

Deswegen sollte man so einen Unfug lassen. Gerade Leute, die auf 
unterschiedlichen µC unterwegs sind, wollen sich über so etwas keine 
Gedanken machen. Also schreibt man als überall korrektes Idiom
1
r = EnumName.BitName;

oder
1
r = r | EnumName.BitName;

(ab C++20 sind die compound-operator für volatile qualifizierte DT 
deprecated)

Damit erreicht man Gleichförmigkeit im Code, eine klare Intentionalität 
und immer korrektes Verhalten, egal welcher µC oder Compiler.

von Peter C. (peter_c49)


Lesenswert?

Hallole,

>was steht dann in bData[0], bData[1], bData[2] und bData[3] drin?
da ich gerade eben einen PIC32MX ausgebudelt habe und ich den Geruch von 
der alten Phenolharz-Platine so anregend empfinde...

wie erwartet steht da das drin:
bData[0]:EF
bData[1]:BE
bData[2]:AD
bData[3]:DE

mfG
Peter ;-)

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Peter C. schrieb:
> wie erwartet steht da das drin:

 Und, wie erwartet, 45 Beiträge waren dafür nötig.
 Lol.

von Bernd K. (prof7bit)


Lesenswert?

Peter D. schrieb:
> Zeitverschwendung wäre, wenn man bei neuen Projekten mit anderen
> Compilern und Targets jedesmal neu darüber nachdenken müßte und den Code
> ändern muß.

Es ging als Vorbedingung für meine Aussage um Code der jeweils nur auf 
dieser einen Hardware läuft (MCU), nicht um eine Library die in 30 
Jahren jemand anders auf irgendwelchen außerirdischen Plattformen für 
was völlig anderes nutzen kann. Und ich warte auch nicht bis ins hohe 
Alter auf die Wiederauferstehung von Big-Endian und schreib schonmal 
vorsorglich Code dafür (den ich dann aber leider nirgends testen kann).

Anstatt in solchen lokal begrenzten Projekten (MCU, Arm Linux, x86) 
deren Code eh nur intern verwendet wird umständlichen Code zu schreiben 
mit dem ich dann völlig unnötig (ich kenn ja mein ABI) jeden Parameter 
einzeln schieben, einzeln in dem Sendepuffer kopieren oder da 
rauskratzen muss schreibe ich lieber genau null zusätzlichen Code weil 
ich das ganze struct einfach so über die Leitung donnern kann wie es im 
Speicher liegt weil ich genau weiß dass alle beteiligten ABIs (x86, 
amd64, arm, avr, RISC-V und mit an Sicherheit grenzender 
Wahrscheinlichkeit alle zukünftigen ebenfalls) little-endian und 
self-aligned oder weniger sind und genau 8 Bit pro Byte haben.

Darauf wette ich 2 Bildschirmseiten eingesparten Code in jedem Projekt. 
Mal sehen wer gewinnt.

von (prx) A. K. (prx)


Lesenswert?

Also ganz sicher nie Coldfire? Oder läufts dann umgekehrt, es kommen 
also eben deshalb querbeet in der Vernetzung nur Cores in Frage, die LE 
arbeiten? Zugegeben, das sind die meisten, zumal mittlerweile sogar IBM 
eingelenkt hat

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

A. K. schrieb:
> Also ganz sicher nie Coldfire?

Mit an Sicherheit grenzender Wahrscheinlichkleit nicht. Die fallen ja 
auch nicht einfach so vom Himmel und ich muss sie dann zwingend 
unterstützen sonderen es orientiert sich an dem was man selbst nutzen 
will wenn man seine eigene Hardware entwickelt und sich einen Controller 
dafür aussucht.

von Bernd K. (prof7bit)


Lesenswert?

A. K. schrieb:
> in der Vernetzung nur Cores in Frage, die LE
> arbeiten?

Sollte ich wirklich zufällig mal einen mit Big-Endian haben aus welchen 
Gründen auch immer weiß ich was zu tun ist. Aber davon bleiben meine 
ganzen ARM-Cortexe unberührt.

Ich muss momentan an einer Stelle auch ein Big-Endian-Protokoll 
unterstützen und da muss ich dann tatsächlich den Schiebezirkus machen, 
aber alle internen Protokolle und Datenformate sind bei mir LE und 
bleiben das auch.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Bernd K. schrieb:
> Ich muss momentan an einer Stelle auch ein Big-Endian-Protokoll
> unterstützen und da muss ich dann tatsächlich den Schiebezirkus machen,

 inline asm und REV machen das bei ARM in einem einzigen Befehl.

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
Noch kein Account? Hier anmelden.