|
|
View previous topic :: View next topic |
Author |
Message |
m4k
Joined: 27 Mar 2018 Posts: 29
|
prom ms5611 i2c reading |
Posted: Sun May 20, 2018 6:31 am |
|
|
Hi...i try to read ms5611 prom barometric sensor but i do'nt know how to read multiple bytes from the sensor.
I know the write and read sequence of i2c.
For example :
Code: |
i2c_start();
i2c_write(addw);
i2c_start();
i2c_write(addr);
var=i2c_read();
i2c_stop();
|
But how to read prom memory of ms5611?
This table show register map of the sensor:
ms5611 datasheet link
http://s8.picofile.com/file/8326933418/ENG_DS_MS5611_01BA03_B.pdf.html
0xA0 to 0xAE
Anybody can help me? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19480
|
|
Posted: Sun May 20, 2018 7:17 am |
|
|
Are you using a PIC24/30?.
If not, you have a problem. The sensor requires 64bit integer arithmetic for it's compensation arithmetic.
Honestly far easier to use one of the (more expensive) sensors that has internal compensation. Otherwise you are going to have to write your own 64bit arithmetic routines.
Chips like the MPL3115A2.
Also as another alternative, the Honeywell similar sensors do offer a 16bit arithmetic library.
The sequencing only gives the register sequence you show immediately after a reset. You can just do sequential reads (without a NACK) to get all the values. After this you just trigger the ADC cycles and read the 24bit ADC result. Problem is though that the offset and sensitivity calculations require signed int64 maths. |
|
|
m4k
Joined: 27 Mar 2018 Posts: 29
|
|
Posted: Sun May 20, 2018 8:24 am |
|
|
Ttelmah wrote: | Are you using a PIC24/30?.
If not, you have a problem. The sensor requires 64bit integer arithmetic for it's compensation arithmetic.
|
Tnx for reply.
Yes...I am using 33fj series.
When i want to read 16 bit value with i2c protocol i do it:
Code: |
i2c_start();
i2c_write(addw);
i2c_start();
i2c_write(addr);
varh=i2c_read(1);
varl=i2c_read(0);
i2c_stop();
|
I can read 16 bit value with ack, but i don't know how to read 6 byte with 1 i2c sequence.
According to the register map the prom address is 0xa0 to 0xae. How to do coding to read prom memory?
Do you have an example code ?
I am beginner in ccs, it's difficult understand for me.
tnx |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19480
|
|
Posted: Sun May 20, 2018 9:07 am |
|
|
Read bytes. Put them together into the values you want after reading. Problem is that chip actually does different numbers of bytes for different things. The ADC values (which you need to read to actually get a reading), are sent as 24bit values. 3 8bit transfers. The configuration values are a mix of 32bit values and 16bit values. Some have to be stored as signed. So you declare a structure containing the same pattern of registers as are in the device, then do a repeated byte transfer for all the bytes (using i2c_read(0) for all but the lats read), into the bytes represented by this structure. You then have the data stored in the format required. For the ADC value transfer it into the top three bytes of an int32 (bottom zero), and divide by 256. This way the sign will correctly be handled. |
|
|
m4k
Joined: 27 Mar 2018 Posts: 29
|
|
Posted: Sun May 20, 2018 9:42 am |
|
|
Ttelmah wrote: | Read bytes. Put them together into the values you want after reading. Problem is that chip actually does different numbers of bytes for different things. The ADC values (which you need to read to actually get a reading), are sent as 24bit values. 3 8bit transfers. The configuration values are a mix of 32bit values and 16bit values. Some have to be stored as signed. So you declare a structure containing the same pattern of registers as are in the device, then do a repeated byte transfer for all the bytes (using i2c_read(0) for all but the lats read), into the bytes represented by this structure. You then have the data stored in the format required. For the ADC value transfer it into the top three bytes of an int32 (bottom zero), and divide by 256. This way the sign will correctly be handled. |
For read pressure or temp according to datasheet i can read 3 byte and i don't have any problem.
My problem is read prom and big problem is that i don't know the right address of prom memory.
Can you give me any example ?
tnx |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19480
|
|
Posted: Sun May 20, 2018 11:27 am |
|
|
The PROM contains six int16 values C1 to C6. Issue the Read_Prom command with the pattern 0b10100010. This returns the data from address 1. The int16's start at this address. Read 16 bits. This is the C1 coefficient. Use pattern 0b10100100 to get C2 etc... The table on page8, gives what the 16bit values coefficients 'are'.
Address 0 contains factory data and the setup, addresses 1-6 calibration coefficients and address 7 contains the serial code and CRC. |
|
|
m4k
Joined: 27 Mar 2018 Posts: 29
|
|
Posted: Sun May 20, 2018 2:52 pm |
|
|
Ttelmah wrote: | The PROM contains six int16 values C1 to C6. Issue the Read_Prom command with the pattern 0b10100010. This returns the data from address 1. The int16's start at this address. Read 16 bits. This is the C1 coefficient. Use pattern 0b10100100 to get C2 etc... The table on page8, gives what the 16bit values coefficients 'are'.
Address 0 contains factory data and the setup, addresses 1-6 calibration coefficients and address 7 contains the serial code and CRC. |
tnx a lot dear Ttelmah..its work properly...i try to read pressure and temp raw value but :
according to datasheet
this is my code
Code: | i2c_start();
i2c_write(0xee);
i2c_write(0x58);
i2c_stop();
delay_ms(10);
i2c_start();
i2c_write(0xee);
i2c_write(0x00);
i2c_stop();
i2c_start();
i2c_write(0xee);
i2c_write(0x48);
i2c_start();
i2c_write(0xef);
p1=i2c_read();
p2=i2c_read(0);
p3=i2c_read(0);
i2c_stop(); |
but its not work
What is my mistake? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun May 20, 2018 6:38 pm |
|
|
Quote: |
i2c_start();
i2c_write(0xee);
i2c_write(0x48);
i2c_start();
i2c_write(0xef);
p1=i2c_read();
p2=i2c_read(0);
p3=i2c_read(0);
i2c_stop();
What is my mistake?
|
You have two NACK's at the end. Only the last byte should do the NACK. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19480
|
|
Posted: Mon May 21, 2018 12:30 am |
|
|
Just to reinforce this, look at the figure 15 you post. Look at the I2C transactions. You will see 'A' for acknowledge on all except the very last one for the data, that has 'N' for not acknowledge.
I notice something else. Comments inline:
Code: |
2c_start();
i2c_write(0xee);
i2c_write(0x58);
i2c_stop(); //This triggers a D2 conversion
delay_ms(10);
i2c_start();
i2c_write(0xee);
i2c_write(0x00);
i2c_stop(); //Why?. This is the read command can't do this
//till after you have a value to read....
//You have the D2 value but then don't read this.....
i2c_start();
i2c_write(0xee);
i2c_write(0x48);
//Er at this point you have triggered a D1 conversion. You need to
//wait at least 8.22mSec after this....
i2c_start();
i2c_write(0xef);
//Er what is this doing?. 0xEF is again not a legitimate command....
//Ah twigged the device read address. Give them names...
//You have not issued a read after the D1 conversion, or allowed time
//for it to complete
p1=i2c_read();
p2=i2c_read(0); //er only NACK the last byte
p3=i2c_read(0);
i2c_stop(); //will return 0 since the conversion has not completed...
|
Code: |
#DEFINE RESET 0x1E
#DEFINE CONV_D14096 0x48
#DEFINE CONV_D24096 0x58
#DEFINE READ_ADC 0x0
#DEFINE CHIP_ADDR 0xEE
#DEFINE READ 1
int32 read_conv(int8 cmd)
{
int32 result;
int8 LSB, MSB,MMSB;
i2c_start();
i2c_write(CHIP_ADDR);
i2c_write(cmd); //send the specified command
i2c_stop();
delay_ms(10); //9.04mSec worst case
//now get the reply
i2c_start();
i2c_write(CHIP_ADDR);
i2c_write(READ_ADC); //ask to read the value
i2c_stop();
//The data sheet shows a stop being issued before the bus
//is reversed, rather than a restart.
delay_us(2); //I2C required minimum 1.2uSec between a stop
//and start
i2c_start();
i2c_write(CHIP_ADDR | READ); //read command
MMSB=i2c_read();
MSB=i2c_read();
LSB=i2c_read(0); //NACK the last byte
i2c_stop(); //Figure 15 in the data sheet.
result=make32(0,MMSB,MSB,LSB); //build 32bit result
return result;
}
//Then call
D1=read_conv(CONV_D14096);
D2=read_conv(CONV_D24096);
//To get the two conversion values.
|
Untested but this follows what the data sheet shows.
Note also that the data sheet is very specific that you must get a proper chip reset on startup or it will return zero values. |
|
|
m4k
Joined: 27 Mar 2018 Posts: 29
|
|
Posted: Tue May 22, 2018 12:32 am |
|
|
Ttelmah wrote: |
Just to reinforce this, look at the figure 15 you post. Look at the I2C
transactions. You will see 'A' for acknowledge on all except the very last
one for the data, that has 'N' for not acknowledge.
|
tnx alot..its work properly
ms5611_prom.c
Code: |
unsigned int8 c1h=0,c1l=0,c2h=0,c2l=0,c3h=0,c3l=0,c4h=0,c4l=0,c5h=0,c5l=0,c6h=0,c6l=0;
unsigned int16 c1=0,c2=0,c3=0,c4=0,c5=0,c6=0;
unsigned int8 p1=0,p2=0,p3=0,t1=0,t2=0,t3=0;
unsigned int32 d1=0,d2=0,db=0;
signed int64 off=0,sens=0,p=0,temp=0;
void ms5611_prom(){
i2c_start();
i2c_write(0xee);
i2c_write(0xa2);
i2c_start();
i2c_write(0xef);
c1h=i2c_read(1);
c1l=i2c_read(0);
i2c_stop();
delay_ms(1);
i2c_start();
i2c_write(0xee);
i2c_write(0xa4);
i2c_start();
i2c_write(0xef);
c2h=i2c_read(1);
c2l=i2c_read(0);
i2c_stop();
delay_ms(1);
i2c_start();
i2c_write(0xee);
i2c_write(0xa6);
i2c_start();
i2c_write(0xef);
c3h=i2c_read(1);
c3l=i2c_read(0);
i2c_stop();
delay_ms(1);
i2c_start();
i2c_write(0xee);
i2c_write(0xa8);
i2c_start();
i2c_write(0xef);
c4h=i2c_read(1);
c4l=i2c_read(0);
i2c_stop();
delay_ms(1);
i2c_start();
i2c_write(0xee);
i2c_write(0xaa);
i2c_start();
i2c_write(0xef);
c5h=i2c_read(1);
c5l=i2c_read(0);
i2c_stop();
delay_ms(1);
i2c_start();
i2c_write(0xee);
i2c_write(0xac);
i2c_start();
i2c_write(0xef);
c6h=i2c_read(1);
c6l=i2c_read(0);
i2c_stop();
delay_ms(1);
c1=make16(c1h,c1l);
c2=make16(c2h,c2l);
c3=make16(c3h,c3l);
c4=make16(c4h,c4l);
c5=make16(c5h,c5l);
c6=make16(c6h,c6l);
}
void get_d1d2(){
i2c_start();
i2c_write(0xee);
i2c_write(0x48);
i2c_stop();
delay_ms(10);
i2c_start();
i2c_write(0xee);
i2c_write(0x00);
i2c_stop();
delay_us(3);
i2c_start();
i2c_write(0xef);
p1=i2c_read();
p2=i2c_read();
p3=i2c_read(0);
i2c_stop();
delay_us(3);
d1=make32(0,p1,p2,p3);
i2c_start();
i2c_write(0xee);
i2c_write(0x58);
i2c_stop();
delay_ms(10);
i2c_start();
i2c_write(0xee);
i2c_write(0x00);
i2c_stop();
delay_us(3);
i2c_start();
i2c_write(0xef);
t1=i2c_read();
t2=i2c_read();
t3=i2c_read(0);
i2c_stop();
delay_us(3);
d2=make32(0,t1,t2,t3);
}
void get_temp(){
db=c5*256;
db=d2-((int32)c5*256);
temp=2000 + db*((float)c6/8388608);
}
void get_pres(){
off=((int64)c2*65536) + ((int64)c4*db)/128;
sens=((int64)c1*32768) + ((int64)c3*db)/256;
p=(((d1*sens)/2097152)-off)/32768;
}
|
ms5611.c
Code: |
#use rs232(xmit=PIN_B0, baud=9600)
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <ms5611prom.c>
void main()
{
ms5611_prom();
delay_ms(50);
char ss[20];
while(TRUE)
{
get_d1d2();
get_temp();
get_pres();
itoa(p,10,ss);
puts(ss);
}
}
|
if code does not have problem i put it in code library?
tnx Ttelmah and pcm |
|
|
m4k
Joined: 27 Mar 2018 Posts: 29
|
|
Posted: Tue May 22, 2018 1:04 am |
|
|
Ttelmah wrote: | Just to reinforce this, look at the figure 15 you post. Look at the I2C transactions. You will see 'A' for acknowledge on all except the very last one for the data, that has 'N' for not acknowledge.
|
i try to calculate altitude from the pressure...
What is the pressure-to-height conversion formula? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19480
|
|
Posted: Tue May 22, 2018 3:09 am |
|
|
You have a lot of maths to do first to get the pressure.
You need the six coefficients read from the PROM. Stored as unsigned int16 values C1 to C6. Then:
Code: |
signed int32 Dt, TEMP;
signed int64 OFF, SENS, P;
Dt = (signed int32) D2 - ((signed int32)C5 * 256);
TEMP = 2000 + Dt * (signed int32)C6 / 8388608;
OFF = (signed int64)C2 * 65536+(C4 * (signed int64)Dt)/128;
SENS = (signed int64)C1 * 32768+(C3 * (signed int64)Dt)/256;
P = (D1*SENS/2097152 - OFF)/32768;
//This then gives P in 10's of nBar
|
Then you have to make a decision whether you want to work in standardised altitude or local.
If you look at aircraft, when they want their 'real' altitude at low levels, they work using the local reference pressure. So an aircraft is given the Qnh, which is the local sea level pressure, and can then get their altitude above 'sea level'. When flying into a local airfield and wanting to know their height above this they use Qfe (which is the pressure measured at that airfield). However when working at high altitude they switch to using the 'ICAO standard atmosphere', and work from a base pressure of 1013.25mBar. Now of course if you used this at low altitude on days when the pressure varied significantly you could find yourself many hundreds of feet above or below your 'real' altitude. But at high altitude all the aircraft being a few hundred feet high or low doesn't matter, so long as they are all using the same reference. The altitude at which you switch is the 'transition altitude'.
Now for your pressure to give altitude with good accuracy at a location, you would have to feed it the pressure at a know altitude for the same region. So on GPS's for example with pressure sensors, you can put in a known altitude when you are sitting on an airfield,and it then uses this as the reference. Or in flight you can put in the Qfe or Qnh so you know your reference and height relative to this. The formula for the conversion is the barometric formula. Doing it properly is quite complex. However the simplified form used for aviation is:
Palt = Psurf*(1-22.556922E-6*Alt)^5.2561
Working with pressures in hectoPascals (mBar), and altitude in meters.
However the even simpler formula for low altitude is that the pressure drops by 11.3Pascals per meter.
If you feed an altitude of 100m with a starting pressure of 1013.25mB into the formula above, you get:
Palt = 1001.29
which is a change of 1195 Pascals. The 11.3 Pascal figure (1130 at 100m) would be out by only 5%.
Last edited by Ttelmah on Wed May 23, 2018 1:00 am; edited 1 time in total |
|
|
m4k
Joined: 27 Mar 2018 Posts: 29
|
|
Posted: Tue May 22, 2018 10:04 am |
|
|
Ttelmah wrote: | You have a lot of maths to do first to get the pressure.
You need the six coefficients read from the PROM. Stored as unsigned int16 values C1 to C6. Then:
. |
i try to export altitude in meter unit and I succeeded..
but I have a strange problem...the datasheet said "High resolution module, 10 cm" According to the output values of pressure its not true...
Please take a look at these values
its not stable
whats wrong? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19480
|
|
Posted: Tue May 22, 2018 12:04 pm |
|
|
Itg wouldn't be. Barometric pressure is changing all the time. The 10cm is the smallest height change it can resolve, not an 'accuracy' that you can achieve. The pressure will even shift as you walk past the sensor.
If you have a phone with a baro sensor, stcnd still and see how it drifts. If I stand in my garden for 10 minutes I'll often find the altitude from the baro drifting by a couple of metres. Day by day, if you have something like a thunderstorm come across, pressure altitude can change by about 300 metres.
If you fly an aircraft for an hour, and land back at the same airport without adjusting the Qfe, it is common to find the 'altitude' has changed by many feet.
Your figure also bears no resemblance to the pressure reading. Typical value should be around ten million. I'd suspect you are only reading the low two bytes. They divide the value by a significant amount for the final pressure figure, which means small changes in the last few bits won't even show... |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19480
|
|
Posted: Tue May 22, 2018 2:07 pm |
|
|
OK I see you are actually printing the output. Still a wrong value. Should be about 100000. Look at the maths. For instance:
Code: |
void get_temp(){
db=c5*256; //Er... Why?.
//Also this is using int16 arithmetic....
db=d2-((int32)c5*256);
temp=2000 + db*((float)c6/8388608);
//Float not wanted here. All the maths is integer
}
|
Now compare to my version:
Code: |
Dt = (signed int32) D2 - ((signed int32)C5 * 256);
TEMP = 2000 + Dt * (signed int32)C6 / 8388608;
|
This is smaller and faster, and more accurate.
General comment, stop using 'undefined' numbers like the 0xEE etc.. Use defines and give these names. That way your code becomes much easier to understand and read.
Now why fiddle using itoa. Just print.
Code: |
itoa(p,10,ss);
puts(ss);
//just use
printf("%ld", p);
|
itoa is only really ever needed if you are going to a base other than 10, or want to fiddle with the string after generating it. printf handles base 10, and base 16 intrinsically.
Something somewhere is going wildly wrong with the maths, since you should be seeing a result around 100000. 85000 is much too low (unless you were sitting in the eye of a major storm). (actually too low even then - below the lowest ever recorded barometric reading 870hPa).....
Start by printing the numbers you are actually getting from each read. They give typical values for each value in the data sheet, so be really suspicious if any value differs from the expected result by more than perhaps 50%.
Then print the values from each part of the maths. Does it match what it should be (after all you have the earlier values so can check)?.
Long term, is the unit likely to be used at a temperature much below 20C?. If so you have to look at implementing the second order temperature compensation described in the data sheet. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|