byte[4] -> long - funktioniert nur bedingt.

Grüß Gott Freunde,

ich habe ein Problemchen mit dem folgenden Sketch. Kurz gesagt möchte ich ein long in vier Bytes wandeln, die ich auf einem I2C Eeprom speichern möchte - anschließend abfragen und wieder in float wandeln.

vollständigkeitshalber der Sketch:

#include <Wire.h>
#define I2CBaustein 0x50 // Festlegen der I2C Zugriffsadresse

byte awert[4];


void setup(){
  Wire.begin();
  Serial.begin(9600);
  
  unsigned long n=65535;
  byte wert[4]; 

  
  
wert[0] = (byte )((n >> 24) & 0xff);
wert[1] = (byte )((n >> 16) & 0xff);
wert[2] = (byte )((n >> 8) & 0xff);
wert[3] = (byte )(n & 0xff);




 
  schreibeEEPROM(I2CBaustein, 0, wert[0]); // Schreiben
    schreibeEEPROM(I2CBaustein, 1, wert[1]); // Schreiben
      schreibeEEPROM(I2CBaustein, 2, wert[2]); // Schreiben
        schreibeEEPROM(I2CBaustein, 3, wert[3]); // Schreiben
  
}





void loop(){
  
awert[0]=leseEEPROM(I2CBaustein, 0); // Lesen
awert[1]=leseEEPROM(I2CBaustein, 1);
awert[2]=leseEEPROM(I2CBaustein, 2);
awert[3]=leseEEPROM(I2CBaustein, 3);

unsigned long value = (awert[3]) | (awert[2] << 8) | (awert[1] << 16) | (awert[0] << 24);

Serial.println(value, DEC);
Serial.println(awert[0], BIN);
Serial.println(awert[1], BIN);
Serial.println(awert[2], BIN);
Serial.println(awert[3], BIN);
Serial.println("");




delay(5000);
  
}





void schreibeEEPROM(int I2CBausteinAdresse, unsigned int speicherAdresse,byte daten){
  
  Wire.beginTransmission(I2CBausteinAdresse); // Verbindung zu I2C
  // initiieren
#if ARDUINO < 100
  Wire.send((byte)(speicherAdresse >> 8)); // MSB (höherwertiges Byte)
  // senden
  Wire.send((byte)(speicherAdresse & 0xFF)); // LSB (niederweriges Byte)
  // senden
  Wire.send(daten); // Daten-Byte zum Speichern
  // senden
#else
  Wire.write((byte)(speicherAdresse >> 8)); // MSB (höherwertiges Byte)
  // senden
  Wire.write((byte)(speicherAdresse & 0xFF)); // LSB (niederweriges Byte)
  // senden
  Wire.write(daten); // Daten-Byte zum Speichern
  // senden
#endif
  Wire.endTransmission(); // Verbindung zu I2C trennen
  delay(5); // Kurze Pause. Äußerst
  // wichtig!!!
}





byte leseEEPROM(int I2CBausteinAdresse, unsigned int speicherAdresse){ 
 
  byte datenByte = 0xFF;
  Wire.beginTransmission(I2CBausteinAdresse); // Verbindung zu I2C
  // initiieren
#if ARDUINO < 100
  Wire.send((byte)(speicherAdresse >> 8)); // MSB (höherwertiges Byte)
  // senden
  Wire.send((byte)(speicherAdresse & 0xFF)); // LSB (niederwertiges Byte)
  // senden
#else
  Wire.write((byte)(speicherAdresse >> 8)); // MSB (höherwertiges Byte)
  // senden
  Wire.write((byte)(speicherAdresse & 0xFF)); // LSB (niederwertiges Byte)
  // senden
#endif
  Wire.endTransmission(); // Verbindung zu I2C trennen
  Wire.requestFrom(I2CBausteinAdresse, 1); // Anfordern der Daten vom
  // Slave
#if ARDUINO < 100
  if(Wire.available()) datenByte = Wire.receive(); // Sind Daten vorhanden?
#else
  if(Wire.available()) datenByte = Wire.read(); // Sind Daten vorhanden?
#endif
  return datenByte; // Daten-Byte
  // zurückliefern
}

Ich kann den long erfolgreich in 4 Bytes wandeln, sie speichern und abrufen - aber wandeln geht nur bis 15 Bits. Ab 16Bits kommen fehler auf.

Probleme macht folgende Anweisung

unsigned long value = (awert[3]) | (awert[2] << 8) | (awert[1] << 16) | (awert[0] << 24);

