diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 7d552a5432..c216f626fe 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -58,3 +58,6 @@ endif ifneq (,$(filter bh1750fvi,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/bh1750fvi/include endif +ifneq (,$(filter bmp180,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/bmp180/include +endif diff --git a/drivers/bmp180/Makefile b/drivers/bmp180/Makefile new file mode 100644 index 0000000000..43df70d8c6 --- /dev/null +++ b/drivers/bmp180/Makefile @@ -0,0 +1,3 @@ +MODULE = bmp180 + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/bmp180/bmp180.c b/drivers/bmp180/bmp180.c new file mode 100644 index 0000000000..362ec9fca1 --- /dev/null +++ b/drivers/bmp180/bmp180.c @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2016 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_bmp180 + * @{ + * + * @file + * @brief Device driver implementation for the BMP180/BMP085 temperature and pressure sensor. + * + * @author Alexandre Abadie + * + * @} + */ + +#include +#include "bmp180.h" +#include "bmp180_internals.h" +#include "periph/i2c.h" +#include "xtimer.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* Internal function prototypes */ +static int _read_ut(bmp180_t *dev, int32_t *ut); +static int _read_up(bmp180_t *dev, int32_t *up); +static int _compute_b5(bmp180_t *dev, int32_t ut, int32_t *b5); + +/*---------------------------------------------------------------------------* + * BMP180 Core API * + *---------------------------------------------------------------------------*/ + +int bmp180_init(bmp180_t *dev, i2c_t i2c, uint8_t mode) +{ + dev->i2c_dev = i2c; + + /* Clamp oversampling mode */ + if (mode > BMP180_ULTRAHIGHRES) { + mode = BMP180_ULTRAHIGHRES; + } + + /* Setting oversampling mode */ + dev->oversampling = mode; + + /* Initialize I2C interface */ + if (i2c_init_master(dev->i2c_dev, I2C_SPEED_NORMAL)) { + DEBUG("[Error] I2C device not enabled\n"); + return -1; + } + + /* Acquire exclusive access */ + i2c_acquire(dev->i2c_dev); + + /* Check sensor ID */ + char checkid; + i2c_write_byte(dev->i2c_dev, BMP180_ADDR, BMP180_REGISTER_ID); + i2c_read_reg(dev->i2c_dev, BMP180_ADDR, BMP180_REGISTER_ID, &checkid); + if (checkid != 0x55) { + DEBUG("[Error] Wrong device ID\n"); + return -1; + } + + char buffer[22] = {0}; + /* Read calibration values, using contiguous register addresses */ + i2c_write_byte(dev->i2c_dev, BMP180_ADDR, BMP180_CALIBRATION_AC1); + i2c_read_regs(dev->i2c_dev, BMP180_ADDR, BMP180_CALIBRATION_AC1, buffer, 22); + dev->calibration.ac1 = (int16_t)(buffer[0] << 8) | buffer[1]; + dev->calibration.ac2 = (int16_t)(buffer[2] << 8) | buffer[3]; + dev->calibration.ac3 = (int16_t)(buffer[4] << 8) | buffer[4]; + dev->calibration.ac4 = (uint16_t)(buffer[6] << 8) | buffer[7]; + dev->calibration.ac5 = (uint16_t)(buffer[8] << 8) | buffer[9]; + dev->calibration.ac6 = (uint16_t)(buffer[10] << 8) | buffer[11]; + dev->calibration.b1 = (int16_t)(buffer[12] << 8) | buffer[13]; + dev->calibration.b2 = (int16_t)(buffer[14] << 8) | buffer[15]; + dev->calibration.mb = (int16_t)(buffer[16] << 8) | buffer[17]; + dev->calibration.mc = (int16_t)(buffer[18] << 8) | buffer[19]; + dev->calibration.md = (int16_t)(buffer[20] << 8) | buffer[21]; + + /* Release I2C device */ + i2c_release(dev->i2c_dev); + + DEBUG("AC1: %i\n", (int)dev->calibration.ac1); + DEBUG("AC2: %i\n", (int)dev->calibration.ac2); + DEBUG("AC3: %i\n", (int)dev->calibration.ac3); + DEBUG("AC4: %i\n", (int)dev->calibration.ac4); + DEBUG("AC5: %i\n", (int)dev->calibration.ac5); + DEBUG("AC6: %i\n", (int)dev->calibration.ac6); + DEBUG("B1: %i\n", (int)dev->calibration.b1); + DEBUG("B2: %i\n", (int)dev->calibration.b2); + DEBUG("MB: %i\n", (int)dev->calibration.mb); + DEBUG("MC: %i\n", (int)dev->calibration.mc); + DEBUG("MD: %i\n", (int)dev->calibration.md); + return 0; +} + +int bmp180_read_temperature(bmp180_t *dev, int32_t *temperature) +{ + int32_t ut, b5; + /* Acquire exclusive access */ + if (i2c_acquire(dev->i2c_dev)) { + return -1; + } + + /* Read uncompensated value */ + _read_ut(dev, &ut); + + /* Compute true temperature value following datasheet formulas */ + _compute_b5(dev, ut, &b5); + *temperature = (b5 + 8) >> 4; + + /* Release I2C device */ + i2c_release(dev->i2c_dev); + + return 0; +} + +int bmp180_read_pressure(bmp180_t *dev, int32_t *pressure) +{ + int32_t ut, up, x1, x2, x3, b3, b5, b6, p; + uint32_t b4, b7; + + /* Acquire exclusive access */ + if (i2c_acquire(dev->i2c_dev)) { + return -1; + } + + /* Read uncompensated values: first temperature, second pressure */ + _read_ut(dev, &ut); + _read_up(dev, &up); + + /* Compute true pressure value following datasheet formulas */ + _compute_b5(dev, ut, &b5); + b6 = b5 - 4000; + x1 = ((int32_t)dev->calibration.b2 * ((b6 * b6) >> 12)) >> 11; + x2 = ((int32_t)dev->calibration.ac2 * b6) >> 11; + x3 = x1 + x2; + b3 = ((((int32_t)dev->calibration.ac1*4 + x3) << dev->oversampling) + 2) >> 2; + x1 = ((int32_t)dev->calibration.ac3 * b6) >> 13; + x2 = ((int32_t)dev->calibration.b1 * (b6 * b6) >> 12) >> 16; + x3 = ((x1 + x2) + 2) >> 2; + b4 = (int32_t)dev->calibration.ac4 * (uint32_t)(x3+32768) >> 15; + b7 = ((uint32_t)up - b3) * (uint32_t)(50000UL >> dev->oversampling); + if (b7 < 0x80000000) { + p = (b7 * 2) / b4; + } + else { + p = (b7 / b4) * 2; + } + + x1 = (p >> 8) * (p >> 8); + x1 = (x1 * 3038) >> 16; + x2 = (-7357 * p) >> 16; + *pressure = p + ((x1 + x2 + 3791) >> 4); + + /* release I2C device */ + i2c_release(dev->i2c_dev); + + return 0; +} + +int bmp180_altitude(bmp180_t *dev, int32_t pressure_0, int32_t *altitude) +{ + int32_t p; + bmp180_read_pressure(dev, &p); + + *altitude = (int32_t)(44330.0 * (1.0 - pow((double)p / pressure_0, 0.1903))); + + return 0; +} + +int bmp180_sealevel_pressure(bmp180_t *dev, int32_t altitude, int32_t *pressure_0) +{ + int32_t p; + bmp180_read_pressure(dev, &p); + + *pressure_0 = (int32_t)((double)p / pow(1.0 - (altitude / 44330.0), 5.255)); + + return 0; +} + +/*------------------------------------------------------------------------------------*/ +/* Internal functions */ +/*------------------------------------------------------------------------------------*/ + +static int _read_ut(bmp180_t *dev, int32_t *output) +{ + /* Read UT (Uncompsensated Temperature value) */ + char ut[2] = {0}; + char control[2] = { BMP180_REGISTER_CONTROL, BMP180_TEMPERATURE_COMMAND }; + i2c_write_bytes(dev->i2c_dev, BMP180_ADDR, control, 2); + xtimer_usleep(BMP180_ULTRALOWPOWER_DELAY); + i2c_read_regs(dev->i2c_dev, BMP180_ADDR, BMP180_REGISTER_DATA, ut, 2); + *output = ( ut[0] << 8 ) | ut[1]; + + DEBUG("UT: %i\n", (int)*output); + + return 0; +} + +static int _read_up(bmp180_t *dev, int32_t *output) +{ + /* Read UP (Uncompsensated Pressure value) */ + char up[3] = {0}; + char control[2] = { BMP180_REGISTER_CONTROL, BMP180_PRESSURE_COMMAND | (dev->oversampling & 0x3) << 6 }; + i2c_write_bytes(dev->i2c_dev, BMP180_ADDR, control, 2); + switch (dev->oversampling) { + case BMP180_ULTRALOWPOWER: + xtimer_usleep(BMP180_ULTRALOWPOWER_DELAY); + break; + case BMP180_STANDARD: + xtimer_usleep(BMP180_STANDARD_DELAY); + break; + case BMP180_HIGHRES: + xtimer_usleep(BMP180_HIGHRES_DELAY); + break; + case BMP180_ULTRAHIGHRES: + xtimer_usleep(BMP180_ULTRAHIGHRES_DELAY); + break; + default: + xtimer_usleep(BMP180_ULTRALOWPOWER_DELAY); + break; + } + i2c_read_regs(dev->i2c_dev, BMP180_ADDR, BMP180_REGISTER_DATA, up, 3); + + *output = ((up[0] << 16) | (up[1] << 8) | up[2]) >> (8 - dev->oversampling); + + DEBUG("UP: %i\n", (int)*output); + + return 0; +} + +static int _compute_b5(bmp180_t *dev, int32_t ut, int32_t *output) +{ + int32_t x1, x2; + x1 = (ut - dev->calibration.ac6) * dev->calibration.ac5 >> 15; + x2 = (dev->calibration.mc << 11) / (x1 + dev->calibration.md); + + *output = x1 + x2; + + return 0; +} diff --git a/drivers/bmp180/include/bmp180_internals.h b/drivers/bmp180/include/bmp180_internals.h new file mode 100644 index 0000000000..5bdf79abcb --- /dev/null +++ b/drivers/bmp180/include/bmp180_internals.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_bmp180 BMP180 + * @ingroup drivers_sensors + * @brief Internal addresses, registers, constants for the BMP180 sensor. + * @{ + * + * @file + * @brief Internal addresses, registers, constants for the BMP180 sensor. + * + * @author Alexandre Abadie + */ + +#ifndef BMP180_REGS_H_ +#define BMP180_REGS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name BMP180 I2C address + * @{ + */ +#define BMP180_ADDR (0x77) /* 7 bit address */ +/** @} */ + +/** + * @name BMP180 registers + * @{ + */ +#define BMP180_REGISTER_ID (0xD0) +#define BMP180_REGISTER_CONTROL (0xF4) +#define BMP180_REGISTER_DATA (0xF6) +#define BMP180_TEMPERATURE_COMMAND (0x2E) +#define BMP180_PRESSURE_COMMAND (0x34) +#define BMP180_CALIBRATION_AC1 (0xAA) +/** @} */ + +/** + * @name Oversampling modes delays (micros) + * @{ + */ +#define BMP180_ULTRALOWPOWER_DELAY (5000UL) +#define BMP180_STANDARD_DELAY (8000UL) +#define BMP180_HIGHRES_DELAY (14000UL) +#define BMP180_ULTRAHIGHRES_DELAY (26000UL) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* BMP180_REGS_H_ */ +/** @} */ diff --git a/drivers/include/bmp180.h b/drivers/include/bmp180.h new file mode 100644 index 0000000000..09a3f00299 --- /dev/null +++ b/drivers/include/bmp180.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2016 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_bmp180 BMP180 + * @ingroup drivers_sensors + * @brief Device driver interface for the BMP180 sensor + * @{ + * + * @file + * @brief Device driver interface for the BMP180 sensor. + * + * @author Alexandre Abadie + */ + +#ifndef BMP180_H_ +#define BMP180_H_ + +#include "periph/i2c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Oversampling modes + * @{ + */ +#define BMP180_ULTRALOWPOWER (0) +#define BMP180_STANDARD (1) +#define BMP180_HIGHRES (2) +#define BMP180_ULTRAHIGHRES (3) +/** @} */ + +/** + * @brief Calibration struct for the BMP180 sensor + */ +typedef struct { + int16_t ac1; /**< ac1 coefficient */ + int16_t ac2; /**< ac2 coefficient */ + int16_t ac3; /**< ac3 coefficient */ + int16_t b1; /**< b1 coefficient */ + int16_t b2; /**< b2 coefficient */ + int16_t mb; /**< mb coefficient */ + int16_t mc; /**< mc coefficient */ + int16_t md; /**< md coefficient */ + uint16_t ac4; /**< ac4 coefficient */ + uint16_t ac5; /**< ac5 coefficient */ + uint16_t ac6; /**< ac6 coefficient */ +} bmp180_calibration_t; + +/** + * @brief Device descriptor for the BMP180 sensor + */ +typedef struct { + i2c_t i2c_dev; /**< I2C device which is used */ + bmp180_calibration_t calibration; /**< Device calibration */ + uint8_t oversampling; /**< Oversampling mode */ +} bmp180_t; + +/** + * @brief Initialize the given BMP180 device + * + * @param[out] dev Initialized device descriptor of BMP180 device + * @param[in] i2c I2C bus the sensor is connected to + * @param[in] mode BMP180 oversampling mode + * + * @return 0 on success + * @return -1 if given I2C is not enabled in board config + */ +int bmp180_init(bmp180_t *dev, i2c_t i2c, uint8_t mode); + +/** + * @brief Read temperature value from the given BMP180 device, returned in d°C + * + * @param[in] dev Device descriptor of BMP180 device to read from + * @param[out] temperature Temperature in d°C + * + * @return 0 on success + * @return -1 if device's I2C is not enabled in board config + */ +int bmp180_read_temperature(bmp180_t *dev, int32_t *temperature); + +/** + * @brief Read pressure value from the given BMP180 device, returned in Pa + * + * @param[in] dev Device descriptor of BMP180 device to read from + * @param[out] pressure Pressure in Pa + * + * @return 0 on success + * @return -1 if device's I2C is not enabled in board config + */ +int bmp180_read_pressure(bmp180_t *dev, int32_t *pressure); + +/** + * @brief Compute altitude, returned in m. + * + * @param[in] dev Device descriptor of BMP180 device to read from + * @param[in] pressure_0 The pressure at sea level in Pa + * @param[out] altitude Altitude in m + * + * @return 0 on success + * @return -1 if device's I2C is not enabled in board config + */ +int bmp180_altitude(bmp180_t *dev, int32_t pressure_0, int32_t *altitude); + +/** + * @brief Compute pressure at sea level, returned in Pa. + * + * @param[in] dev Device descriptor of BMP180 device to read from + * @param[in] altitude Altitude in m + * @param[out] pressure_0 Pressure at sea level in Pa + * + * @return 0 on success + * @return -1 if device's I2C is not enabled in board config + */ +int bmp180_sealevel_pressure(bmp180_t *dev, int32_t altitude, int32_t *pressure_0); + +#ifdef __cplusplus +} +#endif + +#endif /* BMP180_H_ */ +/** @} */ diff --git a/tests/driver_bmp180/Makefile b/tests/driver_bmp180/Makefile new file mode 100644 index 0000000000..8c80fbf1a2 --- /dev/null +++ b/tests/driver_bmp180/Makefile @@ -0,0 +1,20 @@ +APPLICATION = driver_bmp180 +include ../Makefile.tests_common + +FEATURES_REQUIRED = periph_i2c + +USEMODULE += bmp180 +USEMODULE += xtimer +USEMODULE += printf_float + +# set default device parameters in case they are undefined +TEST_I2C ?= I2C_0 +TEST_MEASURE_OVERSAMPLING ?= BMP180_ULTRALOWPOWER +TEST_ALTITUDE ?= 158 # altitude in Polytechnique School campus + +# export parameters +CFLAGS += -DTEST_I2C=$(TEST_I2C) +CFLAGS += -DTEST_MEASURE_OVERSAMPLING=$(TEST_MEASURE_OVERSAMPLING) +CFLAGS += -DTEST_ALTITUDE=$(TEST_ALTITUDE) + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_bmp180/Readme.md b/tests/driver_bmp180/Readme.md new file mode 100644 index 0000000000..3a7382b5b9 --- /dev/null +++ b/tests/driver_bmp180/Readme.md @@ -0,0 +1,15 @@ +## About +This is a test application for the BMP180 Pressure and Temperature sensor. + +## Usage +The application will initialize the BMP180 sensor and display its calibration +coefficients. Please see section 3.4 of the +[datasheet](https://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf) for +more information. + +After initialization, every 2 seconds, the application: +* reads the temperature (d°C); +* reads the pressure (Pa); +* computes the pressure at sea level based on the TEST_ALTITUDE variable; +* computes the altitude based on the pressure at sea level calculated above; +* those values are then printed to STDOUT. diff --git a/tests/driver_bmp180/main.c b/tests/driver_bmp180/main.c new file mode 100644 index 0000000000..cb9e771fb0 --- /dev/null +++ b/tests/driver_bmp180/main.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for the BMP180 pressure and temperature sensor + * + * @author Alexandre Abadie + * + * @} + */ + +#ifndef TEST_I2C +#error "TEST_I2C not defined" +#endif + +#ifndef TEST_MEASURE_OVERSAMPLING +#error "TEST_MEASURE_OVERSAMPLING not defined" +#endif + +#ifndef TEST_ALTITUDE +#error "TEST_ALTITUDE not defined" +#endif + +#include +#include + +#include "bmp180.h" +#include "xtimer.h" +#include "board.h" + +#define SLEEP_2S (2 * 1000 * 1000u) /* 2 seconds delay between printf */ + +int main(void) +{ + bmp180_t dev; + int32_t temperature, pressure, altitude, pressure_0; + int result; + + puts("BMP180 test application\n"); + + printf("+------------Initializing------------+\n"); + result = bmp180_init(&dev, TEST_I2C, TEST_MEASURE_OVERSAMPLING); + if (result == -1) { + puts("[Error] The given i2c is not enabled"); + return 1; + } + else if (result == -2) { + puts("[Error] The sensor did not answer correctly on the given address"); + return 1; + } + else { + printf("Initialization successful\n\n"); + } + + printf("+------------Calibration------------+\n"); + printf("AC1: %i\n", dev.calibration.ac1); + printf("AC2: %i\n", dev.calibration.ac2); + printf("AC3: %i\n", dev.calibration.ac3); + printf("AC4: %i\n", dev.calibration.ac4); + printf("AC5: %i\n", dev.calibration.ac5); + printf("AC6: %i\n", dev.calibration.ac6); + printf("B1: %i\n", dev.calibration.b1); + printf("B2: %i\n", dev.calibration.b2); + printf("MB: %i\n", dev.calibration.mb); + printf("MC: %i\n", dev.calibration.mc); + printf("MD: %i\n", dev.calibration.md); + printf("\n+--------Starting Measurements--------+\n"); + while (1) { + /* Get temperature in deci degrees celsius */ + bmp180_read_temperature(&dev, &temperature); + + /* Get pressure in Pa */ + bmp180_read_pressure(&dev, &pressure); + + /* Get pressure at sealevel in Pa */ + bmp180_sealevel_pressure(&dev, (int32_t)TEST_ALTITUDE, &pressure_0); + + /* Get altitude in meters */ + bmp180_altitude(&dev, pressure_0, &altitude); + + printf("Temperature [°C]: %.1f\n" + "Pressure [hPa]: %.2f\n" + "Pressure at see level [hPa]: %.2f\n" + "Altitude [m]: %i\n" + "\n+-------------------------------------+\n", + (double)temperature / 10.0, (double)pressure / 100.0, + (double)pressure_0 / 100.0, (int)altitude); + + xtimer_usleep(SLEEP_2S); + } + + return 0; +}