HMC5983 Output konvertieren

OP #4169867
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?
#4169993
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.
OP #4170024
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?
#4170030
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.
OP #4170045
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.
#4170065
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.
OP #4170240
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?

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