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.
Oh, sorry. I see that the 2 LSBs are zeros. I still don't see how you get 0.000790 though.
d.
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
Thanks Tomas,
I get it now.
Dave
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
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?
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.
Bill,
Did you ever resolve this?
Regards, Mark Leman
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)
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"); }
;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
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
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
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!!!
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
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.
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
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?
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
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
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