diff --git a/drivers/sensor/bosch/CMakeLists.txt b/drivers/sensor/bosch/CMakeLists.txt index e7652dbf0b582..4125ab1e7ed19 100644 --- a/drivers/sensor/bosch/CMakeLists.txt +++ b/drivers/sensor/bosch/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory_ifdef(CONFIG_BMI160 bmi160) add_subdirectory_ifdef(CONFIG_BMI270 bmi270) add_subdirectory_ifdef(CONFIG_BMI323 bmi323) add_subdirectory_ifdef(CONFIG_BMM150 bmm150) +add_subdirectory_ifdef(CONFIG_BMP180 bmp180) add_subdirectory_ifdef(CONFIG_BMP388 bmp388) add_subdirectory_ifdef(CONFIG_BMP581 bmp581) # zephyr-keep-sorted-stop diff --git a/drivers/sensor/bosch/Kconfig b/drivers/sensor/bosch/Kconfig index 5e27c3d3f9ee2..795284d3a2d5f 100644 --- a/drivers/sensor/bosch/Kconfig +++ b/drivers/sensor/bosch/Kconfig @@ -13,6 +13,7 @@ source "drivers/sensor/bosch/bmi160/Kconfig" source "drivers/sensor/bosch/bmi270/Kconfig" source "drivers/sensor/bosch/bmi323/Kconfig" source "drivers/sensor/bosch/bmm150/Kconfig" +source "drivers/sensor/bosch/bmp180/Kconfig" source "drivers/sensor/bosch/bmp388/Kconfig" source "drivers/sensor/bosch/bmp581/Kconfig" # zephyr-keep-sorted-stop diff --git a/drivers/sensor/bosch/bmp180/CMakeLists.txt b/drivers/sensor/bosch/bmp180/CMakeLists.txt new file mode 100644 index 0000000000000..64805a007921b --- /dev/null +++ b/drivers/sensor/bosch/bmp180/CMakeLists.txt @@ -0,0 +1,6 @@ +# Copyright (C) 2024 Jakov Jovic +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(bmp180.c) diff --git a/drivers/sensor/bosch/bmp180/Kconfig b/drivers/sensor/bosch/bmp180/Kconfig new file mode 100644 index 0000000000000..2639e6048f401 --- /dev/null +++ b/drivers/sensor/bosch/bmp180/Kconfig @@ -0,0 +1,12 @@ +# Bosch BMP180 configuration options + +# Copyright (C) 2024 Jakov Jovic +# SPDX-License-Identifier: Apache-2.0 + +config BMP180 + bool "Bosch BMP180 digital pressure and temperature sensor" + depends on DT_HAS_BOSCH_BMP180_ENABLED + select I2C + default y + help + Enable the driver for the Bosch BMP180 pressure sensor. diff --git a/drivers/sensor/bosch/bmp180/bmp180.c b/drivers/sensor/bosch/bmp180/bmp180.c new file mode 100644 index 0000000000000..3e0b2d8b8e2e6 --- /dev/null +++ b/drivers/sensor/bosch/bmp180/bmp180.c @@ -0,0 +1,294 @@ +/* + * Driver for Bosch BMP180 digital pressure and temperature sensor + * + * Copyright (C) 2024 Jakov Jovic + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://soldered.com/productdata/2022/03/Soldered_BMP180_datasheet.pdf + */ + +#define DT_DRV_COMPAT bosch_bmp180 + +#include "zephyr/device.h" +#include +#include +#include +#include +#include +#include +#include + +#include "bmp180.h" + +LOG_MODULE_REGISTER(BMP180, CONFIG_SENSOR_LOG_LEVEL); + +static int bmp180_read_calib_data(const struct device *dev) +{ + const struct bmp180_config *config = dev->config; + struct bmp180_data *data = dev->data; + uint8_t buf[22]; + + if (i2c_burst_read_dt(&config->i2c, BMP180_REG_CALIB, buf, sizeof(buf)) < 0) { + LOG_ERR(ERROR_MSG_FMT, "read", "calibration data"); + return -EIO; + } + + data->ac1 = (int16_t)sys_get_be16(&buf[0]); + data->ac2 = (int16_t)sys_get_be16(&buf[2]); + data->ac3 = (int16_t)sys_get_be16(&buf[4]); + data->ac4 = sys_get_be16(&buf[6]); /* AC4 is unsigned */ + data->ac5 = sys_get_be16(&buf[8]); /* AC5 is unsigned */ + data->ac6 = sys_get_be16(&buf[10]); /* AC6 is unsigned */ + data->b1 = (int16_t)sys_get_be16(&buf[12]); + data->b2 = (int16_t)sys_get_be16(&buf[14]); + data->mb = (int16_t)sys_get_be16(&buf[16]); + data->mc = (int16_t)sys_get_be16(&buf[18]); + data->md = (int16_t)sys_get_be16(&buf[20]); + + return 0; +} + +static int bmp180_read_uncomp_temp(const struct device *dev) +{ + const struct bmp180_config *config = dev->config; + struct bmp180_data *data = dev->data; + uint8_t buf[2]; + + if (i2c_reg_write_byte_dt(&config->i2c, BMP180_REG_CTRL_MEAS, BMP180_CMD_READ_TEMP) < 0) { + LOG_ERR(ERROR_MSG_FMT, "start", "temperature measurement"); + return -EIO; + } + + k_msleep(5); + + if (i2c_burst_read_dt(&config->i2c, BMP180_REG_OUT_MSB, buf, sizeof(buf)) < 0) { + LOG_ERR(ERROR_MSG_FMT, "read", "temperature measurement"); + return -EIO; + } + + data->uncomp_temp = (int16_t)sys_get_be16(&buf[0]); + + return 0; +} + +static int bmp180_read_uncomp_press(const struct device *dev) +{ + const struct bmp180_config *config = dev->config; + struct bmp180_data *data = dev->data; + uint8_t buf[3]; + uint8_t oversampling_setting = config->oversampling; + /* See Table 8 from the datasheet */ + uint8_t ctrl_reg_val = BMP180_CMD_READ_PRESS + (oversampling_setting << 6); + + if (i2c_reg_write_byte_dt(&config->i2c, BMP180_REG_CTRL_MEAS, ctrl_reg_val) < 0) { + LOG_ERR(ERROR_MSG_FMT, "start", "pressure measurement"); + return -EIO; + } + + /* Sleep for x ms based on the oversampling setting */ + switch (oversampling_setting) { + case BMP180_MODE_ULTRA_LOW_POWER: + k_msleep(5); + break; + case BMP180_MODE_STANDARD: + k_msleep(8); + break; + case BMP180_MODE_HIGH_RESOLUTION: + k_msleep(14); + break; + case BMP180_MODE_ULTRA_HIGH_RESOLUTION: + k_msleep(26); + break; + default: + LOG_ERR("Invalid oversampling setting."); + return -EINVAL; + } + + /* Read the MSB, LSB, and XLSB from the sensor */ + if (i2c_burst_read_dt(&config->i2c, BMP180_REG_OUT_MSB, buf, sizeof(buf)) < 0) { + LOG_ERR(ERROR_MSG_FMT, "read", "pressure measurement"); + return -EIO; + } + + data->uncomp_press = (int32_t)sys_get_be24(buf); + data->uncomp_press >>= (8 - oversampling_setting); + + return 0; +} + +static int bmp180_read_temp(const struct device *dev) +{ + struct bmp180_data *data = dev->data; + + if (bmp180_read_uncomp_temp(dev) < 0) { + return -EIO; + } + + int32_t x1 = (data->uncomp_temp - (int32_t)data->ac6) * ((int32_t)data->ac5) >> 15; + int32_t x2 = ((int32_t)data->mc << 11) / (x1 + (int32_t)data->md); + int32_t b5 = x1 + x2; + + data->temp = (b5 + 8) >> 4; + + return 0; +} + +static int bmp180_read_press(const struct device *dev) +{ + const struct bmp180_config *config = dev->config; + struct bmp180_data *data = dev->data; + + if (bmp180_read_uncomp_temp(dev) < 0) { + return -EIO; + } + + if (bmp180_read_uncomp_press(dev) < 0) { + return -EIO; + } + + /* See datasheet, page 15 for more details */ + int32_t x1 = (data->uncomp_temp - (int32_t)data->ac6) * ((int32_t)data->ac5) >> 15; + int32_t x2 = ((int32_t)data->mc << 11) / (x1 + (int32_t)data->md); + int32_t b5 = x1 + x2; + + int32_t b6 = b5 - 4000; + + x1 = (data->b2 * ((b6 * b6) >> 12)) >> 11; + x2 = (data->ac2 * b6) >> 11; + int32_t x3 = x1 + x2; + int32_t b3 = (((((int32_t)data->ac1) * 4 + x3) << config->oversampling) + 2) / 4; + + x1 = (data->ac3 * b6) >> 13; + x2 = (data->b1 * ((b6 * b6) >> 12)) >> 16; + x3 = ((x1 + x2) + 2) >> 2; + uint32_t b4 = (data->ac4 * (uint32_t)(x3 + 32768)) >> 15; + + uint32_t b7 = ((uint32_t)data->uncomp_press - b3) * (50000 >> config->oversampling); + int32_t p; + + 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; + p = p + ((x1 + x2 + 3791) >> 4); /* Final pressure in Pa */ + + data->press = p; + + return 0; +} + +static int bmp180_calculate_altitude(const struct device *dev) +{ + struct bmp180_data *data = dev->data; + /* Standard atmospheric pressure at sea level in Pa */ + const double p0 = 101325; + const double pwr = 1. / 5.255; + + if (data->press == 0) { + return -EINVAL; + } + + /* International barometric formula - datasheet page 16 */ + data->alt = 44330 * (1.0 - pow((data->press / p0), pwr)); + return 0; +} + +static int bmp180_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL); + + if (bmp180_read_temp(dev) < 0) { + return -EIO; + } + + if (bmp180_read_press(dev) < 0) { + return -EIO; + } + + if (bmp180_calculate_altitude(dev) < 0) { + return -EIO; + } + + return 0; +} + +static int bmp180_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct bmp180_data *data = dev->data; + + switch (chan) { + case SENSOR_CHAN_AMBIENT_TEMP: + /* Temperature is calculated in steps of 0.1 °C*/ + val->val1 = data->temp / 10; + val->val2 = data->temp % 10 * 100000; + break; + case SENSOR_CHAN_PRESS: + /* Pressure is calculated in steps of 1 Pa */ + val->val1 = data->press; + val->val2 = 0; + break; + case SENSOR_CHAN_ALTITUDE: + val->val1 = data->alt; + val->val2 = 0; + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct sensor_driver_api bmp180_driver_api = { + .sample_fetch = bmp180_sample_fetch, + .channel_get = bmp180_channel_get, +}; + +static int bmp180_init(const struct device *dev) +{ + const struct bmp180_config *config = dev->config; + uint8_t id = 0U; + + if (!i2c_is_ready_dt(&config->i2c)) { + LOG_ERR("I2C device is not ready."); + return -ENODEV; + } + + if (i2c_reg_read_byte_dt(&config->i2c, BMP180_REG_CHIP_ID, &id) < 0) { + LOG_ERR("Error reading chip ID."); + return -EIO; + } + + if (id != BMP180_CHIP_ID) { + LOG_ERR("Chip ID mismatch: read 0x%x, expected 0x%x", id, BMP180_CHIP_ID); + return -EIO; + } + + if (bmp180_read_calib_data(dev) < 0) { + LOG_ERR("Calibration data read failed."); + return -EIO; + } + + LOG_INF("BMP180 initialized successfully"); + return 0; +} + +#define BMP180_DEFINE(inst) \ + static struct bmp180_data bmp180_data_##inst; \ + \ + static const struct bmp180_config bmp180_config_##inst = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + .oversampling = DT_INST_PROP(inst, oversampling), \ + }; \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, bmp180_init, NULL, &bmp180_data_##inst, \ + &bmp180_config_##inst, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &bmp180_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(BMP180_DEFINE) diff --git a/drivers/sensor/bosch/bmp180/bmp180.h b/drivers/sensor/bosch/bmp180/bmp180.h new file mode 100644 index 0000000000000..31eb6d263226d --- /dev/null +++ b/drivers/sensor/bosch/bmp180/bmp180.h @@ -0,0 +1,70 @@ +/* + * Driver for Bosch BMP180 digital pressure and temperature sensor + * + * Copyright (C) 2024 Jakov Jovic + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_BMP180_BMP180_H_ +#define ZEPHYR_DRIVERS_SENSOR_BMP180_BMP180_H_ + +#include +#include +#include + +/* Generic log string */ +#define ERROR_MSG_FMT "Failed to %s the %s." + +struct bmp180_data { + int16_t ac1; + int16_t ac2; + int16_t ac3; + uint16_t ac4; + uint16_t ac5; + uint16_t ac6; + int16_t b1; + int16_t b2; + int16_t mb; + int16_t mc; + int16_t md; + + int16_t uncomp_temp; + int32_t uncomp_press; + int32_t temp; + int32_t press; + int32_t alt; +}; + +enum bmp180_oversampling_setting { + BMP180_MODE_ULTRA_LOW_POWER = 0, /* 1 sample, 4.5 ms */ + BMP180_MODE_STANDARD, /* 2 samples, 7.5 ms */ + BMP180_MODE_HIGH_RESOLUTION, /* 4 samples, 13.5 ms */ + BMP180_MODE_ULTRA_HIGH_RESOLUTION, /* 8 samples, 25.5 ms */ +}; + +struct bmp180_config { + struct i2c_dt_spec i2c; + enum bmp180_oversampling_setting oversampling; +}; + +/* Register definitions */ +#define BMP180_REG_OUT_XLSB 0xF8 +#define BMP180_REG_OUT_LSB 0xF7 +#define BMP180_REG_OUT_MSB 0xF6 +#define BMP180_REG_CTRL_MEAS 0xF4 +#define BMP180_REG_CTRL_RESET 0xE0 +#define BMP180_REG_CHIP_ID 0xD0 +#define BMP180_REG_CALIB 0xAA + +/* Chip ID value stored in BMP180_REG_CHIP_ID */ +#define BMP180_CHIP_ID 0x55 + +/* Value for BMP180_REG_CTRL_RESET */ +#define BMP180_RESET_VALUE 0xB6 + +/* Read values for BMP180_REG_CTRL_MEAS */ +#define BMP180_CMD_READ_TEMP 0x2E +#define BMP180_CMD_READ_PRESS 0x34 + +#endif /* ZEPHYR_DRIVERS_SENSOR_BMP180_BMP180_H_ */ diff --git a/dts/bindings/sensor/bosch,bmp180.yaml b/dts/bindings/sensor/bosch,bmp180.yaml new file mode 100644 index 0000000000000..7287c6346c356 --- /dev/null +++ b/dts/bindings/sensor/bosch,bmp180.yaml @@ -0,0 +1,20 @@ +# Copyright (C) 2024 Jakov Jovic +# SPDX-License-Identifier: Apache-2.0 + +include: [sensor-device.yaml, i2c-device.yaml] + +description: Bosch BMP180 Digital pressure and temperature sensor + +compatible: "bosch,bmp180" + +properties: + oversampling: + type: int + required: true + description: | + Hardware pressure sampling accuracy modes. Available modes: + - 0: BMP180_MODE_ULTRA_LOW_POWER (1 sample, 4.5 ms) + - 1: BMP180_MODE_STANDARD (2 samples, 7.5 ms) + - 2: BMP180_MODE_HIGH_RESOLUTION (4 samples, 13.5 ms) + - 3: BMP180_MODE_ULTRA_HIGH_RESOLUTION (8 samples, 25.5 ms) + enum: [0, 1, 2, 3] diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index 8fb151efdf61d..db8c50b629052 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -1072,3 +1072,10 @@ test_i2c_lsm9ds1: lsm9ds1@91 { compatible = "st,lsm9ds1"; reg = <0x8e>; }; + +test_i2c_bmp180: bmp180@92 { + compatible = "bosch,bmp180"; + status = "okay"; + reg = <0x92>; + oversampling = <1>; +};