MPL115A fixed point question

cancel
Showing results for 
Search instead for 
Did you mean: 

MPL115A fixed point question

6,001 Views
BillTheBiker
Contributor III
I am trying to make sense of the coefficients I receive from the pressure sensor MPL115A.  I can successfully access the IIC bus and get the data, but the algorithm provided in the AN3785 does not appear to work (at least for me).  Closer inspection of the comments raises some questions.  For example, the coeff C11 is described as a signed number with no int bits and 10 frac bits.  There are 11 pad zeros.  So I think the number looks like this:

S0.00000000000 F10 F9 F8 F7 F6 F5 F4 F3 F2 F1 F0

To me the representation of this number is s(0,21), but the comment says s(16,27).

When I extract the data from the sensor, C11 is given as 0x0340.  This is a 2's compliment number and only the first 11 bits are used (the 10 frac bits and the sign bit).  So if I convert this to a real number (not something I would do in the processor, but just to see if I am understanding this correctly),  I get

S0.00000000000 0000 0011 010x xxxx

this gives 2^-17+ 2^-18 + 2^-20 = 0.00001239776611328125

this is the extent of my understanding, and I wonder if someone could confirm I have done this correctly.  In addition, as the algorithm in AN3785 keeps everything in fixed point, I cannot see where lt1 (a 32-bit value) can be assigned C11 and this be correctly justified.  When I assign 0x0340 to the long I get

lt1    0x00000340
= 0000 0000 0000 0000 0000 0011 010x xxxx

so you can see I am not correctly aligned as per the bit-width specifications.  The following is the code snippet.  The comments make no sense to me, so any clarification you could prrovide would be most helpful

 //******* STEP 1 c11x1= c11 * Padc
 lt1 = (S32)sic11;                  // s(16,27)    s(N,F+zeropad) goes from s(11,10)+11ZeroPad = s(11,22) => Left Justified = s(16,27)

Thanks

-Bill
0 Kudos
35 Replies

758 Views
d_
Contributor I

Gee, I know this thread is years old.  But I am dense.  Bill's original post was about c12 where he said the data sheet shows 11 pad zeros.  My data sheet says 9 pad zeros.

And I'm lost in that data sheet where the value of c12 is 0x33C8.  I don't see how you get from that to the value 0.000790.  If you pad F12 - F0 with 9 zeros, you get 0000000001001....  That would mean the the first 1 bit would be 2^^-10, or 0.0009xxx, which is already greater than 0.000790.

Sorry, I don't get it.  Anybody have some thoughts for me?

Thanks,

d.

0 Kudos

758 Views
d_
Contributor I

Oh, sorry.  I see that the 2 LSBs are zeros.  I still don't see how you get 0.000790 though.

d.

0 Kudos

758 Views
TomasVaverka
NXP TechSupport
NXP TechSupport

Hi Dave,

I have just explained it in your SR 1-1348149811.

First, you need to take into consideration that the last two bits of the c12_LSB are zero as shown on page 5 of the data sheet:

c12 MSB byte = c12[13:6] = [b13 , b12 , b11 , b10 , b9 , b8 , b7 , b6]

c12 LSB byte = c12[5:0] & “00” = [b5 , b4 , b3 , b2 , b1 , b0 , 0 , 0]

Then the c12 coefficient is formatted as follows:

c12 = b13 0 . 000 000 000 b12 b11 b10 b9 b8 b7 b6 b5 b4 b3 b2 b1 b0

So, each fractional bit has the value of 1 / 2^(9+13) and the 16-bit c12 value needs to be first right shifted by 2 (or divided by 4) to obtain 13 fractional bits.

Considering our example, the 16-bit c12 = 0x33C8.

Then c12 >> 2 = 0x33C8 >> 2 = 0xCF2 = 3314


and c12 coefficient = 3314 / 2^22 = 0.00079


I hope it helps.


Regards,

Tomas

0 Kudos

758 Views
d_
Contributor I

Thanks Tomas,

I get it now.

Dave

0 Kudos

758 Views
Jack_at_Oxigraf
Contributor III

I coded this in an old assembler for the 68hc912b32

Note each coefficient is placed in a 32 bit variable.

I am sure there is room for optimizations.

   .include   "globals.s"

   .include   "hc12.s"

