drivers: sensor: Add VCNL4040 driver
Add support for Vishay VCNL4040 proximity and light sensor Signed-off-by: Richard Osterloh <richard.osterloh@gmail.com>
This commit is contained in:
parent
2be0086e87
commit
0a0026afe2
10 changed files with 981 additions and 0 deletions
|
@ -70,6 +70,7 @@ add_subdirectory_ifdef(CONFIG_TH02 th02)
|
|||
add_subdirectory_ifdef(CONFIG_TMP007 tmp007)
|
||||
add_subdirectory_ifdef(CONFIG_TMP112 tmp112)
|
||||
add_subdirectory_ifdef(CONFIG_TMP116 tmp116)
|
||||
add_subdirectory_ifdef(CONFIG_VCNL4040 vcnl4040)
|
||||
add_subdirectory_ifdef(CONFIG_VL53L0X vl53l0x)
|
||||
add_subdirectory_ifdef(CONFIG_TEMP_KINETIS nxp_kinetis_temp)
|
||||
add_subdirectory_ifdef(CONFIG_TACH_XEC mchp_tach_xec)
|
||||
|
|
|
@ -179,6 +179,8 @@ source "drivers/sensor/tmp112/Kconfig"
|
|||
|
||||
source "drivers/sensor/tmp116/Kconfig"
|
||||
|
||||
source "drivers/sensor/vcnl4040/Kconfig"
|
||||
|
||||
source "drivers/sensor/vl53l0x/Kconfig"
|
||||
|
||||
source "drivers/sensor/nxp_kinetis_temp/Kconfig"
|
||||
|
|
6
drivers/sensor/vcnl4040/CMakeLists.txt
Normal file
6
drivers/sensor/vcnl4040/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_VCNL4040 vcnl4040.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_VCNL4040_TRIGGER vcnl4040_trigger.c)
|
57
drivers/sensor/vcnl4040/Kconfig
Normal file
57
drivers/sensor/vcnl4040/Kconfig
Normal file
|
@ -0,0 +1,57 @@
|
|||
# VCNL4040 Proximity and Ambient Light Sensor configuration options
|
||||
|
||||
# Copyright (c) 2020 Richard Osterloh
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig VCNL4040
|
||||
bool "VCNL4040 Proximity and Ambient Light Sensor"
|
||||
depends on I2C && HAS_DTS_I2C && HAS_DTS_GPIO
|
||||
help
|
||||
Enable driver for VCNL4040 sensors.
|
||||
|
||||
if VCNL4040
|
||||
|
||||
config VCNL4040_ENABLE_ALS
|
||||
bool "Enable ambient light sensor"
|
||||
help
|
||||
Enable Ambient Light Sense (ALS).
|
||||
|
||||
config VCNL4040_TRIGGER
|
||||
bool
|
||||
|
||||
choice
|
||||
prompt "Trigger mode"
|
||||
default VCNL4040_TRIGGER_NONE
|
||||
help
|
||||
Specify the type of triggering to be used by the driver.
|
||||
|
||||
config VCNL4040_TRIGGER_NONE
|
||||
bool "No trigger"
|
||||
|
||||
config VCNL4040_TRIGGER_GLOBAL_THREAD
|
||||
bool "Use global thread"
|
||||
depends on GPIO
|
||||
select VCNL4040_TRIGGER
|
||||
|
||||
config VCNL4040_TRIGGER_OWN_THREAD
|
||||
bool "Use own thread"
|
||||
depends on GPIO
|
||||
select VCNL4040_TRIGGER
|
||||
|
||||
endchoice
|
||||
|
||||
config VCNL4040_THREAD_PRIORITY
|
||||
int "Thread priority"
|
||||
depends on VCNL4040_TRIGGER_OWN_THREAD
|
||||
default 10
|
||||
help
|
||||
Priority of thread used by the driver to handle interrupts.
|
||||
|
||||
config VCNL4040_THREAD_STACK_SIZE
|
||||
int "Thread stack size"
|
||||
depends on VCNL4040_TRIGGER_OWN_THREAD
|
||||
default 1024
|
||||
help
|
||||
Stack size of thread used by the driver to handle interrupts.
|
||||
|
||||
endif # VCNL4040
|
382
drivers/sensor/vcnl4040/vcnl4040.c
Normal file
382
drivers/sensor/vcnl4040/vcnl4040.c
Normal file
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Richard Osterloh
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT vishay_vcnl4040
|
||||
|
||||
#include "vcnl4040.h"
|
||||
#include <sys/__assert.h>
|
||||
#include <sys/byteorder.h>
|
||||
#include <sys/util.h>
|
||||
#include <logging/log.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
LOG_MODULE_REGISTER(vcnl4040, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
int vcnl4040_read(const struct device *dev, uint8_t reg, uint16_t *out)
|
||||
{
|
||||
const struct vcnl4040_config *config = dev->config;
|
||||
struct vcnl4040_data *data = dev->data;
|
||||
uint8_t buff[2] = { 0 };
|
||||
int ret = 0;
|
||||
|
||||
ret = i2c_write_read(data->i2c, config->i2c_address,
|
||||
®, sizeof(reg), buff, sizeof(buff));
|
||||
|
||||
if (!ret)
|
||||
*out = sys_get_le16(buff);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vcnl4040_write(const struct device *dev, uint8_t reg, uint16_t value)
|
||||
{
|
||||
const struct vcnl4040_config *config = dev->config;
|
||||
struct vcnl4040_data *data = dev->data;
|
||||
struct i2c_msg msg;
|
||||
int ret;
|
||||
uint8_t buff[3];
|
||||
|
||||
sys_put_le16(value, &buff[1]);
|
||||
|
||||
buff[0] = reg;
|
||||
|
||||
msg.buf = buff;
|
||||
msg.flags = 0;
|
||||
msg.len = sizeof(buff);
|
||||
|
||||
ret = i2c_transfer(data->i2c, &msg, 1, config->i2c_address);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERR("write block failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vcnl4040_sample_fetch(const struct device *dev,
|
||||
enum sensor_channel chan)
|
||||
{
|
||||
struct vcnl4040_data *data = dev->data;
|
||||
int ret = 0;
|
||||
|
||||
#ifdef CONFIG_VCNL4040_ENABLE_ALS
|
||||
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL ||
|
||||
chan == SENSOR_CHAN_PROX ||
|
||||
chan == SENSOR_CHAN_LIGHT);
|
||||
#else
|
||||
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_PROX);
|
||||
#endif
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_PROX) {
|
||||
ret = vcnl4040_read(dev, VCNL4040_REG_PS_DATA,
|
||||
&data->proximity);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Could not fetch proximity");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_VCNL4040_ENABLE_ALS
|
||||
if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_LIGHT) {
|
||||
ret = vcnl4040_read(dev, VCNL4040_REG_ALS_DATA,
|
||||
&data->light);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Could not fetch ambient light");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
exit:
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vcnl4040_channel_get(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
struct vcnl4040_data *data = dev->data;
|
||||
int ret = 0;
|
||||
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_PROX:
|
||||
val->val1 = data->proximity;
|
||||
val->val2 = 0;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_VCNL4040_ENABLE_ALS
|
||||
case SENSOR_CHAN_LIGHT:
|
||||
val->val1 = data->light * data->sensitivity;
|
||||
val->val2 = 0;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ret = -ENOTSUP;
|
||||
}
|
||||
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vcnl4040_proxy_setup(const struct device *dev)
|
||||
{
|
||||
const struct vcnl4040_config *config = dev->config;
|
||||
uint16_t conf = 0;
|
||||
|
||||
if (vcnl4040_read(dev, VCNL4040_REG_PS_MS, &conf)) {
|
||||
LOG_ERR("Could not read proximity config");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Set LED current */
|
||||
conf |= config->led_i << VCNL4040_LED_I_POS;
|
||||
|
||||
if (vcnl4040_write(dev, VCNL4040_REG_PS_MS, conf)) {
|
||||
LOG_ERR("Could not write proximity config");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (vcnl4040_read(dev, VCNL4040_REG_PS_CONF, &conf)) {
|
||||
LOG_ERR("Could not read proximity config");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Set PS_HD */
|
||||
conf |= VCNL4040_PS_HD_MASK;
|
||||
/* Set duty cycle */
|
||||
conf |= config->led_dc << VCNL4040_PS_DUTY_POS;
|
||||
/* Set integration time */
|
||||
conf |= config->proxy_it << VCNL4040_PS_IT_POS;
|
||||
/* Clear proximity shutdown */
|
||||
conf &= ~VCNL4040_PS_SD_MASK;
|
||||
|
||||
if (vcnl4040_write(dev, VCNL4040_REG_PS_CONF, conf)) {
|
||||
LOG_ERR("Could not write proximity config");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VCNL4040_ENABLE_ALS
|
||||
static int vcnl4040_ambient_setup(const struct device *dev)
|
||||
{
|
||||
const struct vcnl4040_config *config = dev->config;
|
||||
struct vcnl4040_data *data = dev->data;
|
||||
uint16_t conf = 0;
|
||||
|
||||
if (vcnl4040_read(dev, VCNL4040_REG_ALS_CONF, &conf)) {
|
||||
LOG_ERR("Could not read proximity config");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Set ALS integration time */
|
||||
conf |= config->als_it << VCNL4040_ALS_IT_POS;
|
||||
/* Clear ALS shutdown */
|
||||
conf &= ~VCNL4040_ALS_SD_MASK;
|
||||
|
||||
if (vcnl4040_write(dev, VCNL4040_REG_ALS_CONF, conf)) {
|
||||
LOG_ERR("Could not write proximity config");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* scale the lux depending on the value of the integration time
|
||||
* see page 8 of the VCNL4040 application note:
|
||||
* https://www.vishay.com/docs/84307/designingvcnl4040.pdf
|
||||
*/
|
||||
switch (config->als_it) {
|
||||
case VCNL4040_AMBIENT_INTEGRATION_TIME_80MS:
|
||||
data->sensitivity = 0.12;
|
||||
break;
|
||||
case VCNL4040_AMBIENT_INTEGRATION_TIME_160MS:
|
||||
data->sensitivity = 0.06;
|
||||
break;
|
||||
case VCNL4040_AMBIENT_INTEGRATION_TIME_320MS:
|
||||
data->sensitivity = 0.03;
|
||||
break;
|
||||
case VCNL4040_AMBIENT_INTEGRATION_TIME_640MS:
|
||||
data->sensitivity = 0.015;
|
||||
break;
|
||||
default:
|
||||
data->sensitivity = 1.0;
|
||||
LOG_WRN("Cannot set ALS sensitivity from ALS_IT=%d",
|
||||
config->als_it);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
static int vcnl4040_device_ctrl(const struct device *dev,
|
||||
uint32_t ctrl_command, void *context,
|
||||
device_pm_cb cb, void *arg)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (ctrl_command == DEVICE_PM_SET_POWER_STATE) {
|
||||
uint32_t device_pm_state = *(uint32_t *)context;
|
||||
uint16_t ps_conf;
|
||||
|
||||
ret = vcnl4040_read(dev, VCNL4040_REG_PS_CONF, &ps_conf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#ifdef CONFIG_VCNL4040_ENABLE_ALS
|
||||
uint16_t als_conf;
|
||||
|
||||
ret = vcnl4040_read(dev, VCNL4040_REG_ALS_CONF, &als_conf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#endif
|
||||
if (device_pm_state == DEVICE_PM_ACTIVE_STATE) {
|
||||
/* Clear proximity shutdown */
|
||||
ps_conf &= ~VCNL4040_PS_SD_MASK;
|
||||
|
||||
ret = vcnl4040_write(dev, VCNL4040_REG_PS_CONF,
|
||||
ps_conf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#ifdef CONFIG_VCNL4040_ENABLE_ALS
|
||||
/* Clear als shutdown */
|
||||
als_conf &= ~VCNL4040_ALS_SD_MASK;
|
||||
|
||||
ret = vcnl4040_write(dev, VCNL4040_REG_ALS_CONF,
|
||||
als_conf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#endif
|
||||
} else {
|
||||
/* Set proximity shutdown bit 0 */
|
||||
ps_conf |= VCNL4040_PS_SD_MASK;
|
||||
|
||||
ret = vcnl4040_write(dev, VCNL4040_REG_PS_CONF,
|
||||
ps_conf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#ifdef CONFIG_VCNL4040_ENABLE_ALS
|
||||
/* Clear als shutdown bit 0 */
|
||||
als_conf |= VCNL4040_ALS_SD_MASK;
|
||||
|
||||
ret = vcnl4040_write(dev, VCNL4040_REG_ALS_CONF,
|
||||
als_conf)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
} else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) {
|
||||
*((uint32_t *)context) = DEVICE_PM_ACTIVE_STATE;
|
||||
}
|
||||
|
||||
if (cb) {
|
||||
cb(dev, ret, context, arg);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int vcnl4040_init(const struct device *dev)
|
||||
{
|
||||
const struct vcnl4040_config *config = dev->config;
|
||||
struct vcnl4040_data *data = dev->data;
|
||||
uint16_t id;
|
||||
|
||||
/* Get the I2C device */
|
||||
data->i2c = device_get_binding(config->i2c_name);
|
||||
if (data->i2c == NULL) {
|
||||
LOG_ERR("Could not find I2C device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check device id */
|
||||
if (vcnl4040_read(dev, VCNL4040_REG_DEVICE_ID, &id)) {
|
||||
LOG_ERR("Could not read device id");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (id != VCNL4040_DEFAULT_ID) {
|
||||
LOG_ERR("Incorrect device id (%d)", id);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (vcnl4040_proxy_setup(dev)) {
|
||||
LOG_ERR("Failed to setup proximity functionality");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VCNL4040_ENABLE_ALS
|
||||
if (vcnl4040_ambient_setup(dev)) {
|
||||
LOG_ERR("Failed to setup ambient light functionality");
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
k_sem_init(&data->sem, 0, UINT_MAX);
|
||||
|
||||
#if CONFIG_VCNL4040_TRIGGER
|
||||
if (vcnl4040_trigger_init(dev)) {
|
||||
LOG_ERR("Could not initialise interrupts");
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
LOG_DBG("Init complete");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sensor_driver_api vcnl4040_driver_api = {
|
||||
.sample_fetch = vcnl4040_sample_fetch,
|
||||
.channel_get = vcnl4040_channel_get,
|
||||
#ifdef CONFIG_VCNL4040_TRIGGER
|
||||
.attr_set = vcnl4040_attr_set,
|
||||
.trigger_set = vcnl4040_trigger_set,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct vcnl4040_config vcnl4040_config = {
|
||||
.i2c_name = DT_INST_BUS_LABEL(0),
|
||||
.i2c_address = DT_INST_REG_ADDR(0),
|
||||
#ifdef CONFIG_VCNL4040_TRIGGER
|
||||
#if DT_INST_NODE_HAS_PROP(0, int_gpios)
|
||||
.gpio_name = DT_INST_GPIO_LABEL(0, int_gpios),
|
||||
.gpio_pin = DT_INST_GPIO_PIN(0, int_gpios),
|
||||
.gpio_flags = DT_INST_GPIO_FLAGS(0, int_gpios),
|
||||
#else
|
||||
.gpio_name = NULL,
|
||||
.gpio_pin = 0,
|
||||
.gpio_flags = 0,
|
||||
#endif
|
||||
#endif
|
||||
.led_i = DT_ENUM_IDX(DT_DRV_INST(0), led_current),
|
||||
.led_dc = DT_ENUM_IDX(DT_DRV_INST(0), led_duty_cycle),
|
||||
.als_it = DT_ENUM_IDX(DT_DRV_INST(0), als_it),
|
||||
.proxy_it = DT_ENUM_IDX(DT_DRV_INST(0), proximity_it),
|
||||
.proxy_type = DT_ENUM_IDX(DT_DRV_INST(0), proximity_trigger),
|
||||
};
|
||||
|
||||
static struct vcnl4040_data vcnl4040_data;
|
||||
|
||||
#ifndef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
DEVICE_AND_API_INIT(vcnl4040, DT_INST_LABEL(0), vcnl4040_init,
|
||||
&vcnl4040_data, &vcnl4040_config,
|
||||
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
|
||||
&vcnl4040_driver_api);
|
||||
#else
|
||||
DEVICE_DEFINE(vcnl4040, DT_INST_LABEL(0), vcnl4040_init,
|
||||
vcnl4040_device_ctrl, &vcnl4040_data, &vcnl4040_config,
|
||||
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &vcnl4040_driver_api);
|
||||
#endif
|
148
drivers/sensor/vcnl4040/vcnl4040.h
Normal file
148
drivers/sensor/vcnl4040/vcnl4040.h
Normal file
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Richard Osterloh
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_VCNL4040_VCNL4040_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_VCNL4040_VCNL4040_H_
|
||||
|
||||
#include <drivers/sensor.h>
|
||||
#include <drivers/i2c.h>
|
||||
#include <drivers/gpio.h>
|
||||
|
||||
/* Registers all 16 bits */
|
||||
#define VCNL4040_REG_ALS_CONF 0x00
|
||||
#define VCNL4040_REG_ALS_THDH 0x01
|
||||
#define VCNL4040_REG_ALS_THDL 0x02
|
||||
#define VCNL4040_REG_PS_CONF 0x03
|
||||
#define VCNL4040_REG_PS_MS 0x04
|
||||
#define VCNL4040_REG_PS_CANC 0x05
|
||||
#define VCNL4040_REG_PS_THDL 0x06
|
||||
#define VCNL4040_REG_PS_THDH 0x07
|
||||
#define VCNL4040_REG_PS_DATA 0x08
|
||||
#define VCNL4040_REG_ALS_DATA 0x09
|
||||
#define VCNL4040_REG_WHITE_DATA 0x0A
|
||||
#define VCNL4040_REG_INT_FLAG 0x0B
|
||||
#define VCNL4040_REG_DEVICE_ID 0x0C
|
||||
|
||||
#define VCNL4040_DEFAULT_ID 0x0186
|
||||
|
||||
#define VCNL4040_LED_I_POS 8
|
||||
#define VCNL4040_PS_HD_POS 11
|
||||
#define VCNL4040_PS_HD_MASK (0x01 << VCNL4040_PS_HD_POS)
|
||||
#define VCNL4040_PS_DUTY_POS 6
|
||||
#define VCNL4040_PS_IT_POS 1
|
||||
#define VCNL4040_PS_SD_POS 0
|
||||
#define VCNL4040_PS_SD_MASK (0x01 << VCNL4040_PS_SD_POS)
|
||||
#define VCNL4040_ALS_IT_POS 6
|
||||
#define VCNL4040_ALS_SD_POS 0
|
||||
#define VCNL4040_ALS_SD_MASK (0x01 << VCNL4040_ALS_SD_POS)
|
||||
|
||||
enum led_current {
|
||||
VCNL4040_LED_CURRENT_50MA,
|
||||
VCNL4040_LED_CURRENT_75MA,
|
||||
VCNL4040_LED_CURRENT_100MA,
|
||||
VCNL4040_LED_CURRENT_120MA,
|
||||
VCNL4040_LED_CURRENT_140MA,
|
||||
VCNL4040_LED_CURRENT_160MA,
|
||||
VCNL4040_LED_CURRENT_180MA,
|
||||
VCNL4040_LED_CURRENT_200MA,
|
||||
};
|
||||
|
||||
enum led_duty_cycle {
|
||||
VCNL4040_LED_DUTY_1_40,
|
||||
VCNL4040_LED_DUTY_1_80,
|
||||
VCNL4040_LED_DUTY_1_160,
|
||||
VCNL4040_LED_DUTY_1_320,
|
||||
};
|
||||
|
||||
enum ambient_integration_time {
|
||||
VCNL4040_AMBIENT_INTEGRATION_TIME_80MS,
|
||||
VCNL4040_AMBIENT_INTEGRATION_TIME_160MS,
|
||||
VCNL4040_AMBIENT_INTEGRATION_TIME_320MS,
|
||||
VCNL4040_AMBIENT_INTEGRATION_TIME_640MS,
|
||||
};
|
||||
|
||||
enum proximity_integration_time {
|
||||
VCNL4040_PROXIMITY_INTEGRATION_TIME_1T,
|
||||
VCNL4040_PROXIMITY_INTEGRATION_TIME_1_5T,
|
||||
VCNL4040_PROXIMITY_INTEGRATION_TIME_2T,
|
||||
VCNL4040_PROXIMITY_INTEGRATION_TIME_2_5T,
|
||||
VCNL4040_PROXIMITY_INTEGRATION_TIME_3T,
|
||||
VCNL4040_PROXIMITY_INTEGRATION_TIME_3_5T,
|
||||
VCNL4040_PROXIMITY_INTEGRATION_TIME_4T,
|
||||
VCNL4040_PROXIMITY_INTEGRATION_TIME_8T,
|
||||
};
|
||||
|
||||
enum proximity_type {
|
||||
VCNL4040_PROXIMITY_INT_DISABLE,
|
||||
VCNL4040_PROXIMITY_INT_CLOSE,
|
||||
VCNL4040_PROXIMITY_INT_AWAY,
|
||||
VCNL4040_PROXIMITY_INT_CLOSE_AWAY,
|
||||
};
|
||||
|
||||
enum interrupt_type {
|
||||
VCNL4040_PROXIMITY_AWAY = 1,
|
||||
VCNL4040_PROXIMITY_CLOSE,
|
||||
VCNL4040_AMBIENT_HIGH = 4,
|
||||
VCNL4040_AMBIENT_LOW,
|
||||
};
|
||||
|
||||
struct vcnl4040_config {
|
||||
char *i2c_name;
|
||||
uint8_t i2c_address;
|
||||
#ifdef CONFIG_VCNL4040_TRIGGER
|
||||
const char *gpio_name;
|
||||
gpio_pin_t gpio_pin;
|
||||
gpio_dt_flags_t gpio_flags;
|
||||
#endif
|
||||
enum led_current led_i;
|
||||
enum led_duty_cycle led_dc;
|
||||
enum ambient_integration_time als_it;
|
||||
enum proximity_integration_time proxy_it;
|
||||
enum proximity_type proxy_type;
|
||||
};
|
||||
|
||||
struct vcnl4040_data {
|
||||
const struct device *i2c;
|
||||
struct k_sem sem;
|
||||
#ifdef CONFIG_VCNL4040_TRIGGER
|
||||
const struct device *dev;
|
||||
const struct device *gpio;
|
||||
uint8_t gpio_pin;
|
||||
struct gpio_callback gpio_cb;
|
||||
enum interrupt_type int_type;
|
||||
sensor_trigger_handler_t proxy_handler;
|
||||
sensor_trigger_handler_t als_handler;
|
||||
#endif
|
||||
#ifdef CONFIG_VCNL4040_TRIGGER_OWN_THREAD
|
||||
K_THREAD_STACK_MEMBER(thread_stack, CONFIG_VCNL4040_THREAD_STACK_SIZE);
|
||||
struct k_thread thread;
|
||||
struct k_sem trig_sem;
|
||||
#endif
|
||||
#ifdef CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD
|
||||
struct k_work work;
|
||||
#endif
|
||||
uint16_t proximity;
|
||||
uint16_t light;
|
||||
float sensitivity;
|
||||
};
|
||||
|
||||
int vcnl4040_read(const struct device *dev, uint8_t reg, uint16_t *out);
|
||||
int vcnl4040_write(const struct device *dev, uint8_t reg, uint16_t value);
|
||||
|
||||
#ifdef CONFIG_VCNL4040_TRIGGER
|
||||
int vcnl4040_trigger_init(const struct device *dev);
|
||||
|
||||
int vcnl4040_attr_set(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
const struct sensor_value *val);
|
||||
|
||||
int vcnl4040_trigger_set(const struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler);
|
||||
#endif
|
||||
|
||||
#endif
|
294
drivers/sensor/vcnl4040/vcnl4040_trigger.c
Normal file
294
drivers/sensor/vcnl4040/vcnl4040_trigger.c
Normal file
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Richard Osterloh
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "vcnl4040.h"
|
||||
#include <logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(vcnl4040, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static void vcnl4040_handle_cb(struct vcnl4040_data *data)
|
||||
{
|
||||
gpio_pin_interrupt_configure(data->gpio, data->gpio_pin,
|
||||
GPIO_INT_DISABLE);
|
||||
|
||||
#if defined(CONFIG_VCNL4040_TRIGGER_OWN_THREAD)
|
||||
k_sem_give(&data->trig_sem);
|
||||
#elif defined(CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_submit(&data->work);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void vcnl4040_gpio_callback(const struct device *dev,
|
||||
struct gpio_callback *cb,
|
||||
uint32_t pin_mask)
|
||||
{
|
||||
struct vcnl4040_data *data =
|
||||
CONTAINER_OF(cb, struct vcnl4040_data, gpio_cb);
|
||||
|
||||
if ((pin_mask & BIT(data->gpio_pin)) == 0U) {
|
||||
return;
|
||||
}
|
||||
|
||||
vcnl4040_handle_cb(data);
|
||||
}
|
||||
|
||||
static int vcnl4040_handle_proxy_int(const struct device *dev)
|
||||
{
|
||||
struct vcnl4040_data *data = dev->data;
|
||||
struct sensor_trigger proxy_trig = {
|
||||
.type = SENSOR_TRIG_THRESHOLD,
|
||||
.chan = SENSOR_CHAN_PROX,
|
||||
};
|
||||
|
||||
if (data->proxy_handler) {
|
||||
data->proxy_handler(dev, &proxy_trig);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vcnl4040_handle_als_int(const struct device *dev)
|
||||
{
|
||||
struct vcnl4040_data *data = dev->data;
|
||||
struct sensor_trigger als_trig = {
|
||||
.type = SENSOR_TRIG_THRESHOLD,
|
||||
.chan = SENSOR_CHAN_LIGHT,
|
||||
};
|
||||
|
||||
if (data->als_handler) {
|
||||
data->als_handler(dev, &als_trig);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vcnl4040_handle_int(const struct device *dev)
|
||||
{
|
||||
const struct vcnl4040_config *config = dev->config;
|
||||
struct vcnl4040_data *data = dev->data;
|
||||
uint16_t int_source;
|
||||
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
if (vcnl4040_read(dev, VCNL4040_REG_INT_FLAG, &int_source)) {
|
||||
LOG_ERR("Could not read interrupt source");
|
||||
int_source = 0U;
|
||||
}
|
||||
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
data->int_type = int_source >> 8;
|
||||
|
||||
if (data->int_type == VCNL4040_PROXIMITY_AWAY ||
|
||||
data->int_type == VCNL4040_PROXIMITY_CLOSE) {
|
||||
vcnl4040_handle_proxy_int(dev);
|
||||
} else if (data->int_type == VCNL4040_AMBIENT_HIGH ||
|
||||
data->int_type == VCNL4040_AMBIENT_LOW) {
|
||||
vcnl4040_handle_als_int(dev);
|
||||
} else {
|
||||
LOG_ERR("Unknown interrupt source %d", data->int_type);
|
||||
}
|
||||
|
||||
gpio_pin_interrupt_configure(data->gpio, config->gpio_pin,
|
||||
GPIO_INT_EDGE_TO_ACTIVE);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VCNL4040_TRIGGER_OWN_THREAD
|
||||
static void vcnl4040_thread_main(struct vcnl4040_data *data)
|
||||
{
|
||||
while (true) {
|
||||
k_sem_take(&data->trig_sem, K_FOREVER);
|
||||
vcnl4040_handle_int(data->dev);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD
|
||||
static void vcnl4040_work_handler(struct k_work *work)
|
||||
{
|
||||
struct vcnl4040_data *data =
|
||||
CONTAINER_OF(work, struct vcnl4040_data, work);
|
||||
|
||||
vcnl4040_handle_int(data->dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
int vcnl4040_attr_set(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
const struct sensor_value *val)
|
||||
{
|
||||
struct vcnl4040_data *data = dev->data;
|
||||
int ret = 0;
|
||||
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
if (chan == SENSOR_CHAN_PROX) {
|
||||
if (attr == SENSOR_ATTR_UPPER_THRESH) {
|
||||
if (vcnl4040_write(dev, VCNL4040_REG_PS_THDH,
|
||||
(uint16_t)val->val1)) {
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto exit;
|
||||
}
|
||||
if (attr == SENSOR_ATTR_LOWER_THRESH) {
|
||||
if (vcnl4040_write(dev, VCNL4040_REG_PS_THDL,
|
||||
(uint16_t)val->val1)) {
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_VCNL4040_ENABLE_ALS
|
||||
if (chan == SENSOR_CHAN_LIGHT) {
|
||||
if (attr == SENSOR_ATTR_UPPER_THRESH) {
|
||||
if (vcnl4040_write(dev, VCNL4040_REG_ALS_THDH,
|
||||
(uint16_t)val->val1)) {
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto exit;
|
||||
}
|
||||
if (attr == SENSOR_ATTR_LOWER_THRESH) {
|
||||
if (vcnl4040_write(dev, VCNL4040_REG_ALS_THDL,
|
||||
(uint16_t)val->val1)) {
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
ret = -ENOTSUP;
|
||||
exit:
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vcnl4040_trigger_set(const struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
const struct vcnl4040_config *config = dev->config;
|
||||
struct vcnl4040_data *data = dev->data;
|
||||
uint16_t conf;
|
||||
int ret = 0;
|
||||
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
switch (trig->type) {
|
||||
case SENSOR_TRIG_THRESHOLD:
|
||||
if (trig->chan == SENSOR_CHAN_PROX) {
|
||||
if (vcnl4040_read(dev, VCNL4040_REG_PS_CONF, &conf)) {
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Set interrupt bits 1:0 of high register */
|
||||
conf |= config->proxy_type << 8;
|
||||
|
||||
if (vcnl4040_write(dev, VCNL4040_REG_PS_CONF, conf)) {
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
data->proxy_handler = handler;
|
||||
} else if (trig->chan == SENSOR_CHAN_LIGHT) {
|
||||
#ifdef CONFIG_VCNL4040_ENABLE_ALS
|
||||
if (vcnl4040_read(dev, VCNL4040_REG_ALS_CONF, &conf)) {
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Set interrupt enable bit 1 */
|
||||
conf |= 1 << 1;
|
||||
|
||||
if (vcnl4040_write(dev, VCNL4040_REG_ALS_CONF, conf)) {
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
data->als_handler = handler;
|
||||
#else
|
||||
ret = -ENOTSUP;
|
||||
goto exit;
|
||||
#endif
|
||||
} else {
|
||||
ret = -ENOTSUP;
|
||||
goto exit;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Unsupported sensor trigger");
|
||||
ret = -ENOTSUP;
|
||||
goto exit;
|
||||
}
|
||||
exit:
|
||||
k_sem_give(&data->sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int vcnl4040_trigger_init(const struct device *dev)
|
||||
{
|
||||
const struct vcnl4040_config *config = dev->config;
|
||||
struct vcnl4040_data *data = dev->data;
|
||||
|
||||
data->dev = dev;
|
||||
|
||||
#if defined(CONFIG_VCNL4040_TRIGGER_OWN_THREAD)
|
||||
k_sem_init(&data->trig_sem, 0, UINT_MAX);
|
||||
k_thread_create(&data->thread, data->thread_stack,
|
||||
CONFIG_VCNL4040_THREAD_STACK_SIZE,
|
||||
(k_thread_entry_t)vcnl4040_thread_main,
|
||||
data, NULL, NULL,
|
||||
K_PRIO_COOP(CONFIG_VCNL4040_THREAD_PRIORITY),
|
||||
0, K_NO_WAIT);
|
||||
k_thread_name_set(&data->thread, "VCNL4040 trigger");
|
||||
#elif defined(CONFIG_VCNL4040_TRIGGER_GLOBAL_THREAD)
|
||||
data->work.handler = vcnl4040_work_handler;
|
||||
#endif
|
||||
|
||||
/* Get the GPIO device */
|
||||
data->gpio = device_get_binding(config->gpio_name);
|
||||
if (data->gpio == NULL) {
|
||||
LOG_ERR("Could not find GPIO device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->gpio_pin = config->gpio_pin;
|
||||
|
||||
gpio_pin_configure(data->gpio, config->gpio_pin,
|
||||
GPIO_INPUT | config->gpio_flags);
|
||||
|
||||
gpio_init_callback(&data->gpio_cb, vcnl4040_gpio_callback,
|
||||
BIT(config->gpio_pin));
|
||||
|
||||
if (gpio_add_callback(data->gpio, &data->gpio_cb) < 0) {
|
||||
LOG_DBG("Failed to set gpio callback!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
gpio_pin_interrupt_configure(data->gpio, config->gpio_pin,
|
||||
GPIO_INT_EDGE_TO_ACTIVE);
|
||||
|
||||
if (gpio_pin_get(data->gpio, config->gpio_pin) > 0) {
|
||||
vcnl4040_handle_cb(data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
81
dts/bindings/sensor/vishay,vcnl4040.yaml
Normal file
81
dts/bindings/sensor/vishay,vcnl4040.yaml
Normal file
|
@ -0,0 +1,81 @@
|
|||
# Copyright (c) 2020 Richard Osterloh
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: VCNL4040 proximity and ambient light sensor
|
||||
|
||||
compatible: "vishay,vcnl4040"
|
||||
|
||||
include: i2c-device.yaml
|
||||
|
||||
properties:
|
||||
int-gpios:
|
||||
type: phandle-array
|
||||
required: false
|
||||
description: |
|
||||
The INT pin signals that a programmable interrupt function
|
||||
for ALS and PS with upper and lower thresholds has been
|
||||
triggered. The sensor generates an active-low level signal
|
||||
which remains asserted until the data is read.
|
||||
|
||||
led-current:
|
||||
type: int
|
||||
required: false
|
||||
default: 50
|
||||
description: LED current in mA
|
||||
enum:
|
||||
- 50
|
||||
- 75
|
||||
- 100
|
||||
- 120
|
||||
- 140
|
||||
- 160
|
||||
- 180
|
||||
- 200
|
||||
|
||||
led-duty-cycle:
|
||||
type: int
|
||||
required: false
|
||||
default: 40
|
||||
description: LED duty cycle in Hz
|
||||
enum:
|
||||
- 40
|
||||
- 80
|
||||
- 160
|
||||
- 320
|
||||
|
||||
proximity-it:
|
||||
type: string
|
||||
required: false
|
||||
default: "1"
|
||||
description: Proximity integration time in T
|
||||
enum:
|
||||
- "1"
|
||||
- "1.5"
|
||||
- "2"
|
||||
- "2.5"
|
||||
- "3"
|
||||
- "3.5"
|
||||
- "4"
|
||||
- "8"
|
||||
|
||||
proximity-trigger:
|
||||
type: string
|
||||
required: false
|
||||
default: "close-away"
|
||||
description: Proximity trigger type
|
||||
enum:
|
||||
- "disabled"
|
||||
- "close"
|
||||
- "away"
|
||||
- "close-away"
|
||||
|
||||
als-it:
|
||||
type: int
|
||||
required: false
|
||||
default: 80
|
||||
description: ALS integration time in ms
|
||||
enum:
|
||||
- 80
|
||||
- 160
|
||||
- 320
|
||||
- 640
|
|
@ -559,3 +559,10 @@ test_i2c_max17055: max17055@49 {
|
|||
desired-charging-current = <2000>;
|
||||
rsense-mohms = <5>;
|
||||
};
|
||||
|
||||
test_i2c_vcnl4040: vcnl4040@60 {
|
||||
compatible = "vishay,vcnl4040";
|
||||
label = "VCNL4040";
|
||||
reg = <0x60>;
|
||||
int-gpios = <&test_gpio 0 0>;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
CONFIG_MAX_THREAD_BYTES=4
|
||||
CONFIG_TEST=y
|
||||
CONFIG_TEST_USERSPACE=y
|
||||
CONFIG_MAX_THREAD_BYTES=3
|
||||
CONFIG_I2C=y
|
||||
CONFIG_ADC=y
|
||||
CONFIG_GPIO=y
|
||||
|
@ -36,3 +37,5 @@ CONFIG_SX9500_TRIGGER_OWN_THREAD=y
|
|||
CONFIG_TMP007=y
|
||||
CONFIG_TMP007_TRIGGER_OWN_THREAD=y
|
||||
CONFIG_ITDS=y
|
||||
CONFIG_VCNL4040=y
|
||||
CONFIG_VCNL4040_TRIGGER_OWN_THREAD=y
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue