bring up code for the Bosch barometer and thermometer BMP085. This code is forked from http://mbed.org/users/tkreyche/notebook/bmp085-pressure-sensor/ but with a lot more comments and links to references. The extraction of the altitude via 2 methods is also implemented. Overall temperature seems way off (but implementation is correct). Altitude is off too (but implementation is correct).
main.cpp
00001 #include "mbed.h" 00002 00003 /* sample code for the barometer BMP085 as provided on the breakout board from sparkfun. 00004 Pull up resistors for I2C are already provided (aka already on the board). 00005 Link to sparkfun board: https://www.sparkfun.com/products/11282? 00006 00007 Datasheet of barometer: http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Pressure/BST-BMP085-DS000-06.pdf 00008 00009 Comments: 00010 - pressure values returned by the moving average seems believable (1090 Hecto Pascal on 12/18/2012) 00011 - Temperature seems to oscillate between 365 and 87 and seems totally wrong. 00012 - Altitude is off (obviously fluctates with weather) but roughly speaking 00013 indicates -800m when altitude should be 0m 00014 00015 Example of output in the terminal: 00016 00017 Temperature: 87 (raw value (uncomp): 61184) 00018 Pressure (Pa): 106626 (raw: 249856) Mov Avg: 62353 00019 altitude is (m): -432.270721 00020 wikipedia altitude (m): -432.061523 00021 00022 Temperature: 365 (raw value (uncomp): 62464) 00023 Pressure (Pa): 109312 (raw: 244736) Mov Avg: 67558 00024 altitude is (m): -644.688416 00025 wikipedia altitude (m): -644.382446 00026 00027 Temperature: 365 (raw value (uncomp): 62464) 00028 Pressure (Pa): 111417 (raw: 249856) Mov Avg: 72864 00029 altitude is (m): -808.229309 00030 wikipedia altitude (m): -807.841614 00031 00032 */ 00033 00034 #define EE 22 //The EEPROM has 176bits of calibration data (176/8 = 22 Bytes) 00035 #define BMP085ADDR 0xEF // I2C address of the barometer 00036 00037 //Calibration variables 00038 long b5; 00039 short ac1; 00040 short ac2; 00041 short ac3; 00042 unsigned short ac4; 00043 unsigned short ac5; 00044 unsigned short ac6; 00045 short b1; 00046 short b2; 00047 short mb; 00048 short mc; 00049 short md; 00050 00051 // Function to compute the human interpretable values 00052 int calcPress(int upp, int oversampling_setting); 00053 int calcTemp(int ut); 00054 float calcAltitude(int pressure); 00055 float calcWikipediaAltitude(int pressure); 00056 00057 //Moving average variables 00058 #define COEFZ 21 // 21 bits used for computing the moving average 00059 static int k[COEFZ]; // an array used for the moving average 00060 static int movAvgIntZ (int input); 00061 00062 //address of commands 00063 #define CMD_READ_VALUE 0xF6 00064 #define CMD_READ_CALIBRATION 0xAA 00065 00066 #define OVERSAMPLING_ULTRA_LOW_POWER 0 00067 #define OVERSAMPLING_STANDARD 1 00068 #define OVERSAMPLING_HIGH_RESOLUTION 2 00069 #define OVERSAMPLING_ULTRA_HIGH_RESOLUTION 3 00070 00071 const float ALTITUDE_EXPONENT = 1/5.255; 00072 const float ALTITUDE_PRESSURE_REFERENCE = 101325.0; // In Pa 00073 const float ALTITUDE_COEFF = 44330.0; 00074 00075 const float TEMPERATURE_LAPSE_RATE = 0.0065; // in K/m 00076 const float SEA_LEVEL_STANDARD_TEMPERATURE = 288.15; // in Kelvins 00077 const float GRAVITATIONAL_ACCELERATION = 9.81; // in m/(s*s) 00078 const float MOLAR_MASS_AIR = 0.0289644; // in Kg/mol 00079 const float R = 8.31447; // in J/(mol*K) universal gas constant 00080 00081 const float WIKIPEDIA_EXPONENT = GRAVITATIONAL_ACCELERATION * MOLAR_MASS_AIR/ (R * TEMPERATURE_LAPSE_RATE); 00082 00083 00084 I2C i2c(p28, p27); // sda, scl 00085 InterruptIn dr(p26); // EOC, conversion notification 00086 // Note that XCLR is floating = not connected (power for analog portion?) 00087 00088 uint32_t drFlag; 00089 00090 void cvt(); // Send I2C command to start pressure conversion (?) 00091 void drSub(); // set a flag to one. Pretty poorly written code IMO. 00092 00093 Serial pc(USBTX, USBRX); // tx, rx 00094 00095 int main() { 00096 00097 // let hardware settle 00098 wait(1); 00099 00100 ///////////////////////////////////////////////// 00101 // set up timer to trigger pressure conversion 00102 ///////////////////////////////////////////////// 00103 Ticker convert; 00104 convert.attach_us(&cvt, 50000); // 50 ms, 20 Hz //Pointer to a void function 00105 00106 ///////////////////////////////////////////////// 00107 // set up data ready interrupts 00108 ///////////////////////////////////////////////// 00109 // set up interrupts 00110 __disable_irq(); 00111 // ADC data ready 00112 drFlag = 0; 00113 dr.mode(PullDown); 00114 dr.rise(&drSub); 00115 00116 ///////////////////////////////////////////////// 00117 // set up i2c 00118 ///////////////////////////////////////////////// 00119 char addr = BMP085ADDR; // define the I2C Address 00120 int oversampling_setting = OVERSAMPLING_HIGH_RESOLUTION; // called oss in the documentation 00121 // read page 12 of doc. 3 is ultra high precision and it requires a lot of time for the device to do the conversion 00122 char rReg[3] = {0,0,0}; // read registers for I2C 00123 char wReg[2] = {0,0}; // write registers for I2C 00124 char cmd = 0x00; 00125 00126 ///////////////////////////////////////////////// 00127 // get EEPROM calibration parameters 00128 ///////////////////////////////////////////////// 00129 00130 char data[EE]; 00131 cmd = CMD_READ_CALIBRATION; // EEPROM calibration command 00132 00133 for (int i = 0; i < EE; i++) { // read over the 22 registers of the EEPROM 00134 i2c.write(addr, &cmd, 1); 00135 i2c.read(addr, rReg, 1); // rReg is array? of size 3. We only read the 1st register??? 00136 data[i] = rReg[0]; 00137 cmd += 1; 00138 wait_ms(10); 00139 } 00140 00141 // parameters AC1-AC6 00142 //The calibration is partioned in 11 words of 16 bits, each of them representing a coefficient 00143 ac1 = (data[0] <<8) | data[1]; // AC1(0xAA, 0xAB)... and so on 00144 ac2 = (data[2] <<8) | data[3]; 00145 ac3 = (data[4] <<8) | data[5]; 00146 ac4 = (data[6] <<8) | data[7]; 00147 ac5 = (data[8] <<8) | data[9]; 00148 ac6 = (data[10] <<8) | data[11]; 00149 // parameters B1,B2 00150 b1 = (data[12] <<8) | data[13]; 00151 b2 = (data[14] <<8) | data[15]; 00152 // parameters MB,MC,MD 00153 mb = (data[16] <<8) | data[17]; 00154 mc = (data[18] <<8) | data[19]; 00155 md = (data[20] <<8) | data[21]; 00156 00157 // ready to start sampling loop 00158 __enable_irq(); 00159 00160 // END OF INITIALIZATION 00161 00162 ///////////////////////////////////////////////// 00163 // main 00164 ///////////////////////////////////////////////// 00165 pc.printf("Starting barometer main()!!\n\r"); 00166 while (1) { 00167 if (drFlag == 1) { 00168 // Documentation recommend the following chronology: 00169 // read uncomp temp, read uncomp pressure, compute true temp, 00170 // compute true pressure 00171 00172 ///////////////////////////////////////////////// 00173 // uncomp temperature 00174 ///////////////////////////////////////////////// 00175 wReg[0] = 0xF4; 00176 wReg[1] = 0x2E; 00177 i2c.write(addr, wReg, 2); // write 0x2E in reg 0XF4 00178 wait_ms(4.5); 00179 cmd = CMD_READ_VALUE; // 0xF6 00180 i2c.write(addr, &cmd, 1); // set pointer on 0xF6 before reading it? 00181 i2c.read(addr, rReg, 2); // read 0xF6 (MSB) and 0xF7 (LSB)// rReg is 3 long though 00182 int uncomp_temperature = (rReg[0] << 8) | rReg[1]; // UT = MSB << 8 + LSB 00183 00184 ///////////////////////////////////////////////// 00185 // uncomp pressure 00186 ///////////////////////////////////////////////// 00187 00188 //write 0x34 + (oss<<6) into reg 0xF4, wait (waiting time not specified in doc) 00189 int uncomp_pressure_cmd = 0x34 + (oversampling_setting<<6); 00190 wReg[0] = 0xF4; 00191 wReg[1] = uncomp_pressure_cmd; 00192 i2c.write(addr, wReg, 2); 00193 // See page 12 of documentation 00194 switch (oversampling_setting) { 00195 case OVERSAMPLING_ULTRA_LOW_POWER: 00196 wait_ms(4.5); 00197 break; 00198 case OVERSAMPLING_STANDARD: 00199 wait_ms(7.5); 00200 break; 00201 case OVERSAMPLING_HIGH_RESOLUTION: 00202 wait_ms(13.5); 00203 break; 00204 case OVERSAMPLING_ULTRA_HIGH_RESOLUTION: 00205 wait_ms(25.5); 00206 break; 00207 } 00208 00209 cmd = CMD_READ_VALUE; // 0xF6 00210 i2c.write(addr, &cmd, 1); 00211 i2c.read(addr, rReg, 3); // read 0xF6 (MSB), 0xF7 (LSB), 0xF8 (XLSB) 00212 int uncomp_pressure = ((rReg[0] << 16) | (rReg[1] << 8) | rReg[2]) >> (8 - oversampling_setting); 00213 // UP = (MSB<<16 + LSB<<8 + XLSB)>>(8-oss) 00214 00215 // true temperature 00216 int temperature = calcTemp(uncomp_temperature); 00217 pc.printf("Temperature: %d (raw value (uncomp): %d)\n\r", temperature, uncomp_temperature); 00218 // even though implementation seems right, 00219 //returned value is wrong hostilating between 87 and 365 (i.e. 8.7C and 36.5C) 00220 00221 // True pressure 00222 int pressure = calcPress(uncomp_pressure, oversampling_setting); 00223 int avg_pressure = movAvgIntZ(pressure); 00224 pc.printf("Pressure (Pa): %d (raw: %d) Mov Avg: %d\n\r", pressure, uncomp_pressure, avg_pressure); 00225 00226 //Compute an estimate of altitude 00227 // see page 15 of manual 00228 float altitude = calcAltitude(pressure); 00229 pc.printf("altitude is (m): %f\n\r", altitude); // note that the implementation is correct but returns wrong absolute numbers 00230 00231 float wiki_altitude = calcWikipediaAltitude(pressure); 00232 pc.printf("wikipedia altitude (m): %f\n\r", wiki_altitude); // results are VERY similar and therefore not 00233 00234 ///////////////////////////////////////////////// 00235 // data ready cleanup tasks 00236 ///////////////////////////////////////////////// 00237 drFlag = 0; // This should stop the while loop... but it does not! 00238 pc.printf("\n\r"); 00239 } 00240 wait(3); // just to make reading in the terminal easier 00241 } //end while 00242 } 00243 00244 //////////////////////////////////////////////////////////////////////////////////// 00245 // start pressure conversion 00246 //////////////////////////////////////////////////////////////////////////////////// 00247 void cvt() { //This is only called once during the initialization phase! 00248 char w[2] = {0xF4, 0xF4}; 00249 i2c.write(BMP085ADDR, w, 2); 00250 } 00251 00252 //////////////////////////////////////////////////////////////////////////////////// 00253 // Handle data ready interrupt, just sets data ready flag 00254 //////////////////////////////////////////////////////////////////////////////////// 00255 void drSub() { 00256 drFlag = 1; 00257 } 00258 00259 ///////////////////////////////////////////////// 00260 // calculate compensated pressure 00261 ///////////////////////////////////////////////// 00262 int calcPress(int upp, int oversampling_setting) { 00263 // Return the pressure in Pa (Pascals) 00264 00265 long pressure, x1, x2, x3, b3, b6; 00266 unsigned long b4, b7; 00267 // read page 12 of doc. This is ultra high precision and it requires a lot of time for the device to do the conversion 00268 00269 unsigned long up = (unsigned long)upp; 00270 00271 b6 = b5 - 4000; 00272 // calculate B3 00273 x1 = (b6*b6) >> 12; // full formula(b2*(b6*b6)/pow(2,12))/pow(2,11) 00274 x1 *= b2; 00275 x1 >>= 11; 00276 00277 x2 = (ac2*b6); 00278 x2 >>= 11; 00279 00280 x3 = x1 + x2; 00281 00282 b3 = (((((long)ac1 )*4 + x3) <<oversampling_setting) + 2) >> 2; // doc says /4! 00283 00284 // calculate B4 00285 x1 = (ac3* b6) >> 13; 00286 x2 = (b1 * ((b6*b6) >> 12) ) >> 16; 00287 x3 = ((x1 + x2) + 2) >> 2; 00288 b4 = (ac4 * (unsigned long) (x3 + 32768)) >> 15; 00289 00290 b7 = ((unsigned long) up - b3) * (50000>>oversampling_setting); 00291 if (b7 < 0x80000000) { 00292 pressure = (b7 << 1) / b4; 00293 } else { 00294 pressure = (b7 / b4) << 1; 00295 } 00296 00297 x1 = pressure >> 8; 00298 x1 *= x1; // pressure/pow(2,8) * pressure/pow(2, 8) 00299 x1 = (x1 * 3038) >> 16; 00300 x2 = (pressure * -7357) >> 16; 00301 pressure += (x1 + x2 + 3791) >> 4; // pressure in Pa 00302 00303 return (pressure); 00304 } 00305 00306 ///////////////////////////////////////////////// 00307 // calculate compensated temp from uncompensated 00308 ///////////////////////////////////////////////// 00309 int calcTemp(int ut) { 00310 // Returns the temperature in °C 00311 00312 int temp; 00313 long x1, x2; 00314 00315 x1 = (((long) ut - (long) ac6) * (long) ac5) >> 15; // aka (ut-ac6) * ac5/pow(2,15) 00316 x2 = ((long) mc << 11) / (x1 + md); // aka mc * pow(2, 11) / (x1 + md) 00317 b5 = x1 + x2; 00318 temp = ((b5 + 8) >> 4); // (b5+8)/pow(2, 4) 00319 // temperature in 0.1°C (aka 1903 = 190.3°C) 00320 return (temp); 00321 } 00322 00323 //////////////////////////////////////////////////////////////////////////////////// 00324 // int version of moving average filter 00325 //////////////////////////////////////////////////////////////////////////////////// 00326 00327 static int movAvgIntZ (int input) { 00328 int cum = 0; 00329 for (int i = 0; i < COEFZ; i++) { 00330 k[i] = k[i+1]; // do we really need k as a global variable? 00331 } 00332 k[COEFZ - 1] = input; 00333 for (int i = 0; i < COEFZ; i++) { 00334 cum += k[i]; 00335 } 00336 return (cum/COEFZ) ; 00337 } 00338 00339 float calcAltitude(int pressure) { 00340 //Compute an estimate of altitude 00341 // see page 15 of manual 00342 float float_comp_pressure = (float) pressure; 00343 float altitude = float_comp_pressure/ALTITUDE_PRESSURE_REFERENCE; 00344 altitude = pow(altitude, ALTITUDE_EXPONENT); 00345 altitude = 1.0 - altitude; 00346 altitude *= ALTITUDE_COEFF; 00347 // formula is: altitude = 44330 * (1-pow((p/p0), 1/5.255)) 00348 return altitude; 00349 } 00350 00351 float calcWikipediaAltitude(int pressure) { 00352 // compute the altitude in meters 00353 // per: http://en.wikipedia.org/wiki/Atmospheric_pressure 00354 float float_comp_pressure = (float) pressure; 00355 00356 float altitude = float_comp_pressure/ALTITUDE_PRESSURE_REFERENCE; 00357 float exponent = 1.0/WIKIPEDIA_EXPONENT; 00358 altitude = pow(altitude, exponent); 00359 altitude = 1.0 - altitude; 00360 altitude *= (SEA_LEVEL_STANDARD_TEMPERATURE/TEMPERATURE_LAPSE_RATE); 00361 00362 return altitude; 00363 }
Generated on Tue Jul 12 2022 19:31:50 by 1.7.2