Merge pull request #11297 from SteveCEvans/baro_state
[betaflight.git] / src / main / drivers / barometer / barometer_bmp085.c
blobe51faf8e0461dd3ca60e6cd950ce625fe38f653d
1 /*
2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
8 * any later version.
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
21 #include <stdbool.h>
22 #include <stdint.h>
24 #include "platform.h"
26 #include "build/build_config.h"
28 #include "barometer.h"
30 #include "drivers/bus.h"
31 #include "drivers/bus_i2c.h"
32 #include "drivers/bus_i2c_busdev.h"
33 #include "drivers/exti.h"
34 #include "drivers/io.h"
35 #include "drivers/nvic.h"
36 #include "drivers/time.h"
38 #include "barometer_bmp085.h"
40 #ifdef USE_BARO
42 static bool isConversionComplete = false;
43 static bool isEOCConnected = false;
44 #define BMP085_DATA_TEMP_SIZE 2
45 #define BMP085_DATA_PRES_SIZE 3
46 #define BMP085_DATA_FRAME_SIZE 3
47 static uint8_t sensor_data[BMP085_DATA_FRAME_SIZE];
49 #ifdef USE_EXTI
50 static IO_t eocIO;
51 static extiCallbackRec_t exti;
53 static void bmp085ExtiHandler(extiCallbackRec_t* cb)
55 UNUSED(cb);
56 isConversionComplete = true;
58 #endif
60 typedef struct {
61 int16_t ac1;
62 int16_t ac2;
63 int16_t ac3;
64 uint16_t ac4;
65 uint16_t ac5;
66 uint16_t ac6;
67 int16_t b1;
68 int16_t b2;
69 int16_t mb;
70 int16_t mc;
71 int16_t md;
72 } bmp085_smd500_calibration_param_t;
74 typedef struct {
75 bmp085_smd500_calibration_param_t cal_param;
76 uint8_t mode;
77 uint8_t chip_id, ml_version, al_version;
78 uint8_t dev_addr;
79 int32_t param_b5;
80 int16_t oversampling_setting;
81 } bmp085_t;
83 #define BMP085_I2C_ADDR 0x77
84 #define BMP085_CHIP_ID 0x55
85 #define BOSCH_PRESSURE_BMP085 85
86 #define BMP085_CHIP_ID_REG 0xD0
87 #define BMP085_VERSION_REG 0xD1
88 #define E_SENSOR_NOT_DETECTED (char) 0
89 #define BMP085_PROM_START__ADDR 0xaa
90 #define BMP085_PROM_DATA__LEN 22
91 #define BMP085_T_MEASURE 0x2E // temperature measurement
92 #define BMP085_P_MEASURE 0x34 // pressure measurement
93 #define BMP085_CTRL_MEAS_REG 0xF4
94 #define BMP085_ADC_OUT_MSB_REG 0xF6
95 #define BMP085_ADC_OUT_LSB_REG 0xF7
96 #define BMP085_CHIP_ID__POS 0
97 #define BMP085_CHIP_ID__MSK 0xFF
98 #define BMP085_CHIP_ID__LEN 8
99 #define BMP085_CHIP_ID__REG BMP085_CHIP_ID_REG
101 #define BMP085_ML_VERSION__POS 0
102 #define BMP085_ML_VERSION__LEN 4
103 #define BMP085_ML_VERSION__MSK 0x0F
104 #define BMP085_ML_VERSION__REG BMP085_VERSION_REG
106 #define BMP085_AL_VERSION__POS 4
107 #define BMP085_AL_VERSION__LEN 4
108 #define BMP085_AL_VERSION__MSK 0xF0
109 #define BMP085_AL_VERSION__REG BMP085_VERSION_REG
111 #define BMP085_GET_BITSLICE(regvar, bitname) (regvar & bitname##__MSK) >> bitname##__POS
112 #define BMP085_SET_BITSLICE(regvar, bitname, val) (regvar & ~bitname##__MSK) | ((val<<bitname##__POS)&bitname##__MSK)
114 #define SMD500_PARAM_MG 3038 //calibration parameter
115 #define SMD500_PARAM_MH -7357 //calibration parameter
116 #define SMD500_PARAM_MI 3791 //calibration parameter
118 STATIC_UNIT_TESTED bmp085_t bmp085;
120 #define UT_DELAY 6000 // 1.5ms margin according to the spec (4.5ms T conversion time)
121 #define UP_DELAY 27000 // 6000+21000=27000 1.5ms margin according to the spec (25.5ms P conversion time with OSS=3)
123 static bool bmp085InitDone = false;
124 STATIC_UNIT_TESTED uint16_t bmp085_ut; // static result of temperature measurement
125 STATIC_UNIT_TESTED uint32_t bmp085_up; // static result of pressure measurement
127 static void bmp085ReadCalibrarionParameters(const extDevice_t *dev);
128 static void bmp085StartUT(baroDev_t *baro);
129 static bool bmp085ReadUT(baroDev_t *baro);
130 static bool bmp085GetUT(baroDev_t *baro);
131 static void bmp085StartUP(baroDev_t *baro);
132 static bool bmp085ReadUP(baroDev_t *baro);
133 static bool bmp085GetUP(baroDev_t *baro);
134 static int32_t bmp085GetTemperature(uint32_t ut);
135 static int32_t bmp085GetPressure(uint32_t up);
136 STATIC_UNIT_TESTED void bmp085Calculate(int32_t *pressure, int32_t *temperature);
138 static bool bmp085TestEOCConnected(baroDev_t *baro, const bmp085Config_t *config);
140 static IO_t xclrIO = IO_NONE;
141 #define BMP085_OFF IOLo(xclrIO);
142 #define BMP085_ON IOHi(xclrIO);
144 static void bmp085InitXclrIO(const bmp085Config_t *config)
146 xclrIO = IOGetByTag(config->xclrTag);
147 IOInit(xclrIO, OWNER_BARO_XCLR, 0);
148 IOConfigGPIO(xclrIO, IOCFG_OUT_PP);
151 bool bmp085Detect(const bmp085Config_t *config, baroDev_t *baro)
153 uint8_t data;
154 bool ack;
155 bool defaultAddressApplied = false;
157 if (bmp085InitDone) {
158 return true;
161 bmp085InitXclrIO(config);
162 BMP085_ON; // enable baro
164 #ifdef USE_EXTI
165 // EXTI interrupt for barometer EOC
167 eocIO = IOGetByTag(config->eocTag);
168 IOInit(eocIO, OWNER_BARO_EOC, 0);
169 EXTIHandlerInit(&exti, bmp085ExtiHandler);
170 EXTIConfig(eocIO, &exti, NVIC_PRIO_BARO_EXTI, IOCFG_IN_FLOATING, BETAFLIGHT_EXTI_TRIGGER_RISING);
171 EXTIEnable(eocIO, true);
172 #else
173 UNUSED(config);
174 #endif
176 delay(20); // datasheet says 10ms, we'll be careful and do 20.
178 extDevice_t *dev = &baro->dev;
180 if ((dev->bus->busType == BUS_TYPE_I2C) && (dev->busType_u.i2c.address == 0)) {
181 // Default address for BMP085
182 dev->busType_u.i2c.address = BMP085_I2C_ADDR;
183 defaultAddressApplied = true;
186 ack = busReadRegisterBuffer(dev, BMP085_CHIP_ID__REG, &data, 1); /* read Chip Id */
187 if (ack) {
188 bmp085.chip_id = BMP085_GET_BITSLICE(data, BMP085_CHIP_ID);
189 bmp085.oversampling_setting = 3;
191 if (bmp085.chip_id == BMP085_CHIP_ID) { /* get bitslice */
192 busDeviceRegister(dev);
194 busReadRegisterBuffer(dev, BMP085_VERSION_REG, &data, 1); /* read Version reg */
195 bmp085.ml_version = BMP085_GET_BITSLICE(data, BMP085_ML_VERSION); /* get ML Version */
196 bmp085.al_version = BMP085_GET_BITSLICE(data, BMP085_AL_VERSION); /* get AL Version */
197 bmp085ReadCalibrarionParameters(dev); /* readout bmp085 calibparam structure */
198 baro->ut_delay = UT_DELAY;
199 baro->up_delay = UP_DELAY;
200 baro->start_ut = bmp085StartUT;
201 baro->read_ut = bmp085ReadUT;
202 baro->get_ut = bmp085GetUT;
203 baro->start_up = bmp085StartUP;
204 baro->read_up = bmp085ReadUP;
205 baro->get_up = bmp085GetUP;
206 baro->calculate = bmp085Calculate;
208 isEOCConnected = bmp085TestEOCConnected(baro, config);
210 bmp085InitDone = true;
211 return true;
215 #ifdef USE_EXTI
216 if (eocIO) {
217 IORelease(eocIO);
218 EXTIRelease(eocIO);
220 #endif
222 BMP085_OFF;
224 if (defaultAddressApplied) {
225 dev->busType_u.i2c.address = 0;
228 return false;
231 static int32_t bmp085GetTemperature(uint32_t ut)
233 int32_t temperature;
234 int32_t x1, x2;
236 x1 = (((int32_t) ut - (int32_t) bmp085.cal_param.ac6) * (int32_t) bmp085.cal_param.ac5) >> 15;
237 x2 = ((int32_t) bmp085.cal_param.mc << 11) / (x1 + bmp085.cal_param.md);
238 bmp085.param_b5 = x1 + x2;
239 temperature = ((bmp085.param_b5 * 10 + 8) >> 4); // temperature in 0.01 C (make same as MS5611)
241 return temperature;
244 static int32_t bmp085GetPressure(uint32_t up)
246 int32_t pressure, x1, x2, x3, b3, b6;
247 uint32_t b4, b7;
249 b6 = bmp085.param_b5 - 4000;
250 // *****calculate B3************
251 x1 = (b6 * b6) >> 12;
252 x1 *= bmp085.cal_param.b2;
253 x1 >>= 11;
255 x2 = (bmp085.cal_param.ac2 * b6);
256 x2 >>= 11;
258 x3 = x1 + x2;
260 b3 = (((((int32_t) bmp085.cal_param.ac1) * 4 + x3) << bmp085.oversampling_setting) + 2) >> 2;
262 // *****calculate B4************
263 x1 = (bmp085.cal_param.ac3 * b6) >> 13;
264 x2 = (bmp085.cal_param.b1 * ((b6 * b6) >> 12)) >> 16;
265 x3 = ((x1 + x2) + 2) >> 2;
266 b4 = (bmp085.cal_param.ac4 * (uint32_t)(x3 + 32768)) >> 15;
268 b7 = ((uint32_t)(up - b3) * (50000 >> bmp085.oversampling_setting));
269 if (b7 < 0x80000000) {
270 pressure = (b7 << 1) / b4;
271 } else {
272 pressure = (b7 / b4) << 1;
275 x1 = pressure >> 8;
276 x1 *= x1;
277 x1 = (x1 * SMD500_PARAM_MG) >> 16;
278 x2 = (pressure * SMD500_PARAM_MH) >> 16;
279 pressure += (x1 + x2 + SMD500_PARAM_MI) >> 4; // pressure in Pa
281 return pressure;
284 static void bmp085StartUT(baroDev_t *baro)
286 isConversionComplete = false;
288 busWriteRegisterStart(&baro->dev, BMP085_CTRL_MEAS_REG, BMP085_T_MEASURE);
291 static bool bmp085ReadUT(baroDev_t *baro)
293 if (busBusy(&baro->dev, NULL)) {
294 return false;
297 // return old baro value if conversion time exceeds datasheet max when EOC is connected
298 if (isEOCConnected && !isConversionComplete) {
299 return false;
302 busReadRegisterBufferStart(&baro->dev, BMP085_ADC_OUT_MSB_REG, sensor_data, BMP085_DATA_TEMP_SIZE);
304 return true;
307 static bool bmp085GetUT(baroDev_t *baro)
309 if (busBusy(&baro->dev, NULL)) {
310 return false;
313 bmp085_ut = sensor_data[0] << 8 | sensor_data[1];
315 return true;
318 static void bmp085StartUP(baroDev_t *baro)
320 uint8_t ctrl_reg_data;
322 ctrl_reg_data = BMP085_P_MEASURE + (bmp085.oversampling_setting << 6);
324 isConversionComplete = false;
326 busWriteRegisterStart(&baro->dev, BMP085_CTRL_MEAS_REG, ctrl_reg_data);
329 static bool bmp085ReadUP(baroDev_t *baro)
331 if (busBusy(&baro->dev, NULL)) {
332 return false;
335 // return old baro value if conversion time exceeds datasheet max when EOC is connected
336 if (isEOCConnected && !isConversionComplete) {
337 return false;
340 busReadRegisterBufferStart(&baro->dev, BMP085_ADC_OUT_MSB_REG, sensor_data, BMP085_DATA_PRES_SIZE);
342 return true;
345 /** read out up for pressure conversion
346 depending on the oversampling ratio setting up can be 16 to 19 bit
347 \return up parameter that represents the uncompensated pressure value
349 static bool bmp085GetUP(baroDev_t *baro)
351 if (busBusy(&baro->dev, NULL)) {
352 return false;
355 bmp085_up = (uint32_t)(sensor_data[0] << 16 | sensor_data[1] << 8 | sensor_data[2])
356 >> (8 - bmp085.oversampling_setting);
358 return true;
361 STATIC_UNIT_TESTED void bmp085Calculate(int32_t *pressure, int32_t *temperature)
363 int32_t temp, press;
365 temp = bmp085GetTemperature(bmp085_ut);
366 press = bmp085GetPressure(bmp085_up);
367 if (pressure)
368 *pressure = press;
369 if (temperature)
370 *temperature = temp;
373 static void bmp085ReadCalibrarionParameters(const extDevice_t *dev)
375 uint8_t data[22];
376 busReadRegisterBuffer(dev, BMP085_PROM_START__ADDR, data, BMP085_PROM_DATA__LEN);
378 /*parameters AC1-AC6*/
379 bmp085.cal_param.ac1 = data[0] << 8 | data[1];
380 bmp085.cal_param.ac2 = data[2] << 8 | data[3];
381 bmp085.cal_param.ac3 = data[4] << 8 | data[5];
382 bmp085.cal_param.ac4 = data[6] << 8 | data[7];
383 bmp085.cal_param.ac5 = data[8] << 8 | data[9];
384 bmp085.cal_param.ac6 = data[10] << 8 | data[11];
386 /*parameters B1,B2*/
387 bmp085.cal_param.b1 = data[12] << 8 | data[13];
388 bmp085.cal_param.b2 = data[14] << 8 | data[15];
390 /*parameters MB,MC,MD*/
391 bmp085.cal_param.mb = data[16] << 8 | data[17];
392 bmp085.cal_param.mc = data[18] << 8 | data[19];
393 bmp085.cal_param.md = data[20] << 8 | data[21];
396 static bool bmp085TestEOCConnected(baroDev_t *baro, const bmp085Config_t *config)
398 UNUSED(config);
400 #ifdef USE_EXTI
401 if (!bmp085InitDone && eocIO) {
402 // EOC should be low at this point. If not, assume EOC is not working
403 if (IORead(eocIO)) {
404 return false;
407 bmp085StartUT(baro);
408 delayMicroseconds(UT_DELAY * 2); // wait twice as long as normal, just to be sure
410 // conversion should have finished now so check if EOC is high
411 uint8_t status = IORead(eocIO);
412 if (status) {
413 return true;
416 #else
417 UNUSED(baro);
418 #endif
420 return false; // assume EOC is not connected
423 #endif /* BARO */