View previous topic :: View next topic |
Author |
Message |
demedeiros
Joined: 27 Dec 2013 Posts: 71
|
MS5803 Help |
Posted: Tue Apr 12, 2016 2:18 pm |
|
|
Hi All,
I am using a PIC16F1829 with a Meas-Spec MS5803-14BA sensor. I am having trouble calculating the pressure from the unit. I think my issue is stemming from variable type, but im not sure.
I can read all of the registers in the device over I2C without issue, but I am having issues with the following function. Calculation of "dT" is correct, but "off", "SENS" and "Pressure" are not calculating correctly.
Code: | float32 GetBusPressure(){
GetD1D2();
signed int32 dT = D2-Amb_C[5]*twoto(8);
float32 off = Amb_C[2]*twoto(16)+((dT*Amb_C[4])/twoto(7));
float32 SENS = Amb_C[1]*twoto(15)+((dT*Amb_C[3])/twoto(8));
float32 Pressure = (((D1*SENS)/(twoto(21)-off))/twoto(15));
return Pressure;
}
float32 twoto(int factor) //Efficiently return integer 2^factor Graciously provided by Ttelmah
{
int32 res=1;
res<<=(factor);
return (float32)res;
}
|
The following values are read from the sensor:
Amb_C[1] = 45342
Amb_C[2] = 39644
Amb_C[3] = 28929
Amb_C[4] = 27159
Amb_C[5] = 31972
Amb_C[6] = 28819
D1 = 4122586
D2 =9156890
Plugging these into an excel spreadsheet yields a pressure of 10140 04 1.014bar which is correct. The output of the above function is showing the following:
dT = 972058 (Matches Excel Calcs)
off = -1691933824 (Does not match Excel)(Excel = 2804360147
)
SENS = -669311168 (Does not match Excel)(Excel = 1595613007
)
Pressure=-49.70 (Does not match Excel)(Excel = 10140.97)
Can anyone provide any insight to what is going on here?
Sensor Datasheet: http://www.meas-spec.com/downloads/MS5803-14BA.pdf |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9244 Location: Greensville,Ontario
|
|
Posted: Tue Apr 12, 2016 2:53 pm |
|
|
quick reply
ANYTIME I have to do 'fancy math', I make sure I have LOTS of braces to ensure the compiler KNOWS what I want calculated 1st,2nd, 3rd.
You may want to 'breakup the math into smaller 'steps' and test/printout results to be sure the interim numbers agree with what is expected.
While it might not be your problem... doesn't hurt to check.
Jay |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1909
|
|
Posted: Tue Apr 12, 2016 3:27 pm |
|
|
I strongly suspect you're mixing signed and unsigned types. Be explicit with your declarations in your functions and/or with your variables. If something is supposed to be unsigned, include unsigned in the declaration.
I've also learned that my CCS code always gives me what I expect if I explicitly make it use the variable size I want for intermediate calculations by using (casts).
The other suggestion to break apart calculations into smaller pieces is also very good. I've found that CCS sometimes doesn't always output what I expect if I string together too many variables in an equation. |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
Re: MS5803 Help |
Posted: Wed Apr 13, 2016 2:03 am |
|
|
The datasheet is quite useful in leading you to a good implementation off this. It gives suggested variable sizes, all integers, for all these values, and gives worked examples. I can see you've read that bit as you've used those examples. So I have to ask why are you not using the suggested varaible types? Granted, there is an issue that on the 16s and 18 there is no support in CCS C for unsigned int64, but the datasheet doesn't suggest it's use.
You see, when done as the datasheet suggests, all the maths are done in integers. The "* 2^n"s become simple shifts, and need none of that float multiply by float constant nonsense.
The C values are calibrations and need only be read once, and where they are multiplied by some power of two (by shifts in integers remember) that only needs to be done once, mand the modified constant used thereafter.
So, I suggest you ditch the floats and do it again as the datasheet says, i.e. in integers. The start of the function might then look like this:
Code: |
...
// Somewhere in some initialisation code:
unsigned int16 Amb_C[6];
Amb_C[5] = Read_Cal(5) << 8; // Pre-multiply the constant to save work later
...
float32 GetBusPressure(){
GetD1D2();
// dT can be negative, therefore we must do the maths in signed int32.
signed int32 dT = (signed int32)D2-Amb_C[5];
|
Be aware that while we know that the result of int16 * int16 can be int32, the C standard doesn't, and will only give an int16 result. Therefore you need to promote, by casting, one of the operands in the expression to int32 to force int32 arithmetic to be used. I suspect that's the sort of thing your code is falling over on, especially as you are getting sign changes. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9244 Location: Greensville,Ontario
|
|
Posted: Wed Apr 13, 2016 5:17 am |
|
|
re read this post...
man ,it must take a donkey's age to do THREE complex floats ! Done in integer math, probably less than 1/4 of the time or . Be interesting to time them and post the results so others can see why 'we old guys' are always harping that floats in PICs are 'bad'.
Jay |
|
|
demedeiros
Joined: 27 Dec 2013 Posts: 71
|
|
Posted: Thu Apr 14, 2016 5:54 am |
|
|
All,
Thanks for the replies, i'll give these a try ASAP. Had to jump on another project for the time being. I'll be sure to post back with my results.
As a side note, Meas-Spec actually has some example code where they do these calcs as floats (albeit on an Atmel). Why they show ints in the datasheet and floats in the example code is beyond me!
Thanks again! |
|
|
demedeiros
Joined: 27 Dec 2013 Posts: 71
|
|
Posted: Tue Jun 07, 2016 12:23 pm |
|
|
Hi all,
Finally back on this project. I think i've figured out the reason for my wacky numbers.
Some of my 32bit signed integers are turning out to be larger than 32bits. The datasheet suggests the use of 64bit signed integers, but that isnt possible on this PIC.
I have no need for a 64bit pressure value, I dont need that kind of accuracy. Is there anyway that I can "drop" some of the precision to have my math end up being 32bit?
Heres the current code:
Code: |
signed int32 dT = D2-Amb_C[5]*twoto(8);
signed int32 Temp = 2000+(dT*((signed int32)Amb_C[6]/twoto(23)));
signed int32 off =(signed int32)(Amb_C[2]*twoto(16))+((dT*Amb_C[4])/twoto(7));
signed int32 SENS = (signed int32)(Amb_C[1]*twoto(15))+((dT*Amb_C[3])/twoto(8));
signed int32 Pressure = ((((signed int32)D1*SENS)/(twoto(21)-off))/twoto(15));
|
Thanks! |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9244 Location: Greensville,Ontario
|
|
Posted: Tue Jun 07, 2016 2:41 pm |
|
|
My 'gut' instinct is to just take the reading and 'dumb it down', from 24 bit to 16 bits. That would be fast and easy (right shifts).You will have to 'do some math', test some numbers to see if this is possible and gives the proper results.
Jay |
|
|
demedeiros
Joined: 27 Dec 2013 Posts: 71
|
|
Posted: Wed Jun 08, 2016 11:45 am |
|
|
Thanks,
When I try to right shift, I am given the following error:
"Only integers are supported for this operation"
The code is:
Code: |
signed int32 off =((((signed int32)(Amb_C[2]*twoto(16)))+((dT*Amb_C[4])/twoto(7)))) >> 16; |
I cant seem to figure out why this error comes up. Any ideas? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Jun 08, 2016 11:55 am |
|
|
Post a test program for this problem. Post the all the variable declarations
for the variables used in your equation. |
|
|
demedeiros
Joined: 27 Dec 2013 Posts: 71
|
|
Posted: Fri Jul 01, 2016 7:22 am |
|
|
I was finally able to get this to work. I did the following
Code: |
dT = dT/1000;
float32 OffL = 65.536*Amb_C[2]; //Left portion of OFF calculation, multiply by 1000 to get actual value
float32 OffR = ((Amb_C[4]/1000)*dT)/twoto(7); //Right portion of OFF, multiply by 1E6 to get actual value
float32 SENSL = 32.768*(float32)Amb_C[1]; //Left Portion of SENS calculation, multiply by 1000 to get actual value
float32 SENSR = (((float32)Amb_C[3]/1000)*(float32)dT)/256; //Right portion of SENS calculation, multiply by 1E6 to get actual value
float32 Pressure = (D1 *((SENSL*1000)+(SENSR*1000000))/twoto(21)-((OffL*1000)+(OffR*1000000)))/twoto(15);
Pressure = 14.5*(Pressure/10000); |
I split up the calculation of "OFF" and "SENS" into two portions, making sure that no step in their calculation would ever be greater than a float32 could handle. Then, in the pressure calculation, I multiple by the required amount to bring the values "back to normal". There is a tiny bit of precision lost somewhere, but this works for my application.
Thanks to everyone for the help! |
|
|
|