223 lines
6.1 KiB
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)
|