drivers: sensor: add tsl2561 basic support
Add basic support for ams TSL2561 light sensor. Triggers, attributes and manual integration time are currently not supported. Signed-off-by: Gustavo Silva <gustavograzs@gmail.com>
This commit is contained in:
parent
9bb44b8e5f
commit
b4625d6f13
7 changed files with 396 additions and 0 deletions
|
@ -146,6 +146,7 @@ add_subdirectory_ifdef(CONFIG_TMP108 tmp108)
|
|||
add_subdirectory_ifdef(CONFIG_TMP112 tmp112)
|
||||
add_subdirectory_ifdef(CONFIG_TMP116 tmp116)
|
||||
add_subdirectory_ifdef(CONFIG_TSL2540 tsl2540)
|
||||
add_subdirectory_ifdef(CONFIG_TSL2561 tsl2561)
|
||||
add_subdirectory_ifdef(CONFIG_VCMP_IT8XXX2 ite_vcmp_it8xxx2)
|
||||
add_subdirectory_ifdef(CONFIG_VCNL4040 vcnl4040)
|
||||
add_subdirectory_ifdef(CONFIG_VEML7700 veml7700)
|
||||
|
|
|
@ -202,6 +202,7 @@ source "drivers/sensor/tmp108/Kconfig"
|
|||
source "drivers/sensor/tmp112/Kconfig"
|
||||
source "drivers/sensor/tmp116/Kconfig"
|
||||
source "drivers/sensor/tsl2540/Kconfig"
|
||||
source "drivers/sensor/tsl2561/Kconfig"
|
||||
source "drivers/sensor/vcnl4040/Kconfig"
|
||||
source "drivers/sensor/veml7700/Kconfig"
|
||||
source "drivers/sensor/vl53l0x/Kconfig"
|
||||
|
|
5
drivers/sensor/tsl2561/CMakeLists.txt
Normal file
5
drivers/sensor/tsl2561/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(tsl2561.c)
|
10
drivers/sensor/tsl2561/Kconfig
Normal file
10
drivers/sensor/tsl2561/Kconfig
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Copyright (c) 2023 Gustavo Silva
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config TSL2561
|
||||
bool "OSRAM-AMS TSL2561 light sensor"
|
||||
default y
|
||||
depends on DT_HAS_AMS_TSL2561_ENABLED
|
||||
select I2C
|
||||
help
|
||||
Enable driver for TSL2561 sensor.
|
346
drivers/sensor/tsl2561/tsl2561.c
Normal file
346
drivers/sensor/tsl2561/tsl2561.c
Normal file
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Gustavo Silva
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT ams_tsl2561
|
||||
|
||||
#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(TSL2561, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
#define TSL2561_CHIP_ID 0x05
|
||||
|
||||
#define TSL2561_GAIN_1X 0x00
|
||||
#define TSL2561_GAIN_16X 0x01
|
||||
|
||||
#define TSL2561_INTEGRATION_13MS 0x00
|
||||
#define TSL2561_INTEGRATION_101MS 0x01
|
||||
#define TSL2561_INTEGRATION_402MS 0x02
|
||||
|
||||
/* Register set */
|
||||
#define TSL2561_REG_CONTROL 0x00
|
||||
#define TSL2561_REG_TIMING 0x01
|
||||
#define TSL2561_REG_THRESHLOWLOW 0x02
|
||||
#define TSL2561_REG_THRESHLOWHIGH 0x03
|
||||
#define TSL2561_REG_THRESHHIGHLOW 0x04
|
||||
#define TSL2561_REG_THRESHHIGHHIGH 0x05
|
||||
#define TSL2561_REG_INTERRUPT 0x06
|
||||
#define TSL2561_REG_ID 0x0A
|
||||
#define TSL2561_REG_DATA0LOW 0x0C
|
||||
#define TSL2561_REG_DATA0HIGH 0x0D
|
||||
#define TSL2561_REG_DATA1LOW 0x0E
|
||||
#define TSL2561_REG_DATA1HIGH 0x0F
|
||||
|
||||
/* Command register fields */
|
||||
#define TSL2561_COMMAND_CMD BIT(7)
|
||||
#define TSL2561_COMMAND_WORD BIT(5)
|
||||
|
||||
/* Control register fields */
|
||||
#define TSL2561_CONTROL_POWER_UP 0x03
|
||||
#define TSL2561_CONTROL_POWER_DOWN 0x00
|
||||
|
||||
/* Timing register fields */
|
||||
#define TSL2561_TIMING_GAIN BIT(4)
|
||||
#define TSL2561_TIMING_INTEG GENMASK(1, 0)
|
||||
|
||||
/* ID register part number mask */
|
||||
#define TSL2561_ID_PARTNO GENMASK(7, 4)
|
||||
|
||||
/* Lux calculation constants */
|
||||
#define TSL2561_LUX_SCALE 14U
|
||||
#define TSL2561_RATIO_SCALE 9U
|
||||
#define TSL2561_CH_SCALE 10U
|
||||
#define TSL2561_CHSCALE_TINT0 0x7517
|
||||
#define TSL2561_CHSCALE_TINT1 0x0FE7
|
||||
|
||||
#define TSL2561_LUX_K1T 0X0040 /* 0.125 * 2^RATIO_SCALE */
|
||||
#define TSL2561_LUX_B1T 0X01F2 /* 0.0304 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_M1T 0X01BE /* 0.0272 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_K2T 0X0080 /* 0.250 * 2^RATIO_SCALE */
|
||||
#define TSL2561_LUX_B2T 0X0214 /* 0.0325 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_M2T 0X02D1 /* 0.0440 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_K3T 0X00C0 /* 0.375 * 2^RATIO_SCALE */
|
||||
#define TSL2561_LUX_B3T 0X023F /* 0.0351 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_M3T 0X037B /* 0.0544 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_K4T 0X0100 /* 0.50 * 2^RATIO_SCALE */
|
||||
#define TSL2561_LUX_B4T 0X0270 /* 0.0381 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_M4T 0X03FE /* 0.0624 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_K5T 0X0138 /* 0.61 * 2^RATIO_SCALE */
|
||||
#define TSL2561_LUX_B5T 0X016F /* 0.0224 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_M5T 0X01FC /* 0.0310 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_K6T 0X019A /* 0.80 * 2^RATIO_SCALE */
|
||||
#define TSL2561_LUX_B6T 0X00D2 /* 0.0128 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_M6T 0X00FB /* 0.0153 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_K7T 0X029A /* 1.3 * 2^RATIO_SCALE */
|
||||
#define TSL2561_LUX_B7T 0X0018 /* 0.00146 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_M7T 0X0012 /* 0.00112 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_K8T 0X029A /* 1.3 * 2^RATIO_SCALE */
|
||||
#define TSL2561_LUX_B8T 0X0000 /* 0.000 * 2^LUX_SCALE */
|
||||
#define TSL2561_LUX_M8T 0X0000 /* 0.000 * 2^LUX_SCALE */
|
||||
|
||||
struct tsl2561_config {
|
||||
struct i2c_dt_spec i2c;
|
||||
uint16_t integration_time;
|
||||
uint8_t gain;
|
||||
};
|
||||
|
||||
struct tsl2561_data {
|
||||
uint16_t ch0;
|
||||
uint16_t ch1;
|
||||
uint32_t ch_scale;
|
||||
};
|
||||
|
||||
static int tsl2561_reg_read(const struct device *dev, uint8_t reg, uint8_t *buf, uint8_t size)
|
||||
{
|
||||
int ret;
|
||||
const struct tsl2561_config *config = dev->config;
|
||||
uint8_t cmd = (TSL2561_COMMAND_CMD | TSL2561_COMMAND_WORD | reg);
|
||||
|
||||
ret = i2c_write_read_dt(&config->i2c, &cmd, 1U, buf, size);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed reading register 0x%02x", reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tsl2561_reg_write(const struct device *dev, uint8_t reg, uint8_t val)
|
||||
{
|
||||
int ret;
|
||||
const struct tsl2561_config *config = dev->config;
|
||||
uint8_t buf[2];
|
||||
|
||||
buf[0] = (TSL2561_COMMAND_CMD | TSL2561_COMMAND_WORD | reg);
|
||||
buf[1] = val;
|
||||
|
||||
ret = i2c_write_dt(&config->i2c, buf, 2U);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed writing register 0x%02x", reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tsl2561_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
||||
{
|
||||
const struct tsl2561_config *config = dev->config;
|
||||
struct tsl2561_data *data = dev->data;
|
||||
uint8_t bytes[2];
|
||||
uint8_t ret;
|
||||
|
||||
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_LIGHT) {
|
||||
LOG_ERR("Unsupported sensor channel");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ret = tsl2561_reg_write(dev, TSL2561_REG_CONTROL, TSL2561_CONTROL_POWER_UP);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to power up device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Short sleep after power up. Not in the datasheet, but found by trial and error */
|
||||
k_msleep(5);
|
||||
|
||||
k_msleep(config->integration_time);
|
||||
|
||||
/* Read data register's lower and upper bytes consecutively */
|
||||
ret = tsl2561_reg_read(dev, TSL2561_REG_DATA0LOW, bytes, 2U);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed reading channel0 data");
|
||||
return ret;
|
||||
}
|
||||
data->ch0 = bytes[1] << 8 | bytes[0];
|
||||
|
||||
ret = tsl2561_reg_read(dev, TSL2561_REG_DATA1LOW, bytes, 2U);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed reading channel1 data");
|
||||
return ret;
|
||||
}
|
||||
data->ch1 = bytes[1] << 8 | bytes[0];
|
||||
|
||||
ret = tsl2561_reg_write(dev, TSL2561_REG_CONTROL, TSL2561_CONTROL_POWER_DOWN);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to power down device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
LOG_DBG("channel0: 0x%x; channel1: 0x%x", data->ch0, data->ch1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tsl2561_channel_get(const struct device *dev, enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
struct tsl2561_data *data = dev->data;
|
||||
uint32_t channel0;
|
||||
uint32_t channel1;
|
||||
|
||||
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_LIGHT) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
channel0 = (data->ch0 * data->ch_scale) >> TSL2561_CH_SCALE;
|
||||
channel1 = (data->ch1 * data->ch_scale) >> TSL2561_CH_SCALE;
|
||||
|
||||
uint32_t ratio1 = 0;
|
||||
|
||||
if (channel0 != 0) {
|
||||
ratio1 = (channel1 << (TSL2561_RATIO_SCALE + 1)) / channel0;
|
||||
}
|
||||
|
||||
/* Round the ratio value */
|
||||
uint32_t ratio = (ratio1 + 1) >> 1;
|
||||
|
||||
uint32_t b = 0;
|
||||
uint32_t m = 0;
|
||||
|
||||
if (ratio <= TSL2561_LUX_K1T) {
|
||||
b = TSL2561_LUX_B1T;
|
||||
m = TSL2561_LUX_M1T;
|
||||
} else if (ratio <= TSL2561_LUX_K2T) {
|
||||
b = TSL2561_LUX_B2T;
|
||||
m = TSL2561_LUX_M2T;
|
||||
} else if (ratio <= TSL2561_LUX_K3T) {
|
||||
b = TSL2561_LUX_B3T;
|
||||
m = TSL2561_LUX_M3T;
|
||||
} else if (ratio <= TSL2561_LUX_K4T) {
|
||||
b = TSL2561_LUX_B4T;
|
||||
m = TSL2561_LUX_M4T;
|
||||
} else if (ratio <= TSL2561_LUX_K5T) {
|
||||
b = TSL2561_LUX_B5T;
|
||||
m = TSL2561_LUX_M5T;
|
||||
} else if (ratio <= TSL2561_LUX_K6T) {
|
||||
b = TSL2561_LUX_B6T;
|
||||
m = TSL2561_LUX_M6T;
|
||||
} else if (ratio <= TSL2561_LUX_K7T) {
|
||||
b = TSL2561_LUX_B7T;
|
||||
m = TSL2561_LUX_M7T;
|
||||
} else if (ratio > TSL2561_LUX_K8T) {
|
||||
b = TSL2561_LUX_B8T;
|
||||
m = TSL2561_LUX_M8T;
|
||||
}
|
||||
|
||||
int32_t tmp = ((channel0 * b) - (channel1 * m));
|
||||
|
||||
/* Round LSB (2^(LUX_SCALE−1)) */
|
||||
tmp += (1 << (TSL2561_LUX_SCALE - 1));
|
||||
|
||||
/* Strip off fractional portion */
|
||||
val->val1 = tmp >> TSL2561_LUX_SCALE;
|
||||
|
||||
val->val2 = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sensor_driver_api tsl2561_driver_api = {
|
||||
.sample_fetch = tsl2561_sample_fetch,
|
||||
.channel_get = tsl2561_channel_get
|
||||
};
|
||||
|
||||
static int tsl2561_sensor_setup(const struct device *dev)
|
||||
{
|
||||
const struct tsl2561_config *config = dev->config;
|
||||
struct tsl2561_data *data = dev->data;
|
||||
uint8_t timing_reg;
|
||||
uint8_t chip_id;
|
||||
uint8_t tmp;
|
||||
int ret;
|
||||
|
||||
ret = tsl2561_reg_read(dev, TSL2561_REG_ID, &chip_id, 1U);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed reading chip ID");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (FIELD_GET(TSL2561_ID_PARTNO, chip_id) != TSL2561_CHIP_ID) {
|
||||
LOG_ERR("Chip ID is invalid! Device @%02x is not TSL2561!", config->i2c.addr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch (config->integration_time) {
|
||||
case 13:
|
||||
tmp = TSL2561_INTEGRATION_13MS;
|
||||
data->ch_scale = TSL2561_CHSCALE_TINT0;
|
||||
break;
|
||||
case 101:
|
||||
tmp = TSL2561_INTEGRATION_101MS;
|
||||
data->ch_scale = TSL2561_CHSCALE_TINT1;
|
||||
break;
|
||||
case 402:
|
||||
tmp = TSL2561_INTEGRATION_402MS;
|
||||
data->ch_scale = (1 << TSL2561_CH_SCALE);
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Invalid integration time");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
timing_reg = TSL2561_TIMING_INTEG & tmp;
|
||||
|
||||
switch (config->gain) {
|
||||
case 1:
|
||||
tmp = TSL2561_GAIN_1X;
|
||||
data->ch_scale = data->ch_scale << 4;
|
||||
break;
|
||||
case 16:
|
||||
tmp = TSL2561_GAIN_16X;
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Invalid ADC gain");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
timing_reg |= FIELD_PREP(TSL2561_TIMING_GAIN, tmp);
|
||||
|
||||
ret = tsl2561_reg_write(dev, TSL2561_REG_TIMING, timing_reg);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed setting timing register");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tsl2561_init(const struct device *dev)
|
||||
{
|
||||
const struct tsl2561_config *config = dev->config;
|
||||
int ret;
|
||||
|
||||
if (!i2c_is_ready_dt(&config->i2c)) {
|
||||
LOG_ERR("I2C dev %s not ready", config->i2c.bus->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = tsl2561_sensor_setup(dev);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to configure device");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define TSL2561_INIT_INST(n) \
|
||||
static struct tsl2561_data tsl2561_data_##n; \
|
||||
static const struct tsl2561_config tsl2561_config_##n = { \
|
||||
.i2c = I2C_DT_SPEC_INST_GET(n), \
|
||||
.integration_time = DT_INST_PROP(n, integration_time), \
|
||||
.gain = DT_INST_PROP(n, gain)}; \
|
||||
SENSOR_DEVICE_DT_INST_DEFINE(n, tsl2561_init, NULL, &tsl2561_data_##n, \
|
||||
&tsl2561_config_##n, POST_KERNEL, \
|
||||
CONFIG_SENSOR_INIT_PRIORITY, &tsl2561_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(TSL2561_INIT_INST)
|
28
dts/bindings/sensor/ams,tsl2561.yaml
Normal file
28
dts/bindings/sensor/ams,tsl2561.yaml
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Copyright (c) 2023, Gustavo Silva
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: |
|
||||
OSRAM ams TSL2561 light sensor.
|
||||
|
||||
compatible: "ams,tsl2561"
|
||||
|
||||
include: [sensor-device.yaml, i2c-device.yaml]
|
||||
|
||||
properties:
|
||||
integration-time:
|
||||
type: int
|
||||
default: 402
|
||||
description: |
|
||||
ADC integration time in ms. The default value matches Timing Register's value at power on.
|
||||
enum:
|
||||
- 13
|
||||
- 101
|
||||
- 402
|
||||
gain:
|
||||
type: int
|
||||
default: 16
|
||||
description: |
|
||||
ADC gain factor. The default value matches Timing Register's value at power on.
|
||||
enum:
|
||||
- 1
|
||||
- 16
|
|
@ -794,3 +794,8 @@ test_i2c_adxl367: adxl367@77 {
|
|||
reg = <0x77>;
|
||||
odr = <4>;
|
||||
};
|
||||
|
||||
test_i2c_tsl2561: tsl2561@78 {
|
||||
compatible = "ams,tsl2561";
|
||||
reg = <0x78>;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue