Go Down

Topic: byte[4] -> long - funktioniert nur bedingt. (Read 1 time) previous topic - next topic

Orestos

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:

Code: [Select]
#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

Code: [Select]
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?


uwefed

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 = value*8 + awert[1];
value = value*8 + awert[2];
value = value*8 + awert[3];

Grüße Uwe

olikraus

Alternativ zur Lösung vom Uwe, könnte man beim "verodern" die Datenbreite angeben. Das behebt den Fehler direkt:
Code: [Select]
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

jurs

#4
Jan 31, 2013, 07:37 am Last Edit: Jan 31, 2013, 07:56 am by jurs Reason: 1

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:
Code: [Select]

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


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

michael_x

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.

Orestos


Alternativ zur Lösung vom Uwe, könnte man beim "verodern" die Datenbreite angeben. Das behebt den Fehler direkt:
Code: [Select]
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 :-)

olikraus

Quote
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

michael_x

#8
Feb 01, 2013, 10:15 am Last Edit: Feb 01, 2013, 10:38 am by uwefed Reason: 1

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.

Udo Klein

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

Code: [Select]

uint32_t Ergebnis = *((uint32_t *)Wert);
Check out my experiments http://blog.blinkenlight.net

michael_x


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  ;)

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.

Go Up