diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index f276bc7c402be..dff121da69461 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory_ifdef(CONFIG_AMG88XX amg88xx) add_subdirectory_ifdef(CONFIG_AMS_AS5600 ams_as5600) add_subdirectory_ifdef(CONFIG_AMS_IAQ_CORE ams_iAQcore) add_subdirectory_ifdef(CONFIG_APDS9960 apds9960) +add_subdirectory_ifdef(CONFIG_BH1750 bh1750) add_subdirectory_ifdef(CONFIG_BMA280 bma280) add_subdirectory_ifdef(CONFIG_BMC150_MAGN bmc150_magn) add_subdirectory_ifdef(CONFIG_BME280 bme280) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index ffdb74c424c4c..18cfafae50988 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -59,6 +59,8 @@ source "drivers/sensor/ams_iAQcore/Kconfig" source "drivers/sensor/apds9960/Kconfig" +source "drivers/sensor/bh1750/Kconfig" + source "drivers/sensor/bma280/Kconfig" source "drivers/sensor/bmc150_magn/Kconfig" diff --git a/drivers/sensor/bh1750/CMakeLists.txt b/drivers/sensor/bh1750/CMakeLists.txt new file mode 100644 index 0000000000000..41ab9c53551b8 --- /dev/null +++ b/drivers/sensor/bh1750/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(bh1750.c) diff --git a/drivers/sensor/bh1750/Kconfig b/drivers/sensor/bh1750/Kconfig new file mode 100644 index 0000000000000..9df2261173d3a --- /dev/null +++ b/drivers/sensor/bh1750/Kconfig @@ -0,0 +1,12 @@ +# BH1750 ambient light sensor configuration options + +# Copyright (c) 2022 Michal Morsisko +# SPDX-License-Identifier: Apache-2.0 + +config BH1750 + bool "BH1750 ambient light sensor" + default y + depends on DT_HAS_ROHM_BH1750_ENABLED + select I2C + help + Enable driver for BH1750 ambient light sensor. diff --git a/drivers/sensor/bh1750/bh1750.c b/drivers/sensor/bh1750/bh1750.c new file mode 100644 index 0000000000000..09a3de3ef9535 --- /dev/null +++ b/drivers/sensor/bh1750/bh1750.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2022, Michal Morsisko + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT rohm_bh1750 + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(BH1750, CONFIG_SENSOR_LOG_LEVEL); + +#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 +#define BH1750_CONTINUOUS_HIGH_RES_MODE_2 0x11 +#define BH1750_CONTINUOUS_LOW_RES_MODE 0x13 +#define BH1750_ONE_TIME_HIGH_RES_MODE 0x20 +#define BH1750_ONE_TIME_HIGH_RES_MODE_2 0x21 +#define BH1750_ONE_TIME_LOW_RES_MODE 0x23 +#define BH1750_MTREG_HIGH_BYTE 0x40 +#define BH1750_MTREG_LOW_BYTE 0x60 +#define BH1750_MTREG_HIGH_BYTE_MASK 0xE0 +#define BH1750_MTREG_LOW_BYTE_MASK 0x1F + +#define BH1750_DEFAULT_MTREG 69U +#define BH1750_LOW_RES_MODE_MAX_WAIT 24U +#define BH1750_HIGH_RES_MODE_MAX_WAIT 180U +#define BH1750_LOW_RES_MODE_TYPICAL_WAIT 16U +#define BH1750_HIGH_RES_MODE_TYPICAL_WAIT 120U + +#define BH1750_LOW_RES_DTS_ENUM 0U +#define BH1750_HIGH_RES_DTS_ENUM 1U +#define BH1750_HIGH_RES_2_DTS_ENUM 2U + +struct bh1750_dev_config { + struct i2c_dt_spec bus; + uint8_t resolution; + uint8_t mtreg; +}; + +struct bh1750_data { + uint16_t sample; +}; + +static int bh1750_opcode_read(const struct device *dev, uint8_t opcode, + uint16_t *val) +{ + const struct bh1750_dev_config *cfg = dev->config; + int rc; + + rc = i2c_burst_read_dt(&cfg->bus, opcode, (uint8_t *)val, 2); + if (rc < 0) { + return rc; + } + + *val = sys_be16_to_cpu(*val); + return 0; +} + +static int bh1750_opcode_write(const struct device *dev, uint8_t opcode) +{ + const struct bh1750_dev_config *cfg = dev->config; + + return i2c_write_dt(&cfg->bus, &opcode, 1); +} + +static int bh1750_mtreg_write(const struct device *dev, uint8_t mtreg) +{ + int rc; + uint8_t high_byte = mtreg & BH1750_MTREG_HIGH_BYTE_MASK; + uint8_t low_byte = mtreg & BH1750_MTREG_LOW_BYTE_MASK; + + rc = bh1750_opcode_write(dev, BH1750_MTREG_HIGH_BYTE | (high_byte >> 5)); + if (rc < 0) { + LOG_ERR("%s, Failed to write high byte of mtreg!", + dev->name); + return rc; + } + + rc = bh1750_opcode_write(dev, BH1750_MTREG_LOW_BYTE | low_byte); + if (rc < 0) { + LOG_ERR("%s, Failed to write low byte of mtreg!", + dev->name); + return rc; + } + + return 0; +} + +static uint8_t bh1750_get_mode_from_dts_device(const struct device *dev) +{ + const struct bh1750_dev_config *cfg = dev->config; + + if (cfg->resolution == BH1750_HIGH_RES_2_DTS_ENUM) { + return BH1750_ONE_TIME_HIGH_RES_MODE_2; + } else if (cfg->resolution == BH1750_HIGH_RES_DTS_ENUM) { + return BH1750_ONE_TIME_HIGH_RES_MODE; + } else { + return BH1750_ONE_TIME_LOW_RES_MODE; + } +} + +static int bh1750_get_wait_time_from_dts_device(const struct device *dev) +{ + const struct bh1750_dev_config *cfg = dev->config; + + if (cfg->resolution == BH1750_HIGH_RES_2_DTS_ENUM) { + return BH1750_HIGH_RES_MODE_MAX_WAIT; + } else if (cfg->resolution == BH1750_HIGH_RES_DTS_ENUM) { + return BH1750_HIGH_RES_MODE_MAX_WAIT; + } else { + return BH1750_LOW_RES_MODE_MAX_WAIT; + } +} + +static int bh1750_sample_fetch(const struct device *dev, + enum sensor_channel chan) +{ + const struct bh1750_dev_config *cfg = dev->config; + struct bh1750_data *drv_data = dev->data; + const int max_wait = bh1750_get_wait_time_from_dts_device(dev); + const uint8_t mode = bh1750_get_mode_from_dts_device(dev); + int rc; + int wait_time; + + if (chan != SENSOR_CHAN_ALL && + chan != SENSOR_CHAN_LIGHT) { + return -ENOTSUP; + } + + /* Start the measurement */ + rc = bh1750_opcode_write(dev, mode); + if (rc < 0) { + LOG_ERR("%s, Failed to start measurement!", + dev->name); + return rc; + } + + /* Calculate measurement time */ + wait_time = (max_wait * (cfg->mtreg * 10000 / BH1750_DEFAULT_MTREG)) / 10000; + + /* Wait for the measurement to be stored in the sensor memory */ + k_msleep(wait_time); + + /* Fetch result */ + rc = bh1750_opcode_read(dev, mode, &drv_data->sample); + if (rc < 0) { + LOG_ERR("%s: Failed to read measurement result!", + dev->name); + return rc; + } + + return 0; +} + +static int bh1750_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + const struct bh1750_dev_config *cfg = dev->config; + struct bh1750_data *drv_data = dev->data; + uint32_t tmp; + + if (chan != SENSOR_CHAN_ALL && + chan != SENSOR_CHAN_LIGHT) { + return -ENOTSUP; + } + + /* See datasheet (Technical note 11046EDT01), page 11 + * https://www.mouser.com/datasheet/2/348/Rohm_11162017_ROHMS34826-1-1279292.pdf + * for more information how to convert raw sample to lx + */ + tmp = (drv_data->sample * 1000 / 12) * (BH1750_DEFAULT_MTREG * 100 / cfg->mtreg); + + if (cfg->resolution == BH1750_HIGH_RES_2_DTS_ENUM) { + tmp /= 2; + } + + val->val1 = tmp / 10000; + val->val2 = (tmp % 10000) * 100; + + return 0; +} + +static const struct sensor_driver_api bh1750_driver_api = { + .sample_fetch = bh1750_sample_fetch, + .channel_get = bh1750_channel_get +}; + +static int bh1750_init(const struct device *dev) +{ + const struct bh1750_dev_config *cfg = dev->config; + + if (!device_is_ready(cfg->bus.bus)) { + LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name); + return -ENODEV; + } + + if (cfg->mtreg != BH1750_DEFAULT_MTREG) { + bh1750_mtreg_write(dev, cfg->mtreg); + } + + return 0; +} + +#define DEFINE_BH1750(_num) \ + static struct bh1750_data bh1750_data_##_num; \ + static const struct bh1750_dev_config bh1750_config_##_num = { \ + .bus = I2C_DT_SPEC_INST_GET(_num), \ + .mtreg = DT_INST_PROP(_num, mtreg), \ + .resolution = DT_INST_PROP(_num, resolution) \ + }; \ + SENSOR_DEVICE_DT_INST_DEFINE(_num, bh1750_init, NULL, \ + &bh1750_data_##_num, &bh1750_config_##_num, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &bh1750_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(DEFINE_BH1750) diff --git a/dts/bindings/sensor/rohm,bh1750.yaml b/dts/bindings/sensor/rohm,bh1750.yaml new file mode 100644 index 0000000000000..b60ae1db189b0 --- /dev/null +++ b/dts/bindings/sensor/rohm,bh1750.yaml @@ -0,0 +1,37 @@ +# Copyright (c) 2022, Michal Morsisko +# SPDX-License-Identifier: Apache-2.0 + +description: Rohm BH1750 ambient light sensor. + +compatible: "rohm,bh1750" + +include: [sensor-device.yaml, i2c-device.yaml] + +properties: + resolution: + type: int + default: 1 + description: | + Resolution of the measurement result + 0 = low resolution + 1 = high resolution + 2 = high resolution 2 + The default mode number 1 is mentioned + in the datasheet as recommended by the + sensor manufacturer. One should pick + other mode, if higher or lower resolution + is desired. + enum: + - 0 + - 1 + - 2 + mtreg: + type: int + default: 69 + description: | + Duration of single measurement process. + The greater the value, the longer the duration + and greater accuracy. After sensor power-on + mtreg register is set to default value of + this property (chosen by sensor manufacturer): 69. + Valid values are in range 31-254 (inclusive). diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index db6cfab33a902..a40ce6770a865 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -646,3 +646,8 @@ test_i2c_as5600: as5600@64 { compatible = "ams,as5600"; reg = <0x64>; }; + +test_i2c_bh1750: bh1750@65 { + compatible = "rohm,bh1750"; + reg = <0x65>; +};