Shop OBEX P1 Docs P2 Docs Learn Events
signed math and prop — Parallax Forums

signed math and prop

smbakersmbaker Posts: 164
edited 2011-09-11 13:09 in Propeller 1
I'm working on converting some C code for an MPL115A1 sensor over to spin, and I have a few questions about doing signed math.

Let's say I read a 16-bit value into a 32-bit integer. The value is read out of two registers, a hi byte and a low byte. The hi byte has a sign bit in it. My code so far looks like this:
a0l := read_reg(A0MSB) << 8 + read_reg(A0LSB) & $00FF

a0 is a signed value. Do I need to do anything special to sign extend it? The original C code looked something like this:
   signed char a0msb = read_reg(A0MSB)
   signed char a0lsb = read_reg(A0LSB)
   signed int a0 = (signed int) a0msb<<8 
   a0 = a0 + (signed int) a0lsb & 0x00FF
   signed long a0l = (signed long) a0

Comments

  • TappermanTapperman Posts: 319
    edited 2011-09-10 14:24
    smbaker wrote: »
    I'm working on converting some C code for an MPL115A1 sensor over to spin, and I have a few questions about doing signed math.

    Let's say I read a 16-bit value into a 32-bit integer.

    Try this right after your line of code
      a01 := (a01 << 16) ~> 16
    
    ... Tim
  • MagIO2MagIO2 Posts: 2,243
    edited 2011-09-10 14:32
    Why not simply use the oparator ~~ ?

    It says sign extend of bit 15 in the quick reference.
  • TappermanTapperman Posts: 319
    edited 2011-09-10 14:40
    MagIO2 wrote: »
    Why not simply use the oparator ~~ ?

    It says sign extend of bit 15 in the quick reference.

    That it does ... hadn't noticed that before!

    ... Tim
  • Mike GreenMike Green Posts: 23,101
    edited 2011-09-10 14:40
    You can use the ~~ operator too.
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-09-10 14:46
    Or you could do
    a0l := ((read_reg(A0MSB) << 24) ~> 16) + (read_reg(A0LSB) & $FF)
    
    You may not need the "& $FF" if read_reg returns an unsigned byte.

    EDIT: Actually if read_reg returns a signed extended byte value you would just need to do
    a0l := (read_reg(A0MSB) << 8) + (read_reg(A0LSB) & $FF)
    
    If it returns an unsigned byte value you could get by with
    a0l := ((read_reg(A0MSB) << 24) ~> 16) + read_reg(A0LSB)
    
    If you don't know whether the bytes are sign-extended or not the first statement I posted will work.
  • smbakersmbaker Posts: 164
    edited 2011-09-10 18:46
    I must still be doing something wrong because I'm getting nonsensical results. The original C from the datasheet looks like this:
    //******* STEP 1 c11x1= c11 * Padc
    lt1 = (S32)sic11;
    lt2 = (S32)uiPadc;
    lt3 = lt1 * lt2
    si_c11x1 = (S32)(lt3)
    
    //******* STEP 2 a11= b1 + c11x1
    lt1 = ((S32)sib1<<14); 
    lt2 = (S32)si_c11x1; 
    lt3 = lt1 + lt2; 
    si_a11 = (S32)(lt3>>14);
    
    //******* STEP 3 c12x2= c12 * Tadc
    
    lt1 = (S32)sic12;
    lt2 = (S32)uiTadc; 
    lt3 = lt1 * lt2; 
    si_c12x2 = (S32)(lt3);
    
    //******* STEP 4 a1= a11 + c12x2
    lt1 = ((S32)si_a11<<11); 
    lt2 = (S32)si_c12x2;
    lt3 = lt1 + lt2; 
    si_a1 =(S32)(lt3>>11);
    
    //******* STEP 5 c22x2= c22 * Tadc
    lt1 = (S32)sic22; 
    lt2 = (S32)uiTadc; 
    lt3 = lt1 * lt2; 
    si_c22x2 = (S32)(lt3); 
    
    //******* STEP 6 a2= b2 + c22x2
    lt1 = ((S32)sib2<<15); 
    lt2 = ((S32)si_c22x2>>1); 
    lt3 = lt1+lt2;
    si_a2 = ((S32)lt3>>16);
    
    //******* STEP 7 a1x1= a1 * Padc
    lt1 = (S32)si_a1; 
    lt2 = (S32)uiPadc; 
    lt3 = lt1 * lt2; 
    si_a1x1 = (S32)(lt3);
    
    //******* STEP 8 y1= a0 + a1x1
    lt1 = ((S32)sia0<<10); 
    lt2 = (S32)si_a1x1; 
    lt3 = lt1 + lt2; 
    si_y1 = ((S32)lt3>>10); 
    
    //******* STEP 9 a2x2= a2 *Tadc
    lt1 = (S32)si_a2; 
    lt2 = (S32)uiTadc; 
    lt3 = lt1 * lt2;
    si_a2x2 = (S32)(lt3);
    
    //******* STEP 10 pComp = y1 +a2x2
    
    lt1 = ((S32)si_y1<<10); 
    lt2 = (S32)si_a2x2;
    lt3 = lt1 + lt2; 
    // FIXED POINT RESULT WITH ROUNDING:
    siPcomp = ((S16)lt3>>13); // goes to no fractional parts since this is an ADC count.
    

    My Spin equivalent looks like this:
        a0 := read_signed_reg_16(A0MSB)
        b1 := read_signed_reg_16(B1MSB)
        b2 := read_signed_reg_16(B2MSB)
        c12 := read_signed_reg_16(C12MSB)
        c11 := read_signed_reg_16(C11MSB)
        c22 := read_signed_reg_16(C22MSB)
    
        P := P >> 6
        T := T >> 6
    
        c11x1 := c11*P
        a11 := (b1<<14 + c11x1) >> 14
        c12x2 := c12*T
        a1 := (a11<<11 + c12x2) >> 11
        c22x2 := c22*T
        a2 := (b2<<15 + c22x2>>1)>>16
        a1x1 := a1*P
        y1 := (a0<<10 + a1x1) >> 10
        a2x2 := a2*T
        lt3 := y1<<10 + a2x2
    
        Pcomp := lt3/8192
    

    I'm wondering if I botched something in my conversion. In particular, there's lots of type conversions in the C code applied to bytes that are shifted, perhaps I'm missing something here. In particular, the value I'm getting for Pcomp is well outside the acceptable range of [0..1023]; I'm getting about 17080.

    The datasheet is at http://cache.freescale.com/files/sensors/doc/app_note/AN3785.pdf?fpsp=1&WT_TYPE=Application%20Notes&WT_VENDOR=FREESCALE&WT_FILE_FORMAT=pdf&WT_ASSET=Documentation if anyone is interested...

    As far as reading the signed values and extending them, I'm doing this:
    pri read_reg_16(reg) : result | msb, lsb
        ' reg is shifted left 1 bit, as bit 0 is a do-not-care bit
        msb := read_reg(reg)
        ' reg+2 is the next register after reg
        lsb := read_reg(reg+2)
        return (msb<<8) + (lsb & $FF)
    
    pri read_signed_reg_16(reg) : result | tmp
        tmp := read_reg_16(reg) 
        return ~~tmp
        'return ((read_reg(reg) << 24) ~> 16) + (read_reg(reg+2) & $FF)
    

    I tried both Dave's and MagiO2's suggestions and both worked out equivalently.
  • RaymanRayman Posts: 14,999
    edited 2011-09-10 18:56
    That's a very cool sensor! Hope you get it going. I just took a peek to make sure the output is 2's compliment and it says it is.
    So, I think the advise above should work...
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-09-10 20:22
    Signed values that are right-shifted should use the ~> operator instead of the >> operator. This is probably why you are getting large positive values. A negative number shifted down with the >> operator will put zeros in the high order bits.
  • smbakersmbaker Posts: 164
    edited 2011-09-10 20:53
    Dave Hein wrote: »
    Signed values that are right-shifted should use the ~> operator instead of the >> operator. This is probably why you are getting large positive values. A negative number shifted down with the >> operator will put zeros in the high order bits.

    Ah, I think I understand. So, in C, a shift of an unsigned value preserves the sign bit:

    -4096 >> 1 = -2048
    -4096 << 1 = -8192

    Spin has two different operators. << and >> would act like C's << and >> on unsigned variables. ~< and ~> would act like C's << and >> on signed variables.

    So, in spin:

    -4096 ~> 1 = -2048
    -4096 ~< 1 = -8192

    -4096 >> 1 = some large positive number, because sign bit is shifted down one bit
    -4096 << 1 = some positive number, maybe 8192? (I'd have to look up 2s compliment arithmetic to be sure) since the sign bit was discarded

    Am I understanding it correctly?
  • TorTor Posts: 2,010
    edited 2011-09-11 05:40
    smbaker wrote: »
    Ah, I think I understand. So, in C, a shift of an unsigned value preserves the sign bit:
    I would formulate it a bit differently.. in C, a right-shift of an unsigned value doesn't sign-extend the result: bits coming in from the left will be 0. A right-shift of a signed value sign-extends the result: bits coming in from the left will all be 1 if the sign bit was 1, or 0 otherwise.

    int a = -4096 >> 1
    will give a different result from
    int b = (unsigned int) -4096 >> 1

    -4096 = 0xfffff000
    so in the above 'a' will be -2048 or 0xfffff800
    and 'b' will be 2147481600 or 0x7ffff800

    -Tor
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-09-11 06:16
    smbaker, I think you got the basic idea, except that there is not a ~< operator. A left shift always fills in with zeros, so there is no need for an arithmetic left shift. Looking at your original C code, it looks like all of the right shifts are done on signed values. You should use the ~> operator instead of >>.
  • smbakersmbaker Posts: 164
    edited 2011-09-11 13:09
    Dave Hein wrote: »
    smbaker, I think you got the basic idea, except that there is not a ~< operator. A left shift always fills in with zeros, so there is no need for an arithmetic left shift.

    Ah, I see. As usual, I had forgotten how 2s compliment numbers were represented.

    I think I have some good ideas now on fixing this code. I'll keep the thread updated if I get it working (and hopefully submit the completed object to the OBEX).

    EDIT: I think with the changes suggested, this is working now. At least it's returning an altitude computation that's close to my actual altitude. Only problem with this sensor appears that it's very noisy. At least 3 of the 12 bits are noise, maybe 4. I'm probably going to switch my application over to use a BMP85 (this will teach me to read the datasheets more carefully!).

    Scott
Sign In or Register to comment.