From e9d7d5c2427fff1b7bd2d7a674be12e8a15e036b Mon Sep 17 00:00:00 2001 From: Leonard Pollak Date: Sun, 20 Feb 2022 09:55:18 +0100 Subject: [PATCH 1/4] drivers: sensor: bme680: fix constants This fixes the constant for the mem page and replaces a magic number with the already defined `BME680_LEN_COEFF2` constant. Signed-off-by: Leonard Pollak --- drivers/sensor/bme680/bme680.c | 2 +- drivers/sensor/bme680/bme680.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/sensor/bme680/bme680.c b/drivers/sensor/bme680/bme680.c index ca4e8a35342ed..f700aae87ed00 100644 --- a/drivers/sensor/bme680/bme680.c +++ b/drivers/sensor/bme680/bme680.c @@ -292,7 +292,7 @@ static int bme680_read_compensation(const struct device *dev) } err = bme680_reg_read(dev, BME680_REG_COEFF2, &buff[BME680_LEN_COEFF1], - 16); + BME680_LEN_COEFF2); if (err < 0) { return err; } diff --git a/drivers/sensor/bme680/bme680.h b/drivers/sensor/bme680/bme680.h index b4d624cc4414d..27f7ac27cea46 100644 --- a/drivers/sensor/bme680/bme680.h +++ b/drivers/sensor/bme680/bme680.h @@ -29,7 +29,7 @@ #define BME680_REG_CTRL_HUM 0x72 #define BME680_REG_CTRL_MEAS 0x74 #define BME680_REG_CONFIG 0x75 -#define BME680_REG_MEM_PAGE 0xf3 +#define BME680_REG_MEM_PAGE 0x73 #define BME680_REG_UNIQUE_ID 0x83 #define BME680_REG_COEFF1 0x8a #define BME680_REG_CHIP_ID 0xd0 From 90b3eeb83a205ddbda72746e035b0042204624d3 Mon Sep 17 00:00:00 2001 From: Leonard Pollak Date: Sun, 20 Feb 2022 10:08:27 +0100 Subject: [PATCH 2/4] drivers: sensor: bme680: prep work Consolidate the initialization routines and change the include guard to conform with the coding guidelines as a preparation for the following commits which add support for the SPI interface. Signed-off-by: Leonard Pollak --- drivers/sensor/bme680/bme680.c | 32 ++++++++++---------------------- drivers/sensor/bme680/bme680.h | 12 +++++++----- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/drivers/sensor/bme680/bme680.c b/drivers/sensor/bme680/bme680.c index f700aae87ed00..a3373d97d9373 100644 --- a/drivers/sensor/bme680/bme680.c +++ b/drivers/sensor/bme680/bme680.c @@ -6,6 +6,7 @@ /* * Copyright (c) 2018 Bosch Sensortec GmbH + * Copyright (c) 2022, Leonard Pollak * * SPDX-License-Identifier: Apache-2.0 */ @@ -342,11 +343,17 @@ static int bme680_read_compensation(const struct device *dev) return 0; } -static int bme680_chip_init(const struct device *dev) +static int bme680_init(const struct device *dev) { struct bme680_data *data = dev->data; + const struct bme680_config *config = dev->config; int err; + if (!device_is_ready(config->bus.bus)) { + LOG_ERR("I2C master %s not ready", config->bus.bus->name); + return -EINVAL; + } + err = bme680_reg_read(dev, BME680_REG_CHIP_ID, &data->chip_id, 1); if (err < 0) { return err; @@ -355,7 +362,7 @@ static int bme680_chip_init(const struct device *dev) if (data->chip_id == BME680_CHIP_ID) { LOG_DBG("BME680 chip detected"); } else { - LOG_ERR("Bad BME680 chip id 0x%x", data->chip_id); + LOG_ERR("Bad BME680 chip id: 0x%x", data->chip_id); return -ENOTSUP; } @@ -394,27 +401,8 @@ static int bme680_chip_init(const struct device *dev) err = bme680_reg_write(dev, BME680_REG_CTRL_MEAS, BME680_CTRL_MEAS_VAL); - if (err < 0) { - return err; - } - - return 0; -} -static int bme680_init(const struct device *dev) -{ - const struct bme680_config *config = dev->config; - - if (!device_is_ready(config->bus.bus)) { - LOG_ERR("I2C master %s not ready", config->bus.bus->name); - return -EINVAL; - } - - if (bme680_chip_init(dev) < 0) { - return -EINVAL; - } - - return 0; + return err; } static const struct sensor_driver_api bme680_api_funcs = { diff --git a/drivers/sensor/bme680/bme680.h b/drivers/sensor/bme680/bme680.h index 27f7ac27cea46..5653a0e0b272a 100644 --- a/drivers/sensor/bme680/bme680.h +++ b/drivers/sensor/bme680/bme680.h @@ -1,12 +1,14 @@ /* * Copyright (c) 2018 Bosch Sensortec GmbH + * Copyright (c) 2022, Leonard Pollak * * SPDX-License-Identifier: Apache-2.0 */ -#ifndef __SENSOR_BME680_H__ -#define __SENSOR_BME680_H__ +#ifndef __ZEPHYR_DRIVERS_SENSOR_BME680_H__ +#define __ZEPHYR_DRIVERS_SENSOR_BME680_H__ +#include #include #include @@ -32,9 +34,9 @@ #define BME680_REG_MEM_PAGE 0x73 #define BME680_REG_UNIQUE_ID 0x83 #define BME680_REG_COEFF1 0x8a -#define BME680_REG_CHIP_ID 0xd0 -#define BME680_REG_SOFT_RESET 0xe0 #define BME680_REG_COEFF2 0xe1 +#define BME680_REG_CHIP_ID 0xd0 +#define BME680_REG_SOFT_RESET 0xe0 #define BME680_MSK_NEW_DATA 0x80 #define BME680_MSK_GAS_RANGE 0x0f @@ -167,4 +169,4 @@ struct bme680_config { struct i2c_dt_spec bus; }; -#endif /* __SENSOR_BME680_H__ */ +#endif /* __ZEPHYR_DRIVERS_SENSOR_BME680_H__ */ From 568dd961d1698aa23824c26a8494181ac2d992f6 Mon Sep 17 00:00:00 2001 From: Leonard Pollak Date: Sun, 20 Feb 2022 10:09:56 +0100 Subject: [PATCH 3/4] drivers: sensor: bme680: Add SPI interface This enables the SPI interface for the BME680 sensor driver. Signed-off-by: Leonard Pollak --- drivers/sensor/bme680/CMakeLists.txt | 4 +- drivers/sensor/bme680/Kconfig | 6 +- drivers/sensor/bme680/bme680.c | 94 ++++++++++++---- drivers/sensor/bme680/bme680.h | 55 +++++++++- drivers/sensor/bme680/bme680_i2c.c | 43 ++++++++ drivers/sensor/bme680/bme680_spi.c | 124 ++++++++++++++++++++++ dts/bindings/sensor/bosch,bme680-spi.yaml | 10 ++ 7 files changed, 308 insertions(+), 28 deletions(-) create mode 100644 drivers/sensor/bme680/bme680_i2c.c create mode 100644 drivers/sensor/bme680/bme680_spi.c create mode 100644 dts/bindings/sensor/bosch,bme680-spi.yaml diff --git a/drivers/sensor/bme680/CMakeLists.txt b/drivers/sensor/bme680/CMakeLists.txt index 5fffb82e4a917..e7d6f8945d793 100644 --- a/drivers/sensor/bme680/CMakeLists.txt +++ b/drivers/sensor/bme680/CMakeLists.txt @@ -1,9 +1,9 @@ -# # Copyright (c) 2018 Bosch Sensortec GmbH +# Copyright (c) 2022, Leonard Pollak # # SPDX-License-Identifier: Apache-2.0 # zephyr_library() -zephyr_library_sources(bme680.c) +zephyr_library_sources(bme680.c bme680_i2c.c bme680_spi.c) diff --git a/drivers/sensor/bme680/Kconfig b/drivers/sensor/bme680/Kconfig index 4ed0f3473c2ee..98f0d18465b47 100644 --- a/drivers/sensor/bme680/Kconfig +++ b/drivers/sensor/bme680/Kconfig @@ -1,13 +1,15 @@ # BME680 temperature, pressure, humidity and gas sensor configuration options # Copyright (c) 2018 Bosch Sensortec GmbH +# Copyright (c) 2022, Leonard Pollak +# # SPDX-License-Identifier: Apache-2.0 menuconfig BME680 bool "BME680 sensor" - depends on I2C + depends on I2C || SPI help - Enable driver for BME680 I2C-based based temperature, pressure, humidity and gas sensor. + Enable driver for BME680 I2C- or SPI- based temperature, pressure, humidity and gas sensor. if BME680 diff --git a/drivers/sensor/bme680/bme680.c b/drivers/sensor/bme680/bme680.c index a3373d97d9373..97512b8d8b4b6 100644 --- a/drivers/sensor/bme680/bme680.c +++ b/drivers/sensor/bme680/bme680.c @@ -11,33 +11,49 @@ * SPDX-License-Identifier: Apache-2.0 */ -#define DT_DRV_COMPAT bosch_bme680 - -#include "bme680.h" #include -#include #include #include #include #include #include - #include + +#include "bme680.h" + LOG_MODULE_REGISTER(bme680, CONFIG_SENSOR_LOG_LEVEL); -static int bme680_reg_read(const struct device *dev, uint8_t start, - uint8_t *buf, int size) + +#if BME680_BUS_SPI +static inline bool bme680_is_on_spi(const struct device *dev) { const struct bme680_config *config = dev->config; - return i2c_burst_read_dt(&config->bus, start, buf, size); + return config->bus_io == &bme680_bus_io_spi; } +#endif -static int bme680_reg_write(const struct device *dev, uint8_t reg, uint8_t val) +static inline int bme680_bus_check(const struct device *dev) { const struct bme680_config *config = dev->config; - return i2c_reg_write_byte_dt(&config->bus, reg, val); + return config->bus_io->check(&config->bus); +} + +static inline int bme680_reg_read(const struct device *dev, + uint8_t start, uint8_t *buf, int size) +{ + const struct bme680_config *config = dev->config; + + return config->bus_io->read(dev, start, buf, size); +} + +static inline int bme680_reg_write(const struct device *dev, uint8_t reg, + uint8_t val) +{ + const struct bme680_config *config = dev->config; + + return config->bus_io->write(dev, reg, val); } static void bme680_calc_temp(struct bme680_data *data, uint32_t adc_temp) @@ -346,13 +362,22 @@ static int bme680_read_compensation(const struct device *dev) static int bme680_init(const struct device *dev) { struct bme680_data *data = dev->data; - const struct bme680_config *config = dev->config; int err; - if (!device_is_ready(config->bus.bus)) { - LOG_ERR("I2C master %s not ready", config->bus.bus->name); - return -EINVAL; + err = bme680_bus_check(dev); + if (err < 0) { + LOG_ERR("Bus not ready for '%s'", dev->name); + return err; + } + +#if BME680_BUS_SPI + if (bme680_is_on_spi(dev)) { + err = bme680_reg_read(dev, BME680_REG_MEM_PAGE, &data->mem_page, 1); + if (err < 0) { + return err; + } } +#endif err = bme680_reg_read(dev, BME680_REG_CHIP_ID, &data->chip_id, 1); if (err < 0) { @@ -410,12 +435,39 @@ static const struct sensor_driver_api bme680_api_funcs = { .channel_get = bme680_channel_get, }; -static struct bme680_data bme680_data; +/* Initializes a struct bme680_config for an instance on a SPI bus. */ +#define BME680_CONFIG_SPI(inst) \ + { \ + .bus.spi = SPI_DT_SPEC_INST_GET( \ + inst, BME680_SPI_OPERATION, 0), \ + .bus_io = &bme680_bus_io_spi, \ + } -static const struct bme680_config bme680_config = { - .bus = I2C_DT_SPEC_INST_GET(0) -}; +/* Initializes a struct bme680_config for an instance on an I2C bus. */ +#define BME680_CONFIG_I2C(inst) \ + { \ + .bus.i2c = I2C_DT_SPEC_INST_GET(inst), \ + .bus_io = &bme680_bus_io_i2c, \ + } -DEVICE_DT_INST_DEFINE(0, bme680_init, NULL, &bme680_data, - &bme680_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, - &bme680_api_funcs); +/* + * Main instantiation macro, which selects the correct bus-specific + * instantiation macros for the instance. + */ +#define BME680_DEFINE(inst) \ + static struct bme680_data bme680_data_##inst; \ + static const struct bme680_config bme680_config_##inst = \ + COND_CODE_1(DT_INST_ON_BUS(inst, spi), \ + (BME680_CONFIG_SPI(inst)), \ + (BME680_CONFIG_I2C(inst))); \ + DEVICE_DT_INST_DEFINE(inst, \ + bme680_init, \ + NULL, \ + &bme680_data_##inst, \ + &bme680_config_##inst, \ + POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, \ + &bme680_api_funcs); + +/* Create the struct device for every status "okay" node in the devicetree. */ +DT_INST_FOREACH_STATUS_OKAY(BME680_DEFINE) diff --git a/drivers/sensor/bme680/bme680.h b/drivers/sensor/bme680/bme680.h index 5653a0e0b272a..647a288d3af66 100644 --- a/drivers/sensor/bme680/bme680.h +++ b/drivers/sensor/bme680/bme680.h @@ -10,8 +10,51 @@ #include #include +#include +#include #include +#define DT_DRV_COMPAT bosch_bme680 + +#define BME680_BUS_SPI DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) +#define BME680_BUS_I2C DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + +union bme680_bus { +#if BME680_BUS_SPI + struct spi_dt_spec spi; +#endif +#if BME680_BUS_I2C + struct i2c_dt_spec i2c; +#endif +}; + +typedef int (*bme680_bus_check_fn)(const union bme680_bus *bus); +typedef int (*bme680_reg_read_fn)(const struct device *dev, + uint8_t start, uint8_t *buf, int size); +typedef int (*bme680_reg_write_fn)(const struct device *dev, + uint8_t reg, uint8_t val); + +struct bme680_bus_io { + bme680_bus_check_fn check; + bme680_reg_read_fn read; + bme680_reg_write_fn write; +}; + +#if BME680_BUS_SPI +#define BME680_SPI_OPERATION (SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_MODE_CPOL \ + | SPI_MODE_CPHA | SPI_OP_MODE_MASTER) +extern const struct bme680_bus_io bme680_bus_io_spi; +#endif + +#if BME680_BUS_I2C +extern const struct bme680_bus_io bme680_bus_io_i2c; +#endif + +struct bme680_config { + union bme680_bus bus; + const struct bme680_bus_io *bus_io; +}; + #define BME680_CHIP_ID 0x61 #define BME680_LEN_FIELD 15 @@ -44,6 +87,12 @@ #define BME680_MSK_RANGE_SW_ERR 0xf0 #define BME680_MSK_HEATR_STAB 0x10 +#define BME680_SPI_READ_BIT 0x80 +#define BME680_SPI_WRITE_MSK 0x7f + +#define BME680_MEM_PAGE1 0x00 +#define BME680_MEM_PAGE0 0x10 + #if defined CONFIG_BME680_TEMP_OVER_1X #define BME680_TEMP_OVER (1 << 5) #elif defined CONFIG_BME680_TEMP_OVER_2X @@ -163,10 +212,10 @@ struct bme680_data { int32_t t_fine; uint8_t chip_id; -}; -struct bme680_config { - struct i2c_dt_spec bus; +#if BME680_BUS_SPI + uint8_t mem_page; +#endif }; #endif /* __ZEPHYR_DRIVERS_SENSOR_BME680_H__ */ diff --git a/drivers/sensor/bme680/bme680_i2c.c b/drivers/sensor/bme680/bme680_i2c.c new file mode 100644 index 0000000000000..faa8cea045a6c --- /dev/null +++ b/drivers/sensor/bme680/bme680_i2c.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, 2017 Intel Corporation + * Copyright (c) 2017 IpTronix S.r.l. + * Copyright (c) 2021 Nordic Semiconductor ASA + * Copyright (c) 2022, Leonard Pollak + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Bus-specific functionality for BME680s accessed via I2C. + */ + +#include "bme680.h" + +#if BME680_BUS_I2C +static int bme680_bus_check_i2c(const union bme680_bus *bus) +{ + return device_is_ready(bus->i2c.bus) ? 0 : -ENODEV; +} + +static int bme680_reg_read_i2c(const struct device *dev, + uint8_t start, uint8_t *buf, int size) +{ + const struct bme680_config *config = dev->config; + + return i2c_burst_read_dt(&config->bus.i2c, start, buf, size); +} + +static int bme680_reg_write_i2c(const struct device *dev, + uint8_t reg, uint8_t val) +{ + const struct bme680_config *config = dev->config; + + return i2c_reg_write_byte_dt(&config->bus.i2c, reg, val); +} + +const struct bme680_bus_io bme680_bus_io_i2c = { + .check = bme680_bus_check_i2c, + .read = bme680_reg_read_i2c, + .write = bme680_reg_write_i2c, +}; +#endif /* BME680_BUS_I2C */ diff --git a/drivers/sensor/bme680/bme680_spi.c b/drivers/sensor/bme680/bme680_spi.c new file mode 100644 index 0000000000000..14107186336d6 --- /dev/null +++ b/drivers/sensor/bme680/bme680_spi.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016, 2017 Intel Corporation + * Copyright (c) 2017 IpTronix S.r.l. + * Copyright (c) 2021 Nordic Semiconductor ASA + * Copyright (c) 2022, Leonard Pollak + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Bus-specific functionality for BME680s accessed via SPI. + */ + +#include +#include "bme680.h" + +#if BME680_BUS_SPI + +LOG_MODULE_DECLARE(bme680, CONFIG_SENSOR_LOG_LEVEL); + +static int bme680_bus_check_spi(const union bme680_bus *bus) +{ + return spi_is_ready(&bus->spi) ? 0 : -ENODEV; +} + +static inline int bme680_set_mem_page(const struct device *dev, uint8_t addr) +{ + const struct bme680_config *config = dev->config; + struct bme680_data *data = dev->data; + uint8_t page; + int err = 0; + + if (addr > 0x7f) { + page = BME680_MEM_PAGE1; + } else { + page = BME680_MEM_PAGE0; + } + + if (data->mem_page != page) { + + data->mem_page = page; + uint8_t cmd[] = { BME680_REG_MEM_PAGE & BME680_SPI_WRITE_MSK, data->mem_page }; + const struct spi_buf tx_buf = { + .buf = cmd, + .len = sizeof(cmd) + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + err = spi_write_dt(&config->bus.spi, &tx); + } + return err; +} + +static int bme680_reg_write_spi(const struct device *dev, + uint8_t reg, uint8_t val) +{ + const struct bme680_config *config = dev->config; + int err; + uint8_t cmd[] = { reg & BME680_SPI_WRITE_MSK, val }; + const struct spi_buf tx_buf = { + .buf = cmd, + .len = sizeof(cmd) + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + err = bme680_set_mem_page(dev, reg); + if (err) { + return err; + } + + err = spi_write_dt(&config->bus.spi, &tx); + + return err; +} + +static int bme680_reg_read_spi(const struct device *dev, + uint8_t start, uint8_t *buf, int size) +{ + const struct bme680_config *config = dev->config; + int err; + uint8_t addr; + const struct spi_buf tx_buf = { + .buf = &addr, + .len = 1 + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + struct spi_buf rx_buf[2]; + const struct spi_buf_set rx = { + .buffers = rx_buf, + .count = ARRAY_SIZE(rx_buf) + }; + + rx_buf[0].buf = NULL; + rx_buf[0].len = 1; + + addr = start | BME680_SPI_READ_BIT; + rx_buf[1].buf = buf; + rx_buf[1].len = size; + + err = bme680_set_mem_page(dev, start); + if (err) { + return err; + } + + err = spi_transceive_dt(&config->bus.spi, &tx, &rx); + + return err; +} + +const struct bme680_bus_io bme680_bus_io_spi = { + .check = bme680_bus_check_spi, + .read = bme680_reg_read_spi, + .write = bme680_reg_write_spi, +}; +#endif /* BME680_BUS_SPI */ diff --git a/dts/bindings/sensor/bosch,bme680-spi.yaml b/dts/bindings/sensor/bosch,bme680-spi.yaml new file mode 100644 index 0000000000000..0e9daea40f1b7 --- /dev/null +++ b/dts/bindings/sensor/bosch,bme680-spi.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2022, Leonard Pollak +# SPDX-License-Identifier: Apache-2.0 + +description: | + The BME680 is an integrated environmental sensor that measures + temperature, pressure, humidity and air quality + +compatible: "bosch,bme680" + +include: spi-device.yaml From 9548c78413d02e1c5523718b79be8111c4c9df05 Mon Sep 17 00:00:00 2001 From: Leonard Pollak Date: Sun, 20 Feb 2022 10:10:08 +0100 Subject: [PATCH 4/4] tests: drivers: Add bme680-spi to build_all Add BME680 (SPI) sensor driver to build_all tests. Signed-off-by: Leonard Pollak --- tests/drivers/build_all/sensor/spi.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/drivers/build_all/sensor/spi.dtsi b/tests/drivers/build_all/sensor/spi.dtsi index 2b99c05504bac..f5a65c0092dee 100644 --- a/tests/drivers/build_all/sensor/spi.dtsi +++ b/tests/drivers/build_all/sensor/spi.dtsi @@ -337,3 +337,10 @@ test_spi_icm42670: icm42670@3c { gyro-hz = <800>; gyro-fs = <2000>; }; + +test_spi_bme680: bme680@3c { + compatible = "bosch,bme680"; + label = "BME680"; + reg = <0x3c>; + spi-max-frequency = <0>; +};