Kurzer Hintergrund zuerst; Ich habe Daten vom CAN-Bus eines Lenkwinkels, der offensichtlich in Hex ist. Der Lenkwinkel umfasst zwei Bytes einer Nachricht. Das Spezifikationsdokument, das ich habe, besagt, dass diese beiden Bytes ein 16-Bit-Int mit Vorzeichen bilden, das der Lenkwinkel ist, der ein Prescaler von 1/1024 ist, das ist alles, was ich habe (ich habe keinen Zugriff auf die Quelle). Was ich versuche, ist, diese Hex-Werte in signed int umzuwandeln, aber ich bin mir nicht sicher, wie ich es richtig machen soll.
Ein kleiner Ausschnitt der CAN-Nachricht in kurzer Zeit (wir konzentrieren uns auf Byte 2 und 3):
can0 700 [8] 00 00 99 93 55 0B EF BD
can0 700 [8] 00 00 95 95 10 0C 17 BE
can0 700 [8] 00 00 6F 97 FB 0A 17 BE
can0 700 [8] 00 00 39 99 5C 0A 40 BE
can0 700 [8] 00 00 AD 9A 62 08 EF BD
can0 700 [8] 00 00 EF 9B B5 08 40 BE
can0 700 [8] 00 00 CA 9D 9A 09 17 BE
can0 700 [8] 00 00 3E 9F 55 09 40 BE
can0 700 [8] 00 00 91 A0 ED 09 17 BE
Soweit ich weiß, folgen Daten in CAN-Nachrichten normalerweise diesem Format: ein Byte für die eigentlichen Daten, ein Byte für die Anzahl der Überläufe.
Nehmen wir zum Beispiel ein unsigned int mit einem Wert von 2000. Angenommen, Byte Nr. 0 ist für Überläufe, Byte Nr. 1 für tatsächliche Daten, erhalten wir:
CAN message -> [07, D0, x, x, x, x, x, x]
07 zeigt an, dass es 7 Überläufe gegeben hat, D0 zeigt an, dass der Rest 208 ist, daher:
7*255 + 208 = 2000
Ich verstehe, wie man es mit vorzeichenlosen Werten macht. Aber diesmal habe ich es in meinem Szenario mit vorzeichenbehafteten Werten zu tun. Ich gehe davon aus, dass ein Byte für Überläufe ist, ein Byte für den Rest, aber ich bin mir nicht sicher.
Wie werden Überläufe für vorzeichenbehaftete Werte berechnet? Ist es Überlauf += 1 bei Wert > 127 und Überlauf -= bei Wert < -128? Macht es überhaupt Sinn, dass Überläufe Vorzeichen haben?
Wie kann ich diese Bytes in C/C++ in vorzeichenbehaftete Dezimalzahlen umwandeln? Nehmen wir an, mein Bytewert in Hex ist 91. Als ich das letzte Mal versucht habe, ihn in int zu speichern und auszudrucken, wurde 145 (normale Binärzahl) und nicht -111 (2er-Komplement) ausgedruckt. Wie kann ich das 2er-Komplement (falls das Sinn macht) in meinem Code erzwingen?
Könnte ich das Byte-Format falsch interpretieren? Alles, was es sagt, dass diese zwei Bytes den Lenkwinkel darstellen und dass der Lenkwinkel int16_t ist. Ich habe beobachtet, wie sie sich in Echtzeit ändern, und einer von ihnen ändert sich unregelmäßig, fast so, als würde er von 00 auf FF springen, während der andere langsam und fast linear mit der Zeit zunimmt/abnimmt. Ideen?
Ich stecke hier wirklich fest, ich weiß nicht einmal, ob ich in die richtige Richtung gehe.. Alle Vorschläge und Hilfe sind sehr willkommen!
Wahrscheinlich hilft es, einige Missverständnisse auszuräumen.
Erstens sind Ihre Daten ein 16-Bit-Wert. Es gibt keine "Überläufe" und "tatsächlichen Daten" - die 16 Bits werden nur in zwei 8-Bit-Stücke (Bytes) geteilt. Um den richtigen Binärwert zu erhalten, müssen Sie die Bytes verketten. In C können Sie dies tun, indem Sie mit vorzeichenlosen Werten beginnen und bitweise Operatoren wie folgt verwenden:
uint16_t highbyte, lowbyte, data;
highbyte = get_can_byte(); //Do whatever you normally do to get the bytes
lowbyte = get_can_byte();
data = highbyte<<8 | lowbyte;
Jetzt haben Sie den richtigen 16-Bit-Wert. Wenn Sie möchten, dass das Ergebnis signiert wird, können Sie den Wert einfach in einen signierten Typ umwandeln:
int16_t signed_data;
signed_data = (int16_t)(highbyte<<8 | lowbyte);
Um Ihre konkreten Fragen zu beantworten:
Überläufe erzeugen die gleichen Binärwerte, unabhängig davon, ob Ihre Variable vorzeichenbehaftet oder vorzeichenlos ist. Zum Beispiel 0x7fff + 1 == 0x8000. Ob Sie 0x8000 als 32768 oder -32768 interpretieren, hängt vom Datentyp ab. (Beachten Sie, dass der Überlauf von vorzeichenbehafteten Ganzzahlen im C-Standard technisch nicht definiert ist – dies wird Ihre CPU tun.)
Wie gesagt, es hängt alles vom Datentyp ab:
uint16_t ui = 0xffff; //65535
int16_t i = 0xffff; // -1
Was Sie beschreiben, klingt für eine hochpräzise Messung vernünftig. Eine Änderung von +1 oder -1 in Ihrem oberen Byte repräsentiert nur 1/256 Ihres gesamten Steuerbereichs. Das Low-Byte reagiert extrem empfindlich auf den genauen Winkel.
Beachten Sie, dass es am besten ist, so spät wie möglich in einen signierten Wert zu konvertieren und dies mit einer expliziten Umwandlung zu tun. Dinge wie bitweise Operatoren können sich anders verhalten oder ein undefiniertes Verhalten erzeugen, wenn sie für vorzeichenbehaftete Werte verwendet werden. Verwenden Sie im Allgemeinen bei jeder Bitmanipulation vorzeichenlose Werte.
Du verkomplizierst das zu sehr.
can0 700 [8] 00 00 *99 93* 55 0B EF BD -> 0x9399 -> -27751
can0 700 [8] 00 00 *95 95* 10 0C 17 BE -> 0x9595 -> -27243
can0 700 [8] 00 00 *6F 97* FB 0A 17 BE -> 0x976F -> -26769
can0 700 [8] 00 00 *39 99* 5C 0A 40 BE -> 0x9939 -> -26311
usw ...
Die wahrscheinlichste Erklärung dafür, warum ein Byte zufällig erscheint und sich linear ändert, ist, dass das lineare Byte das Byte höherer Ordnung ist (die Überläufe). Das niederwertige Byte erscheint zufällig, da es sich im Vergleich zur Aktualisierungsrate schnell ändert.
Ich würde sagen, dass Byte 3 das höherwertige Byte und Byte 2 das niederwertige Byte ist. In vorzeichenbehafteten Ganzzahlen mit Zweierkomplementdarstellung (die heutzutage fast universell ist) werden bei negativen Zahlen höherwertige Bits auf 1 gesetzt. Wenn Sie sich das Hexadezimal ansehen, wenn das höherwertige Byte größer als 0x80 ist, ist es eine negative Zahl. In Ihrem Beispiel sind also alle Zahlen negative Zahlen. 0xffff ist -1, 0xfffe ist -2 usw. Was Sie in Ihrem Snippet haben, ist:
Da alle diese Zahlen größer als 0x8000 sind, sind sie alle negativ.
Das Beste, was Sie tun können, ist, sich mit der Zweierkomplementdarstellung von vorzeichenbehafteten ganzen Zahlen zu beschäftigen. Es hört sich so an, als wüssten Sie, wie man eine uint16_t-Konvertierung durchführt. Was für Sie funktionieren könnte, ist, dies einfach zu tun und den Wert dann einer Variablen von int16_t zuzuweisen. Der Compiler konvertiert es möglicherweise korrekt für Sie (es wird nicht durch die C-Spezifikation garantiert, aber viele Compiler machen es so).
Ich hoffe, das hilft dir ein bisschen.
Ich weiß nichts über tatsächliche Lenkwinkelsensoren in Autos ...,
Beachten Sie jedoch, dass ein Winkelsensor möglicherweise mehrere Kurvenwerte meldet. dh eine Umdrehung könnte 0-1024 sein, und der Sensor kann +/-32 Umdrehungen in einer 16-Bit-Zahl melden.
Warum sollte jemand das tun?
Stellen Sie sich vor, das Lenkrad befindet sich genau am 0/360-Grad-Punkt und dreht sich zwischen 0 und 360 mit Straßenvibrationen. Was passiert, wenn Sie Rauschen herausfiltern? Sie erhalten 180 Grad, was völlig falsch ist.
Dies ist immer ein Problem, das bei der Verwendung von frei drehbaren Winkelsensoren irgendwo behandelt werden muss. Einige Systeme geben zwei Winkelwerte bei 90 Grad (sin+cos) zurück, um diese Mehrdeutigkeit aufzulösen. Andere erweitern den Winkelwert auf 360 + 180 Grad mit Hysterese, während einige eine Multiturn-Winkelakkumulation durchführen.
Die anderen Antworten erklären die Nachrichtenformatierung gut, aber ich würde es wahrscheinlich so auffassen:
union
{
struct
{
uint8_t hi; //the order of these two depends on the endian-nesss of your specific micro
uint8_t lo; //swap if the data is garbled
};
uint16_t all; //or sint16_t if you like: the only difference so far is at what value it wraps to the opposite end of its range
} reading;
uint16_t get_reading()
{
reading.hi = get_hi_byte();
reading.lo = get_lo_byte();
return reading.all;
}
Der Vorteil dabei ist, dass die scheinbare Berechnung (shift + or) tatsächlich ohne wirklichen Aufwand vollständig von der Speicherstruktur erledigt wird, sodass selbst ein dummer Compiler immer noch effizienten Code generiert. Und Sie schreiben es direkt so, wie es tatsächlich passiert, was meiner Meinung nach ein großes Plus für die Lesbarkeit ist.
Wenn Sie dies häufig tun, können Sie:
typedef union
{
struct
{
uint8_t hi;
uint8_t lo;
};
uint16_t all;
} byte_int;
byte_int steering;
byte_int throttle;
//etc.
typedef
s in eine separate Header-Datei eingefügt, die eine von zwei ist, die dem jeweiligen Chip entsprechen. (der andere hat eine Reihe chipspezifischer Definitionen für E / A und grundlegende Funktionen) Dann wird der obige Quellcode portabel.Wenn Sie eine Dezimalzahl wie 106 hätten, könnten Sie sagen, dass sie einen Überlauf hatte, bei dem Sie über 99 hinausgegangen sind und einmal auf 100 "übergelaufen" sind. Sie könnten zustimmen, dass dies besser als "Übertrag" bezeichnet wird. Sie verstehen das, weil Sie in Ihrem Beitrag 7 * 256 multipliziert haben, bevor Sie 208 addiert haben, um die Antwort von 2000 zu erhalten. (Sie haben 255 geschrieben, aber ich denke, das ist ein Ausrutscher, da die Antwort richtig war).
Sie haben nicht gesagt, ob Sie in C oder einer anderen Sprache arbeiten. Es ist möglich, dass Sie nur auf einen CAN-Dump starren und die Dinge auf Papier ausarbeiten. Aus dieser Sicht ist der einfachste Weg zu einer Antwort, dass Sie, wenn die Zahl negativ ist (d. h. 2^15 (32768) oder größer ist), 2^16 (65536) vom Messwert abziehen, um die Antwort zu erhalten. Das heißt, wenn Sie F8 33 hätten und F8 * 256 + 33 berechnen, erhalten Sie 63539. Dann ist 63539 - 65536 = -1997, was der Wert ist, der von F833 dargestellt wird, wenn er als vorzeichenbehaftete Zahl betrachtet wird.
Die Informationen in den anderen Antworten sind gültig und werden meistens empfohlen, aber dies kann Ihnen den Einstieg erleichtern und möglicherweise für jemanden geeignet sein, der sich noch nicht mit dem Programmierende befasst hat. Es ist auch eine gute Möglichkeit, die Antwort zu überprüfen, die Ihr Programm Ihnen gibt, nachdem Sie es ausprobiert haben.
mkeith
Ignacio Vazquez-Abrams
Shibalicious
Shibalicious
Ignacio Vazquez-Abrams
Shibalicious
mkeith
Ignacio Vazquez-Abrams
Shibalicious
Shibalicious
Davislor
mkeith
Lundin
Lundin