Hallo Leute,
ich bin scheinbar zu blöde auf einem STM32F415 aus zwei 8Bit Variablen,
die einen Signed Wert für einen externen ADC (MCP3901) darstellen in
eine 32Bit Signed Variable zu packen. Auf einem AVR speichere ich das
Ergebnis in ein signed 16Bit => Klappt. Auf einem STM32 macht das wenig
Sinn, somit sind dort alle meine Variablen int (= int32_t).
So sieht die Sache aus:
1
intWert;
2
signedcharBuffer[2];// Hier wird das ADC Ergebnis gespeichert
Wenn ich mir den Wert ausgebe, scheint das Vorzeichen verloren zu gehen,
denn anstatt -xyz (bei negativen Werten, positiv klappt) erhalte ich
Werte um die 100000, also alles andere als negativ.
Die Ausgabe erfolgt über:
printf("\nData:\t%d", Wert;)
Code kann ich leider nicht posten, geht nur um die Umwandlung, wie
gesagt auf einem AVR klappt das (Selbes IC).
Ingo
Ingo schrieb:>> Wenn ich mir den Wert ausgebe, scheint das Vorzeichen verloren zu gehen,
Yep.
> // HighByte LowByte> Wert = ((int)Buffer[0] & 0x00ff) << 8 | ((int)Buffer[1] & 0x00ff);
(int)Buffer[0]
du lässt dir zunächst den signed int auf einen int hochcasten.
soweit so gut. Da wird eine sign Extension gemacht und der int hat noch
das Vorzeichen
& 0x00ff
und dann schneidest du die oberen Bits (und damit auch das Vorzeichenbit
weg.
> Code kann ich leider nicht posten, geht nur um die Umwandlung, wie> gesagt auf einem AVR klappt das (Selbes IC).
logisch
weil dort die Zusammensetzung von 2 8 Bit WErten bereits einen
vollständigen int (16 Bit) ergibt.
Aber hier hast du nicht diesen Fall. Du setzt zwar die 16 Bit korrekt
zusammen, aber niemand kümmert sich um die höherwertigen 16 Bit, die
nach fehlen um auf 32 Bit zu kommen.
Du könntest die ganze OPeration vom Compiler erledigen lassen, indem du
zunächst wieder die 16 Bit zusammensetzt, aber diesmal auch wirklich 16
Bit Operationen bzw. Variablen benutzt.
Und erst ganz zum Schluss, wenn alles in den 16 Bit fertig ist, dann
castest du hoch auf einen 32 Bit int. Dann macht dir der Compiler die
Sign extension.
Du kannst es allerdings auch händisch machen. Denn die oberen 16 Bit
können nur 0x0000 (positive Zahl) bzw. 0xFFFF (negative Zahl) sein.
probier mal
Wert = ((int16_t)Buffer[0] & 0x00ff) << 8 | ((int16_t)Buffer[1] &
0x00ff);
das sollte das Problem eigentlich lösen, weil dadurch der Übergang von
16 Bit auf 32 Bit erst erfolgt, wenn die 16 Bit komplett zusammen
gesetzt worden sind und damit der Compiler ganz zum Schluss die Sign
Extension einfügen muss.
holger schrieb:> Wert = (int)Buffer[0] * 256 + Buffer[1];
Da hast du Recht!
@ karl-heinz:
Vielen Dank für die ausführliche Erklärung. Ich hab den Fehler erst zum
Feierabend gemerkt, sodass ich nicht mehr viel Zeit hatte ihn zu
beheben.
Ich werde beides morgen ausprobieren, wiedereinmal danke!
Merke: 32 Bit hat auch seine Fallen ;)
Ingo
>Wert = ((int16_t)Buffer[0] & 0x00ff) << 8 | ((int16_t)Buffer[1] &>0x00ff);>>>das sollte das Problem eigentlich lösen,
Ich glaube nicht das das klappt. Mein Vorschlag
möglicherweise aber auch nicht. Das Problem ist das signed char.
Beide Bytes können dann ein Vorzeichen habe. Ich würde die
unsigned zusammenfügen, auf int16_t casten und zum Schluss
ein cast auf int.
>>> Wert = (int)Buffer[0] * 256 + Buffer[1];>>Da hast du Recht!>>Wirklich?>>Bei zB.>Buffer[0] = 0xFF;>Buffer[1] = 0xFF;>sollte doch 0xFFFFFFFF rauskommen?
Siehe meinen letzten Post. Das geht so nicht.
Eher so:
Wert = (int16_t)((uint16_t)Buffer[0] << 8 + (uint8_t)Buffer[1]);
>signed char Buffer[2]; // Hier wird das ADC Ergebnis gespeichert
Allerdings frage ich mich gerade wie man auf die Idee kommt
einen 16Bit Wert in zwei signed chars zu verpacken;)
Das macht nur Ärger.
Fabian O. schrieb:> Etwas kürzer:int Wert;> unsigned char Buffer[2];>> Wert = (int16_t) ((Buffer[0] << 8) | Buffer[1]);
das haut mit dem Vorzeichen nicht hin, ich bin immer noch guter Hoffnung
dass mein Vorschlag funktioniert:
Walter S. schrieb:> geht nicht auch das:> Wert = ((int)Buffer[0] << 8 ) | ((int)Buffer[1] & 0xff);
positiv ist sowieso kein Problem, negativ mit Beispiel -1:
0xffff ergibt die chars 0xff 0xff
wert = (0xffffffff << 8) | (0xffffffff&0xff)
= 0xffffffff = -1
>> unsigned char Buffer[2];>>>> Wert = (int16_t) ((Buffer[0] << 8) | Buffer[1]);>>das haut mit dem Vorzeichen nicht hin, ich bin immer noch guter Hoffnung>dass mein Vorschlag funktioniert:
Doch das haut hin weil Buffer jetzt unsigned ist.
In C werden Berechnungen immer mit mindestens int-Breite durchgeführt.
Das nennt man Integer-Promotion. In dem Fall wird aus dem unsigned char
implizit ein unsigned int. Insgesamt passiert also folgendes:
Walter S. schrieb:> Laut K&R A.6.2 ist das implementierungsabhängig da 0xffff in int16_t> nicht dargestellt werden kann:>> (int16_t) ( (uint16_t)0xffff ) ist nicht definiert
Das "Problem" hast Du aber immer, wenn Du von einem externen Gerät (hier
ADC) vorzeichenbehaftete Zahlen bekommst, denn die werden ja erstmal
unsigned aus einem Hardware-Register gelesen (SPI, I2C, ...).
Wenn man es ganz pedantisch machen will, müsste man also die Bits
unsigned empfangen, von Hand das Vorzeichenbit prüfen, ggf. das
Zweierkomplement zurückrechnen und dann die Zahl wieder negieren.
Oder man wählt den praktischen Ansatz und geht davon aus, dass der
Prozessor bzw. die C-Implementierung mit Zweierkomplement rechnet und
einem nichts böses will, dann funktioniert es einfach mit dem Cast. :)
Fabian O. schrieb:> Das "Problem" hast Du aber immer, wenn Du von einem externen Gerät (hier> ADC) vorzeichenbehaftete Zahlen bekommst, denn die werden ja erstmal> unsigned aus einem Hardware-Register gelesen (SPI, I2C, ...).
Wieso?
Nur weil ein bestimmter Header das so macht, ist doch niemand gezwungen,
es genauso zu machen...
>> Laut K&R A.6.2 ist das implementierungsabhängig da 0xffff in int16_t>> nicht dargestellt werden kann:>>>> (int16_t) ( (uint16_t)0xffff ) ist nicht definiert
Mag sein, aber ein cast ist ja keine Berechnung eines
Wertes den der uC durchführen muss. Es ist eine Änderung
der Interpretation der Werte im Speicher. Von daher gibt es
keine Probleme. 0xFFFF ist bei int16_t halt -1.
Johann L. schrieb:> Wieso?>> Nur weil ein bestimmter Header das so macht, ist doch niemand gezwungen,> es genauso zu machen...> #define ADC_VALUE (*(const int16_t volatile*) ADC_ADDRESS)
In dem Fall hat er ja einen externen ADC. Der Wert liegt also nicht
schon als 16- oder 32-Bit-Wert mit garantiert passender
Zahlenrepräsentation in einem Register. Wenn es so wäre, könnte man sich
das ja alles sparen ...
Sondern er wird wohl per SPI oder I2C byteweise empfangen. Und da gibt
es erstmal nur Bits, die nacheinander eintreffen und dann in einer
bestimmten Reihenfolge im Empfangsregister stehen. Im Datenblatt des
ADCs steht, dass man sie als vorzeichenbehaftete Zahl gemäß
Zweierkomplement zu interpretieren hat.
Wenn man dem C-Compiler sagt, er soll den Wert im Register als
vorzeichenbehaftetete Zahl interpretieren, dann nimmt er die
Zahlenrepräsentation der Maschine und nicht zwangsläufig
Zweierkomplement. Also ist es streng genommen
plattform/implementierunsabhängig, was bei dem Code rauskommt.
Fabian O. schrieb:> Der Wert liegt also nicht schon als 16- oder 32-Bit-Wert mit> garantiert passender Zahlenrepräsentation in einem Register.> Wenn es so wäre, könnte man sich das ja alles sparen ...>> Sondern er wird wohl per SPI oder I2C byteweise empfangen.
2 Bytes zu einem 16-Bitwert zu kombinieren ist doch keine
Wissenschaft...
Oder ist die hochkomplexe Rechnung für Generation PISA?