sensors: Add driver for Vischay VCNL36825T Proximity Sensor

Driver for the Vishay VCNL36825T including power management support.

Signed-off-by: Juliane Schulze <juliane.schulze@deveritec.com>
This commit is contained in:
Juliane Schulze 2023-12-14 16:11:04 +01:00 committed by David Leach
parent 68bc981c52
commit 34cb22e919
8 changed files with 896 additions and 0 deletions

View file

@ -158,6 +158,7 @@ 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_VCNL36825T vcnl36825t)
add_subdirectory_ifdef(CONFIG_VEML7700 veml7700)
add_subdirectory_ifdef(CONFIG_VL53L0X vl53l0x)
add_subdirectory_ifdef(CONFIG_VL53L1X vl53l1x)

View file

@ -237,6 +237,7 @@ source "drivers/sensor/tmp116/Kconfig"
source "drivers/sensor/tsl2540/Kconfig"
source "drivers/sensor/tsl2561/Kconfig"
source "drivers/sensor/vcnl4040/Kconfig"
source "drivers/sensor/vcnl36825t/Kconfig"
source "drivers/sensor/veml7700/Kconfig"
source "drivers/sensor/vl53l0x/Kconfig"
source "drivers/sensor/vl53l1x/Kconfig"

View file

@ -0,0 +1,6 @@
# Copyright (c) 2024 Juliane Schulze, deveritec Gmbh
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources(vcnl36825t.c)

View file

@ -0,0 +1,12 @@
# VCNL36825T Proximity Sensor configuration options
# Copyright (c) 2024 Juliane Schulze, deveritec Gmbh
# SPDX-License-Identifier: Apache-2.0
config VCNL36825T
bool "VCNL36825T Proximity Sensor"
default y
depends on DT_HAS_VISHAY_VCNL36825T_ENABLED
select I2C
help
Enable driver for VCNL36825T sensors.

View file

@ -0,0 +1,478 @@
/*
* Copyright (c) 2024 Juliane Schulze, deveritec GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT vishay_vcnl36825t
#include "vcnl36825t.h"
#include <stdlib.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/device.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
LOG_MODULE_REGISTER(VCNL36825T, CONFIG_SENSOR_LOG_LEVEL);
static int vcnl36825t_read(const struct i2c_dt_spec *spec, uint8_t reg_addr, uint16_t *value)
{
uint8_t rx_buf[2];
int rc;
rc = i2c_write_read_dt(spec, &reg_addr, sizeof(reg_addr), rx_buf, sizeof(rx_buf));
if (rc < 0) {
return rc;
}
*value = sys_get_le16(rx_buf);
return 0;
}
static int vcnl36825t_write(const struct i2c_dt_spec *spec, uint8_t reg_addr, uint16_t value)
{
uint8_t tx_buf[3] = {reg_addr};
sys_put_le16(value, &tx_buf[1]);
return i2c_write_dt(spec, tx_buf, sizeof(tx_buf));
}
static int vcnl36825t_update(const struct i2c_dt_spec *spec, uint8_t reg_addr, uint16_t mask,
uint16_t value)
{
int rc;
uint16_t old_value, new_value;
rc = vcnl36825t_read(spec, reg_addr, &old_value);
if (rc < 0) {
return rc;
}
new_value = (old_value & ~mask) | (value & mask);
if (new_value == old_value) {
return 0;
}
return vcnl36825t_write(spec, reg_addr, new_value);
}
#if CONFIG_PM_DEVICE
static int vcnl36825t_pm_action(const struct device *dev, enum pm_device_action action)
{
const struct vcnl36825t_config *config = dev->config;
int rc = 0;
switch (action) {
case PM_DEVICE_ACTION_RESUME:
rc = vcnl36825t_update(&config->i2c, VCNL36825T_REG_PS_CONF1, VCNL36825T_PS_ON_MSK,
VCNL36825T_PS_ON);
if (rc < 0) {
return rc;
}
rc = vcnl36825t_update(&config->i2c, VCNL36825T_REG_PS_CONF2, VCNL36825T_PS_ST_MSK,
VCNL36825T_PS_ST_START);
if (rc < 0) {
return rc;
}
break;
case PM_DEVICE_ACTION_SUSPEND:
rc = vcnl36825t_update(&config->i2c, VCNL36825T_REG_PS_CONF2, VCNL36825T_PS_ST_MSK,
VCNL36825T_PS_ST_STOP);
if (rc < 0) {
return rc;
}
rc = vcnl36825t_update(&config->i2c, VCNL36825T_REG_PS_CONF1, VCNL36825T_PS_ON_MSK,
VCNL36825T_PS_OFF);
if (rc < 0) {
return rc;
}
break;
default:
LOG_ERR("action %d not supported", (int)action);
return -ENOTSUP;
}
return 0;
}
#endif
static int vcnl36825t_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
const struct vcnl36825t_config *config = dev->config;
struct vcnl36825t_data *data = dev->data;
int rc;
#if CONFIG_PM_DEVICE
enum pm_device_state state;
(void)pm_device_state_get(dev, &state);
if (state != PM_DEVICE_STATE_ACTIVE) {
return -EBUSY;
}
#endif
switch (chan) {
case SENSOR_CHAN_ALL:
case SENSOR_CHAN_PROX:
if (config->operation_mode == VCNL36825T_OPERATION_MODE_FORCE) {
rc = vcnl36825t_update(&config->i2c, VCNL36825T_REG_PS_CONF3,
VCNL36825T_PS_TRIG_MSK, VCNL36825T_PS_TRIG_ONCE);
if (rc < 0) {
LOG_ERR("could not trigger proximity measurement %d", rc);
return rc;
}
k_usleep(data->meas_timeout_us);
}
rc = vcnl36825t_read(&config->i2c, VCNL36825T_REG_PS_DATA, &data->proximity);
if (rc < 0) {
LOG_ERR("could not fetch proximity measurement %d", rc);
return rc;
}
break;
default:
LOG_ERR("invalid sensor channel");
return -EINVAL;
}
return 0;
}
static int vcnl36825t_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct vcnl36825t_data *data = dev->data;
switch (chan) {
case SENSOR_CHAN_ALL:
case SENSOR_CHAN_PROX:
val->val1 = data->proximity & VCNL36825T_OS_DATA_MSK;
val->val2 = 0;
break;
default:
return -ENOTSUP;
}
return 0;
}
/**
* @brief helper function to configure the registers
*
* @param dev pointer to the VCNL36825T instance
*/
static int vcnl36825t_init_registers(const struct device *dev)
{
const struct vcnl36825t_config *config = dev->config;
struct vcnl36825t_data *data = dev->data;
int rc;
uint16_t reg_value;
/* reset registers as defined by the datasheet */
const uint16_t resetValues[][2] = {
{VCNL36825T_REG_PS_CONF1, VCNL36825T_CONF1_DEFAULT},
{VCNL36825T_REG_PS_CONF2, VCNL36825T_CONF2_DEFAULT},
{VCNL36825T_REG_PS_CONF3, VCNL36825T_CONF3_DEFAULT},
{VCNL36825T_REG_PS_THDL, VCNL36825T_THDL_DEFAULT},
{VCNL36825T_REG_PS_THDH, VCNL36825T_THDH_DEFAULT},
{VCNL36825T_REG_PS_CANC, VCNL36825T_CANC_DEFAULT},
{VCNL36825T_REG_PS_CONF4, VCNL36825T_CONF4_DEFAULT},
};
for (size_t i = 0; i < ARRAY_SIZE(resetValues); ++i) {
vcnl36825t_write(&config->i2c, resetValues[i][0], resetValues[i][1]);
}
data->meas_timeout_us = 1;
/* PS_CONF1 */
reg_value = 0x01; /* must be set according to datasheet */
reg_value |= VCNL36825T_PS_ON;
rc = vcnl36825t_write(&config->i2c, VCNL36825T_REG_PS_CONF1, reg_value);
if (rc < 0) {
LOG_ERR("I2C for PS_ON returned %d", rc);
return -EIO;
}
reg_value |= VCNL36825T_PS_CAL;
reg_value |= FIELD_PREP(1 << 9, 1); /* reserved, must be set by datasheet */
rc = vcnl36825t_write(&config->i2c, VCNL36825T_REG_PS_CONF1, reg_value);
if (rc < 0) {
LOG_ERR("I2C for PS_CAL returned %d", rc);
}
/* PS_CONF2 */
reg_value = 0;
switch (config->period) {
case VCNL36825T_MEAS_PERIOD_10MS:
reg_value |= VCNL36825T_PS_PERIOD_10MS;
break;
case VCNL36825T_MEAS_PERIOD_20MS:
reg_value |= VCNL36825T_PS_PERIOD_20MS;
break;
case VCNL36825T_MEAS_PERIOD_40MS:
reg_value |= VCNL36825T_PS_PERIOD_40MS;
break;
case VCNL36825T_MEAS_PERIOD_80MS:
__fallthrough;
default:
reg_value |= VCNL36825T_PS_PERIOD_80MS;
break;
}
reg_value |= VCNL36825T_PS_PERS_1;
reg_value |= VCNL36825T_PS_ST_STOP;
switch (config->proximity_it) {
case VCNL36825T_PROXIMITY_INTEGRATION_1T:
reg_value |= VCNL36825T_PS_IT_1T;
data->meas_timeout_us *= 1 * VCNL36825T_FORCED_FACTOR_SCALE;
break;
case VCNL36825T_PROXIMITY_INTEGRATION_1_5T:
reg_value |= VCNL36825T_PS_IT_1_5T;
data->meas_timeout_us *= 1.5 * VCNL36825T_FORCED_FACTOR_SCALE;
break;
case VCNL36825T_PROXIMITY_INTEGRATION_2T:
reg_value |= VCNL36825T_PS_IT_2T;
data->meas_timeout_us *= 2 * VCNL36825T_FORCED_FACTOR_SCALE;
break;
case VCNL36825T_PROXIMITY_INTEGRATION_2_5T:
reg_value |= VCNL36825T_PS_IT_2_5T;
data->meas_timeout_us *= 2.5 * VCNL36825T_FORCED_FACTOR_SCALE;
break;
case VCNL36825T_PROXIMITY_INTEGRATION_3T:
reg_value |= VCNL36825T_PS_IT_3T;
data->meas_timeout_us *= 3 * VCNL36825T_FORCED_FACTOR_SCALE;
break;
case VCNL36825T_PROXIMITY_INTEGRATION_3_5T:
reg_value |= VCNL36825T_PS_IT_3_5T;
data->meas_timeout_us *= 3.5 * VCNL36825T_FORCED_FACTOR_SCALE;
break;
case VCNL36825T_PROXIMITY_INTEGRATION_4T:
reg_value |= VCNL36825T_PS_IT_4T;
data->meas_timeout_us *= 4 * VCNL36825T_FORCED_FACTOR_SCALE;
break;
case VCNL36825T_PROXIMITY_INTEGRATION_8T:
__fallthrough;
default:
reg_value |= VCNL36825T_PS_IT_8T;
data->meas_timeout_us *= 8 * VCNL36825T_FORCED_FACTOR_SCALE;
break;
}
switch (config->multi_pulse) {
case VCNL38652T_MULTI_PULSE_1:
reg_value |= VCNL36825T_MPS_PULSES_1;
break;
case VCNL38652T_MULTI_PULSE_2:
reg_value |= VCNL36825T_MPS_PULSES_2;
break;
case VCNL38652T_MULTI_PULSE_4:
reg_value |= VCNL36825T_MPS_PULSES_4;
break;
case VCNL38652T_MULTI_PULSE_8:
__fallthrough;
default:
reg_value |= VCNL36825T_MPS_PULSES_8;
break;
}
switch (config->proximity_itb) {
case VCNL36825T_PROXIMITY_INTEGRATION_DURATION_25us:
reg_value |= VCNL36825T_PS_ITB_25us;
data->meas_timeout_us *= 25;
break;
case VCNL36825T_PROXIMITY_INTEGRATION_DURATION_50us:
__fallthrough;
default:
reg_value |= VCNL36825T_PS_ITB_50us;
data->meas_timeout_us *= 50;
break;
}
if (config->high_gain) {
reg_value |= VCNL36825T_PS_HG_HIGH;
}
rc = vcnl36825t_write(&config->i2c, VCNL36825T_REG_PS_CONF2, reg_value);
if (rc < 0) {
LOG_ERR("I2C for setting PS_CONF2 returned %d", rc);
return -EIO;
}
/* PS_CONF3 */
reg_value = 0;
if (config->operation_mode == VCNL36825T_OPERATION_MODE_FORCE) {
reg_value |= VCNL36825T_PS_AF_FORCE;
}
switch (config->laser_current) {
case VCNL36825T_LASER_CURRENT_10MS:
reg_value |= VCNL36825T_PS_I_VCSEL_10MA;
break;
case VCNL36825T_LASER_CURRENT_12MS:
reg_value |= VCNL36825T_PS_I_VCSEL_12MA;
break;
case VCNL36825T_LASER_CURRENT_14MS:
reg_value |= VCNL36825T_PS_I_VCSEL_14MA;
break;
case VCNL36825T_LASER_CURRENT_16MS:
reg_value |= VCNL36825T_PS_I_VCSEL_16MA;
break;
case VCNL36825T_LASER_CURRENT_18MS:
reg_value |= VCNL36825T_PS_I_VCSEL_18MA;
break;
case VCNL36825T_LASER_CURRENT_20MS:
__fallthrough;
default:
reg_value |= VCNL36825T_PS_I_VCSEL_20MA;
break;
}
if (config->high_dynamic_output) {
reg_value |= VCNL36825T_PS_HD_16BIT;
}
if (config->sunlight_cancellation) {
reg_value |= VCNL36825T_PS_SC_ENABLED;
}
rc = vcnl36825t_write(&config->i2c, VCNL36825T_REG_PS_CONF3, reg_value);
if (rc < 0) {
LOG_ERR("I2C for setting PS_CONF3 returned %d", rc);
return -EIO;
}
/* PS_CONF4 */
reg_value = 0;
if (config->low_power) {
reg_value |= VCNL36825T_PS_LPEN_ENABLED;
}
switch (config->period) {
case VCNL36825T_MEAS_PERIOD_40MS:
reg_value |= VCNL36825T_PS_LPPER_40MS;
break;
case VCNL36825T_MEAS_PERIOD_80MS:
reg_value |= VCNL36825T_PS_LPPER_80MS;
break;
case VCNL36825T_MEAS_PERIOD_160MS:
reg_value |= VCNL36825T_PS_LPPER_160MS;
break;
case VCNL36825T_MEAS_PERIOD_320MS:
__fallthrough;
default:
reg_value |= VCNL36825T_PS_LPPER_320MS;
break;
}
rc = vcnl36825t_write(&config->i2c, VCNL36825T_REG_PS_CONF4, reg_value);
if (rc < 0) {
LOG_ERR("I2C for setting PS_CONF4 returned %d", rc);
return -EIO;
}
/* calculate measurement timeout
* Note: always add 1 to prevent corner case losses due to precision.
*/
data->meas_timeout_us =
(data->meas_timeout_us * VCNL36825T_FORCED_FACTOR_SUM) /
(VCNL36825T_FORCED_FACTOR_SCALE * VCNL36825T_FORCED_FACTOR_SCALE) +
1;
return 0;
}
static int vcnl36825t_init(const struct device *dev)
{
const struct vcnl36825t_config *config = dev->config;
int rc;
uint16_t reg_value;
if (!i2c_is_ready_dt(&config->i2c)) {
LOG_ERR("device is not ready");
return -ENODEV;
}
rc = vcnl36825t_read(&config->i2c, VCNL36825T_REG_DEV_ID, &reg_value);
if (rc < 0) {
LOG_ERR("could not read device id");
return rc;
}
if ((reg_value & VCNL36825T_ID_MSK) != VCNL36825T_DEVICE_ID) {
LOG_ERR("incorrect device id (%d)", reg_value);
return -EIO;
}
LOG_INF("version code: 0x%X",
(unsigned int)FIELD_GET(VCNL36825T_VERSION_CODE_MSK, reg_value));
rc = vcnl36825t_init_registers(dev);
if (rc < 0) {
return rc;
}
if (config->operation_mode == VCNL36825T_OPERATION_MODE_AUTO) {
rc = vcnl36825t_update(&config->i2c, VCNL36825T_REG_PS_CONF2, VCNL36825T_PS_ST_MSK,
VCNL36825T_PS_ST_START);
if (rc < 0) {
LOG_ERR("error starting measurement");
return -EIO;
}
}
return 0;
}
static const struct sensor_driver_api vcnl36825t_driver_api = {
.sample_fetch = vcnl36825t_sample_fetch,
.channel_get = vcnl36825t_channel_get,
};
#define VCNL36825T_DEFINE(inst) \
BUILD_ASSERT(!DT_INST_PROP(inst, low_power) || (DT_INST_PROP(inst, measurement_period) >= \
VCNL36825T_PS_LPPER_VALUE_MIN_MS), \
"measurement-period must be greater/equal 40 ms in low-power mode"); \
BUILD_ASSERT( \
DT_INST_PROP(inst, low_power) || (DT_INST_PROP(inst, measurement_period) <= \
VCNL36825T_PS_PERIOD_VALUE_MAX_MS), \
"measurement-period must be less/equal 80 ms with deactivated low-power mode"); \
static struct vcnl36825t_data vcnl36825t_data_##inst; \
static const struct vcnl36825t_config vcnl36825t_config_##inst = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
.operation_mode = DT_INST_ENUM_IDX(inst, operation_mode), \
.period = DT_INST_ENUM_IDX(inst, measurement_period), \
.proximity_it = DT_INST_ENUM_IDX(inst, proximity_it), \
.proximity_itb = DT_INST_ENUM_IDX(inst, proximity_itb), \
.multi_pulse = DT_INST_ENUM_IDX(inst, multi_pulse), \
.low_power = DT_INST_PROP(inst, low_power), \
.high_gain = DT_INST_PROP(inst, high_gain), \
.laser_current = DT_INST_ENUM_IDX(inst, laser_current), \
.high_dynamic_output = DT_INST_PROP(inst, high_dynamic_output), \
.sunlight_cancellation = DT_INST_PROP(inst, sunlight_cancellation), \
}; \
IF_ENABLED(CONFIG_PM_DEVICE, (PM_DEVICE_DT_INST_DEFINE(inst, vcnl36825t_pm_action))); \
SENSOR_DEVICE_DT_INST_DEFINE( \
inst, vcnl36825t_init, \
COND_CODE_1(CONFIG_PM_DEVICE, (PM_DEVICE_DT_INST_GET(inst)), (NULL)), \
&vcnl36825t_data_##inst, &vcnl36825t_config_##inst, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &vcnl36825t_driver_api);
DT_INST_FOREACH_STATUS_OKAY(VCNL36825T_DEFINE)

View file

@ -0,0 +1,298 @@
/*
* Copyright (c) 2024 Juliane Schulze, deveritec GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_SENSOR_VCNL36825T_VCNL36825T_H_
#define ZEPHYR_DRIVERS_SENSOR_VCNL36825T_VCNL36825T_H_
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/util.h>
#define VCNL36825T_REG_PS_CONF1 0x00
#define VCNL36825T_REG_PS_CONF2 0x03
#define VCNL36825T_REG_PS_CONF3 0x04
#define VCNL36825T_REG_PS_THDL 0x05
#define VCNL36825T_REG_PS_THDH 0x06
#define VCNL36825T_REG_PS_CANC 0x07
#define VCNL36825T_REG_PS_CONF4 0x08
#define VCNL36825T_REG_PS_DATA 0xF8
#define VCNL36825T_REG_INT_FLAG 0xF9
#define VCNL36825T_REG_DEV_ID 0xFA
#define VCNL36825T_REG_PS_AC_DATA 0xFB
/* default values */
#define VCNL36825T_CONF1_DEFAULT 0x0001
#define VCNL36825T_CONF2_DEFAULT 0x0000
#define VCNL36825T_CONF3_DEFAULT 0x0000
#define VCNL36825T_THDL_DEFAULT 0x0000
#define VCNL36825T_THDH_DEFAULT 0x0000
#define VCNL36825T_CANC_DEFAULT 0x0000
#define VCNL36825T_CONF4_DEFAULT 0x0000
/* PS_CONF1 */
#define VCNL36825T_PS_ON_POS 1
#define VCNL36825T_PS_CAL_POS 7
#define VCNL36825T_PS_ON_MSK GENMASK(1, 1)
#define VCNL36825T_PS_ON (1 << VCNL36825T_PS_ON_POS)
#define VCNL36825T_PS_OFF (0 << VCNL36825T_PS_ON_POS)
#define VCNL36825T_PS_CAL (1 << VCNL36825T_PS_CAL_POS)
/* PS_CONF2 */
#define VCNL36825T_PS_ST_POS 0
#define VCNL36825T_PS_PS_SMART_PERS_POS 1
#define VCNL36825T_PS_INT_POS 2
#define VCNL36825T_PS_PERS_POS 4
#define VCNL36825T_PS_PERIOD_POS 6
#define VCNL36825T_PS_HG_POS 10
#define VCNL36825T_PS_ITB_POS 11
#define VCNL36825T_PS_MPS_POS 12
#define VCNL36825T_PS_IT_POS 14
#define VCNL36825T_PS_ST_MSK GENMASK(0, 0)
#define VCNL36825T_PS_ST_START (0 << VCNL36825T_PS_ST_POS)
#define VCNL36825T_PS_ST_STOP (1 << VCNL36825T_PS_ST_POS)
#define VCNL36825T_PS_SMART_PERS_DISABLED (0 << VCNL36825T_PS_PS_SMART_PERS_POS)
#define VCNL36825T_PS_SMART_PERS_ENABLED (1 << VCNL36825T_PS_PS_SMART_PERS_POS)
#define VCNL36825T_PS_INT_DISABLE (0 << VCNL36825T_PS_INT_POS)
#define VCNL36825T_PS_INT_THDH_PERS_LATCHED (1 << VCNL36825T_PS_INT_POS)
#define VCNL36825T_PS_INT_THDH_FIRST_LATCHED (2 << VCNL36825T_PS_INT_POS)
#define VCNL36825T_PS_INT_ENABLED (3 << VCNL36825T_PS_INT_POS)
#define VCNL36825T_PS_PERS_1 (0 << VCNL36825T_PS_PERS_POS)
#define VCNL36825T_PS_PERS_2 (1 << VCNL36825T_PS_PERS_POS)
#define VCNL36825T_PS_PERS_3 (2 << VCNL36825T_PS_PERS_POS)
#define VCNL36825T_PS_PERS_4 (3 << VCNL36825T_PS_PERS_POS)
#define VCNL36825T_PS_PERIOD_10MS (0 << VCNL36825T_PS_PERIOD_POS)
#define VCNL36825T_PS_PERIOD_20MS (1 << VCNL36825T_PS_PERIOD_POS)
#define VCNL36825T_PS_PERIOD_40MS (2 << VCNL36825T_PS_PERIOD_POS)
#define VCNL36825T_PS_PERIOD_80MS (3 << VCNL36825T_PS_PERIOD_POS)
#define VCNL36825T_PS_HG_LOW (0 << VCNL36825T_PS_HG_POS)
#define VCNL36825T_PS_HG_HIGH (1 << VCNL36825T_PS_HG_POS)
#define VCNL36825T_PS_ITB_25us (0 << VCNL36825T_PS_ITB_POS)
#define VCNL36825T_PS_ITB_50us (1 << VCNL36825T_PS_ITB_POS)
#define VCNL36825T_MPS_PULSES_1 (0 << VCNL36825T_PS_MPS_POS)
#define VCNL36825T_MPS_PULSES_2 (1 << VCNL36825T_PS_MPS_POS)
#define VCNL36825T_MPS_PULSES_4 (2 << VCNL36825T_PS_MPS_POS)
#define VCNL36825T_MPS_PULSES_8 (4 << VCNL36825T_PS_MPS_POS)
#define VCNL36825T_PS_IT_1T (0 << VCNL36825T_PS_IT_POS)
#define VCNL36825T_PS_IT_1_5T (1 << VCNL36825T_PS_IT_POS)
#define VCNL36825T_PS_IT_2T (2 << VCNL36825T_PS_IT_POS)
#define VCNL36825T_PS_IT_2_5T (3 << VCNL36825T_PS_IT_POS)
#define VCNL36825T_PS_IT_3T (4 << VCNL36825T_PS_IT_POS)
#define VCNL36825T_PS_IT_3_5T (5 << VCNL36825T_PS_IT_POS)
#define VCNL36825T_PS_IT_4T (6 << VCNL36825T_PS_IT_POS)
#define VCNL36825T_PS_IT_8T (7 << VCNL36825T_PS_IT_POS)
/* PS_CONF3 */
#define VCNL36825T_PS_SP_INT_POS 2
#define VCNL36825T_PS_FORCENUM_POS 4
#define VCNL36825T_PS_TRIG_POS 5
#define VCNL36825T_PS_AF_POS 6
#define VCNL36825T_PS_I_VCSEL_POS 8
#define VCNL36825T_PS_HD_POS 12
#define VCNL36825T_PS_SC_POS 13
#define VCNL36825T_PS_TRIG_MSK GENMASK(5, 5)
#define VCNL36825T_PS_SP_INT_DISABLED (0 << VCNL36825T_PS_SP_INT_POS)
#define VCNL36825T_PS_SP_INT_ENABLED (1 << VCNL36825T_PS_SP_INT_POS)
#define VCNL36825T_PS_FORCENUM_ONE_CYCLE (0 << VCNL36825T_PS_FORCENUM_POS)
#define VCNL36825T_PS_FORCENUM_TWO_CYCLES (1 << VCNL36825T_PS_FORCENUM_POS)
#define VCNL36825T_PS_TRIG_NONE (0 << VCNL36825T_PS_TRIG_POS)
#define VCNL36825T_PS_TRIG_ONCE (1 << VCNL36825T_PS_TRIG_POS)
#define VCNL36825T_PS_AF_AUTO (0 << VCNL36825T_PS_AF_POS)
#define VCNL36825T_PS_AF_FORCE (1 << VCNL36825T_PS_AF_POS)
#define VCNL36825T_PS_I_VCSEL_10MA (2 << VCNL36825T_PS_I_VCSEL_POS)
#define VCNL36825T_PS_I_VCSEL_12MA (3 << VCNL36825T_PS_I_VCSEL_POS)
#define VCNL36825T_PS_I_VCSEL_14MA (4 << VCNL36825T_PS_I_VCSEL_POS)
#define VCNL36825T_PS_I_VCSEL_16MA (5 << VCNL36825T_PS_I_VCSEL_POS)
#define VCNL36825T_PS_I_VCSEL_18MA (6 << VCNL36825T_PS_I_VCSEL_POS)
#define VCNL36825T_PS_I_VCSEL_20MA (7 << VCNL36825T_PS_I_VCSEL_POS)
#define VCNL36825T_PS_HD_12BIT (0 << VCNL36825T_PS_HD_POS)
#define VCNL36825T_PS_HD_16BIT (1 << VCNL36825T_PS_HD_POS)
#define VCNL36825T_PS_SC_DISABLED (0 << VCNL36825T_PS_SC_POS)
#define VCNL36825T_PS_SC_ENABLED (7 << VCNL36825T_PS_SC_POS)
/* PS CONF4 */
#define VCNL36825T_PS_AC_INT_POS 0
#define VCNL36825T_PS_AC_TRIG_POS 2
#define VCNL36825T_PS_AC_POS 3
#define VCNL36825T_PS_AC_NUM_POS 4
#define VCNL36825T_PS_AC_PERIOD_POS 6
#define VCNL36825T_PS_LPEN_POS 8
#define VCNL36825T_PS_LPPER_POS 9
#define VCNL36825T_PS_AC_INT_DISABLED (0 << VCNL36825T_PS_AC_INT_POS)
#define VCNL36825T_PS_AC_INT_ENABLED (1 << VCNL36825T_PS_AC_INT_POS)
#define VCNL36825T_PS_AC_TRIG_DISABLED (0 << VCNL36825T_PS_AC_TRIG_POS)
#define VCNL36825T_PS_AC_TRIG_ONCE (1 << VCNL36825T_PS_AC_TRIG_POS)
#define VCNL36825T_PS_AC_DISABLED (0 << VCNL36825T_PS_AC_POS)
#define VCNL36825T_PS_AC_ENABLED (1 << VCNL36825T_PS_AC_POS)
#define VCNL36825T_PS_AC_NUM_1 (0 << VCNL36825T_PS_AC_NUM_POS)
#define VCNL36825T_PS_AC_NUM_2 (1 << VCNL36825T_PS_AC_NUM_POS)
#define VCNL36825T_PS_AC_NUM_4 (2 << VCNL36825T_PS_AC_NUM_POS)
#define VCNL36825T_PS_AC_NUM_8 (3 << VCNL36825T_PS_AC_NUM_POS)
#define VCNL36825T_PS_AC_PERIOD_3MS (0 << VCNL36825T_PS_AC_PERIOD_POS)
#define VCNL36825T_PS_AC_PERIOD_6MS (1 << VCNL36825T_PS_AC_PERIOD_POS)
#define VCNL36825T_PS_AC_PERIOD_12MS (2 << VCNL36825T_PS_AC_PERIOD_POS)
#define VCNL36825T_PS_AC_PERIOD_24MS (3 << VCNL36825T_PS_AC_PERIOD_POS)
#define VCNL36825T_PS_LPEN_DISABLED (0 << VCNL36825T_PS_LPEN_POS)
#define VCNL36825T_PS_LPEN_ENABLED (1 << VCNL36825T_PS_LPEN_POS)
#define VCNL36825T_PS_LPPER_40MS (0 << VCNL36825T_PS_LPPER_POS)
#define VCNL36825T_PS_LPPER_80MS (1 << VCNL36825T_PS_LPPER_POS)
#define VCNL36825T_PS_LPPER_160MS (2 << VCNL36825T_PS_LPPER_POS)
#define VCNL36825T_PS_LPPER_320MS (3 << VCNL36825T_PS_LPPER_POS)
/* PS_DATA */
#define VCNL36825T_PS_DATA_L_POS 0
#define VCNL36825T_PS_DATA_H_POS 8
#define VCNL36825T_PS_DATA_L_MSK GENMASK(7, 0)
#define VCNL36825T_PS_DATA_H_MSK GENMASK(11, 8)
#define VCNL36825T_OS_DATA_MSK (VCNL36825T_PS_DATA_L_MSK | VCNL36825T_PS_DATA_H_MSK)
/* INT_FLAG */
#define VCNL36825T_PS_IF_AWAY_POS 8
#define VCNL36825T_PS_IF_CLOSE_POS 9
#define VCNL36825T_PS_SPFLAG_POS 12
#define VCNL36825T_PS_ACFLAG_POS 13
#define VCNL36825T_PS_IF_AWAY_MSK GENMASK(8, 8)
#define VCNL36825T_PS_IF_CLOSE_MSK GENMASK(9, 9)
#define VCNL36825T_PS_SPFLAG_MSK GENMASK(12, 12)
#define VCNL36825T_PS_ACFLAG_MSK GENMASK(13, 13)
/* ID */
#define VCNL36825T_ID_POS 0
#define VCNL36825T_VERSION_CODE_POS 8
#define VCNL36825T_ID_MSK GENMASK(7, 0)
#define VCNL36825T_VERSION_CODE_MSK GENMASK(11, 8)
#define VCNL36825T_DEVICE_ID 0b00100110
/* PS_AC_DATA */
#define VCNL36825T_AC_DATA_L_POS 0
#define VCNL36825T_AC_DATA_H_POS 8
#define VCNL36825T_AC_SUN_POS 14
#define VCNL36825T_AC_BUSY_POS 15
#define VCNL36825T_AC_DATA_L_MSK GENMASK(7, 0)
#define VCNL36825T_AC_DATA_H_MSK GENMASK(11, 8)
#define VCNL36825T_AC_SUN_MSK GENMASK(14, 14)
#define VCNL36825T_AC_BUSY_MSK GENMASK(15, 15)
/* --- */
#define VCNL36825T_PS_PERIOD_VALUE_MAX_MS 80
#define VCNL36825T_PS_LPPER_VALUE_MIN_MS 40
#define VCNL36825T_FORCED_FACTOR_TIME_TO_TRIGGER 0.5
#define VCNL36825T_FORCED_FACTOR_DC_KILL_AMBIENT 3
#define VCNL36825T_FORCED_FACTOR_MEASUREMENT 1
#define VCNL36825T_FORCED_FACTOR_SHUTDOWN 1
#define VCNL36825T_FORCED_FACTOR_SCALE 10
#define VCNL36825T_FORCED_FACTOR_SUM \
((VCNL36825T_FORCED_FACTOR_TIME_TO_TRIGGER + VCNL36825T_FORCED_FACTOR_DC_KILL_AMBIENT + \
VCNL36825T_FORCED_FACTOR_MEASUREMENT + VCNL36825T_FORCED_FACTOR_SHUTDOWN) * \
VCNL36825T_FORCED_FACTOR_SCALE)
enum vcnl36825t_operation_mode {
VCNL36825T_OPERATION_MODE_AUTO,
VCNL36825T_OPERATION_MODE_FORCE,
};
enum vcnl36825t_measurement_period {
VCNL36825T_MEAS_PERIOD_10MS,
VCNL36825T_MEAS_PERIOD_20MS,
VCNL36825T_MEAS_PERIOD_40MS,
VCNL36825T_MEAS_PERIOD_80MS,
VCNL36825T_MEAS_PERIOD_160MS,
VCNL36825T_MEAS_PERIOD_320MS,
};
enum vcnl36825t_proximity_integration_time {
VCNL36825T_PROXIMITY_INTEGRATION_1T,
VCNL36825T_PROXIMITY_INTEGRATION_1_5T,
VCNL36825T_PROXIMITY_INTEGRATION_2T,
VCNL36825T_PROXIMITY_INTEGRATION_2_5T,
VCNL36825T_PROXIMITY_INTEGRATION_3T,
VCNL36825T_PROXIMITY_INTEGRATION_3_5T,
VCNL36825T_PROXIMITY_INTEGRATION_4T,
VCNL36825T_PROXIMITY_INTEGRATION_8T,
};
enum vcnl36825t_proximity_integration_duration {
VCNL36825T_PROXIMITY_INTEGRATION_DURATION_25us,
VCNL36825T_PROXIMITY_INTEGRATION_DURATION_50us,
};
enum vcnl36825t_multi_pulse {
VCNL38652T_MULTI_PULSE_1,
VCNL38652T_MULTI_PULSE_2,
VCNL38652T_MULTI_PULSE_4,
VCNL38652T_MULTI_PULSE_8,
};
enum vcnl38625t_laser_current {
VCNL36825T_LASER_CURRENT_10MS,
VCNL36825T_LASER_CURRENT_12MS,
VCNL36825T_LASER_CURRENT_14MS,
VCNL36825T_LASER_CURRENT_16MS,
VCNL36825T_LASER_CURRENT_18MS,
VCNL36825T_LASER_CURRENT_20MS,
};
struct vcnl36825t_config {
struct i2c_dt_spec i2c;
enum vcnl36825t_operation_mode operation_mode;
enum vcnl36825t_measurement_period period;
enum vcnl36825t_proximity_integration_time proximity_it;
enum vcnl36825t_proximity_integration_duration proximity_itb;
enum vcnl36825t_multi_pulse multi_pulse;
bool low_power;
bool high_gain;
enum vcnl38625t_laser_current laser_current;
bool high_dynamic_output;
bool sunlight_cancellation;
};
struct vcnl36825t_data {
uint16_t proximity;
int meas_timeout_us; /** wait time for finished measurement for "forced" operation mode */
};
#endif

View file

@ -0,0 +1,90 @@
# Copyright (c) 2024 Juliane Schulze, deveritec GmbH
# SPDX-License-Identifier: Apache-2.0
description: |
VCNL36825T proximity and ambient light sensor. See datasheet at
https://www.vishay.com/docs/84274/vcnl36825t.pdf
compatible: "vishay,vcnl36825t"
include: [sensor-device.yaml, i2c-device.yaml]
properties:
operation-mode:
type: string
default: "auto"
enum: ["auto", "force"]
description: |
Mode of operation.
- "auto": the sensor performs sampling continuously,
- "force": the sampling is performed on every fetch command.
Defaults to sensor reset value.
measurement-period:
type: int
default: 40
enum: [10, 20, 40, 80, 160, 320]
description: |
Measurement period of the sensors in ms.
Higher values yield lower power consumption.
Note:
- [10, 80] ms only if low power mode is inactive
- [80, 320] ms only in low power mode
Defaults to 40 ms which is supported in both normal and low-power mode.
proximity-it:
type: string
default: "1"
enum: ["1", "1.5", "2", "2.5", "3", "3.5", "4", "8"]
description: |
Proximity integration time in T.
Defaults to sensor reset value.
proximity-itb:
type: int
default: 25
enum: [25, 50]
description: |
Duration of the proximity integration time T in us.
Defaults to sensor reset value.
multi-pulse:
type: int
default: 1
enum: [1, 2, 4, 8]
description: |
Number of pulses per single measurement.
Higher values increase accuracy and power consumption.
Defaults to sensor reset value.
laser-current:
type: int
default: 10
enum: [10, 12, 14, 16, 18, 20]
description: |
Current used by the laser during measurement periods.
Defaults to minimum allowed value.
low-power:
type: boolean
description: |
Activate low power mode.
This allows to increase the measurement period up to 320 ms.
high-gain:
type: boolean
description: |
Activate the high gain mode.
sunlight-cancellation:
type: boolean
description: |
Activate sunlight cancellation.
high-dynamic-output:
type: boolean
description: |
Activate 16bit high dynamic output mode.
Cannot be used with threshold interrupt.

View file

@ -905,3 +905,13 @@ test_i2c_lis2de12: lis2de12@80 {
accel-range = <LIS2DE12_DT_FS_16G>;
accel-odr = <LIS2DE12_DT_ODR_AT_100Hz>;
};
test_i2c_vishay_vcnl36825t: vcnl36825t@81 {
compatible = "vishay,vcnl36825t";
reg = <0x81>;
proximity-it = "3.5";
multi-pulse = <8>;
low-power;
};