INFO: Dieses Forum nutzt Cookies...
Cookies sind für den Betrieb des Forums unverzichtbar. Mit der Nutzung des Forums erklärst Du dich damit einverstanden, dass wir Cookies verwenden.

Es wird in jedem Fall ein Cookie gesetzt um diesen Hinweis nicht mehr zu erhalten. Desweiteren setzen wir Google Adsense und Google Analytics ein.

Antwort schreiben 
 
Themabewertung:
  • 0 Bewertungen - 0 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Datentypen und Datenaustausch zwischen 8 und 32 Bit Prozessoren
04.01.2018, 14:34 (Dieser Beitrag wurde zuletzt bearbeitet: 04.01.2018 15:00 von Tommy56.)
Beitrag #1
Datentypen und Datenaustausch zwischen 8 und 32 Bit Prozessoren
Unsere Arduinowelt ist ja gerade dabei durch 32-Bit-Prozessoren (z.B. ESP8266 und ESP32) erweitert zu werden.
Ich habe deshalb unter Nutzung von Sketches von ardu_arne (hier und hier) eine Übersicht erstellt, wie sich die Längen der Variablen verhält.
Besonders interessant wird dieses Problem, wenn man binäre Daten zwischen beiden Welten austauschen möchte. Dazu werden meist Strukturen verwendet. In diesen sollten immer die Variablenbezeichner, die die Länge beinhalten (wie z.B. uint32_t) benutzt werden.
Bei den 32-Bit Prozessoren tritt noch ein zusätzliches Problem auf: Auf Adressen, die durch 4 teilbar sind kann der Prozessor besonders schnell zugreifen. Deshalb werden Speicherstrukturen an diesen Grenzen ausgerichtet. Das bedeutet im Endeffekt, dass die gleiche Struktur auf dem ESP8266 größer ist, als auf einem UNO.
Code:
struct daten {
  uint8_t  byte1 = 0x01;
  uint32_t ulong1 = 0x22334455;
  uint8_t byte2 = 0x11;  
  uint16_t uint1 = 0x7788;
  uint8_t byte3 = 0x99;
} daten1;

ergibt auf dem UNO:

Size: 9 Byte - Data = 0x01 0x55 0x44 0x33 0x22 0x11 0x88 0x77 0x99
Struktur ungepackt sizeof:    9
Adressen: Anfang: 0x112 byte1: 0x112 ulong1: 0x113 byte2: 0x117 uint1: 0x118 byte3: 0x11A

und auf dem ESP8266 bzw. ESP 32:

Size: 16 Byte - Data = 0x01 0x00 0x00 0x00 0x55 0x44 0x33 0x22 0x11 0x00 0x88 0x77 0x99 0x00 0x00 0x00
Struktur ungepackt sizeof:    16
Adressen: Anfang: 0x3FFE8374 byte1: 0x3FFE8374 ulong1: 0x3FFE8378 byte2: 0x3FFE837C uint1: 0x3FFE837E byte3: 0x3FFE8380
Wir sehen, das einzelne Byte wird mit 3 Füllbytes aufgefüllt, erst dann beginnt der uint32_t. Die Struktur ist auf dem UNO 9 Byte groß, auf dem ESP 16 Byte. Wenn wir die Einzelbytes der Struktur an den Anfang sortieren, ergibt sich folgendes Bild:

Code:
struct sdaten {
  uint8_t byte1 = 0x01;
  uint8_t byte2 = 0x11;  
  uint8_t byte3 = 0x99;
  uint32_t ulong1 = 0x22334455;
  uint16_t uint1 = 0x7788;
} sdaten1;

ergibt auf dem UNO die gewohnte Größe:

Size: 9 Byte - Data = 0x01 0x11 0x99 0x55 0x44 0x33 0x22 0x88 0x77
Struktur ungepackt sortiert sizeof:    9
Adressen: Anfang: 0x109 byte1: 0x109 byte2: 0x10A byte3: 0x10B ulong1: 0x10C uint1: 0x110

