Electronic – How to deal with signed int overflows

binaryccanconversionmicrocontroller

Brief background first; I have data from CAN bus of a steering angle which is obviously in hex. The steering angle covers two bytes of a message. The specification document I have says that those two bytes form a 16-bit signed int which are the steering angle which a prescaler of 1/1024, that's all I have got (I don't have access to the source). What I am trying to do is to convert those hex values into signed int, however I am not sure how to do it correctly.

A small section of the CAN message within short time period (we are focusing on byte 2 and 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

As far as I know, typically, data in CAN messages follow this format: one byte for the actual data, one byte for the number of overflows.

For example, let's take an unsigned int of a value of 2000. Assuming byte #0 is for overflows, byte #1 is for actual data, we get:

CAN message -> [07, D0, x, x, x, x, x, x]

07 indicating that there have been 7 overflows, D0 indicating the remainder is 208, therefore:

7*255 + 208 = 2000

I understand how to do it with unsigned values. But this time in my scenario I am dealing with signed values. I am assuming one byte is for overflows, one byte is for the remainder, however I am not sure.

  1. How are overflows calculated for signed values? Is it overflow += 1 when value > 127 and overflow -= when value < -128? Does it even make sense for overflows to have signedness?

  2. How can I convert these bytes into signed decimal in C/C++? Let's say my byte value in hex is 91. Last time I tried storing it in int and printed it out, it printed out 145 (normal binary) and not -111 (2's complement). How can I enforce 2's complement (if that makes sense) in my code?

  3. Could I be interpreting the byte format wrong? All it says that these two bytes represent the steering angle and that the steering angle is int16_t. I have monitored them change in real time and one of them changes erratically, almost like jumping from 00 to FF, whereas the other one is increasing/decreasing slowly and almost linearly with time. Ideas?

I am really stuck here, I don't even know if I am going in the correct direction.. Any suggestions and help are really appreciated!

Best Answer

Clearing up some misunderstandings will probably help.

First, your data is a 16-bit value. There's no "overflows" and "actual data" -- the 16 bits are just divided into two 8-bit pieces (bytes). To get the right binary value, you need to concatenate the bytes. In C, you can do it by starting with unsigned values and using bitwise operators, like this:

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;

Now you have the correct 16-bit value. If you want the result to be signed, you can simply cast the value to a signed type:

int16_t signed_data;

signed_data = (int16_t)(highbyte<<8 | lowbyte);

To answer your specific questions:

  1. Overflows produce the same binary values regardless of whether your variable is signed or unsigned. For example, 0x7fff + 1 == 0x8000. Whether you interpret 0x8000 as 32768 or -32768 depends on the data type. (Note that signed integer overflow is technically undefined in the C standard -- this is what your CPU will do.)

  2. As I said, it all depends on the data type:

    uint16_t ui = 0xffff;    //65535
    int16_t   i = 0xffff;    //   -1
    
  3. What you're describing sounds reasonable for a high-precision measurement. A change of +1 or -1 in your upper byte represents only 1/256 of your total steering range. The low byte will be extremely sensitive to the exact angle.

Note that it's best to convert to a signed value as late as possible, and to do it with an explicit cast. Things like bitwise operators can behave differently or produce undefined behavior when used on signed values. In general, whenever you're doing bit manipulation, use unsigned values.