c12x2: .blkb 4

a1: .blkb 4

a1x1: .blkb 4

y1: .blkb 4

a2x2: .blkb 4

PComp32: .blkb 4

baro_fltr: .blkb 3

 

;***************************************************************************************************************

; Initialize and read coefficients

  movb #0b00000000,sp0br  ; set spi to 4 MHz clock 

ibaro:  bclr portb,#0b10000000  ; Set CS low

  movb #$88,sp0dr ; read a0 msb

  brclr sp0sr,#SPIF,.  ; wait for spi

  movb #0,sp0dr ; clearing byte

  brclr sp0sr,#SPIF,.  ; wait for spi

ldaa sp0dr ; get a0 msb

  movb #$8A,sp0dr ; read a0 LSB

brclr sp0sr,#SPIF,.  ; wait for spi

movb #0,sp0dr ; clearing byte

brclr sp0sr,#SPIF,.  ; wait for spi

ldab sp0dr ; get a0 LSB

std BaroCoa0 ; save baro coefficient a0

movb #$8C,sp0dr ; read b1 MSB

brclr sp0sr,#SPIF,.  ; wait for spi

movb #0,sp0dr ; clearing byte

brclr sp0sr,#SPIF,.  ; wait for spi

ldaa sp0dr ; get b1 MSB

movb #$8e,sp0dr ; read b1 LSB

brclr sp0sr,#SPIF,.  ; wait for spi

movb #0,sp0dr ; clearing byte

brclr sp0sr,#SPIF,.  ; wait for spi

ldab sp0dr ; get b1 LSB

std BaroCob1 ; save baro coeffiecient b1

movb #$90,sp0dr ; read b2 msb

  brclr sp0sr,#SPIF,.  ; wait for spi

  movb #0,sp0dr ; clearing byte

  brclr sp0sr,#SPIF,.  ; wait for spi

ldaa sp0dr ; get b2 msb

  movb #$92,sp0dr ; read b2 LSB

  brclr sp0sr,#SPIF,.  ; wait for spi

  movb #0,sp0dr ; clearing byte

  brclr sp0sr,#SPIF,.  ; wait for spi

ldab sp0dr ; get b2 LSB

  std BaroCob2 ; save baro Coefficient b2

  movb #$94,sp0dr ; read c12 MSB

  brclr sp0sr,#SPIF,.  ; wait for spi

  movb #0,sp0dr ; clearing byte

  brclr sp0sr,#SPIF,.  ; wait for spi

ldaa sp0dr ; get c12 MSB

  movb #$96,sp0dr ; read c12 LSB

  brclr sp0sr,#SPIF,.  ; wait for spi

  movb #0,sp0dr ; clearing byte

  brclr sp0sr,#SPIF,.  ; wait for spi

ldab sp0dr ; get c12 LSB

  movb #0,sp0dr ; extra clearing byte

  brclr sp0sr,#SPIF,.  ; wait for spi

  bset portb,#0b10000000  ; Set CS hi

  std BaroCoc12 ; save baro coeffiecient c12

rts

CompBaro::

emuls ;result in Y:D needs shift 11 bits

;or divide by 2^11 which is 2048.

ldx #2048 ; **** TODO or could do 3 shifts then load from mid byte

; to acomplishs the other 8 bits.

sty c12x2+2 ; only save as 16 bit **** ;/ *

adca a1 ;preserve sign bit

ldd a1+2 ;get lower 16 bits

ror PComp32 ;Shift 1 bit

; todo check this for best resolution

ldd PComp32+1 ; Get mid bytes effectly shifts 8 bit

ldx #16394 ; 1023... times 16 for binary fraction

;use IRR filter with an index of 15 to smooth out noise

  subd AmbBaro ; subtract last filtered Baro value

  psha  ; save sign

  addd baro_fltr+1  ; add accumulator lsword

  std baro_fltr+1  ; update

  pulb  ; msbyte difference

  tfr b,d  ; extend sign to acca

  adca baro_fltr  ; add with carry to msbyte accumulator

  staa baro_fltr  ; update

  ldd baro_fltr  ; IIR filtered result in msword

  pshd  ; to temp on stack

  ldab baro_fltr+2  ; lsbyte filter for left shift

  ldaa #15 ; filter control, 0 = min fltr, 15= max fltr

  ; flip bits for decrement??

  eora #$F ; *** could use Higer if this is not enough

   ; less filtering => more shifts

bfc1:  beq bfc2  ; begin shift loop for less filtering

  lslb  ; left shift lsbyte

  rol 1,s  ; shift into filtered pressure

  rol ,s  ; msbyte

  deca ; dec shift count by subtracting bit 4

  bra bfc1  ; repeat

bfc2:  puld  ; retrieve result

std AmbBaro ;Results in kPa= mBar*10


Jack

0 Kudos

758 Views
Michael_BY
Contributor I

Do I convert the coefficient of c12?

 

MSB c12 = 00110000  

LSB c12 = 101011xx

 

Coef c12 =S0, 000000000 00 110000101011xx = 2^-10 + 2^-11 + 2^-16 + 2^-18 + 2^-20 + 2^-21 = 0,001485 

 

Is that right? 

 

 

0 Kudos

758 Views
ey7180
Contributor I

I'm trying ot interface to this device and I'm havin some issues. The coefficients  and readings I get from the device are as follows:

 

a0:3b99 b1:ffffb8b9 b2:ffffc24d c12:3274 c11:0 c22:0 Padc:157 Tadc:1f5

Padc:343 Tadc:501

temperature: 26.536017 C

pressure: 51.450000 kPa

Compensated Pressure: 91.935486 kPA

 

Using the coefficient to floating point functino earlier in the forum I get these values:

a0_d:1906.250000

b1_d:-2.056824

b2_d:-0.240997

c12_d:0.000192

c11_d:0.000000

c22_d:0.000000 

Pressure_d: 120.723752 kPa

 

I take those double values and calculate it using the:

 

Pcomp = a0_D + (b1_D + c11_D*Padc + c12_D*Tadc)*Padc + (b2_D + c22_D*Tadc)*Tadc