aber auf dem ESP8266 bzw. ESP32 eine kleinere Struktur:

Size: 12 Byte - Data = 0x01 0x11 0x99 0x00 0x55 0x44 0x33 0x22 0x88 0x77 0x00 0x00
Struktur ungepackt sortiert sizeof:    12
Adressen: Anfang: 0x3FFE8368 byte1: 0x3FFE8368 byte2: 0x3FFE8369 byte3: 0x3FFE836A ulong1: 0x3FFE836C uint1: 0x3FFE8370
Durch das Sortieren kann also Speicherplatz gespart werden, well weniger Füllbytes notwendig sind. Das hilft uns aber beim Datenaustausch immer noch nicht.
Dazu müssen wir dem Compiler/Linker sagen, er soll keine Füllbytes verwenden und alles dicht packen:

Code:
// Anweisung zum Packen: Ausrichtung auf 1 Byte
#pragma pack (push, 1)
struct pdaten {
  uint8_t  byte1 = 0x01;
  uint32_t ulong1 = 0x22334455;
  uint8_t byte2 = 0x11;  
  uint16_t uint1 = 0x7788;
  uint8_t byte3 = 0x99;
} pdaten1;
#pragma pack (pop)

ergibt auf den UNO das gewohnte Bild:

Size: 9 Byte - Data = 0x01 0x55 0x44 0x33 0x22 0x11 0x88 0x77 0x99
Struktur gepackt sizeof:    9
Adressen: Anfang: 0x100 byte1: 0x100 ulong1: 0x101 byte2: 0x105 uint1: 0x106 byte3: 0x108

und auch auf dem ESP8266 bzw. ESP32 haben wir jetzt die gleiche Struktur:

Size: 9 Byte - Data = 0x01 0x55 0x44 0x33 0x22 0x11 0x88 0x77 0x99
Struktur gepackt sizeof:    9
Adressen: Anfang: 0x3FFE835C byte1: 0x3FFE835C ulong1: 0x3FFE835D byte2: 0x3FFE8361 uint1: 0x3FFE8362 byte3: 0x3FFE8364
Damit können wir diese binäre Struktur zum Datenaustausch zwischen beiden Plattformen (über I2C, Netzwerk, usw.) benutzen, ohne uns Probleme einzuhandeln.

Hier noch der Sketch für die Messungen (die Verrenkungen bei der seriellen Ausgabe der Adressen wurden auf den 32-Bit-Typen notwendig):
Code:
#if defined (__AVR__)
  const char typ[] = "AVR";
#elif defined(ESP8266)
  const char typ[] = "ESP8266";
#elif defined(ESP32)
  const char typ[] = "ESP32";
#else
  const char typ[] = "unknown";
#endif

struct daten {
  uint8_t  byte1 = 0x01;
  uint32_t ulong1 = 0x22334455;
  uint8_t byte2 = 0x11;  
  uint16_t uint1 = 0x7788;
  uint8_t byte3 = 0x99;
} daten1;

struct sdaten {
  uint8_t byte1 = 0x01;
  uint8_t byte2 = 0x11;  
  uint8_t byte3 = 0x99;
  uint32_t ulong1 = 0x22334455;
  uint16_t uint1 = 0x7788;
} sdaten1;

#pragma pack (push, 1)
struct pdaten {
  uint8_t  byte1 = 0x01;
  uint32_t ulong1 = 0x22334455;
  uint8_t byte2 = 0x11;  
  uint16_t uint1 = 0x7788;
  uint8_t byte3 = 0x99;
} pdaten1;
#pragma pack (pop)

uint8_t einByte = 0x2, nocheinByte = 0x3;

template <typename T> unsigned int serialPrintAnything (const T& value)
{
  const byte * p = (const byte*) &value;
  unsigned int i;
  Serial.print("Size: "); Serial.print(sizeof value); Serial.print(" Byte - Data = ");
  for (i = 0; i < sizeof value; i++) {
    Serial.print("0x");
    if (*p <= 15) Serial.print("0");
    Serial.print(*p++, HEX);
    Serial.print(" ");
  }
  Serial.println();
  return i;
}

