diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index ffbef132a99e3..942c9cbeffd65 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -10,7 +10,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_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/bh1750/Kconfig b/drivers/sensor/bh1750/Kconfig index 9df2261173d3a..774304ebbcfa9 100644 --- a/drivers/sensor/bh1750/Kconfig +++ b/drivers/sensor/bh1750/Kconfig @@ -1,6 +1,7 @@ # BH1750 ambient light sensor configuration options - # Copyright (c) 2022 Michal Morsisko +# Copyright (c) 2023 Sngular People SL. Jeronimo Agullo +# # SPDX-License-Identifier: Apache-2.0 config BH1750 diff --git a/drivers/sensor/bh1750/bh1750.c b/drivers/sensor/bh1750/bh1750.c index 09a3de3ef9535..d8c07be72f1a0 100644 --- a/drivers/sensor/bh1750/bh1750.c +++ b/drivers/sensor/bh1750/bh1750.c @@ -1,222 +1,222 @@ +/* bh1750.c - Driver for BH1750 ambient light sensor */ /* * Copyright (c) 2022, Michal Morsisko + * Copyright (c) 2023, Sngular People SL. Jeronimo Agullo * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT rohm_bh1750 +#include +#include #include #include -#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; -}; +#define BH1750_POWER_DOWN 0x00 +#define BH1750_POWER_ON 0x01 +#define BH1750_RESET 0x07 -struct bh1750_data { - uint16_t sample; -}; +#define BH1750_MTREG_HIGH_BYTE 0x40 +#define BH1750_MTREG_LOW_BYTE 0x60 -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; - } +enum bh1750_resolution { + BH1750_H_RESOLUTION_MODE = 0, + BH1750_H_RESOLUTION_MODE2 = 1, + BH1750_L_RESOLUTION_MODE = 3 +}; - *val = sys_be16_to_cpu(*val); - return 0; -} +enum bh1750_mode { + BH1750_MODE_CONTINUOUSLY = 0, + BH1750_MODE_ONE_TIME = 1 +}; -static int bh1750_opcode_write(const struct device *dev, uint8_t opcode) -{ - const struct bh1750_dev_config *cfg = dev->config; +struct bh1750_data { + float ambient_light; +}; - return i2c_write_dt(&cfg->bus, &opcode, 1); -} +struct bh1750_config { + struct i2c_dt_spec i2c; + enum bh1750_resolution resolution; + enum bh1750_mode mode; + uint8_t mtreg; +}; -static int bh1750_mtreg_write(const struct device *dev, uint8_t mtreg) +static int bh1750_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) { - int rc; - uint8_t high_byte = mtreg & BH1750_MTREG_HIGH_BYTE_MASK; - uint8_t low_byte = mtreg & BH1750_MTREG_LOW_BYTE_MASK; + struct bh1750_data *data = dev->data; - 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; + if (chan != SENSOR_CHAN_LIGHT) { + LOG_ERR("incorrect channel, please set SENSOR_CHAN_LIGHT"); + return -ENOTSUP; } - 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; - } + val->val1 = (int32_t)(data->ambient_light); + val->val2 = (data->ambient_light - val->val1) * 1000000; 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) + 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; + const struct bh1750_config *cfg = dev->config; + uint8_t buf[2]; + int rc = 0; - if (chan != SENSOR_CHAN_ALL && - chan != SENSOR_CHAN_LIGHT) { - return -ENOTSUP; - } + /* read raw value data */ + rc = i2c_read_dt(&cfg->i2c, buf, 2); - /* Start the measurement */ - rc = bh1750_opcode_write(dev, mode); if (rc < 0) { - LOG_ERR("%s, Failed to start measurement!", - dev->name); - return rc; + LOG_ERR("Failed to read data sample."); + return -EIO; } - /* Calculate measurement time */ - wait_time = (max_wait * (cfg->mtreg * 10000 / BH1750_DEFAULT_MTREG)) / 10000; + /* We need to divide by 1.2 since it is the accuracy according to datasheet */ + float data_value = (((uint16_t)buf[0] << 8) | buf[1]) / 1.2; - /* 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; + /* We need to divide by 2 in case of resolution mode 2 */ + if (cfg->resolution == BH1750_H_RESOLUTION_MODE2) { + data_value /= 2; } + LOG_DBG("Light value: %f", data_value); + drv_data->ambient_light = data_value; + return 0; } -static int bh1750_channel_get(const struct device *dev, - enum sensor_channel chan, - struct sensor_value *val) +static int bh1750_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, const struct sensor_value *val) { - const struct bh1750_dev_config *cfg = dev->config; - struct bh1750_data *drv_data = dev->data; - uint32_t tmp; + const struct bh1750_config *cfg = dev->config; + uint8_t mode; + uint8_t buf; + int rc = 0; - if (chan != SENSOR_CHAN_ALL && - chan != SENSOR_CHAN_LIGHT) { + if (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 (attr == SENSOR_ATTR_CONFIGURATION) { + + /* value in val1 sets the measurement mode */ + if (val->val1 != 0) { + /* value in val1 sets the sensor mode */ + if (val->val1 == BH1750_MODE_CONTINUOUSLY) { + mode = BIT(4); + LOG_DBG("setting continuously mode"); + } else if (val->val1 == BH1750_MODE_ONE_TIME) { + mode = BIT(5); + LOG_DBG("setting one time mode"); + } else { + return -EINVAL; + } + buf = cfg->resolution | mode; + rc = i2c_write_dt(&cfg->i2c, &buf, 1); + if (rc < 0) { + LOG_ERR("Error writing in i2c bus: %d", rc); + return -ENXIO; + } + LOG_INF("The mode was set correctly: 0x%x", buf); + } + + /* value in val2 sets the mtreg time only if it is not zero */ + if (val->val2 != 0) { + LOG_INF("mtreg: 0x%x", val->val2); + /* mtreg high bit */ + LOG_INF("mask1: 0x%x", GENMASK(7, 5)); + buf = BH1750_MTREG_HIGH_BYTE | ((val->val2 & GENMASK(7, 5)) >> 5); + LOG_INF("buf high: 0x%x", buf); + rc = i2c_write_dt(&cfg->i2c, &buf, 1); + + /* mtreg low bit */ + buf = BH1750_MTREG_LOW_BYTE | (val->val2 & GENMASK(4, 0)); + LOG_INF("buf low: 0x%x", buf); + rc = i2c_write_dt(&cfg->i2c, &buf, 1); + LOG_INF("The mode was set correctly: 0x%x", buf); + } - if (cfg->resolution == BH1750_HIGH_RES_2_DTS_ENUM) { - tmp /= 2; + } else { + return -ENOTSUP; } - val->val1 = tmp / 10000; - val->val2 = (tmp % 10000) * 100; - + /* NOTE: in case of BH1750_MODE_ONE_TIME, + * we need to wait for the new measurement to be taken before calling sample fetch + * According to datasheet page 5 section "Measurement mode explanation" + * 120 ms for H_RESOLUTION_MODE and H_RESOLUTION_MODE2 + * 16 ms for L_RESOLUTION_MODE + */ return 0; } static const struct sensor_driver_api bh1750_driver_api = { .sample_fetch = bh1750_sample_fetch, - .channel_get = bh1750_channel_get + .channel_get = bh1750_channel_get, + .attr_set = bh1750_attr_set, }; static int bh1750_init(const struct device *dev) { - const struct bh1750_dev_config *cfg = dev->config; + const struct bh1750_config *cfg = dev->config; + uint8_t mode; + int rc = 0; - if (!device_is_ready(cfg->bus.bus)) { - LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name); + if (!device_is_ready(cfg->i2c.bus)) { + LOG_ERR("Bus device is not ready"); return -ENODEV; } - if (cfg->mtreg != BH1750_DEFAULT_MTREG) { - bh1750_mtreg_write(dev, cfg->mtreg); + LOG_INF("Bus device is ready"); + + /* Set mode */ + if (cfg->mode == BH1750_MODE_CONTINUOUSLY) { + mode = BIT(4); + LOG_DBG("setting continuously mode"); + } else if (cfg->mode == BH1750_MODE_ONE_TIME) { + mode = BIT(5); + LOG_DBG("setting one time mode"); + } else { + return -EINVAL; } + uint8_t buf = cfg->resolution | mode; + + rc = i2c_write_dt(&cfg->i2c, &buf, 1); + if (rc < 0) { + LOG_ERR("Error writing in i2c bus: %d", rc); + return -ENXIO; + } + + LOG_INF("The mode was set correctly: 0x%x", buf); + 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) +#define BH1750_DEFINE(inst) \ + static struct bh1750_data bh1750_data_##inst; \ + \ + /* pull instance configuration from Devicetree */ \ + static const struct bh1750_config bh1750_config_##inst = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + .resolution = DT_INST_PROP(inst, resolution), \ + .mode = DT_INST_PROP(inst, mode), \ + .mtreg = DT_INST_PROP(inst, mtreg), \ + }; \ + \ + /* define a new device inst. with the data and config defined above*/ \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, bh1750_init, NULL, \ + &bh1750_data_##inst, &bh1750_config_##inst, \ + POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ + &bh1750_driver_api); \ + +DT_INST_FOREACH_STATUS_OKAY(BH1750_DEFINE) diff --git a/dts/bindings/sensor/rohm,bh1750.yaml b/dts/bindings/sensor/rohm,bh1750.yaml index b60ae1db189b0..184c383642ba0 100644 --- a/dts/bindings/sensor/rohm,bh1750.yaml +++ b/dts/bindings/sensor/rohm,bh1750.yaml @@ -1,7 +1,8 @@ # Copyright (c) 2022, Michal Morsisko +# Copyright (c) 2023, Sngular People SL. Jeronimo Agullo # SPDX-License-Identifier: Apache-2.0 -description: Rohm BH1750 ambient light sensor. +description: BH1750 ambient light sensor sensor compatible: "rohm,bh1750" @@ -12,19 +13,30 @@ properties: 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. + Sensor resolution. + The values are the corresponding hexadecimal values to + write to configuration register. + Default H-resolution mode (1lx) since it is the recommended + mode in the page 5 of datasheet. enum: - - 0 - - 1 - - 2 + - 0 # 0.5 lx - Measurement Time is typically 120ms + - 1 # 1 lx - Measurement Time is typically 120ms + - 3 # 4 lx - Measurement Time is typically 16ms + + mode: + type: int + default: 0 + description: | + Specifies the measurement mode used by the driver. + The options are continuously mode (one new measurement + is ready continuously) or one-time-mode (perform only one + measurement and then set sensor power down mode). + Default CONTINUOUSLY-MODE for continuous sensing, more + common value. + enum: + - 0 # CONTINUOUSLY-MODE + - 1 # ONE-TIME-MODE + mtreg: type: int default: 69