# MPL115A fixed point question

cancel
Showing results for
Search instead for
Did you mean:

## MPL115A fixed point question

6,228 Views
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
35 Replies
901 Views
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.

901 Views
Contributor I

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

d.

901 Views
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

901 Views
Contributor I

Thanks Tomas,

I get it now.

Dave

901 Views
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

901 Views
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?

901 Views
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.

901 Views
Contributor I

Bill,

Did you ever resolve this?

Regards, Mark Leman

901 Views
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)

901 Views
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

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

901 Views
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

901 Views
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

901 Views
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!!!

901 Views
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

901 Views
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.

901 Views
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

901 Views
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?

901 Views
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: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

901 Views
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

901 Views
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