Then using the (65.0/1023.0*Pcomp + 50, I get Pressure_d. Was there something special I needed to do because neither of thsese match the 101 kPa I was expecintg and what's even more confusing is that these numbers are different. The first compensated pressure value was calculate according the application note sheet.

 

 

0 Kudos

758 Views
markleman
Contributor I

Bill,

Did you ever resolve this?

Regards, Mark Leman

0 Kudos

758 Views
lrf
Contributor I

Has anyone other then "Billthe Biker" tried to use the MPL115A eval boards. My board is an i2c version and I can talk to it but it doesn't want to read any data out. It just hangs and locks up the computer.

Anyone have any idea? I read Bill's posting about the problems with the math and I am concerned if I get thru the hardware issues then I will be faced with math issues. Did I buy a pig here?

 

Reading the manual shows a number of issues which one has to wonder if Freescale has made it work. Any replies would be appreciated. (I hope there are more the what Bill has received)

0 Kudos

758 Views
markleman
Contributor I

Yes I have the I2C version and can read the data from it using both a PIC micro and a NIOS soft CPU, what I can't do is get the coeficients to work Smiley Sad

 

Here is the code I use (you will need your own I2C start/stop/read/write functions)

 

void Measure_ambient_presure(void) { unsigned char x; unsigned char MPL115A2_data[16]; printf(";Read MPL115A2\r"); i2c_start();           // I2C Bus Start condition i2c_write(MPL115A2_I2C_address);     // I2C bus Device address i2c_write(MPL115A2_start_both_conversion_command); // Command to Convert both Pressure and Temperature i2c_write(0x01); i2c_stop();           // I2C Bus Stop condition delay_ms(5);          // wait for A-D conversion to take place i2c_start();           // I2C Bus Start condition i2c_write(MPL115A2_I2C_address);     // I2C bus Device address i2c_write(0x00);         // address in MPL115A2 i2c_start();          // I2C Bus Start condition (a restart) i2c_write(MPL115A2_I2C_address|0x01);    // address in MPL115A2 + read bit MPL115A2_data[0]  = i2c_read(1);     // read with ACK MPL115A2_data[1]  = i2c_read(1);     // read with ACK MPL115A2_data[2]  = i2c_read(1);     // read with ACK MPL115A2_data[3]  = i2c_read(1);     // read with ACK MPL115A2_data[4]  = i2c_read(1);     // read with ACK MPL115A2_data[5]  = i2c_read(1);     // read with ACK MPL115A2_data[6]  = i2c_read(1);     // read with ACK MPL115A2_data[7]  = i2c_read(1);     // read with ACK MPL115A2_data[8]  = i2c_read(1);     // read with ACK MPL115A2_data[9]  = i2c_read(1);     // read with ACK MPL115A2_data[10] = i2c_read(1);     // read with ACK MPL115A2_data[11] = i2c_read(1);     // read with ACK MPL115A2_data[12] = i2c_read(1);     // read with ACK MPL115A2_data[13] = i2c_read(1);     // read with ACK MPL115A2_data[14] = i2c_read(1);     // read with ACK MPL115A2_data[15] = i2c_read(0);     // read with noACK i2c_stop();           // I2C Bus Stop condition  printf(";MPL115A2 Data= "); for (x=0;x<16;x++)  {  printf("%02X ",MPL115A2_data[x]);  } printf("\r"); }

 

Here is some sample data I get from it.

 ;MPL115A2 Data= 64 C0 75 80 3D 9B B9 4A C7 46 32 BC 02 20 FD 60
;MPL115A2 Data= 65 00 76 80 3D 9B B9 4A C7 46 32 BC 02 20 FD 60
;MPL115A2 Data= 65 40 76 80 3D 9B B9 4A C7 46 32 BC 02 20 FD 60
;MPL115A2 Data= 65 00 76 C0 3D 9B B9 4A C7 46 32 BC 02 20 FD 60
;MPL115A2 Data= 65 80 75 C0 3D 9B B9 4A C7 46 32 BC 02 20 FD 60
;MPL115A2 Data= 65 00 75 40 3D 9B B9 4A C7 46 32 BC 02 20 FD 60

 

Regards, Mark Leman

0 Kudos

758 Views
admin
Specialist II

markleman,

 

I've been trying unsuccessfully for two days to start a new pressure and temp conversion with no avail.

 

In AN3785, under the 'Write Mode' header, it shows the commands and gives examples of how to write the 'start both conversions' byte string which shows as:

 

Start bit

Slave address (0x60)

Ack

Start both conversions (0x12)

Ack

Stop bit 

 

But under the 'I2C Simplified' heading it show to write an additional byte of 0x01 after the 0x12 byte and you also have it your code. I added it and now it works, Do you know what is the 0x01 for? It's not documented anywhere in the sheet except in the 'I2C Simplified' header unless I'm not seeing it somewhere else.

 

Thanks!

Robert   

0 Kudos

758 Views
sofie
Contributor I

hi,

 

I also use MPL115A but in SPI, I can read Temperature, pressure and coeffcicient whihout problem

but I'm not sure how to convert 10bit data receive in KPa or Celsuis degree

For example :

 

I get MSB pressure 0x5B

        LSB  pressure 0xC0

I do this : 0x5BC0 >>6 then  Pka = (65/1023)*Padc + 50

but I obtain 73KPa and I'm not at 2500 meters

 

How do you get the correct result ?

 

thanks

 

0 Kudos

758 Views
MPL115A2
Contributor I

I just don't know why the hell 'Freescale' made this device so confused with all the coeficients compensation calculation and not do it internally!!!

0 Kudos

758 Views
PJH
Contributor I

It's much cheaper and easier to do the math in another processor, rather than have the processing be built into the sensor.   I have to agree that it is awkward to figure out, but once done it is works well.  The original app note did have some errors in the description of the storage of the coefficients and the sample code is dependent on certain byte ordering of your processors.

 

The apps person was very helpful and I was able to get up and running within a day or so of getting the sample part.

 

I posted pretty much all the code I used to get this working in this forum, in the hope that others can benefit from the work I had to do.  Please look back at the other messages in this thread to see these examples.

 

if I had one suggestion for improvement, it would be to improve the noise on the device. The readings do bounce around quite a lot, but again a higher level processor with memory is easily able to implement a smoothing algorithm.

 

Good luck

0 Kudos

758 Views
MPL115A2
Contributor I

I'm totally agreed your points, its cheaper to have the cal' externally and thats more open to the users - once you understand the way to convert the coefficient values.

ok, I saw the programs from you and others as well, however, not everyone use C, and of cause not every one use large memory cpu/mcu as well, just think about if you have a 128bytes memory, (ram) and less than 2K code memory (flash or rom)

then your desgin may not be the same. Anyway, according to your experience, every problem solved after few days and the Freescale's support person/engineers are quite helpful, well, thats my third days, and holpfully problem can solved today, and wishing some one from Freescale can give me some helpfull hints.

 

Wish me luck.

cw.

0 Kudos

758 Views
PJH
Contributor I

Getting to the pressure is a multistep process.

 

First you need to read the pressureADC and the temperatureADC.  Both of these are 2 bytes.

 

pressureADC= (pressureMSB<<8+pressureLSB)>>6;

 

temperatureADC=(tempMSB<<8+tempLSB)>>6;

 

Next you need to use those 2 ADC values along with all the coefficients to calculate the pressure.

 

pressure= AO+(B1+C11*pressureADC+C12*temperatureADC)*pressureADC+(B2+C22*temperatureADC)*temperatureADC)

 

finally you make another correction

pressureC=(65/1023*pressure)+50;

 

That finally gives you a temperature compensated pressure in mBar.

 

The hardest thing I found was getting the coefficients to be read in correctly.

 

Peter

0 Kudos

758 Views
pmatil
Contributor I

I just managed to read something from the sensor. First of all, the coeffs C11 and C22 are all zero. Coeffs are like this (coeff1 coeff2 ... coeff12):

 

3E 98 B3 D2 C2 24 38 E0 00 00 00 00

 

And when I calculate the real pressure (pressure adc = 5C80, temp adc = 7F40) it says 918 hPa which cannot be correct since I'm at 25 m above sea level and pressure at sea level should be 1008 hPa.  It's very weird that C11 and C22 are all zero. Any ideas?

0 Kudos

758 Views
PJH
Contributor I

The coefficients are not all the same size,but are packed into a 12 bytes (96 bits) in registers x04 to x0F. The coeffs are really floating point numbers but the decimal point is in a different place in each number and some are negative etc.  So since I was unable to get the sample code to work due possibly to the byte endian nature of the data I wrote a function that can convert the bytes to the doubles.

 

First here is the code to download the coeffs.  Obviously the implementation of the I2C is left to the reader.

 

 

void CMPL115A::smileyvery-happy:ownloadCoeffs()
{
    int err=0;
    int resultL,resultH;
    BYTE data;

    iA0=0;
    iB1=0;
    iB2=0;
    iC11=0;
    iC12=0;
    iC22=0;

// Get A0
    err=m_I2C.ModuleReadByte(BAROMETER,0x05,&data);
    if(err==ERROR_SUCCESS) // Then we probably have a device connected. so assume rest of calls will succeed.
    {
        resultL=INT8(data);
        m_I2C.ModuleReadByte(BAROMETER,0x04,&data);
        resultH=INT8(data);
        iA0=(resultH<<8)+(resultL&0x00FF);
        dA0=ConvertCoeffToDouble(iA0,16,12,3,0);

    // Get B1
        m_I2C.ModuleReadByte(BAROMETER,0x07,&data);
        resultL=INT8(data);
        m_I2C.ModuleReadByte(BAROMETER,0x06,&data);
        resultH=INT8(data);
        iB1=(resultH<<8)+(resultL&0x00FF);
        dB1=ConvertCoeffToDouble(iB1,16,2,13,0);

    // Get B2
        m_I2C.ModuleReadByte(BAROMETER,0x09,&data);
        resultL=INT8(data);
        m_I2C.ModuleReadByte(BAROMETER,0x08,&data);
        resultH=INT8(data);
        iB2=(resultH<<8)+(resultL&0x00FF);
        dB2=ConvertCoeffToDouble(iB2,16,1,14,0);

    // Get C12
        m_I2C.ModuleReadByte(BAROMETER,0x0B,&data);
        resultL=INT8(data);
        m_I2C.ModuleReadByte(BAROMETER,0x0A,&data);
        resultH=INT8(data);
        iC12=(resultH<<8)+(resultL&0x00FF);
        dC12=ConvertCoeffToDouble(iC12,14,0,13,9);

    // Get C11
        m_I2C.ModuleReadByte(BAROMETER,0x0D,&data);
        resultL=INT8(data);
        m_I2C.ModuleReadByte(BAROMETER,0x0C,&data);
        resultH=INT8(data);
        iC11=(resultH<<8)+(resultL&0x00FF);
        dC11=ConvertCoeffToDouble(iC11,11,0,10,11);

    // Get C22
        m_I2C.ModuleReadByte(BAROMETER,0x0F,&data);
        resultL=INT8(data);
        m_I2C.ModuleReadByte(BAROMETER,0x0E,&data);
        resultH=INT8(data);
        iC22=(resultH<<8)+(resultL&0x00FF);
        dC22=ConvertCoeffToDouble(iC22,11,0,10,15);

        m_InitSuccess=true;
    }
    else
    {
        m_InitSuccess=false;
    }
}


and the here is the ConvertCoeffToDouble which takes the 2byte word and then parameters which describe the number of bits, integer bits, fractional bits and padbits. There may be more efficient ways to do this, but since it only has to be done once when the device is initialized it is not critical to optimize.


double ConvertCoeffToDouble(INT16 coeff,short bitCount,short iBits, short fracBits, short padBits)
{
// Utility function to convert the coeff's into floating point parameters.
// See Freescale App note AN3785 for details of how the parameters are stored.

    double result=0;
    bool s=false;
    bool bit;
    int fb=0;

// We start working our way through the bits from high to low.
// The incoming data is a 16bit integer which we have to shorten since the lower LSB's are empty if <16 bits. (see pg 15 of AN3785).
    if (bitCount<16)
    {// Make coeff the appropriate size
        coeff=coeff>>(16-bitCount);
    }

// Sanity check that the parameters passed into the function make sense. We can't have more or less bits than the amount of data.
// Add 1 to account for sign bit.
    if((iBits+fracBits+1)!=bitCount)
    {
        TRACE(_T("Error in parameters!!\n"));
        return 0;
    }
//Sanity check that if we are padding the decimal point that we can't also have integer bits.
    if((padBits>0)&&(iBits>0))
    {
        TRACE(_T("Error in parameters. iBits and padBits not consistent!!\n"));
        return 0;
    }

    for(int b=bitCount;b>0;b--)
    {
        bit=bool((int(pow(2.0,b-1))& coeff)>>(b-1)); // Get each bit in turn as a 1 or a 0.
        if (b==bitCount) // if this is the first bit (sign bit check if it is set)
        {
            s=bit;
            if(s)
            {
                coeff=~coeff+1; //Calculate 2's complement; //(See App note AN3785 middle of Pg15 for explanation.
            }
        }
        else // process the bits in turn.
        {
            if(iBits>0)
            { // we have an integer bit;
                if(bit)
                {
                    result+=pow(2.0,iBits-1);
                }
                iBits--;
            }
            else // we have a fractional bit
            {
                fb++;
                if(bit)
                {
                    result+= (1/pow(2.0,fb+padBits));
                }
                fracBits--;
            }
        }
    }
    if(s)
    {
        result*=-1;
    }
    TRACE(_T("Result=%f\n"),result);
    return result;
}


Only after you have converted all the coefficients to the correct  sign, significant digits etc, does the thing start to make sense and read correctly.

 

Peter

0 Kudos

758 Views
BapGuy
Contributor I

New to the game; however willing to learn. Thank you for your coding example. Do you have raw data and result so I can determine if I have converted to VB correctly? Thanks.

I used:

Padc 64 C0 

Tadc 75 80 

 

A0 - 3D 9B 

B1 - B9 4A 

B2 - C7 46 

C12 - 32 BC 

C11  - 02 20 

C22 - FD 60 

 

 my result was  105.13476438

0 Kudos

758 Views
pmatil
Contributor I

Thanks PJH but I believe I have converted them correctly. Or have I? At the moment the result Pcomp is 1067 which cannot be right since it should be in between 0 and 1023. Converted coeffs are:

 

a0 = 2003

b1 = -2.3118

b2 = -0.15836

c12 = 1.456E-10

c11 = 0

c12 = 0

 

Padc = 370

Tadc = 509

 

 

0 Kudos