zephyr/drivers/sensor/bh1750/bh1750.c

223 lines
6.1 KiB
C

/*
* Copyright (c) 2022, Michal Morsisko
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT rohm_bh1750
#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>
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)