Breve trasfondo primero; Tengo datos del bus CAN de un ángulo de dirección que obviamente está en hexadecimal. El ángulo de dirección cubre dos bytes de un mensaje. El documento de especificaciones que tengo dice que esos dos bytes forman un int firmado de 16 bits que son el ángulo de dirección con un preescalador de 1/1024, eso es todo lo que tengo (no tengo acceso a la fuente). Lo que estoy tratando de hacer es convertir esos valores hexadecimales en int firmados, sin embargo, no estoy seguro de cómo hacerlo correctamente.
Una pequeña sección del mensaje CAN dentro de un período de tiempo corto (nos estamos enfocando en el byte 2 y 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
Por lo que yo sé, normalmente, los datos en los mensajes CAN siguen este formato: un byte para los datos reales, un byte para la cantidad de desbordamientos.
Por ejemplo, tomemos un int sin signo de un valor de 2000. Suponiendo que el byte n.º 0 es para desbordamientos, el byte n.º 1 es para datos reales, obtenemos:
CAN message -> [07, D0, x, x, x, x, x, x]
07 indicando que ha habido 7 desbordamientos, D0 indicando que el resto es 208, por tanto:
7*255 + 208 = 2000
Entiendo cómo hacerlo con valores sin firmar. Pero esta vez en mi escenario estoy tratando con valores firmados. Supongo que un byte es para desbordamientos, un byte es para el resto, sin embargo, no estoy seguro.
¿Cómo se calculan los desbordamientos para valores con signo? ¿Es overflow += 1 cuando el valor > 127 y overflow -= cuando el valor < -128? ¿Tiene sentido que los desbordamientos tengan firma?
¿Cómo puedo convertir estos bytes en decimal con signo en C/C++? Digamos que mi valor de bytes en hexadecimal es 91. La última vez que intenté almacenarlo en int y lo imprimí, imprimió 145 (binario normal) y no -111 (complemento a 2). ¿Cómo puedo hacer cumplir el complemento de 2 (si tiene sentido) en mi código?
¿Podría estar interpretando mal el formato de bytes? Todo lo que dice es que estos dos bytes representan el ángulo de dirección y que el ángulo de dirección es int16_t. He monitoreado su cambio en tiempo real y uno de ellos cambia erráticamente, casi como saltando de 00 a FF, mientras que el otro aumenta/disminuye lentamente y casi linealmente con el tiempo. ¿Ideas?
Estoy realmente atascado aquí, ni siquiera sé si voy en la dirección correcta. ¡Cualquier sugerencia y ayuda son realmente apreciadas!
Aclarar algunos malentendidos probablemente ayudará.
Primero, sus datos son un valor de 16 bits. No hay "desbordamientos" ni "datos reales": los 16 bits simplemente se dividen en dos piezas de 8 bits (bytes). Para obtener el valor binario correcto, debe concatenar los bytes. En C, puede hacerlo comenzando con valores sin signo y usando operadores bit a bit, como este:
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;
Ahora tiene el valor correcto de 16 bits. Si desea que el resultado esté firmado, simplemente puede convertir el valor en un tipo firmado:
int16_t signed_data;
signed_data = (int16_t)(highbyte<<8 | lowbyte);
Para responder a sus preguntas específicas:
Los desbordamientos producen los mismos valores binarios independientemente de si su variable está firmada o no. Por ejemplo, 0x7fff + 1 == 0x8000. Si interpreta 0x8000 como 32768 o -32768 depende del tipo de datos. (Tenga en cuenta que el desbordamiento de enteros con signo no está técnicamente definido en el estándar C; esto es lo que hará su CPU).
Como dije, todo depende del tipo de datos:
uint16_t ui = 0xffff; //65535
int16_t i = 0xffff; // -1
Lo que estás describiendo suena razonable para una medición de alta precisión. Un cambio de +1 o -1 en su byte superior representa solo 1/256 de su rango de dirección total. El byte bajo será extremadamente sensible al ángulo exacto.
Tenga en cuenta que es mejor convertir a un valor con signo lo más tarde posible y hacerlo con una conversión explícita. Cosas como los operadores bit a bit pueden comportarse de manera diferente o producir un comportamiento indefinido cuando se usan en valores firmados. En general, siempre que esté manipulando bits, use valores sin firmar.
Estás complicando demasiado esto.
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
etc ...
La explicación más probable de por qué un byte parece aleatorio y otro cambia linealmente es que el byte lineal es el byte de orden superior (los desbordamientos). El byte de orden inferior parece aleatorio porque cambia rápidamente en comparación con la tasa de actualización.
Diría que el byte 3 es el byte de orden superior y el byte 2 es el byte de orden inferior. En enteros con signo que utilizan la representación de complemento a dos (que es casi universal hoy en día), los números negativos tendrán bits de orden superior establecidos en 1. Mirando el hexadecimal, si el byte de orden superior es mayor que 0x80, es un número negativo. Entonces, en tu ejemplo, todos los números son números negativos. 0xffff es -1, 0xfffe es -2, etc. Lo que tienes en tu fragmento es:
Dado que todos esos números son mayores que 0x8000, todos son negativos.
Lo mejor que puede hacer es estudiar la representación en complemento a dos de los enteros con signo. Parece que sabes cómo hacer la conversión uint16_t. Lo que puede funcionar para usted es simplemente hacer eso, luego asignar el valor a una variable de int16_t. El compilador podría convertirlo correctamente para usted (no está garantizado por la especificación C, pero muchos compiladores lo hacen de esa manera).
Espero que esto te ayude un poco.
No sé nada sobre sensores de ángulo de dirección reales en automóviles ...,
pero tenga en cuenta que un sensor de ángulo puede informar múltiples valores de giro. es decir, un giro puede ser 0-1024 y el sensor puede informar +/-32 giros en un número de 16 bits.
¿Por qué alguien haría esto?
Bueno, imagine que el volante está exactamente en el punto de 0/360 grados y cambiando entre 0 y 360 con la vibración de la carretera. ¿Qué sucede si promedia para filtrar el ruido? Obtienes 180 grados, lo cual es completamente incorrecto.
Este siempre es un problema que debe manejarse en alguna parte cuando se usan sensores de ángulo de rotación libre. Algunos sistemas devuelven dos valores de ángulo a 90 grados (sen+cos) para resolver esta ambigüedad. Otros extienden el valor del ángulo a 360+180 grados con histéresis, mientras que algunos hacen acumulación de ángulo de varias vueltas.
Las otras respuestas hacen un buen trabajo al explicar el formato del mensaje, pero probablemente lo tomaría así:
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;
}
La ventaja aquí es que el cálculo aparente (shift + or) en realidad lo realiza completamente la estructura de la memoria sin ningún esfuerzo real, por lo que incluso un compilador estúpido aún generará un código eficiente. Y lo estás escribiendo directamente de la forma en que realmente sucede, lo que creo que es una gran ventaja para la legibilidad.
Si haces esto mucho, puedes:
typedef union
{
struct
{
uint8_t hi;
uint8_t lo;
};
uint16_t all;
} byte_int;
byte_int steering;
byte_int throttle;
//etc.
typedef
s en un archivo de encabezado separado, que es uno de los dos que corresponden al chip específico. (el otro tiene un montón de definiciones específicas de chip para E/S y funcionalidad básica) Luego, el código fuente anterior se vuelve portátil.Si tuviera un número decimal como 106, podría decir que tuvo un desbordamiento, donde pasó de 99 y "se desbordó" una vez hasta 100. Puede estar de acuerdo en que sería mejor llamarlo "carry". Entiendes esto, porque en tu publicación multiplicaste 7 * 256 antes de sumar 208 para obtener la respuesta de 2000. (Escribiste 255 pero creo que es un desliz ya que la respuesta fue correcta).
No dijiste si estabas trabajando en C o en cualquier otro lenguaje. Es posible que solo esté mirando un volcado de CAN y resolviendo las cosas en papel. Desde este punto de vista, la forma más sencilla de obtener una respuesta es que si el número es negativo (es decir, 2^15 (32768) o mayor), puede restar 2^16 (65536) de la lectura para obtener la respuesta. Es decir, si tuviera F8 33 y calculara F8 * 256 + 33, obtendría 63539. Entonces, 63539 - 65536 = -1997, que es el valor representado por F833 cuando se considera un número con signo.
La información presentada en las otras respuestas es válida y en su mayoría recomendada, pero esto puede ayudarlo a comenzar y podría ser apropiado para alguien que aún no ha abordado el final de la programación. También es una buena manera de verificar la respuesta que le da su programa, una vez que lo prueba.
keith
Ignacio Vázquez-Abrams
Shibalicious
Shibalicious
Ignacio Vázquez-Abrams
Shibalicious
keith
Ignacio Vázquez-Abrams
Shibalicious
Shibalicious
Davislor
keith
Lundin
Lundin