void setup() {
uint32_t addr;
Serial.begin(115200);
Serial.println("\n\nStart");
Serial.println();
Serial.print("Anordnung der Datentypen in Strukturen beim "); Serial.println(typ);
Serial.println();
Serial.println("unsortiert ungepackt");
serialPrintAnything(daten1);
Serial.print("Struktur ungepackt sizeof:\t"); Serial.println(sizeof(daten1));
addr = (uint32_t)&daten1;
Serial.print("Adressen: Anfang: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&daten1.byte1;
Serial.print(" byte1: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&daten1.ulong1;
Serial.print(" ulong1: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&daten1.byte2;
Serial.print(" byte2: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&daten1.uint1;
Serial.print(" uint1: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&daten1.byte3;
Serial.print(" byte3: 0x"); Serial.print(addr,HEX);
Serial.println();
Serial.println();
Serial.println("sortiert ungepackt");
serialPrintAnything(sdaten1);
Serial.print("Struktur ungepackt sortiert sizeof:\t"); Serial.println(sizeof(sdaten1));
addr = (uint32_t)&sdaten1;
Serial.print("Adressen: Anfang: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&sdaten1.byte1;
Serial.print(" byte1: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&sdaten1.byte2;
Serial.print(" byte2: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&sdaten1.byte3;
Serial.print(" byte3: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&sdaten1.ulong1;
Serial.print(" ulong1: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&sdaten1.uint1;
Serial.print(" uint1: 0x"); Serial.print(addr,HEX);
Serial.println();
Serial.println();
Serial.println("unsortiert gepackt");
serialPrintAnything(pdaten1);
Serial.print("Struktur gepackt sizeof:\t"); Serial.println(sizeof(pdaten1));
addr = (uint32_t)&pdaten1;
Serial.print("Adressen: Anfang: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&pdaten1.byte1;
Serial.print(" byte1: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&pdaten1.ulong1;
Serial.print(" ulong1: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&pdaten1.byte2;
Serial.print(" byte2: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&pdaten1.uint1;
Serial.print(" uint1: 0x"); Serial.print(addr,HEX);
addr = (uint32_t)&pdaten1.byte3;
Serial.print(" byte3: 0x"); Serial.print(addr,HEX);
Serial.println();
Serial.println();

Serial.print("Laenge der Datentypen beim "); Serial.println(typ);
Serial.println();
// Serial.print("sizeof void :        "); Serial.print(sizeof(void));         Serial.println(" byte");
Serial.print("sizeof bool :        "); Serial.print(sizeof(bool));         Serial.println(" byte");
Serial.print("sizeof char :        "); Serial.print(sizeof(char));         Serial.println(" byte");
Serial.print("sizeof byte :        "); Serial.print(sizeof(byte));         Serial.println(" byte");
Serial.print("sizeof uint8_t :     "); Serial.print(sizeof(uint8_t));      Serial.println(" byte");
Serial.println();
Serial.print("sizeof short :       "); Serial.print(sizeof(short));        Serial.println(" byte");
Serial.print("sizeof uint16_t :    "); Serial.print(sizeof(uint16_t));     Serial.println(" byte");
Serial.println();
Serial.print("sizeof word :        "); Serial.print(sizeof(word));         Serial.println(" byte");
Serial.print("sizeof int :         "); Serial.print(sizeof(int));          Serial.println(" byte");
Serial.print("sizeof long :        "); Serial.print(sizeof(long));         Serial.println(" byte");
Serial.print("sizeof uint32_t :    "); Serial.print(sizeof(uint32_t));     Serial.println(" byte");
Serial.println();
Serial.print("sizeof long long :   "); Serial.print(sizeof(long long));    Serial.println(" byte");
Serial.print("sizeof uint64_t :    "); Serial.print(sizeof(uint64_t));     Serial.println(" byte");
Serial.println();
Serial.print("sizeof float :       "); Serial.print(sizeof(float));        Serial.println(" byte");
Serial.print("sizeof double :      "); Serial.print(sizeof(double));       Serial.println(" byte");
}

void loop() {
}
Im Anhang noch die seriellen Ausgaben und das Calc-File, falls jemand mit anderen Prozessoren die Tabelle erweitern möchte.

Gruß Tommy


Angehängte Datei(en) Thumbnail(s)
   

.txt  ergebnis.txt (Größe: 3,65 KB / Downloads: 16)

"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
04.01.2018, 15:51
Beitrag #2
RE: Datentypen und Datenaustausch zwischen 8 und 32 Bit Prozessoren
Danke Tommy, klasse gemacht!!!

Erkenntnisse:
Wenn man Strukturen von einem 32 Bit System auf ein 8 Bit System überträgt sollte man die Strukturen auf dem 32 Bit System nach Schema:
Code:
#pragma pack (push, 1)
struct pdaten {
  uint8_t  byte1 = 0x01;
  uint32_t ulong1 = 0x22334455;
  uint8_t byte2 = 0x11;
  uint16_t uint1 = 0x7788;
  uint8_t byte3 = 0x99;
} pdaten1;
#pragma pack (pop)
anlegen sonst erhält man auf der 8 Bit Seite Strukturen deren Bytepositionen nicht deckungsgleich sind.

Besondere Vorsicht ist bei der Übertragung von Variablen der Typen "int", "word" und "double" geboten weil diese zwischen den "Welten" auch unterschiedliche Längen haben.
Eine auf einem 32 Bit System erzeugte Variable vom Typ "int" oder "word" muss man in der Struktur auf einem 8 Bit System vom Typ "long" oder "int32_t" anlegen.
Eine auf einem 32 Bit System erzeugte Variable vom Typ "double" muss man erst auf "float" casten bevor man sie zum 8 Bit System überträgt.

Habe ich das soweit richtig und vollständig erkannt?

Gruß Arne
ExclamationMit zunehmender Anzahl qualifizierter Informationen bei einer Problemstellung, erhöht sich zwangsläufig die Gefahr auf eine zielführende Antwort.Exclamation
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
04.01.2018, 15:53
Beitrag #3
RE: Datentypen und Datenaustausch zwischen 8 und 32 Bit Prozessoren
Ich würde auf jeden Fall die Struktur in eine Datei legen, die unverändert auf beiden Systemen benutzt wird. Das pack stört ja auf dem 8-Bitter nicht.

Gruß Tommy

"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
05.01.2018, 13:28
Beitrag #4
RE: Datentypen und Datenaustausch zwischen 8 und 32 Bit Prozessoren
(04.01.2018 15:53)Tommy56 schrieb:  Ich würde auf jeden Fall die Struktur in eine Datei legen, die unverändert auf beiden Systemen benutzt wird. Das pack stört ja auf dem 8-Bitter nicht.

Gruß Tommy
Ich sehe es auch so, dass es sinnvoll ist diese Struktur(en) die mehrfach verwendet werden in einer zentralen Datei zu definieren. Ich habe das für eine kleines Testprojekt versucht so zu realisieren, scheitere aber dabei diese zentrale Datei mit relativen Pfadangaben zu inkludieren.

Verzeichnisstruktur:
Code:
d:\Arduino_IDE\arduino-1.8.5a\portable\sketchbook\
                                                    |
                                                    Arduino\
                                                    |       |Test\
                                                    |             |Mega_UDP\
                                                    |                       |Mega_UDP_01\
                                                    |                                    |Mega_UDP_01.ino
                                                    |      
                                                    |
                                                    ESP32\
                                                    |
                                                    ESP8266\
                                                    |       |Test\
                                                    |             |Node01\
                                                    |                     |Node01.ino
                                                    |
                                                    libraries\
                                                    |
                                                    projektdaten\
                                                                 |global_structs.h
Die Datei "global_structs.h" soll in "Mega_UDP_01.ino" und "Node01.ino" includiert werden.

Mit vollständiger Pfadangabe
Code:
#include "d:\Arduino_IDE\arduino-1.8.5a\portable\sketchbook\projektdaten\global_structs.h"
funktioniert es.

Mit relativer Pfadangabe
Code:
#include "..\sketchbook\projektdaten\global_structs.h"
findet der Compiler die Datei nicht.

Habe ich einen Schreibfehler bei der relativen Pfadangabe oder funktioniert es generell nicht relativ.

Gruß Arne
ExclamationMit zunehmender Anzahl qualifizierter Informationen bei einer Problemstellung, erhöht sich zwangsläufig die Gefahr auf eine zielführende Antwort.Exclamation
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
05.01.2018, 15:04
Beitrag #5
RE: Datentypen und Datenaustausch zwischen 8 und 32 Bit Prozessoren
Da Du auf beiden Ebenen unterschiedliche Tiefen hast, musst Du in beiden auch unterschiedliche relative Pfade setzen:

Code:
// aus Mega_UDP_01: 4 Mal hoch bis in sketchbook
#include "../../../../projektdaten\global_structs.h"

// aus Node01: 3 Mal hoch bis in sketchbook
#include "../../../projektdaten\global_structs.h"

Gruß Tommy

"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
05.01.2018, 16:06 (Dieser Beitrag wurde zuletzt bearbeitet: 05.01.2018 16:26 von ardu_arne.)
Beitrag #6
RE: Datentypen und Datenaustausch zwischen 8 und 32 Bit Prozessoren
Danke, so funktioniert es.
Ich war der Meinung er würde mit einem einfachen ../ so lange hoch springen bis er den Ordner "projektdaten" findet. Das ist aber wohl nicht so.
Es ist aber, wie ich jetzt feststellen konnte, auch egal ob man / oder \ verwendet. Beides funktioniert bei mir unter Windows. Auch bei gemischter Anwendung wie in deiner Antwort.

Die Datei "global_structs.h" lässt sich jetzt aber nicht mehr so einfach mit der Arduino-IDE bearbeiten weil sie keinem Sketch zugeordnet ist. Deshalb bemüht man dann gerne einen externen Editor.
Mit hier beschriebenen Trick geht es aber trotzdem. Einfach die Headerdatei per Drag&Drop auf den grünen Balken in der geöffneten IDE ziehen.
Das funktioniert auch mit .h oder .cpp Dateien aus beliebigen Librarys.

Gruß Arne
ExclamationMit zunehmender Anzahl qualifizierter Informationen bei einer Problemstellung, erhöht sich zwangsläufig die Gefahr auf eine zielführende Antwort.Exclamation
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
05.01.2018, 16:27
Beitrag #7
RE: Datentypen und Datenaustausch zwischen 8 und 32 Bit Prozessoren
Ja, das kann er selbst ausbügeln. Ich ziehe die / als alter Unixer vor Wink
Die \ waren von Dir kopiert.

Gruß Tommy

"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
14.04.2018, 12:45
Beitrag #8
RE: Datentypen und Datenaustausch zwischen 8 und 32 Bit Prozessoren
(05.01.2018 16:27)Tommy56 schrieb:  Ja, das kann er selbst ausbügeln. Ich ziehe die / als alter Unixer vor Wink
Die \ waren von Dir kopiert.

Gruß Tommy
Noch eine kleine Anmerkung zum Datenaustausch: Der funktioniert auf diese Weise nur bei Plattformen mit der gleichen Byteordnung. Das ist bei AVR und ESP gegeben.

Gruß Tommy

"Wer den schnellen Erfolg sucht, sollte nicht programmieren, sondern Holz hacken." (Quelle unbekannt)
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
Antwort schreiben 


Gehe zu:


Benutzer, die gerade dieses Thema anschauen: 1 Gast/Gäste