Bremen Team - Hangar / SML2

Dependencies:   CalibrateMagneto QuaternionMath

Fork of SML2 by TobyRich GmbH

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Barometer.cpp Source File

Barometer.cpp

00001 #include "Barometer.h"
00002 #define DEBUG "BMP280"
00003 #include "Logger.h"
00004 #include <cmath>
00005 
00006 Barometer::Barometer(I2C &i2c) : I2CPeripheral(i2c, 0xEC /* address */)
00007 {
00008     if (powerOn()) {
00009         INFO("Bosch Sensortec BMP280 atmospheric pressure sensor found");
00010         bmp280_read_calibration();
00011         powerOff();
00012     } else {
00013         WARN("Bosch Sensortec BMP280 atmospheric pressure sensor not found");
00014     }
00015 }
00016 
00017 bool Barometer::powerOn()
00018 {
00019     write_reg(0xE0, 0xB6); // reset
00020     wait_ms(2); // cf. datasheet page 8, t_startup
00021     return read_reg(0xD0) == 0x58; // verify chip ID
00022 }
00023 
00024 void Barometer::powerOff()
00025 {
00026     // nothing to do
00027 }
00028 
00029 void Barometer::start()
00030 {
00031     // reset our initial calibration
00032     nsamples = 0;
00033     sum = 0;
00034     avg = 0;
00035 
00036     // set parameters for Bosch-recommended "Indoor navigation" preset
00037     write_reg(0xF5, 0x10); // 0.5ms t_standby, IIR coefficient=16
00038     write_reg(0xF4, 0x57); // 2x oversampling for temperature, 16x for pressure and power mode "normal"
00039 }
00040 
00041 void Barometer::stop()
00042 {
00043     write_reg(0xF4, 0x54); // keep the oversampling settings but set power mode to "sleep"
00044 }
00045 
00046 Vector3 Barometer::read()
00047 {
00048     uint8_t buffer[6];
00049     /*
00050     for (int i = 0; i < 6; i++)
00051         buffer[i] =  read_reg(0xF7 + i);
00052     */
00053 
00054     read_reg(0xF7, buffer, sizeof buffer);
00055 
00056     const uint32_t adc_P = ((buffer[0] << 16) | (buffer[1] << 8) | buffer[2]) >> 4;
00057     const uint32_t adc_T = ((buffer[3] << 16) | (buffer[4] << 8) | buffer[5]) >> 4;
00058 
00059     const float celsius = bmp280_val_to_temp(adc_T) - 20; // 20 degree offset (?)
00060     const float pa = bmp280_val_to_pa(adc_P);
00061     const float centimeter = pressureToAltitude(pa) * 100.0;
00062 
00063     if (++nsamples < 10) {
00064         sum += centimeter;
00065         avg = sum / nsamples;
00066     }
00067 
00068     return Vector3(celsius, pa, centimeter - avg);
00069 }
00070 
00071 float Barometer::pressureToAltitude(const float pa) const
00072 {
00073     return -44330.7692 * (pow(pa * 0.0000098692, 0.1902632365) - 1);
00074 }
00075 
00076 void Barometer::bmp280_read_calibration()
00077 {
00078     struct {
00079         uint16_t dig_T1;
00080         int16_t  dig_T2;
00081         int16_t  dig_T3;
00082         uint16_t dig_P1;
00083         int16_t  dig_P2;
00084         int16_t  dig_P3;
00085         int16_t  dig_P4;
00086         int16_t  dig_P5;
00087         int16_t  dig_P6;
00088         int16_t  dig_P7;
00089         int16_t  dig_P8;
00090         int16_t  dig_P9;
00091     } cal_data;
00092 
00093     read_reg(0x88, (uint8_t*)&cal_data, sizeof cal_data);
00094 
00095     dig_T1 = cal_data.dig_T1;
00096     dig_T2 = cal_data.dig_T2;
00097     dig_T3 = cal_data.dig_T3;
00098     dig_P1 = cal_data.dig_P1;
00099     dig_P2 = cal_data.dig_P2;
00100     dig_P3 = cal_data.dig_P3;
00101     dig_P4 = cal_data.dig_P4;
00102     dig_P5 = cal_data.dig_P5;
00103     dig_P6 = cal_data.dig_P6;
00104     dig_P7 = cal_data.dig_P7;
00105     dig_P8 = cal_data.dig_P8;
00106     dig_P9 = cal_data.dig_P9;
00107 
00108     LOG("Calibration parameters: T=[%u, %d, %d] P=[%u, %d, %d, %d, %d, %d, %d, %d, %d]",
00109         dig_T1, dig_T2, dig_T3,
00110         dig_P1, dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9);
00111 }
00112 
00113 // Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.
00114 // XXX: converted to return float result directly
00115 float Barometer::bmp280_val_to_temp(BMP280_S32_t adc_T)
00116 {
00117     BMP280_S32_t var1, var2, T;
00118     var1 = ((((adc_T>>3) - ((BMP280_S32_t)dig_T1<<1))) * ((BMP280_S32_t)dig_T2)) >> 11;
00119     var2 = (((((adc_T>>4) - ((BMP280_S32_t)dig_T1)) * ((adc_T>>4) - ((BMP280_S32_t)dig_T1))) >> 12) *
00120             ((BMP280_S32_t)dig_T3)) >> 14;
00121     t_fine = var1 + var2;
00122     T =(t_fine*5+128)>>8;
00123     return T / 100.0f;
00124 }
00125 
00126 // Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
00127 // Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa
00128 // XXX: converted it to return a float directly.
00129 // XXX: uses t_fine, so call temperature conversion BEFORE calling this.
00130 float Barometer::bmp280_val_to_pa(BMP280_S32_t adc_P)
00131 {
00132     BMP280_S64_t var1, var2, p;
00133     var1 = ((BMP280_S64_t)t_fine) - 128000;
00134     var2 = var1 * var1 * (BMP280_S64_t)dig_P6;
00135     var2 = var2 + ((var1*(BMP280_S64_t)dig_P5)<<17);
00136     var2 = var2 + (((BMP280_S64_t)dig_P4)<<35);
00137     var1 = ((var1 * var1 * (BMP280_S64_t)dig_P3)>>8) + ((var1 * (BMP280_S64_t)dig_P2)<<12);
00138     var1 = (((((BMP280_S64_t)1)<<47)+var1))*((BMP280_S64_t)dig_P1)>>33;
00139     if (var1 == 0) {
00140         return 0; // avoid exception caused by division by zero
00141     }
00142     p = 1048576-adc_P;
00143     p = (((p<<31)-var2)*3125)/var1;
00144     var1 = (((BMP280_S64_t)dig_P9) * (p>>13) * (p>>13)) >> 25;
00145     var2 = (((BMP280_S64_t)dig_P8) * p) >> 19;
00146     p = ((p + var1 + var2) >> 8) + (((BMP280_S64_t)dig_P7)<<4);
00147     return ((BMP280_U32_t)p) / 256.0f;
00148 }