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:
  • 1 Bewertungen - 5 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Die Bytemühle - einfacher Zugriff auf einzelne Bytes einer Mehrbyte-Variablen
23.10.2015, 17:19 (Dieser Beitrag wurde zuletzt bearbeitet: 23.10.2015 20:02 von Bitklopfer.)
Beitrag #1
Die Bytemühle - einfacher Zugriff auf einzelne Bytes einer Mehrbyte-Variablen
Manchmal besteht die Notwendigkeit auf einzelne Bytes einer Mehrbyte-Variablen (wie int, float, long, ect) zuzugreifen.
Meist erledigt man das mittels Zeigern/Pointer.
Dies ist aber nicht jedermanns Sache, Einsteigern fällt es oft schwer Zeiger zu verwenden und viele mögen die Operatoren * und & nicht.
Deshalb möchte ich hier einen Weg über eine Hilfsvariable zeigen der vielleicht leichter zu verstehen ist.

Aber Vorsicht:
Es gibt keine Gewähr dass diese Möglichkeit mit jedem Compiler und jeder Zielplattform funktioniert.
Siehe dazu den Hinweis unter "Union mit überlagerten Typen" auf dieser Webseite im Nachbarforum.


Funktioniert hat es bei mir mit der Arduino IDE 1.6.5 und einem ProMini. Deshalb sollte es auch auf einem UNO (und all seinen Brüdern mit dem 328er Controller) und dem MEGA2560 funktionieren.

Um auf die einzelnen Bytes einer Mehrbyte-Variablen leichter zugreifen zu können "basteln" wir uns eine Hilfsvariable aus einem selbst definierten Datentyp. Bei diesem Datentyp kann man dann leicht die 4-Byte Variable und die einzelnen Bytes ansprechen.

Die Definition des neuen Datentyps, eine Union mit dem Namen "bytemuehle32", sieht so aus:
Code:
union bytemuehle32
{
  char           c[4];   //  8 bit (1 byte) -128 bis 127 / -2^7 bis (2^7)-1)
  unsigned char uc[4];   //  8 bit (1 byte) 0 bis 255 / 0 bis (2^8)-1)
  byte           b[4];   //  8 bit (1 byte) 0 bis 255 / 0 bis (2^8)-1)
  int8_t        i8[4];   //  8 bit (1 byte) -128 bis 127 / -2^7 bis (2^7)-1)
  uint8_t      ui8[4];   //  8 bit (1 byte) 0 bis 255 / 0 bis (2^8)-1)
  int            i[2];   // 16 bit (2 byte) -32,768 to 32,767 / -2^15 bis (2^15)-1)
  unsigned int  ui[2];   // 16 bit (2 byte) 0 to 65,535 / 0 bis (2^16)-1)
  word           w[2];   // 16 bit (2 byte) 0 to 65,535 / 0 bis (2^16)-1)
  short          s[2];   // 16 bit (2 byte) -32,768 to 32,767 / -2^15 bis (2^15)-1)
  int16_t      i16[2];   // 16 bit (2 byte) -32,768 to 32,767 / -2^15 bis (2^15)-1)
  uint16_t    ui16[2];   // 16 bit (2 byte) 0 bis 65,535 / 0 bis (2^16)-1)
  float             f;   // 32 bit (4 byte) -3.4028235E^-38 bis 3.4028235^+38
  long              l;   // 32 bit (4 byte) -2,147,483,648 bis 2,147,483,647 / -2^31 bis (2^31)-1)
  unsigned long    ul;   // 32 bit (4 byte) 0 to 4,294,967,295 / 0 bis (2^32)-1)
  double            d;   // 32 bit (4 byte) -3.4028235E^-38 bis 3.4028235^+38
  int32_t         i32;   // 32 bit (4 byte) -2,147,483,648 bis 2,147,483,647 / -2^31 bis (2^31)-1)
  uint32_t       ui32;   // 32 bit (4 byte) 0 to 4,294,967,295 / 0 bis (2^32) - 1)
};
Nachdem der neue Datentyp angelegt ist kann man ihn genau so verwenden wie alle bisher bekannten Datentypen.

