Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
33ba1246aThis file is pinned to the selected release. Download the original to open it in your design tool.
/* Copyright (C) 2015 Kristian Sloth Lauszus. All rights reserved.
This software may be distributed and modified under the terms of the GNU
General Public License version 2 (GPL2) as published by the Free Software
Foundation and appearing in the file GPL2.TXT included in the packaging of
this file. Please note that GPL2 Section 2[b] requires that all works based
on this software must also be made publicly available under the terms of
the GPL2 ("Copyleft").
Contact information
-------------------
Kristian Sloth Lauszus
Web : http://www.lauszus.com
e-mail : lauszus@gmail.com
*/
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#if USE_BARO
#include "BMP180.h"
#include "I2C.h"
#include "Time.h"
#if UART_DEBUG
#include "utils/uartstdio.h" // Add "UART_BUFFERED" to preprocessor
#endif
#define BMP185_DEBUG 0 // Set this to 1 to check temperature and pressure calculation
// Inspired by: https://code.google.com/p/open-headtracker, https://github.com/cleanflight/cleanflight/blob/master/src/main/drivers/compass_hmc5883l.c and https://github.com/adafruit/Adafruit-BMP085-Library/blob/master/Adafruit_BMP085.cpp
#define BMP180_ADDRESS 0x77 // Address of barometer
#define BMP185_CAL_AC1 0xAA // Calibration data (16 bits)
#define BMP185_CAL_LENGTH 22
#define BMP185_CHIP_ID 0xD0
#define BMP185_CHIP_ID_VALUE 0x55
#define BMP185_SOFT_RESET 0xE0
#define BMP185_SOFT_RESET_CMD 0xB6
// Control register
#define BMP185_CONTROL 0xF4
// Control register - pressure accuracy
#define BMP185_CONTROL_LOW_POWER 0 // Ultra low power mode
#define BMP185_CONTROL_STANDARD_MODE 1 // Standard mode
#define BMP185_CONTROL_HIGH_RES 2 // High resolution mode
#define BMP185_CONTROL_ULTRA_HIGH_RES 3 // Ultra high resolution mode
#define BMP185_MEASUREMENT 0xF6
// Commands used to start the measurements
#define BMP185_READ_TEMP_CMD 0x2E
#define BMP185_READ_PRESSURE_CMD 0x34
enum {
START_TEMP = 0,
READ_TEMP,
READ_PRESSURE,
} bmp180State;
// See the datasheet: http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
bool getBMP180Data(bmp180_t *bmp180) {
static uint32_t stateTimer; // Used for time keeping
static uint16_t stateDelay; // Used for delay between measurements
static int32_t UT; // Temperature measurement
static int32_t UP; // Pressure measurement
switch(bmp180State) {
case START_TEMP:
i2cWrite(BMP180_ADDRESS, BMP185_CONTROL, BMP185_READ_TEMP_CMD); // Start temperature measurements
stateTimer = micros();
bmp180State = READ_TEMP;
break;
case READ_TEMP:
if ((int32_t)(micros() - stateTimer) > 4500) { // Wait 4.5 ms before reading data
uint8_t buf[2];
i2cReadData(BMP180_ADDRESS, BMP185_MEASUREMENT, buf, 2); // Get uncompensated temperature value
UT = (buf[0] << 8) | buf[1];
i2cWrite(BMP180_ADDRESS, BMP185_CONTROL, BMP185_READ_PRESSURE_CMD + (bmp180->mode << 6)); // Start pressure measurements
if (bmp180->mode == BMP185_CONTROL_LOW_POWER)
stateDelay = 4500; // Wait 4.5 ms between readings when in ultra low power mode
else if (bmp180->mode == BMP185_CONTROL_STANDARD_MODE)
stateDelay = 7500; // Wait 7.5 ms between readings when in standard mode
else if (bmp180->mode == BMP185_CONTROL_HIGH_RES)
stateDelay = 13500; // Wait 13.5 ms between readings when in high resolution mode
else
stateDelay = 25500; // Wait 25.5 ms between readings when in ultra high resolution mode
stateTimer = micros();
bmp180State = READ_PRESSURE;
}
break;
case READ_PRESSURE:
if ((int32_t)(micros() - stateTimer) > stateDelay) {
uint8_t buf[3];
i2cReadData(BMP180_ADDRESS, BMP185_MEASUREMENT, buf, 3); // Get uncompensated pressure value
UP = (((uint32_t)buf[0] << 16) | (buf[1] << 8) | buf[2]) >> (8 - bmp180->mode);
#if BMP185_DEBUG
// Use datasheet numbers!
bmp180->cal.AC1 = 408;
bmp180->cal.AC2 = -72;
bmp180->cal.AC3 = -14383;
bmp180->cal.AC4 = 32741;
bmp180->cal.AC5 = 32757;
bmp180->cal.AC6 = 23153;
bmp180->cal.B1 = 6190;
bmp180->cal.B2 = 4;
bmp180->cal.MC = -8711;
bmp180->cal.MD = 2868;
UT = 27898;
bmp180->mode = 0;
UP = 23843;
#endif
// Calculate true temperature
int32_t X1 = (UT - (int32_t)bmp180->cal.AC6) * ((int32_t)bmp180->cal.AC5) >> 15;
int32_t X2 = ((int32_t)bmp180->cal.MC << 11) / (X1 + (int32_t)bmp180->cal.MD);
int32_t B5 = X1 + X2;
bmp180->temperature = (B5 + 8) >> 4;
// Calculate true pressure
int32_t B6 = B5 - 4000;
X1 = ((int32_t)bmp180->cal.B2 * (B6 * B6 >> 12)) >> 11;
X2 = ((int32_t)bmp180->cal.AC2 * B6) >> 11;
int32_t X3 = X1 + X2;
int32_t B3 = ((((int32_t)bmp180->cal.AC1 * 4 + X3) << bmp180->mode) + 2) >> 2;
X1 = ((int32_t)bmp180->cal.AC3 * B6) >> 13;
X2 = ((int32_t)bmp180->cal.B1 * ((B6 * B6) >> 12)) >> 16;
X3 = ((X1 + X2) + 2) >> 2;
uint32_t B4 = ((int32_t)bmp180->cal.AC4 * (uint32_t)(X3 + 0x8000UL)) >> 15;
uint32_t B7 = ((uint32_t)UP - B3) * (50000UL >> bmp180->mode);
if (B7 < (1UL << 31))
bmp180->pressure = (B7 << 1) / B4;
else
bmp180->pressure = (B7 / B4) << 1;
X1 = (bmp180->pressure >> 8) * (bmp180->pressure >> 8);
X1 = (X1 * 3038L) >> 16;
X2 = (-7357L * bmp180->pressure) >> 16;
bmp180->pressure += (X1 + X2 + 3791L) >> 4;
static const int32_t p0 = 101325; // Pressure at sea level
bmp180->absoluteAltitude = 44330.0f * (1.0f - powf((float)bmp180->pressure / (float)p0, 1.0f / 5.255f)) * 100.0f; // Get altitude in cm
#if BMP185_DEBUG && UART_DEBUG
UARTprintf("%d == 150\t%d == 69964\t%d == 301666\n", bmp180->temperature, bmp180->pressure, (int32_t)bmp180->absoluteAltitude);
while (1);
#endif
bmp180State = START_TEMP;
return true; // Indicate that new values has been calculated
}
break;
default:
bmp180State = START_TEMP;
break;
}
return false;
}
void initBMP180(bmp180_t *bmp180) {
uint8_t buf[BMP185_CAL_LENGTH]; // Buffer for I2C data
buf[0] = i2cRead(BMP180_ADDRESS, BMP185_CHIP_ID);
if (buf[0] == BMP185_CHIP_ID_VALUE) { // Read Chip ID
#if UART_DEBUG
UARTprintf("BMP180 found\n");
#endif
} else {
#if UART_DEBUG
UARTprintf("Could not find BMP180: %02X\n", buf[0]);
#endif
while (1);
}
i2cWrite(BMP180_ADDRESS, BMP185_SOFT_RESET, BMP185_SOFT_RESET_CMD); // Reset device, this will perform the same sequence as power on reset.
delay(100);
bmp180->mode = BMP185_CONTROL_ULTRA_HIGH_RES; // Set to ultra high resolution mode
// Read calibration values
i2cReadData(BMP180_ADDRESS, BMP185_CAL_AC1, buf, BMP185_CAL_LENGTH);
bmp180->cal.AC1 = (int16_t)((buf[0] << 8) | buf[1]);
bmp180->cal.AC2 = (int16_t)((buf[2] << 8) | buf[3]);
bmp180->cal.AC3 = (int16_t)((buf[4] << 8) | buf[5]);
bmp180->cal.AC4 = (uint16_t)((buf[6] << 8) | buf[7]);
bmp180->cal.AC5 = (uint16_t)((buf[8] << 8) | buf[9]);
bmp180->cal.AC6 = (uint16_t)((buf[10] << 8) | buf[11]);
bmp180->cal.B1 = (int16_t)((buf[12] << 8) | buf[13]);
bmp180->cal.B2 = (int16_t)((buf[14] << 8) | buf[15]);
// The MB calibration value is newer used
bmp180->cal.MC = (int16_t)((buf[18] << 8) | buf[19]);
bmp180->cal.MD = (int16_t)((buf[20] << 8) | buf[21]);
bmp180State = START_TEMP; // Reset state machine
while (!getBMP180Data(bmp180)) {
// Wait until measurement is complete
}
bmp180->groundAltitude = bmp180->absoluteAltitude; // Get initial altitude
}
#endif // USE_BARO