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).
Revision 0:184eb17ff158, committed 2012-12-28
- Comitter:
- Rom
- Date:
- Fri Dec 28 06:09:46 2012 +0000
- Commit message:
- Bring up program of the Bosch Barometer + temperature. Absolute values are wrong despite correct implementation.
Changed in this revision
main.cpp | Show annotated file Show diff for this revision Revisions of this file |
mbed.bld | Show annotated file Show diff for this revision Revisions of this file |
diff -r 000000000000 -r 184eb17ff158 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri Dec 28 06:09:46 2012 +0000 @@ -0,0 +1,363 @@ +#include "mbed.h" + +/* sample code for the barometer BMP085 as provided on the breakout board from sparkfun. +Pull up resistors for I2C are already provided (aka already on the board). +Link to sparkfun board: https://www.sparkfun.com/products/11282? + +Datasheet of barometer: http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Pressure/BST-BMP085-DS000-06.pdf + +Comments: + - pressure values returned by the moving average seems believable (1090 Hecto Pascal on 12/18/2012) + - Temperature seems to oscillate between 365 and 87 and seems totally wrong. + - Altitude is off (obviously fluctates with weather) but roughly speaking + indicates -800m when altitude should be 0m + +Example of output in the terminal: + +Temperature: 87 (raw value (uncomp): 61184) +Pressure (Pa): 106626 (raw: 249856) Mov Avg: 62353 +altitude is (m): -432.270721 +wikipedia altitude (m): -432.061523 + +Temperature: 365 (raw value (uncomp): 62464) +Pressure (Pa): 109312 (raw: 244736) Mov Avg: 67558 +altitude is (m): -644.688416 +wikipedia altitude (m): -644.382446 + +Temperature: 365 (raw value (uncomp): 62464) +Pressure (Pa): 111417 (raw: 249856) Mov Avg: 72864 +altitude is (m): -808.229309 +wikipedia altitude (m): -807.841614 + +*/ + +#define EE 22 //The EEPROM has 176bits of calibration data (176/8 = 22 Bytes) +#define BMP085ADDR 0xEF // I2C address of the barometer + +//Calibration variables +long b5; +short ac1; +short ac2; +short ac3; +unsigned short ac4; +unsigned short ac5; +unsigned short ac6; +short b1; +short b2; +short mb; +short mc; +short md; + +// Function to compute the human interpretable values +int calcPress(int upp, int oversampling_setting); +int calcTemp(int ut); +float calcAltitude(int pressure); +float calcWikipediaAltitude(int pressure); + +//Moving average variables +#define COEFZ 21 // 21 bits used for computing the moving average +static int k[COEFZ]; // an array used for the moving average +static int movAvgIntZ (int input); + +//address of commands +#define CMD_READ_VALUE 0xF6 +#define CMD_READ_CALIBRATION 0xAA + +#define OVERSAMPLING_ULTRA_LOW_POWER 0 +#define OVERSAMPLING_STANDARD 1 +#define OVERSAMPLING_HIGH_RESOLUTION 2 +#define OVERSAMPLING_ULTRA_HIGH_RESOLUTION 3 + +const float ALTITUDE_EXPONENT = 1/5.255; +const float ALTITUDE_PRESSURE_REFERENCE = 101325.0; // In Pa +const float ALTITUDE_COEFF = 44330.0; + +const float TEMPERATURE_LAPSE_RATE = 0.0065; // in K/m +const float SEA_LEVEL_STANDARD_TEMPERATURE = 288.15; // in Kelvins +const float GRAVITATIONAL_ACCELERATION = 9.81; // in m/(s*s) +const float MOLAR_MASS_AIR = 0.0289644; // in Kg/mol +const float R = 8.31447; // in J/(mol*K) universal gas constant + +const float WIKIPEDIA_EXPONENT = GRAVITATIONAL_ACCELERATION * MOLAR_MASS_AIR/ (R * TEMPERATURE_LAPSE_RATE); + + +I2C i2c(p28, p27); // sda, scl +InterruptIn dr(p26); // EOC, conversion notification + // Note that XCLR is floating = not connected (power for analog portion?) + +uint32_t drFlag; + +void cvt(); // Send I2C command to start pressure conversion (?) +void drSub(); // set a flag to one. Pretty poorly written code IMO. + +Serial pc(USBTX, USBRX); // tx, rx + +int main() { + + // let hardware settle + wait(1); + + ///////////////////////////////////////////////// + // set up timer to trigger pressure conversion + ///////////////////////////////////////////////// + Ticker convert; + convert.attach_us(&cvt, 50000); // 50 ms, 20 Hz //Pointer to a void function + + ///////////////////////////////////////////////// + // set up data ready interrupts + ///////////////////////////////////////////////// + // set up interrupts + __disable_irq(); + // ADC data ready + drFlag = 0; + dr.mode(PullDown); + dr.rise(&drSub); + + ///////////////////////////////////////////////// + // set up i2c + ///////////////////////////////////////////////// + char addr = BMP085ADDR; // define the I2C Address + int oversampling_setting = OVERSAMPLING_HIGH_RESOLUTION; // called oss in the documentation + // read page 12 of doc. 3 is ultra high precision and it requires a lot of time for the device to do the conversion + char rReg[3] = {0,0,0}; // read registers for I2C + char wReg[2] = {0,0}; // write registers for I2C + char cmd = 0x00; + + ///////////////////////////////////////////////// + // get EEPROM calibration parameters + ///////////////////////////////////////////////// + + char data[EE]; + cmd = CMD_READ_CALIBRATION; // EEPROM calibration command + + for (int i = 0; i < EE; i++) { // read over the 22 registers of the EEPROM + i2c.write(addr, &cmd, 1); + i2c.read(addr, rReg, 1); // rReg is array? of size 3. We only read the 1st register??? + data[i] = rReg[0]; + cmd += 1; + wait_ms(10); + } + + // parameters AC1-AC6 + //The calibration is partioned in 11 words of 16 bits, each of them representing a coefficient + ac1 = (data[0] <<8) | data[1]; // AC1(0xAA, 0xAB)... and so on + ac2 = (data[2] <<8) | data[3]; + ac3 = (data[4] <<8) | data[5]; + ac4 = (data[6] <<8) | data[7]; + ac5 = (data[8] <<8) | data[9]; + ac6 = (data[10] <<8) | data[11]; + // parameters B1,B2 + b1 = (data[12] <<8) | data[13]; + b2 = (data[14] <<8) | data[15]; + // parameters MB,MC,MD + mb = (data[16] <<8) | data[17]; + mc = (data[18] <<8) | data[19]; + md = (data[20] <<8) | data[21]; + + // ready to start sampling loop + __enable_irq(); + + // END OF INITIALIZATION + + ///////////////////////////////////////////////// + // main + ///////////////////////////////////////////////// + pc.printf("Starting barometer main()!!\n\r"); + while (1) { + if (drFlag == 1) { + // Documentation recommend the following chronology: + // read uncomp temp, read uncomp pressure, compute true temp, + // compute true pressure + + ///////////////////////////////////////////////// + // uncomp temperature + ///////////////////////////////////////////////// + wReg[0] = 0xF4; + wReg[1] = 0x2E; + i2c.write(addr, wReg, 2); // write 0x2E in reg 0XF4 + wait_ms(4.5); + cmd = CMD_READ_VALUE; // 0xF6 + i2c.write(addr, &cmd, 1); // set pointer on 0xF6 before reading it? + i2c.read(addr, rReg, 2); // read 0xF6 (MSB) and 0xF7 (LSB)// rReg is 3 long though + int uncomp_temperature = (rReg[0] << 8) | rReg[1]; // UT = MSB << 8 + LSB + + ///////////////////////////////////////////////// + // uncomp pressure + ///////////////////////////////////////////////// + + //write 0x34 + (oss<<6) into reg 0xF4, wait (waiting time not specified in doc) + int uncomp_pressure_cmd = 0x34 + (oversampling_setting<<6); + wReg[0] = 0xF4; + wReg[1] = uncomp_pressure_cmd; + i2c.write(addr, wReg, 2); + // See page 12 of documentation + switch (oversampling_setting) { + case OVERSAMPLING_ULTRA_LOW_POWER: + wait_ms(4.5); + break; + case OVERSAMPLING_STANDARD: + wait_ms(7.5); + break; + case OVERSAMPLING_HIGH_RESOLUTION: + wait_ms(13.5); + break; + case OVERSAMPLING_ULTRA_HIGH_RESOLUTION: + wait_ms(25.5); + break; + } + + cmd = CMD_READ_VALUE; // 0xF6 + i2c.write(addr, &cmd, 1); + i2c.read(addr, rReg, 3); // read 0xF6 (MSB), 0xF7 (LSB), 0xF8 (XLSB) + int uncomp_pressure = ((rReg[0] << 16) | (rReg[1] << 8) | rReg[2]) >> (8 - oversampling_setting); + // UP = (MSB<<16 + LSB<<8 + XLSB)>>(8-oss) + + // true temperature + int temperature = calcTemp(uncomp_temperature); + pc.printf("Temperature: %d (raw value (uncomp): %d)\n\r", temperature, uncomp_temperature); + // even though implementation seems right, + //returned value is wrong hostilating between 87 and 365 (i.e. 8.7C and 36.5C) + + // True pressure + int pressure = calcPress(uncomp_pressure, oversampling_setting); + int avg_pressure = movAvgIntZ(pressure); + pc.printf("Pressure (Pa): %d (raw: %d) Mov Avg: %d\n\r", pressure, uncomp_pressure, avg_pressure); + + //Compute an estimate of altitude + // see page 15 of manual + float altitude = calcAltitude(pressure); + pc.printf("altitude is (m): %f\n\r", altitude); // note that the implementation is correct but returns wrong absolute numbers + + float wiki_altitude = calcWikipediaAltitude(pressure); + pc.printf("wikipedia altitude (m): %f\n\r", wiki_altitude); // results are VERY similar and therefore not + + ///////////////////////////////////////////////// + // data ready cleanup tasks + ///////////////////////////////////////////////// + drFlag = 0; // This should stop the while loop... but it does not! + pc.printf("\n\r"); + } + wait(3); // just to make reading in the terminal easier + } //end while +} + +//////////////////////////////////////////////////////////////////////////////////// +// start pressure conversion +//////////////////////////////////////////////////////////////////////////////////// +void cvt() { //This is only called once during the initialization phase! + char w[2] = {0xF4, 0xF4}; + i2c.write(BMP085ADDR, w, 2); +} + +//////////////////////////////////////////////////////////////////////////////////// +// Handle data ready interrupt, just sets data ready flag +//////////////////////////////////////////////////////////////////////////////////// +void drSub() { + drFlag = 1; +} + +///////////////////////////////////////////////// +// calculate compensated pressure +///////////////////////////////////////////////// +int calcPress(int upp, int oversampling_setting) { + // Return the pressure in Pa (Pascals) + + long pressure, x1, x2, x3, b3, b6; + unsigned long b4, b7; + // read page 12 of doc. This is ultra high precision and it requires a lot of time for the device to do the conversion + + unsigned long up = (unsigned long)upp; + + b6 = b5 - 4000; + // calculate B3 + x1 = (b6*b6) >> 12; // full formula(b2*(b6*b6)/pow(2,12))/pow(2,11) + x1 *= b2; + x1 >>= 11; + + x2 = (ac2*b6); + x2 >>= 11; + + x3 = x1 + x2; + + b3 = (((((long)ac1 )*4 + x3) <<oversampling_setting) + 2) >> 2; // doc says /4! + + // calculate B4 + x1 = (ac3* b6) >> 13; + x2 = (b1 * ((b6*b6) >> 12) ) >> 16; + x3 = ((x1 + x2) + 2) >> 2; + b4 = (ac4 * (unsigned long) (x3 + 32768)) >> 15; + + b7 = ((unsigned long) up - b3) * (50000>>oversampling_setting); + if (b7 < 0x80000000) { + pressure = (b7 << 1) / b4; + } else { + pressure = (b7 / b4) << 1; + } + + x1 = pressure >> 8; + x1 *= x1; // pressure/pow(2,8) * pressure/pow(2, 8) + x1 = (x1 * 3038) >> 16; + x2 = (pressure * -7357) >> 16; + pressure += (x1 + x2 + 3791) >> 4; // pressure in Pa + + return (pressure); +} + +///////////////////////////////////////////////// +// calculate compensated temp from uncompensated +///////////////////////////////////////////////// +int calcTemp(int ut) { + // Returns the temperature in °C + + int temp; + long x1, x2; + + x1 = (((long) ut - (long) ac6) * (long) ac5) >> 15; // aka (ut-ac6) * ac5/pow(2,15) + x2 = ((long) mc << 11) / (x1 + md); // aka mc * pow(2, 11) / (x1 + md) + b5 = x1 + x2; + temp = ((b5 + 8) >> 4); // (b5+8)/pow(2, 4) + // temperature in 0.1°C (aka 1903 = 190.3°C) + return (temp); +} + +//////////////////////////////////////////////////////////////////////////////////// +// int version of moving average filter +//////////////////////////////////////////////////////////////////////////////////// + +static int movAvgIntZ (int input) { + int cum = 0; + for (int i = 0; i < COEFZ; i++) { + k[i] = k[i+1]; // do we really need k as a global variable? + } + k[COEFZ - 1] = input; + for (int i = 0; i < COEFZ; i++) { + cum += k[i]; + } + return (cum/COEFZ) ; +} + +float calcAltitude(int pressure) { + //Compute an estimate of altitude + // see page 15 of manual + float float_comp_pressure = (float) pressure; + float altitude = float_comp_pressure/ALTITUDE_PRESSURE_REFERENCE; + altitude = pow(altitude, ALTITUDE_EXPONENT); + altitude = 1.0 - altitude; + altitude *= ALTITUDE_COEFF; + // formula is: altitude = 44330 * (1-pow((p/p0), 1/5.255)) + return altitude; +} + +float calcWikipediaAltitude(int pressure) { + // compute the altitude in meters + // per: http://en.wikipedia.org/wiki/Atmospheric_pressure + float float_comp_pressure = (float) pressure; + + float altitude = float_comp_pressure/ALTITUDE_PRESSURE_REFERENCE; + float exponent = 1.0/WIKIPEDIA_EXPONENT; + altitude = pow(altitude, exponent); + altitude = 1.0 - altitude; + altitude *= (SEA_LEVEL_STANDARD_TEMPERATURE/TEMPERATURE_LAPSE_RATE); + + return altitude; +} \ No newline at end of file
diff -r 000000000000 -r 184eb17ff158 mbed.bld --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Fri Dec 28 06:09:46 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/63cdd78b2dc1 \ No newline at end of file