Aus 2x8Bit mach 1x16Bit

Gast #3056112
Lesenswert?

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
int Wert;
2
signed char Buffer[2];      // Hier wird das ADC Ergebnis gespeichert
3
...
4
// Dann wird ausgelsen, soweit alles gut
5
...
6
//           HighByte                         LowByte
7
Wert = ((int)Buffer[0] & 0x00ff) << 8 | ((int)Buffer[1] & 0x00ff);

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
#3056126
Lesenswert?

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.
#3056132
Lesenswert?

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.
Gast #3056137
Lesenswert?

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
Gast #3056155
Lesenswert?

>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.
Gast #3056161
Lesenswert?

>> Wert = (int)Buffer[0] * 256 + Buffer[1];
>Da hast du Recht!

Wirklich?

Bei zB.
Buffer[0] = 0xFF;
Buffer[1] = 0xFF;
sollte doch 0xFFFFFFFF rauskommen?

Tippe eher auf 0xFFFFFF00 + 0xFFFFFFFF = 0xFFFFFeFF
Gast #3056191
Lesenswert?

>>> 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]);
Gast #3056316
Lesenswert?

>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.
#3056327
Lesenswert?

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
Gast #3056332
Lesenswert?

>> 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.
#3056404
Lesenswert?

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:
1
Wert = (int) ((int16_t) ( (((unsigned int) Buffer[0]) << 8) | ((unsigned int) Buffer[1]) ));
#3056427
Lesenswert?

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. :)
Persönliche Seite #3056443
Lesenswert?

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...
1
#define ADC_VALUE (*(const int16_t volatile*) ADC_ADDRESS)
Gast #3056455
Lesenswert?

>> 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.
#3056464
Lesenswert?

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.
Persönliche Seite #3056474
Lesenswert?

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?

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren