Forum: Mikrocontroller und Digitale Elektronik HMC5983 Output konvertieren


von Thomas S. (ts2015)


Lesenswert?

Hallo,

ich habe ein Problem mit dem HMC5983 Magnetometer. Ich lese die Daten 
X,Y und Z Daten mit einem XMEGA256A3U über die ASF TWI Bibliothek aus. 
Damit berechne ich die aktuelle Richtung.

Ausgelesen werden 2 Register a 1-Byte, die zusammen eine 
Vorzeichenbehaftete 16-Bit Zahl darstellen:

"The data output Y registers are two 8-bit registers, data output 
register A and data output register B. These registers store the 
measurement result from channel Y. Data output Y register A contains the 
MSB from the measurement result, and data output Y register B contains 
the LSB from the measurement result. The value stored in these two 
registers is a 16-bit value in 2’s complement form, whose range is 
0xF800 to 0x07FF. DYRA0 through DYRA7 and DYRB0 through DYRB7 indicate 
bit locations, with DYRA and DYRB denoting the bits that are in the data 
output Y registers. DYRA7 and DYRB7 denote the first bit of the data 
stream. The number in parenthesis indicates the default value of that 
bit."

Ich verwende nun folgenenden Code um eine Variable zuerzeugen:
1
// recv_data ist ein uint8_t Array, enthält an Stelle 0 das MSB und an Stelle 1 das LSB 
2
int16_t z_value   = ( ((int16_t)recv_data[0]) <<8)+recv_data[1];

Eigentlich erwarte ich jetzt einen Wert zwischen 0xF800–0x07FF 
(-2048–2047)
Ich bekomme aber Werte von beispielsweise -20000 oder +500000.
Was für einen Fehler mache ich?

In Arduino Beispielen wird generell Float genommen und eine Überprüfung 
gemacht:
1
  if(z_value > 0x07FF){
2
    z_value = 0xFFFF- z_value;
3
  }
Aber damit erhalte ich auch kein besseres Ergebnis.

In diesem Forenthread:
[[Beitrag "HMC5883L Kompassmodul - Ich bin ratlos."]]
Wird andererseits nur das MSB verwendet. Benötigt man das LSB nicht?

von Timmo H. (masterfx)


Lesenswert?

Mmh. Eigentlich ist es ja nur ein 12-Bit wert. Was ist wenn du sowas 
machst als wenn er left-aligned wäre:
1
int16_t z_value   = (( ((int16_t)recv_data[0]) <<8)+recv_data[1]) >> 4;

von holger (Gast)


Lesenswert?

>Ich bekomme aber Werte von beispielsweise -20000 oder +500000.
>Was für einen Fehler mache ich?

Deine Ausgaberoutine baut scheisse.

von Karl H. (kbuchegg)


Lesenswert?

Thomas Schmidt schrieb:

> Ich verwende nun folgenenden Code um eine Variable zuerzeugen:
>
1
> // recv_data ist ein uint8_t Array, enthält an Stelle 0 das MSB und an 
2
> Stelle 1 das LSB
3
> int16_t z_value   = ( ((int16_t)recv_data[0]) <<8)+recv_data[1];
4
>
>
> Eigentlich erwarte ich jetzt einen Wert zwischen 0xF800–0x07FF
> (-2048–2047)
> Ich bekomme aber Werte von beispielsweise -20000 oder +500000.
> Was für einen Fehler mache ich?

Welchen Datentyp hat den recv_data[0].

Generell: Wenn du mit Bytes hantierst, dann willst du solange wie 
möglich auf der Unsigned Schiene bleiben, damit dir keine 
Fehlinterpretation des Most signifikant Bit unterkomme. Du willst 
schliesslich die Bytewerte genau so durch die Gegend schieben, wie du 
sie vom Sensor kriegst.

Und erst dann, wenn alles an seiner korrekten Position ist (für einen 16 
Bit Wert), dann willst du den Übergang auf signed und auf die in deinem 
System herrschende Byte-Breite für int machen. Wobei du zuerst auf 
16-Bit signed gehst und erst dann gegebenenfalls diesen 16-Bit signed 
Wert auf bspw. 32 Bit signed aufblasen lässt.

Das ist alles eine Frage der Reihenfolge der Operationen. Wenn die nicht 
stimmt, dann kommt es zu genau den von dir geschilderten Ergebnissen.

Wobei ein int16_t Wert natürlich nicht +50000 sein kann. Da hat holger 
absolut Recht. Da muss man auch mal die Ausgabe etwas genauer unter die 
Lupe nehmen.

von Karl H. (kbuchegg)


Lesenswert?

Und auch immer berücksichtigen:
In C wird nicht kleiner als int gerechnet. nicht uint8_t, nicht int8_t, 
nicht int16_t oder uint16_t, sondern int! Was auch immer die Bytebreite 
von int auf deinem System ist.

von Thomas S. (ts2015)


Lesenswert?

holger schrieb:
>>Ich bekomme aber Werte von beispielsweise -20000 oder +500000.
>>Was für einen Fehler mache ich?
>
> Deine Ausgaberoutine baut scheisse.

Das war auch schon so eine Vermutung, ich werde dann alle variablen 
wieder als uint32_t deklarieren und nochmal testen.
Ausgabe erfolgt über ein Display, zuerst wird per snprintf die Variable 
in einen String geschrieben und dann ausgeben. Formatspecifier ist %d. 
Oder muss ich hier doch %u verwenden?

von Karl H. (kbuchegg)


Lesenswert?

> Ich bekomme aber Werte von beispielsweise -20000 oder +500000.

mir fällt noch ein Fehler ein, bei dem es so aussieht, als ob derartige 
Werte entstehen würden. (zb auf einem LCD)

Dreh und Angelpunkt ist die Beobachtung, dass ein Überschreiben eines 
Textes mit einem anderen - kürzeren - Text selbstverständlich NICHT den 
überstehenden Textteil überschreibt.

Hab ich Display stehen
1
Hallo Welt
und schreibe ich da, beginnend mit dem Zeilenanfang, den Text "Juhu" 
drüber, dann entsteht logischerweise
1
Juhuo Welt

Bei Texten ist das völlig klar und von weitem zu sehen. Bei Zahlen 
hingegen kann es zb zu der Situation kommen, dass am LCD
1
100
steht. Und wenn ich die mit zb 99 überschreibe und nicht bedenke, dass 
100 aus 3 Stellen besteht, 99 aber nur aus 2, dann landet man bei der 
Anzeige bei
1
990
Ein Mensch liest dann neunhundert-neunzig, wo eigentlich 99 stehen 
sollte und auch steht. Nur steht eben auch noch die Einerstellen-0 von 
den 100, die vorher dort gestanden ist, dort.

printf bietet schöne Möglichkeiten, wie man dafür sorgen kann, dass 
Zahlenausgaben immer die gleiche Länge haben und gegebenenfalls links 
mit wahlweise führenden 0-en oder Leerzeichen aufgefüllt wird.

Wenn es das nicht ist, dann wird dir wohl nichts anderes übrig bleiben, 
als die Salamitaktik aufzugeben und endlich mal etwas kompletteren Code 
zu zeigen, damit man den Datentypfehler suchen kann, der dir irgendwo in 
den Weiten des Programms passiert ist.

von Thomas S. (ts2015)


Lesenswert?

Karl Heinz schrieb:
>> Ich bekomme aber Werte von beispielsweise -20000 oder +500000.
>
> mir fällt noch ein Fehler ein, bei dem es so aussieht, als ob derartige
> Werte entstehen würden. (zb auf einem LCD)
>
> Dreh und Angelpunkt ist die Beobachtung, dass ein Überschreiben eines
> Textes mit einem anderen - kürzeren - Text selbstverständlich NICHT den
> überstehenden Textteil überschreibt.


Vor jeder aktualisierung findet ein Display clear statt, die Werten 
können aber auch von 500000 auf 2000 springen.

von Wolfgang (Gast)


Lesenswert?

Karl Heinz schrieb:
>> int16_t z_value   = ( ((int16_t)recv_data[0]) <<8)+recv_data[1];
>> >
>> Ich bekomme aber Werte von beispielsweise -20000 oder +500000.

+500000 aus einer int16_t herauszulesen, setzt schon eine äußerst 
ungewöhnliche Zahlendarstellung voraus.

von Karl H. (kbuchegg)


Lesenswert?

Thomas Schmidt schrieb:

> Vor jeder aktualisierung findet ein Display clear statt, die Werten
> können aber auch von 500000 auf 2000 springen.

Aber nicht mit einem int16_t

Die größte Zahl, die mit einem int16_t darstellbar ist, ist +32767. Mehr 
geht nicht. Noch nicht mal wenn du unbedingt willst.

Wenn du also auf deinem Display 500000 stehen hast, dann müssen wir uns 
fragen, wo die vielen Stellen eigentlich herkommen. Aus dem int16_t 
können sie nicht stammen. Selbst dann nicht, wenn die Bytes vom Sensor 
vollkommen falsch miteinander verbunden wurden oder die Bits vollkommen 
falsch zusammengewürfelt worden wären.

Wie schon gesagt: Es wird Zeit für mehr Code.

von Thomas S. (ts2015)


Lesenswert?

Ich kann leider erst wieder Montag auf den gesamten Quellcode zugreifen.
Das mit den 500000 muss dann wohl unter uint32_t gewesen sein.
Ich habe mehrmals die variablendeklaration geändert.

Im Grunde kann man doch sagen dass:
1
uint32_t z_value = ( ((uint32_t) recv_data[0]) <<8 ) + recv_data[1];

die Werte korrekt formatieren sollte und ich die Ausgabe über Display 
ignorieren kann?
1
double heading = atan2( (double) y_value, (double) x_value) *180/3.1416

Sollte dann auch eigentlich die Richtung korrekt angeben?

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.