Jetzt können wir eine Hilfsvariable mit diesem neuen Datentyp anlegen.
Code:
bytemuehle32   universalvariable32 ;
Das Besondere an dieser Variablen (universalvariable32) ist, dass sie nur 4 Byte im Speicher belegt. Der Speicherbedarf der Hilfsvariablen richtet sich nach der größten Einzelvariablen welche in der Union definiert wurde.

In 'universalvariable32' liegen jetzt alle in der Union definierten Variablen "übereinander", was bedeutet dass sie alle gleichzeitig vorhanden sind. Dadurch kann man jetzt auf alle in der Union definierten einzelnen Elemente direkt zugreifen.
Die einzelnen Elemente werden dabei über variablenname.elementname angesprochen.

Nehmen wir an wir hätten in unserem Programm eine Variable vom Typ unsigned long, dem Namen 'startzeit' und Inhalt '0x89ABCDEF' und wollen mit Teilbytes dieser Variablen in unserem Programm weiter arbeiten.
Dazu muss man den Inhalt von 'startzeit' passend in 'universalvariable32' kopieren.
Sinnvollerweise kopiert man 'startzeit' also in die unsigned long Variable der Union, also nach 'ul'.

Code:
unsigned long  startzeit  =  0x89ABCDEF;
universalvariable32.ul    =  startzeit;
Jetzt ist universalvariable32.ul = 0x89ABCDEF

Wie die einzelnen Elemente der Union in den 4 Byte von 'universalvariable32' liegen, kann man folgender Tabelle entnehmen.
   

Alle Elemente von 'universalvariable32' (Zeilen der Tabelle) haben den gleichen Inhalt, nämlich 0x89ABCDEF aus der obigen Zuweisung
universalvariable32.ul = startzeit;

Jetzt noch ein paar Beispiele als Programmcode die zeigen sollen wie man einzelne Elemente von 'universalvariable32' mit den Informationen aus der Tabelle lesen/beschreiben kann.
Code:
// schreiben von universalvariable32.ul mit 0x89ABCDEF;
universalvariable32.ul = 0x89ABCDEF;


// Ausgabe des kompletten Inhalts von universalvariable32
Serial.println(universalvariable32.ul,HEX);
// Ausgabeergebnis --> 89ABCDEF


// Ausgabe der höherwertigen Hälfte von universalvariable32
Serial.println(universalvariable32.ui[1],HEX);
// Ausgabeergebnis --> 89AB


// Ausgabe der niederwertigen Hälfte von universalvariable32
Serial.println(universalvariable32.ui[0],HEX);
// Ausgabeergebnis --> CDEF


// Ausgabe der einzelnen Bytes von universalvariable32 aufsteigend
Serial.print(universalvariable32.b[0],HEX);
Serial.print(universalvariable32.b[1],HEX);
Serial.print(universalvariable32.b[2],HEX);
Serial.println(universalvariable32.b[3],HEX);
// Ausgabeergebnis --> EFCDAB89


// Ausgabe der einzelnen Bytes von universalvariable32 absteigend
Serial.print(universalvariable32.b[3],HEX);
Serial.print(universalvariable32.b[2],HEX);
Serial.print(universalvariable32.b[1],HEX);
Serial.println(universalvariable32.b[0],HEX);
// Ausgabeergebnis --> 89ABCDEF


// niederwertigste Stelle um 1 erhöhen und gesamt ausgeben
universalvariable32.b[0]++;
Serial.println(universalvariable32.ul,HEX);
// Ausgabeergebnis --> 89ABCDF0


// höchstwertige Stelle um 2 verringern und gesamt ausgeben
universalvariable32.b[3]-=2;
Serial.println(universalvariable32.ul,HEX);
// Ausgabeergebnis --> 87ABCDF0


// Länge der einzelnen Elemente von universalvariable32 anzeigen
Serial.println(sizeof (universalvariable32) );
// Ausgabeergebnis --> 4
Serial.println(sizeof (universalvariable32.f) );
// Ausgabeergebnis --> 4
Serial.println(sizeof (universalvariable32.ui32) );
// Ausgabeergebnis --> 4
Serial.println(sizeof (universalvariable32.i16[0]) );
// Ausgabeergebnis --> 2
Serial.println(sizeof (universalvariable32.w[1]) );
// Ausgabeergebnis --> 2
Serial.println(sizeof (universalvariable32.i8[3]) );
// Ausgabeergebnis --> 1
Serial.println(sizeof (universalvariable32.uc[2]) );
// Ausgabeergebnis --> 1


// eine float Variable im EEPROM ab Adr. 10 speichern
float testwert_w = 1234.56;
int   adrw       = 10;
universalvariable32.f = testwert_w;
EEPROM.write(adrw+0, universalvariable32.b[0]);
EEPROM.write(adrw+1, universalvariable32.b[1]);
EEPROM.write(adrw+2, universalvariable32.b[2]);
EEPROM.write(adrw+3, universalvariable32.b[3]);


// eine float Variable aus dem EEPROM ab Adr. 10 auslesen
float testwert_r;
int   adrr     = 10;
universalvariable32.b[0] = EEPROM.read(adrr+0);
universalvariable32.b[1] = EEPROM.read(adrr+1);
universalvariable32.b[2] = EEPROM.read(adrr+2);
universalvariable32.b[3] = EEPROM.read(adrr+3);
testwert_r = universalvariable32.f;


// Manche seriellen Übertragungsmethoden lassen nur die Übertragung einzelner Bytes zu.
// Will man damit z.B. eine float Variable übertragen, so muss man diese in einzelne Bytes zerlegen
// Beispiel I2C
//
// float testwert = 1234.56;
// universalvariable32.f = testwert;
// Wire.write(universalvariable32.b[0]);
// Wire.write(universalvariable32.b[1]);
// Wire.write(universalvariable32.b[2]);
// Wire.write(universalvariable32.b[3]);

