Forum: Mikrocontroller und Digitale Elektronik Umrechnung 16Bit 2er Komplement zu 0.65535


von Hans (Gast)


Lesenswert?

Hi Leute!

Ich habe einen ADC mit 16Bit Auflösung. Sein Ausgangssignal ist im 2er 
Komplement, also von 1000 0000 0000 0000 (-32768) bis 0111 1111 1111 
1111 (+32767). Ich hätte das Ergebnis aber lieber als Zahl zwischen 
0...65535. Wie rechne ich das um?

Ich komme nicht dahinter. Muss ich das abziehen von 0xFFFF oder wie geht 
das?

von Hans (Gast)


Lesenswert?

Wenn ich grad selber drüber nachdenke, dann wohl eher alles plus 32768, 
oder? Kann ich dann einfach das Ergebnis + 0xEFFF rechnen?

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:
> Wenn ich grad selber drüber nachdenke, dann wohl eher alles plus 32768,
> oder? Kann ich dann einfach das Ergebnis + 0xEFFF rechnen?

Wenn du mir jetzt auch noch eine gute Begründung gibst, warum du diesen 
Offset UNBEDINGT als Hex-Zahl angeben musst ....

(Übrigens: 0xEFFF wäre sowieso falsch gewesen)

von Oliver (Gast)


Lesenswert?

1
int16_t adcval = ReadADC(); // -32768...32767
2
uint16_t uval = adcval + 32768u;

oder hardcore:
1
uint16_t uval = (uint16_t)ReadADC();

Oliver

von Martin (Gast)


Lesenswert?

32768 ist aber 0x8000

kleinster Wert:    0x8000 + 0x8000 = 0x0000 (als 16-bit-Integer)
Wert in der Mitte: 0x0000 + 0x8000 = 0x8000
größter Wert:      0x7FFF + 0x8000 = 0xFFFF

Sollte also passen...

von Hans (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn du mir jetzt auch noch eine gute Begründung gibst, warum du diesen
> Offset UNBEDINGT als Hex-Zahl angeben musst ....

Weiß nicht, sorry. Ist natürlich egal und

Martin schrieb:
> 32768 ist aber 0x8000

ist natürlich richtig. Dumm von mir, nochmal sorry!

Die beiden Bytes kommen vom ADC natürlich einzeln und ich packe sie 
momentan in einen 16 Bit UNsigned Integer. Mit dem unsigned Interger 
kann ich ja nicht einfach +/- rechnen, sondern müsste dann eine 
Fallunterscheidung machen, ob das MSB gesetzt ist, oder nicht, sehe ich 
das richtig, oder wieder falsch?

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:

> Die beiden Bytes kommen vom ADC natürlich einzeln und ich packe sie
> momentan in einen 16 Bit UNsigned Integer. Mit dem unsigned Interger
> kann ich ja nicht einfach +/- rechnen, sondern müsste dann eine
> Fallunterscheidung machen, ob das MSB gesetzt ist, oder nicht, sehe ich
> das richtig, oder wieder falsch?

Die Frage ist eher: warum packst du die beiden Bytes dann überhaupt in 
einen unsigned Integer? Pack sie halt in einen signed Integer und gut 
ists.

Es hat ja schliesslich seinen Grund, warum dir der ADC einen 
signed-16-Bit Integer in Form von 2 Bytes gibt. Und der Grund ist 
sicherlich nicht, dass du dann Klimmzüge machen musst.

von Hans (Gast)


Lesenswert?

Also dann so, oder?
1
if( adc_data & 0x8000 )
2
{
3
  adc_data -= 0x8000;
4
}
5
else
6
{
7
  adc_data += 0x8000;
8
}

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:
> Also dann so, oder?
>
>
1
> if( adc_data & 0x8000 )
2
> {
3
>   adc_data -= 0x8000;
4
> }
5
> else
6
> {
7
>   adc_data += 0x8000;
8
> }
9
>


Und was soll das sein? Ich denke, dein adc_data ist doch schon un 
unsigned Wert? Einfach 0x8000 dazuzählen und gut ists.

Alerdings erhebe ich trotzdem nochmal die Frage warum adc_data überhaupt 
unsigned ist! Ist doch sinnlos da künstlich mit Vorzeichen und 
Verschiebung rumzumachen, wenn alles was du brauchst ein adc_data ist, 
welches einen signed Datentyp hat. Oder ist das zu einfach?

von Hans (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Die Frage ist eher: warum packst du die beiden Bytes dann überhaupt in
> einen unsigned Integer? Pack sie halt in einen signed Integer und gut
> ists.

Dann brauche ich doch auch eine weitere Variable, oder? Weil mein signed 
kann nicht bis 65535 gehen. Oder den am Ende umcasten

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:
> Karl Heinz Buchegger schrieb:
>> Die Frage ist eher: warum packst du die beiden Bytes dann überhaupt in
>> einen unsigned Integer? Pack sie halt in einen signed Integer und gut
>> ists.
>
> Dann brauche ich doch auch eine weitere Variable, oder? Weil mein signed
> kann nicht bis 65535 gehen.

Wenn dein ADC dir 16 Bit signed liefert, dann kann er kein Ergebnis 
65535 liefern.
Wozu brauchst du eigentlich diese Verschiebung?
Kann ja durchaus sein, dass die ja vernünftig ist.
Das Argument 'aber ich kann ja sonst nicht mit positiven und negativen 
Zahlen rechnen' ist es jedenfalls nicht. Denn da muss man sagen: Wenn du 
2-er Komplement Zahlen hast und die so vom ADC kriegst, dann stopf die 
beiden Bytes halt um Himmels willen in eine signed Variable und wenn der 
ADC -25 meint, dann hast du in deiner Variablen dann auch -25. Ohne 
Klimmzüge. Und wenn du das in eine Spannung umrechnest, dann kommt da 
dann auch eine negative Spannung raus. etc. etc.

D.h. es hängt davon ab, wie es weitergeht, was mit den Zahlen weiter 
gemacht wird, ob diese Verschiebung vernünftig ist oder nicht. Im Moment 
seh ich noch keine Grund, warum man negative Ergebnisse nicht auch als 
negative Ergebnisse weiterbehandeln soll. Ich seh aber eine Menge 
Gründe, warum ich mir keinen derartigen Offset in den Daten wünschen 
würde. Denn dieser Offset muss bei ALLEN weiteren Berechnungen 
berücksichtigt werden.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Hans schrieb:
>> Also dann so, oder?
>>
>>
1
>> if( adc_data & 0x8000 )
2
>> {
3
>>   adc_data -= 0x8000;
4
>> }
5
>> else
6
>> {
7
>>   adc_data += 0x8000;
8
>> }
9
>>
>
>
> Und was soll das sein? Ich denke, dein adc_data ist doch schon un
> unsigned Wert? Einfach 0x8000 dazuzählen und gut ists.

Das war übrigens von uns allen Blödsinn.
Du hast schon recht, dass du das 2-er Komplement rückgängig machen 
müsstest.

von Hans (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> D.h. es hängt davon ab, wie es weitergeht, was mit den Zahlen weiter
> gemacht wird, ob diese Verschiebung vernünftig ist oder nicht.

Aus den Zahlen errechne ich einen Prozentwert. Und auf Basis dieses 
Prozentwertes geht alles andere weiter. Von daher ist es für mich 
sinnvoller, einen positiven Wert als Ausgangsbasis zu haben.

Also jetzt nochmal (danke übrigens für eure Geduld!). Ich nehme meine 
16Bit UNsigned Variable. Als Additionswert nehme ich 0x8000.

Der ADC liefert mir jetzt -32768, also 1000 0000 0000 0000 oder 0x8000
Zu diesem Wert rechne ich jetzt 0x8000 dazu. Das sind dann 32768 + 32768 
= 65536, also Überlauf, also wieder 0 (stimmt doch, oder?)

-1LSB ist 1111 1111 1111 1111, also 65535 + 32768 = 32767

Dann den Wert in der Mitte, also 0x0000 vom ADC, dazu 0x8000 ergibt dann 
eben 32768.

Und nun 32767 vom ADC + 0x8000 ergibt 32767 + 32768 = 65535, also 
Vollausschlag.

Ist das so richtig?

von Hans (Gast)


Lesenswert?

Im Debugger gehts

von der alte Hanns (Gast)


Lesenswert?

Als Assemblerprogrammierer und Tietze/Schenk-Leser negiere ich das 
Vorzeichenbit der Zweierkomplementdarstellung.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

avr-gcc kennt Fixedpoint-Typen:
1
#include <stdfix.h>
2
#include <avr/io.h>
3
4
unsigned fract read_ADC (void)
5
{
6
    return urbits (ADC);
7
}

unsigned fract liegt im Intervall [0, 1).

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.