-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Add support Rohm bh1750 ambient light sensor #53815
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
jeronimoagullo
wants to merge
1
commit into
zephyrproject-rtos:main
from
jeronimoagullo:bh1750_sensor_driver
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <zephyr/device.h> | ||
| #include <zephyr/devicetree.h> | ||
| #include <zephyr/drivers/sensor.h> | ||
| #include <zephyr/drivers/i2c.h> | ||
| #include <zephyr/logging/log.h> | ||
| #include <zephyr/kernel.h> | ||
| #include <zephyr/devicetree.h> | ||
| #include <zephyr/sys/byteorder.h> | ||
| #include <zephyr/sys/__assert.h> | ||
| #include <zephyr/logging/log.h> | ||
| #include <zephyr/sys/util_macro.h> | ||
|
|
||
| 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) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could benefit from a more meaningful name, something like MEASUREMENT_MODE |
||
|
|
||
| /* 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) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You haven't made any changes to this file; don't add your copyright.