Nicht immer hat man mit 4-Byte Variablen zu tun, deshalb jetzt noch eine Union für
2-Byte Variablen
Code:
union bytemuehle16
{
  char           c[2];   //  8 bit (1 byte) -128 bis 127 / -2^7 bis (2^7)-1)
  unsigned char uc[2];   //  8 bit (1 byte) 0 bis 255 / 0 bis (2^8)-1)
  byte           b[2];   //  8 bit (1 byte) 0 bis 255 / 0 bis (2^8)-1)
  int8_t        i8[2];   //  8 bit (1 byte) -128 bis 127 / -2^7 bis (2^7)-1)
  uint8_t      ui8[2];   //  8 bit (1 byte) 0 bis 255 / 0 bis (2^8)-1)
  int               i;   // 16 bit (2 byte) -32,768 to 32,767 / -2^15 bis (2^15)-1)
  unsigned int     ui;   // 16 bit (2 byte) 0 to 65,535 / 0 bis (2^16)-1)
  word              w;   // 16 bit (2 byte) 0 to 65,535 / 0 bis (2^16)-1)
  short             s;   // 16 bit (2 byte) -32,768 to 32,767 / -2^15 bis (2^15)-1)
  int16_t         i16;   // 16 bit (2 byte) -32,768 to 32,767 / -2^15 bis (2^15)-1)
  uint16_t       ui16;   // 16 bit (2 byte) 0 bis 65,535 / 0 bis (2^16)-1)
};
und für 8-Byte Variablen
Code:
union bytemuehle64
{
  char                     c[8];   //  8 bit (1 byte) -128 bis 127 / -2^7 bis (2^7)-1)
  unsigned char           uc[8];   //  8 bit (1 byte) 0 bis 255 / 0 bis (2^8)-1)  
  byte                     b[8];   //  8 bit (1 byte) 0 bis 255 / 0 bis (2^8)-1)
  int8_t                  i8[8];   //  8 bit (1 byte) -128 bis 127 / -2^7 bis (2^7)-1)  
  uint8_t                ui8[8];   //  8 bit (1 byte) 0 bis 255 / 0 bis (2^8)-1)
  int                      i[4];   // 16 bit (2 byte) -32,768 to 32,767 / -2^15 bis (2^15)-1)
  unsigned int            ui[4];   // 16 bit (2 byte) 0 to 65,535 / 0 bis (2^16)-1)
  word                     w[4];   // 16 bit (2 byte) 0 to 65,535 / 0 bis (2^16)-1)
  int16_t                i16[4];   // 16 bit (2 byte) -32,768 bis 32,767 / -2^15 bis (2^15)-1)
  uint16_t              ui16[4];   // 16 bit (2 byte) 0 to 65,535 / 0 bis (2^16)-1)
  float                    f[2];   // 32 bit (4 byte) -3.4028235E^-38 bis 3.4028235^+38
  long                     l[2];   // 32 bit (4 byte) -2,147,483,648 bis 2,147,483,647 / -2^31 bis (2^31)-1)
  unsigned long           ul[2];   // 32 bit (4 byte) 0 to 4,294,967,295 / 0 bis (2^32)-1)
  double                   d[2];   // 32 bit (4 byte) -3.4028235E^-38 bis 3.4028235^+38
  int32_t                i32[2];   // 32 bit (4 byte) -2,147,483,648 bis 2,147,483,647 / -2^31 bis (2^31)-1)  
  uint32_t              ui32[2];   // 32 bit (4 byte) 0 to 4,294,967,295 / 0 bis (2^32) - 1)
  int64_t                   i64;   // 64 bit (8 byte) -9223372036854775808 bis 9223372036854775807/ -2^63 bis (2^63)-1)
  uint64_t                 ui64;   // 64 bit (8 byte) 0 bis 18446744073709551615 / 0 bis (2^64)-1)
  long long int           lli64;   // 64 bit (8 byte) -9223372036854775808 bis 9223372036854775807/ -2^63 bis (2^63)-1)
  unsigned long long int ulli64;   // 64 bit (8 byte) 0 bis 18446744073709551615 / 0 bis (2^64)-1)
};

Das soll es erst mal gewesen sein. Ich wünsche viel Spaß damit.

Sollten sich zu diesem Beitrag Änderungen Rolleyes oder Ergänzungen ergeben die außerhalb des Zeitfensters liegen in dem man den Beitrag noch bearbeiten kann, werden sie als neue Antwort erscheinen. Deshalb also bitte auch in den Antworten danach suchen.

Viele Grüße
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
24.10.2015, 13:15
Beitrag #2
RE: Die Bytemühle - einfacher Zugriff auf einzelne Bytes einer Mehrbyte-Variablen
Hallo Arne,
du hast es mal wieder auf den Punkt gebracht, RESPEKT.
Nachdem ich zuerst wegen dem Datentyp Union skeptisch war habe ich mich da mal eingelesen und habe sehr schnell begriffen wie das geht Sleepy . Fakt ist das das die wohl unkomplizierteste Methode ist um long Variablen in leichter handhabbare Bytes aufzuteilen um sie so weiterzuverarbeiten.
Hier mein kleines Beispielprogramm wo ich unsigned long int Variablen in Bytes verwandle und wieder zurück:
Code:
#include <EEPROM.h>  // EEPROM Lib  wird spaeter benoetigt
/*
* Testprogramm um long int oder float Variablen in ihre 4 Bytes zu
* zerlegen um sie dann im EEPROM speichern zu können oder per Serielle
* Schnittstelle, I2C oder SPI transferieren zu können. Oder für andere
* Vorhaben...
*/


//Variablen:
int i; // der beliebte Int

static union {      // ohne Static ist wieder ein anderes Problem am tanzen...
  byte byteein[4];
  unsigned long int testwert;
};

static union {
  byte byteaus[4];
  unsigned long int testausg;
};

byte esim[10];   // EEPROM Simulationsspeicher

//######################### SETUP #####
void setup(){
Serial.begin(9600);  
testwert = 123;   // Testvariable initialisieren
}

void loop(){

  Serial.print("testwert  ");
  Serial.println(testwert);

//#### Testwert nach esim schreiben
for (int i =0;i <= 3; i++){
esim[i] = byteein[i];
}

//######  esim Array ausgeben
for (int i =0;i <= 5; i++){   // nur 6 Felder ausgeben wegen der Uebersicht
Serial.print("esim[");
Serial.print(i);  //
Serial.print("]  ");
Serial.print("Wert  ");
Serial.println(esim[i]);
}

//#### Testausgabe aus esim lesen
for (int i =0;i <= 3; i++){
byteaus[i] = esim[i];
}
Serial.print("Ausgabe ");
Serial.println(testausg);
Serial.println();


testwert = testwert *255;  // neuen Testwert berechnen
delay(3000);  // einmal auf die Bremse treten
}
lgbk

1+1 = 10 Angel ...und ich bin hier nicht der Suchmaschinen-Ersatz Dodgy...nur mal so als genereller Tipp..
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
26.10.2015, 21:58 (Dieser Beitrag wurde zuletzt bearbeitet: 26.10.2015 21:58 von rkuehle.)
Beitrag #3
RE: Die Bytemühle - einfacher Zugriff auf einzelne Bytes einer Mehrbyte-Variablen
Hallo Arne,
ich benutze auch ab und an Unions. Bisher aber immer auch etwas nebulös, nicht so wirklich zu 100% verstanden.
Deine Darstellung hat damit aufgeräumt.
Was soll man sagen: Exzellente Ausführung.
Danke dafür!
Grüße Ricardo
PS: Komme zur Zeit leider kaum zum Arduino Confused

Nüchtern betrachtet...ist besoffen besser Big Grin
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
29.10.2015, 21:52
Beitrag #4
RE: Die Bytemühle - einfacher Zugriff auf einzelne Bytes einer Mehrbyte-Variablen
Danke für die Blumen @ Ricardo Smile

Bei Strukturen, struct name { elemente, , ,} , ist das sehr ähnlich mit der Anwendung.
Die Elemente liegen dabei aber nicht "übereinander" sondern nebeneinander im RAM.

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
16.11.2015, 19:30
Beitrag #5
RE: Die Bytemühle - einfacher Zugriff auf einzelne Bytes einer Mehrbyte-Variablen
Hallo Arne,
tolle Darstellung von Unions. Jetzt verstehe ich erst, was man damit machen kann, seitdem ich selbst ein solches Problem mit Pointer bearbeitet habe, wo es auch darum ging, auf die einzelnen Bytes einer Float-Zahl zuzugreifen (siehe: http://arduino-projekte.webnode.at/meine...eprogramm/ ). Obwohl, wenn man Pointer einmal versteht, ist es auch nicht schwer. Trotzdem "Danke" für deine verständliche Erklärung.
Gruß
Retian
Webseite des Benutzers besuchen Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
Antwort schreiben 


Möglicherweise verwandte Themen...
Thema: Verfasser Antworten: Ansichten: Letzter Beitrag
  Arduino Zugriff auf MySQL Server - !ohne PHP! rkuehle 9 10.548 05.09.2016 07:18
Letzter Beitrag: mainframe
  F-Makro für Variablen - ein kleiner Tipp rkuehle 0 464 05.08.2016 14:41
Letzter Beitrag: rkuehle
  EEPROM - lesen/schreiben von mehreren Bytes in einem Rutsch rkuehle 2 3.555 16.05.2014 20:01
Letzter Beitrag: rkuehle

Gehe zu:


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