Der erste Byte wird korrekt in den long geschrieben, der Zweite Byte nur solang er kleiner 8 Bits ist. Sobald der 8te Bit dazu kommt, wirft er mir fehlerhafte Werte aus.

Die Bytes werden aber korrekt aus dem eeprom abgerufen.

Jemand eine Idee woran das liegen könnte?

string not byte?

Du hast 2 Probleme:
byte awert[4];
und
unsigned long value = (awert[3]) | (awert[2] << 8) | (awert[1] << 16) | (awert[0] << 24);

Die bitweise oder verknüpfung funktioniert nur mit Byte Variablen.
Wenn Du awert[2] << 8 schiebst dann erhälst Du eine leere Byte Variable.

Du mußt:
unsigned long value = awert[0];
value = value8 + awert[1];
value = value
8 + awert[2];
value = value*8 + awert[3];

Grüße Uwe

Alternativ zur Lösung vom Uwe, könnte man beim "verodern" die Datenbreite angeben. Das behebt den Fehler direkt:

unsigned long value = ((unsigned long)awert[3]) | (((unsigned long)awert[2]) << 8) | (((unsigned long)awert[1]) << 16) | (((unsigned long)awert[0]) << 24);

Grüße,
Oliver

Orestos:
Jemand eine Idee woran das liegen könnte?

Das liegt daran, dass Du es überhaupt mit Hin- und Herwandeln machst, was gar nicht notwendig ist, und Dich dann in den Datenstrukturen verhaspelst.

Ein "long" belegt doch schon vier Bytes im RAM-Speicher und wenn Du auf diese vier Bytes einzeln zugreifen können möchtest, brauchst Du nur eine passende Deklaration für ein Pointer auf byte, nutzbar als byte-Array ohne eigenen Speicher. Das so mittels Pointer-Deklaration erzeugte byte-Array beginnt an der Adresse der long-Variablen.

Und schon liegen die long-Variable "n" und das byte-Array "wert" vollkommen deckungsgleich im Speicher:
Änderst Du wert[0], wert[1], wert[2], wert[3], dann änderst Du n. Und umgekehrt.

Deklaration:

  unsigned long n=65336;
  byte* wert = (byte*) &n;

Wozu irgendwas fehlerträchtig wandeln, das schon im Speicher vorhanden ist?

Alternativ zu pointern ist auch
union { unsigned long l; byte b[4];}
eine elegante Möglichkeit, auf einzelne Bytes eines long zuzugreifen und den gleichen Speicher auf zwei unterschiedliche Arten zu sehen.

olikraus:
Alternativ zur Lösung vom Uwe, könnte man beim "verodern" die Datenbreite angeben. Das behebt den Fehler direkt:

unsigned long value = ((unsigned long)awert[3]) | (((unsigned long)awert[2]) << 8) | (((unsigned long)awert[1]) << 16) | (((unsigned long)awert[0]) << 24);

Grüße,
Oliver

funktioniert wunderbar, danke - auch den Anderen :slight_smile:

Wozu irgendwas fehlerträchtig wandeln, das schon im Speicher vorhanden ist?

Ganz einfach, weil die Array Lösung (ebenso die union Lösung) mindestens genauso fehlerträchtig ist: Nämlich dann, wenn ein Prozessor mit einer anderen byte Reihenfolge verwendet wird.

Oliver

olikraus:
wenn ein Prozessor mit einer anderen byte Reihenfolge verwendet wird.

... funktioniert es genauso.

Es wird nur vorausgesetzt, dass ein long 4 byte hat. ( Bei allen Vorschlägen )

Und dass es nicht zur seriellen Übertragung auf eine andere Maschine mit anderer Bytereihenfolge verwendet wird.
Aber wenn, muss das jede Lösung berücksichtigen, egal wie oft was unnötig umkopiert wird.

Wenn die Reihenfolge der Werte umgekehrt wäre (niedrigstes Byte in Wert[0]), dann könntest Du die Arbeit den Compiler machen lassen:

uint32_t Ergebnis = *((uint32_t *)Wert);

Orestos:
Kurz gesagt möchte ich ein long in vier Bytes wandeln, die ich auf einem I2C Eeprom speichern möchte - anschließend abfragen und wieder in float wandeln.

Man sollte die bytes schon so zurückholen, wie man sie geschrieben hat.
Das gilt für float und für long gleichermassen, aber man sollte sich schon für eins von beiden entscheiden :wink:

Wenn man die eine Richtung unnötig mühsam macht, hat man es zurück genauso unnötig schwer.

union oder gecastete Pointer sind eigentlich egal und gleich einfach.