Joe P2

Member Since: December 3, 2009

Country: United States

  • Product BOB-10859 | about a year ago

    We ran into problems reading ABS_POS on an Arduino board, too. The problem seems to be in the dSPIN_Param function in file dSPIN_support.ino.

    C silently converts types shorter than int to int prior to any arithmetic operations. Ints in Arduino are 16 bits, so the left shift operations on the bytes returned by dSPIN_Xfer don’t work as intended with 16 bit ints.

    Consider how dSPIN_Param handles the first byte returned from dSPIN_Xfer:

    if (byte_len == 3) {
        ret_val |= dSPIN_Xfer(...) < < 16;
    }
    

    dSPIN_Xfer returns a byte, and it is silently converted into a signed, 16 bit int, and then it is left shifted 16 bits. A 16 bit value shifted left 16 bits is 0. So the most significant byte of the ABS_POS is effectively ignored. Surprise #1.

    The next byte is handled the same way, except it’s shifted left 8 bits, but this also causes an unexpected result. Suppose dSPIN_Xfer returns 0x80. It’s first converted to an int (0x0080), then shifted left 8 bits (0x8000). But this is now a negative value in a (signed, 16 bit) int. So when it’s converted to an unsigned long to be OR’d with ret_val, it’s converted to 0xFFFF8000. Surprise #2.

    The solution that worked for us was to explicitly cast the value returned by dSPIN_Xfer to an unsigned long. This forces the conversion of the 8 bit byte to a 32 bit value, which is long enough to handle the left shifts properly.

    ret_val |= (unsigned long)dSPIN_Xfer(...) < < 16;
    ...
    ret_val |= (unsigned long)dSPIN_Xfer(...) < < 8;
    ...
    ret_val |= (unsigned long)dSPIN_Xfer(...);
    

    (It’s not strictly needed in the last case, but I like consistency.)

    I suspect the original code was written for a system that used 32 bit ints, on which the left shifts would have worked as intended.

    I hope Sparkfun will change the example sketch to save other from this subtle bit of C behavior.

No public wish lists :(