From 8f1c37e863760469c6ff9b53abd68de248621f4c Mon Sep 17 00:00:00 2001 From: Andreas Klinger Date: Fri, 16 May 2025 22:50:14 +0200 Subject: [PATCH 1/3] drivers: sensor: add Vishay veml6046 RGBIR color sensor - add driver for Vishay VEML6046 RGBIR color sensor - add new compatible "vishay,veml6046" - support fetch and get sensor subsystem operations - triggered mode and interrupts are not yet supported Signed-off-by: Andreas Klinger --- drivers/sensor/vishay/CMakeLists.txt | 1 + drivers/sensor/vishay/Kconfig | 1 + drivers/sensor/vishay/veml6046/CMakeLists.txt | 5 + drivers/sensor/vishay/veml6046/Kconfig | 12 + drivers/sensor/vishay/veml6046/veml6046.c | 632 ++++++++++++++++++ dts/bindings/sensor/vishay,veml6046.yaml | 10 + include/zephyr/drivers/sensor/veml6046.h | 194 ++++++ tests/drivers/build_all/sensor/i2c.dtsi | 5 + 8 files changed, 860 insertions(+) create mode 100644 drivers/sensor/vishay/veml6046/CMakeLists.txt create mode 100644 drivers/sensor/vishay/veml6046/Kconfig create mode 100644 drivers/sensor/vishay/veml6046/veml6046.c create mode 100644 dts/bindings/sensor/vishay,veml6046.yaml create mode 100644 include/zephyr/drivers/sensor/veml6046.h diff --git a/drivers/sensor/vishay/CMakeLists.txt b/drivers/sensor/vishay/CMakeLists.txt index c0462dba8620d..56ff0b09bbaac 100644 --- a/drivers/sensor/vishay/CMakeLists.txt +++ b/drivers/sensor/vishay/CMakeLists.txt @@ -5,5 +5,6 @@ add_subdirectory_ifdef(CONFIG_VCNL36825T vcnl36825t) add_subdirectory_ifdef(CONFIG_VCNL4040 vcnl4040) add_subdirectory_ifdef(CONFIG_VEML6031 veml6031) +add_subdirectory_ifdef(CONFIG_VEML6046 veml6046) add_subdirectory_ifdef(CONFIG_VEML7700 veml7700) # zephyr-keep-sorted-stop diff --git a/drivers/sensor/vishay/Kconfig b/drivers/sensor/vishay/Kconfig index 564a9740c9c8e..a203cadd9947b 100644 --- a/drivers/sensor/vishay/Kconfig +++ b/drivers/sensor/vishay/Kconfig @@ -5,5 +5,6 @@ source "drivers/sensor/vishay/vcnl36825t/Kconfig" source "drivers/sensor/vishay/vcnl4040/Kconfig" source "drivers/sensor/vishay/veml6031/Kconfig" +source "drivers/sensor/vishay/veml6046/Kconfig" source "drivers/sensor/vishay/veml7700/Kconfig" # zephyr-keep-sorted-stop diff --git a/drivers/sensor/vishay/veml6046/CMakeLists.txt b/drivers/sensor/vishay/veml6046/CMakeLists.txt new file mode 100644 index 0000000000000..c31cfff011a18 --- /dev/null +++ b/drivers/sensor/vishay/veml6046/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright (c) 2025 Andreas Klinger +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(veml6046.c) diff --git a/drivers/sensor/vishay/veml6046/Kconfig b/drivers/sensor/vishay/veml6046/Kconfig new file mode 100644 index 0000000000000..06e057e07ec21 --- /dev/null +++ b/drivers/sensor/vishay/veml6046/Kconfig @@ -0,0 +1,12 @@ +# Copyright (c) 2025 Andreas Klinger +# SPDX-License-Identifier: Apache-2.0 + +# Vishay VEML6046 RGBIR color sensor driver options. + +config VEML6046 + bool "Vishay VEML6046 RGBIR color sensor" + default y + depends on DT_HAS_VISHAY_VEML6046_ENABLED + select I2C + help + Enable Vishay VEML6046 RGBIR color sensor driver. diff --git a/drivers/sensor/vishay/veml6046/veml6046.c b/drivers/sensor/vishay/veml6046/veml6046.c new file mode 100644 index 0000000000000..9f0b54e1f27cd --- /dev/null +++ b/drivers/sensor/vishay/veml6046/veml6046.c @@ -0,0 +1,632 @@ +/* + * Copyright (c) 2025 Andreas Klinger + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT vishay_veml6046 + +#include +#include +#include +#include +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(VEML6046, CONFIG_SENSOR_LOG_LEVEL); + +/* + * ID code of device + */ +#define VEML6046_DEFAULT_ID 0x01 + +/* + * Bit mask to check for data ready in single measurement. + */ +#define VEML6046_AF_DATA_READY BIT(3) + +/* + * Maximum value of RGBIR data which also means that the sensor is in + * saturation and that the measured value might be wrong. + * In such a case the user program should reduce one or more of the following + * attributes to get a relyable value: + * gain + * integration time + * effective photodiode size divider + */ +#define VEML6046_DATA_OVERFLOW 0xFFFF + +/* + * 16-bit command register addresses + */ +#define VEML6046_CMDCODE_RGB_CONF_0 0x00 +#define VEML6046_CMDCODE_RGB_CONF_1 0x01 +#define VEML6046_CMDCODE_G_THDH_L 0x04 +#define VEML6046_CMDCODE_G_THDH_H 0x05 +#define VEML6046_CMDCODE_G_THDL_L 0x06 +#define VEML6046_CMDCODE_G_THDL_H 0x07 +#define VEML6046_CMDCODE_R_DATA_L 0x10 +#define VEML6046_CMDCODE_R_DATA_H 0x11 +#define VEML6046_CMDCODE_G_DATA_L 0x12 +#define VEML6046_CMDCODE_G_DATA_H 0x13 +#define VEML6046_CMDCODE_B_DATA_L 0x14 +#define VEML6046_CMDCODE_B_DATA_H 0x15 +#define VEML6046_CMDCODE_IR_DATA_L 0x16 +#define VEML6046_CMDCODE_IR_DATA_H 0x17 +#define VEML6046_CMDCODE_ID_L 0x18 +#define VEML6046_CMDCODE_ID_H 0x19 +#define VEML6046_CMDCODE_INT_L 0x1A +#define VEML6046_CMDCODE_INT_H 0x1B + +/* + * ALS integration time struct. + */ +struct veml6046_it_data { + enum veml6046_it num; + uint8_t val; + int us; +}; + +/* + * ALS integration time setting values. + * + * The enumerators of enum veml6046_it provide indices into this array to get + * the related value for the ALS_IT configuration bits. + */ +static const struct veml6046_it_data veml6046_it_values[VEML6046_IT_COUNT] = { + {VEML6046_IT_3_125, 0x00, 3125}, /* 3.125 - 0b0000 */ + {VEML6046_IT_6_25, 0x01, 6250}, /* 6.25 - 0b0001 */ + {VEML6046_IT_12_5, 0x02, 12500}, /* 12.5 - 0b0010 */ + {VEML6046_IT_25, 0x03, 25000}, /* 25 - 0b0011 */ + {VEML6046_IT_50, 0x04, 50000}, /* 50 - 0b0100 */ + {VEML6046_IT_100, 0x05, 100000}, /* 100 - 0b0101 */ + {VEML6046_IT_200, 0x06, 200000}, /* 200 - 0b0110 */ + {VEML6046_IT_400, 0x07, 400000}, /* 400 - 0b0111 */ +}; + +/* + * Resolution matrix for values to convert between data provided + * by the sensor ("counts") and lux. + * + * These values depend on the current size, gain and integration time settings. + * The enumerators of enum veml6046_pdd, enum veml6046_gain and enum + * veml6046_als_it are used for indices into this matrix. + */ +static const float + veml6046_resolution[VEML6046_PDD_COUNT][VEML6046_GAIN_COUNT][VEML6046_IT_COUNT] = { + /*3.125ms 6.25ms 12.5ms 25ms 50ms 100ms 200ms 400ms IT */ + /* size 2/2 */ + { + {1.3440f, 0.6720f, 0.3360f, 0.1680f, 0.0840f, 0.0420f, 0.0210f, + 0.0105f}, /* Gain 1 */ + {0.6720f, 0.3360f, 0.1680f, 0.0840f, 0.0420f, 0.0210f, 0.0105f, + 0.0053f}, /* Gain 2 */ + {2.0364f, 1.0182f, 0.5091f, 0.2545f, 0.1273f, 0.0636f, 0.0318f, + 0.0159f}, /* Gain 0.66 */ + {2.6880f, 1.3440f, 0.6720f, 0.3360f, 0.1680f, 0.0840f, 0.0420f, + 0.0210f}, /* Gain 0.5 */ + }, + { + /* size 1/2 */ + {2.6880f, 1.3440f, 0.6720f, 0.3360f, 0.1680f, 0.0840f, 0.0420f, + 0.0210f}, /* Gain 1 */ + {1.3440f, 0.6720f, 0.3360f, 0.1680f, 0.0840f, 0.0420f, 0.0210f, + 0.0105f}, /* Gain 2 */ + {4.0727f, 2.0364f, 1.0182f, 0.5091f, 0.2545f, 0.1273f, 0.0636f, + 0.0318f}, /* Gain 0.66 */ + {5.3760f, 2.6880f, 1.3440f, 0.6720f, 0.3360f, 0.1680f, 0.0840f, + 0.0420f}, /* Gain 0.5 */ + }, +}; + +struct veml6046_config { + struct i2c_dt_spec bus; +}; + +struct veml6046_data { + uint8_t sd; /* Band gap and LDO shutdown */ + uint8_t int_en; /* ALS interrupt enable */ + uint8_t trig; /* ALS active force trigger */ + enum veml6046_pdd pdd; /* effective photodiode size divider */ + enum veml6046_gain gain; /* gain selection */ + enum veml6046_it itim; /* ALS integration time */ + enum veml6046_pers pers; /* ALS persistens protect */ + uint16_t thresh_high; + uint16_t thresh_low; + uint16_t red_data; + uint16_t green_data; + uint16_t blue_data; + uint16_t ir_data; + uint32_t red_lux; + uint32_t green_lux; + uint32_t blue_lux; + uint32_t ir_lux; +}; + +static int veml6046_check_gain(const struct sensor_value *val) +{ + return val->val1 >= VEML6046_GAIN_1 && val->val1 <= VEML6046_GAIN_0_5; +} + +static int veml6046_check_it(const struct sensor_value *val) +{ + return val->val1 >= VEML6046_IT_3_125 && val->val1 <= VEML6046_IT_400; +} + +static int veml6046_check_pdd(const struct sensor_value *val) +{ + return val->val1 >= VEML6046_SIZE_2_2 && val->val1 <= VEML6046_SIZE_1_2; +} + +static int veml6046_check_pers(const struct sensor_value *val) +{ + return val->val1 >= VEML6046_PERS_1 && val->val1 <= VEML6046_PERS_8; +} + +static int veml6046_read16(const struct device *dev, uint8_t cmd, uint16_t *data) +{ + const struct veml6046_config *conf = dev->config; + int ret; + + ret = i2c_burst_read_dt(&conf->bus, cmd, (uint8_t *)data, 2); + if (ret < 0) { + return ret; + } + + *data = sys_le16_to_cpu(*data); + + return 0; +} + +/* + * This function excepts an array of uint8_t data[2] with the two corresponding + * values set accordingly to the register map of the sensor. + */ +static int veml6046_write16(const struct device *dev, uint8_t cmd, uint8_t *data) +{ + const struct veml6046_config *conf = dev->config; + + return i2c_burst_write_dt(&conf->bus, cmd, data, 2); +} + +static int veml6046_write_conf(const struct device *dev) +{ + int ret; + struct veml6046_data *data = dev->data; + uint8_t conf[2] = {0, 0}; + + /* Bits 7 -> RGB_ON_1 */ + if (data->sd) { + conf[1] |= BIT(7); + } + /* Bits 6 -> Effective photodiode size */ + conf[1] |= data->pdd << 6; + /* Bit 5 -> reserved */ + /* Bits 4:3 -> Gain selection */ + conf[1] |= data->gain << 3; + /* Bits 2:1 -> ALS persistence protect number */ + conf[1] |= data->pers << 1; + /* Bit 0 -> Calibration should always be 1 when using the sensor */ + conf[1] |= BIT(0); + /* Bit 7 -> reserved, have to be 0 */ + /* Bits 6:4 -> integration time (ALS_IT) */ + conf[0] |= data->itim << 4; + /* Bit 3 -> Active force mode is always enabled + * Auto mode would continuously deliver data which is not what we want + * in this driver + */ + conf[0] |= BIT(3); + /* Bit 2 -> ALS active force trigger */ + if (data->trig) { + conf[0] |= BIT(2); + } + /* Bit 1 -> ALS interrupt enable */ + if (data->int_en) { + conf[0] |= BIT(1); + } + /* Bit 0 -> shut down setting (SD) */ + if (data->sd) { + conf[0] |= BIT(0); + } + + ret = veml6046_write16(dev, VEML6046_CMDCODE_RGB_CONF_0, conf); + if (ret) { + LOG_ERR("Error while writing conf[0] ret: %d", ret); + return ret; + } + + return 0; +} + +static int veml6046_write_thresh_high(const struct device *dev) +{ + int ret; + const struct veml6046_data *data = dev->data; + uint8_t val[2]; + + val[0] = data->thresh_high & 0xFF; + val[1] = data->thresh_high >> 8; + + LOG_DBG("Writing high threshold counts: %d", data->thresh_high); + ret = veml6046_write16(dev, VEML6046_CMDCODE_G_THDH_L, val); + if (ret) { + return ret; + } + + return 0; +} + +static int veml6046_write_thresh_low(const struct device *dev) +{ + int ret; + const struct veml6046_data *data = dev->data; + uint8_t val[2]; + + val[0] = data->thresh_low & 0xFF; + val[1] = data->thresh_low >> 8; + + LOG_DBG("Writing low threshold counts: %d", data->thresh_low); + ret = veml6046_write16(dev, VEML6046_CMDCODE_G_THDL_L, val); + if (ret) { + return ret; + } + + return 0; +} + +static int veml6046_fetch(const struct device *dev) +{ + struct veml6046_data *data = dev->data; + int ret; + + ret = veml6046_read16(dev, VEML6046_CMDCODE_R_DATA_L, &data->red_data); + if (ret) { + return ret; + } + + ret = veml6046_read16(dev, VEML6046_CMDCODE_G_DATA_L, &data->green_data); + if (ret) { + return ret; + } + + ret = veml6046_read16(dev, VEML6046_CMDCODE_B_DATA_L, &data->blue_data); + if (ret) { + return ret; + } + + ret = veml6046_read16(dev, VEML6046_CMDCODE_IR_DATA_L, &data->ir_data); + if (ret) { + return ret; + } + + data->red_lux = data->red_data * veml6046_resolution[data->pdd][data->gain][data->itim]; + data->green_lux = data->green_data * veml6046_resolution[data->pdd][data->gain][data->itim]; + data->blue_lux = data->blue_data * veml6046_resolution[data->pdd][data->gain][data->itim]; + data->ir_lux = data->ir_data * veml6046_resolution[data->pdd][data->gain][data->itim]; + + LOG_DBG("Read (R/G/B/IR): counts=%d/%d/%d/%d, lux=%d/%d/%d/%d", + data->red_data, data->green_data, data->blue_data, data->ir_data, + data->red_lux, data->green_lux, data->blue_lux, data->ir_lux); + + if ((data->red_data == VEML6046_DATA_OVERFLOW) || + (data->green_data == VEML6046_DATA_OVERFLOW) || + (data->blue_data == VEML6046_DATA_OVERFLOW) || + (data->ir_data == VEML6046_DATA_OVERFLOW)) { + return -E2BIG; + } + + return 0; +} + +static int veml6046_attr_set(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, const struct sensor_value *val) +{ + struct veml6046_data *data = dev->data; + + if (chan != SENSOR_CHAN_LIGHT) { + return -ENOTSUP; + } + + /* SENSOR_ATTR_.*_THRESH are not in enum sensor_attribute_veml6046 */ + switch ((int)attr) { + case SENSOR_ATTR_VEML6046_IT: + if (veml6046_check_it(val)) { + data->itim = (enum veml6046_it)val->val1; + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_VEML6046_PDD: + if (veml6046_check_pdd(val)) { + data->pdd = (enum veml6046_pdd)val->val1; + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_VEML6046_GAIN: + if (veml6046_check_gain(val)) { + data->gain = (enum veml6046_gain)val->val1; + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_VEML6046_PERS: + if (veml6046_check_pers(val)) { + data->pers = (enum veml6046_pers)val->val1; + } else { + return -EINVAL; + } + break; + case SENSOR_ATTR_LOWER_THRESH: + data->thresh_low = + val->val1 / veml6046_resolution[data->pdd][data->gain][data->itim]; + return veml6046_write_thresh_low(dev); + case SENSOR_ATTR_UPPER_THRESH: + data->thresh_high = + val->val1 / veml6046_resolution[data->pdd][data->gain][data->itim]; + return veml6046_write_thresh_high(dev); + default: + return -ENOTSUP; + } + + return 0; +} + +static int veml6046_attr_get(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, struct sensor_value *val) +{ + struct veml6046_data *data = dev->data; + + if (chan != SENSOR_CHAN_LIGHT) { + return -ENOTSUP; + } + + /* SENSOR_ATTR_.*_THRESH are not in enum sensor_attribute_veml6046 */ + switch ((int)attr) { + case SENSOR_ATTR_VEML6046_IT: + val->val1 = data->itim; + break; + case SENSOR_ATTR_VEML6046_PDD: + val->val1 = data->pdd; + break; + case SENSOR_ATTR_VEML6046_GAIN: + val->val1 = data->gain; + break; + case SENSOR_ATTR_VEML6046_PERS: + val->val1 = data->pers; + break; + case SENSOR_ATTR_LOWER_THRESH: + val->val1 = data->thresh_low + * veml6046_resolution[data->pdd][data->gain][data->itim]; + break; + case SENSOR_ATTR_UPPER_THRESH: + val->val1 = data->thresh_high + * veml6046_resolution[data->pdd][data->gain][data->itim]; + break; + default: + return -ENOTSUP; + } + + val->val2 = 0; + + return 0; +} + +static int veml6046_perform_single_measurement(const struct device *dev) +{ + struct veml6046_data *data = dev->data; + int ret; + uint16_t val; + int cnt = 0; + + data->trig = 1; + data->int_en = 0; + data->sd = 0; + + ret = veml6046_write_conf(dev); + if (ret) { + return ret; + } + + ret = veml6046_read16(dev, VEML6046_CMDCODE_INT_L, &val); + if (ret) { + return ret; + } + + k_sleep(K_USEC(veml6046_it_values[data->itim].us)); + + while (1) { + ret = veml6046_read16(dev, VEML6046_CMDCODE_INT_L, &val); + if (ret) { + return ret; + } + + if ((val >> 8) & VEML6046_AF_DATA_READY) { + break; + } + + if (cnt > 10) { + return -EAGAIN; + } + + k_sleep(K_USEC(veml6046_it_values[data->itim].us / 10)); + + cnt++; + } + + LOG_DBG("read VEML6046_CMDCODE_INT_H: %02X (%d)", val >> 8, cnt); + + return 0; +} + +static int veml6046_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + int ret; + + /* Start sensor for new measurement */ + if (chan == SENSOR_CHAN_RED || chan == SENSOR_CHAN_GREEN || + chan == SENSOR_CHAN_BLUE || chan == SENSOR_CHAN_IR || + chan == SENSOR_CHAN_ALL) { + ret = veml6046_perform_single_measurement(dev); + if (ret < 0) { + return ret; + } + + return veml6046_fetch(dev); + } else { + return -ENOTSUP; + } + + return 0; +} + +static int veml6046_channel_get(const struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + struct veml6046_data *data = dev->data; + + switch ((int)chan) { + case SENSOR_CHAN_RED: + val->val1 = data->red_lux; + break; + case SENSOR_CHAN_GREEN: + val->val1 = data->green_lux; + break; + case SENSOR_CHAN_BLUE: + val->val1 = data->blue_lux; + break; + case SENSOR_CHAN_IR: + val->val1 = data->ir_lux; + break; + case SENSOR_CHAN_VEML6046_RED_RAW_COUNTS: + val->val1 = data->red_data; + break; + case SENSOR_CHAN_VEML6046_GREEN_RAW_COUNTS: + val->val1 = data->green_data; + break; + case SENSOR_CHAN_VEML6046_BLUE_RAW_COUNTS: + val->val1 = data->blue_data; + break; + case SENSOR_CHAN_VEML6046_IR_RAW_COUNTS: + val->val1 = data->ir_data; + break; + default: + return -ENOTSUP; + } + + val->val2 = 0; + + return 0; +} + +#ifdef CONFIG_PM_DEVICE + +static int veml6046_set_shutdown_flag(const struct device *dev, uint8_t new_val) +{ + struct veml6046_data *data = dev->data; + uint8_t prev_sd; + int ret; + + prev_sd = data->sd; + data->sd = new_val; + + ret = veml6046_write_conf(dev); + if (ret < 0) { + data->sd = prev_sd; + } + return ret; +} + +static int veml6046_pm_action(const struct device *dev, enum pm_device_action action) +{ + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + return veml6046_set_shutdown_flag(dev, 1); + + case PM_DEVICE_ACTION_RESUME: + return veml6046_set_shutdown_flag(dev, 0); + + default: + return -ENOTSUP; + } + + return 0; +} + +#endif /* CONFIG_PM_DEVICE */ + +static int veml6046_init(const struct device *dev) +{ + const struct veml6046_config *conf = dev->config; + int ret; + uint16_t val; + + if (!i2c_is_ready_dt(&conf->bus)) { + LOG_ERR("VEML device not ready"); + return -ENODEV; + } + + ret = veml6046_read16(dev, VEML6046_CMDCODE_ID_L, &val); + if (ret) { + LOG_ERR("Error while reading ID. ret: %d", ret); + return ret; + } + if ((val & 0x00FF) != VEML6046_DEFAULT_ID) { + LOG_ERR("Device ID wrong: %d", val & 0x00FF); + return -EIO; + } + + LOG_DBG("veml6046 found package: %02d address: %02X version: %3s", + val >> 14, val >> 12 & 0x03 ? 0x10 : 0x29, + val >> 8 & 0x0F ? "XXX" : "A01"); + + /* Initialize sensor configuration */ + ret = veml6046_write_thresh_low(dev); + if (ret < 0) { + LOG_ERR("Error while writing thresh low. ret: %d", ret); + return ret; + } + + ret = veml6046_write_thresh_high(dev); + if (ret < 0) { + LOG_ERR("Error while writing thresh high. ret: %d", ret); + return ret; + } + + ret = veml6046_write_conf(dev); + if (ret < 0) { + LOG_ERR("Error while writing config. ret: %d", ret); + return ret; + } + + return 0; +} + +static DEVICE_API(sensor, veml6046_api) = { + .sample_fetch = veml6046_sample_fetch, + .channel_get = veml6046_channel_get, + .attr_set = veml6046_attr_set, + .attr_get = veml6046_attr_get, +}; + +#define VEML6046_INIT(n) \ + static struct veml6046_data veml6046_data_##n = {.trig = 0, \ + .pdd = VEML6046_SIZE_2_2, \ + .gain = VEML6046_GAIN_1, \ + .itim = VEML6046_IT_100, \ + .pers = VEML6046_PERS_1, \ + .thresh_high = 0xFFFF}; \ + \ + static const struct veml6046_config veml6046_config_##n = { \ + .bus = I2C_DT_SPEC_INST_GET(n)}; \ + \ + PM_DEVICE_DT_INST_DEFINE(n, veml6046_pm_action); \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(n, veml6046_init, PM_DEVICE_DT_INST_GET(n), \ + &veml6046_data_##n, &veml6046_config_##n, POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, &veml6046_api); + +DT_INST_FOREACH_STATUS_OKAY(VEML6046_INIT) diff --git a/dts/bindings/sensor/vishay,veml6046.yaml b/dts/bindings/sensor/vishay,veml6046.yaml new file mode 100644 index 0000000000000..473c131c62792 --- /dev/null +++ b/dts/bindings/sensor/vishay,veml6046.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Andreas Klinger +# SPDX-License-Identifier: Apache-2.0 + +description: | + Vishay VEML6046 RGBIR color sensor with I2C interface. + See: https://www.vishay.com/docs/80173/veml6046x00.pdf + +compatible: "vishay,veml6046" + +include: [sensor-device.yaml, i2c-device.yaml] diff --git a/include/zephyr/drivers/sensor/veml6046.h b/include/zephyr/drivers/sensor/veml6046.h new file mode 100644 index 0000000000000..c5641dccc791d --- /dev/null +++ b/include/zephyr/drivers/sensor/veml6046.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2025 Andreas Klinger + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Header file for extended sensor API of VEML6046 sensor + * @ingroup veml6046_interface + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML6046_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML6046_H_ + +/** + * @defgroup veml6046_interface VEML6046 + * @ingroup sensor_interface_ext + * @brief Vishay VEML6046 RGBIR Sensor + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief VEML6046 integration time options for light measurements. + * + * Possible values for @ref SENSOR_ATTR_VEML6046_IT custom attribute. + */ +enum veml6046_it { + VEML6046_IT_3_125, /**< 3.125 ms */ + VEML6046_IT_6_25, /**< 6.25 ms */ + VEML6046_IT_12_5, /**< 12.5 ms */ + VEML6046_IT_25, /**< 25 ms */ + VEML6046_IT_50, /**< 50 ms */ + VEML6046_IT_100, /**< 100 ms */ + VEML6046_IT_200, /**< 200 ms */ + VEML6046_IT_400, /**< 400 ms */ + /** @cond INTERNAL_HIDDEN */ + VEML6046_IT_COUNT, + /** @endcond */ +}; + +/** + * @brief VEML6046 size options for light measurements. + * + * Possible values for @ref SENSOR_ATTR_VEML6046_PDD custom attribute. + */ +enum veml6046_pdd { + VEML6046_SIZE_2_2 = 0x00, /**< 2/2 photodiode size */ + VEML6046_SIZE_1_2 = 0x01, /**< 1/2 photodiode size */ + /** @cond INTERNAL_HIDDEN */ + VEML6046_PDD_COUNT = 2, + /** @endcond */ +}; + +/** + * @brief VEML6046 gain options for light measurements. + * + * Possible values for @ref SENSOR_ATTR_VEML6046_GAIN custom attribute. + */ +enum veml6046_gain { + VEML6046_GAIN_1 = 0x00, /**< 1x gain */ + VEML6046_GAIN_2 = 0x01, /**< 2x gain */ + VEML6046_GAIN_0_66 = 0x02, /**< 0.66x gain */ + VEML6046_GAIN_0_5 = 0x03, /**< 0.5x gain */ + /** @cond INTERNAL_HIDDEN */ + VEML6046_GAIN_COUNT = 4, + /** @endcond */ +}; + +/** + * @brief VEML6046 interrupt persistence protect number options. + * + * Possible values for @ref SENSOR_ATTR_VEML6046_PERS custom attribute. + */ +enum veml6046_pers { + VEML6046_PERS_1 = 0x00, /**< 1 measurement */ + VEML6046_PERS_2 = 0x01, /**< 2 measurements */ + VEML6046_PERS_4 = 0x02, /**< 4 measurements */ + VEML6046_PERS_8 = 0x03, /**< 8 measurements */ +}; + +/** + * @brief VEML6046 specific sensor attributes. + * + * For high and low threshold window settings (G_THDH_L, G_THDH_H, G_THDL_L and + * G_THDL_H) use the generic attributes @ref SENSOR_ATTR_UPPER_THRESH and + * @ref SENSOR_ATTR_LOWER_THRESH with 16-bit unsigned integer values. Both + * threshold settings are in lux and converted by the driver to a value + * compatible with the sensor. This conversion depends on the current gain, + * integration time and effective photodiode size settings. So a change in + * gain, integration time or effective photodiode size usually requires an + * update of threshold window settings. To get the correct threshold values + * into the sensor update the thresholds -after- a change of gain or + * integration time. + * + * When the sensor goes into saturation @c -E2BIG is returned. This happens + * when the maximum value @c 0xFFFF is returned as raw ALS value. In this case + * it's up to the user to reduce one or more of the following attributes to + * come back into the optimal measurement range of the sensor: + * @ref SENSOR_ATTR_VEML6046_GAIN (gain) + * @ref SENSOR_ATTR_VEML6046_IT (integration time) + * @ref SENSOR_ATTR_VEML6046_PDD (effective photodiode size) + */ +enum sensor_attribute_veml6046 { + /** + * @brief Integration time setting for measurements (IT). + * + * Use enum veml6046_it for attribute values. + */ + SENSOR_ATTR_VEML6046_IT = SENSOR_ATTR_PRIV_START, + /** + * @brief Effective photodiode size (PDD) + * + * Use enum veml6046_pdd for attribute values. + */ + SENSOR_ATTR_VEML6046_PDD, + /** + * @brief Gain setting for measurements (GAIN). + * + * Use enum veml6046_gain for attribute values. + */ + SENSOR_ATTR_VEML6046_GAIN, + /** + * @brief Persistence protect number setting (PERS). + * + * Use enum veml6046_pers for attribute values. + */ + SENSOR_ATTR_VEML6046_PERS, +}; + +/** + * @brief VEML6046 specific sensor channels. + */ +enum sensor_channel_veml6046 { + /** + * @brief Channel for raw red sensor values. + * + * This channel represents the raw measurement counts provided by the + * sensor register. It is useful for estimating good values for + * integration time, effective photodiode size and gain attributes in + * fetch and get mode. + * + * For future implementations with triggers it can also be used to + * estimate the threshold window attributes for the sensor interrupt + * handling. + * + * It cannot be fetched directly. Instead, this channel's value is + * fetched implicitly using @ref SENSOR_CHAN_RED. + * Trying to call sensor_channel_fetch_chan with this enumerator + * as an argument will result in a @c -ENOTSUP. + */ + SENSOR_CHAN_VEML6046_RED_RAW_COUNTS = SENSOR_CHAN_PRIV_START, + + /** + * @brief Channel for green sensor values. + * + * This channel is the raw green channel count output of the sensor. + * About fetching the same as for + * @ref SENSOR_CHAN_VEML6046_RED_RAW_COUNTS applies. + */ + SENSOR_CHAN_VEML6046_GREEN_RAW_COUNTS, + + /** + * @brief Channel for blue sensor values. + * + * This channel is the raw blue channel count output of the sensor. + * About fetching the same as for + * @ref SENSOR_CHAN_VEML6046_RED_RAW_COUNTS applies. + */ + SENSOR_CHAN_VEML6046_BLUE_RAW_COUNTS, + + /** + * @brief Channel for IR sensor values. + * + * This channel is the raw IR channel count output of the sensor. About + * fetching the same as for + * @ref SENSOR_CHAN_VEML6046_RED_RAW_COUNTS applies. + */ + SENSOR_CHAN_VEML6046_IR_RAW_COUNTS, +}; + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML6046_H_ */ diff --git a/tests/drivers/build_all/sensor/i2c.dtsi b/tests/drivers/build_all/sensor/i2c.dtsi index cf21635773b54..5f6afa816a0e9 100644 --- a/tests/drivers/build_all/sensor/i2c.dtsi +++ b/tests/drivers/build_all/sensor/i2c.dtsi @@ -1422,3 +1422,8 @@ test_i2c_als31300: als31300@bd { compatible = "allegro,als31300"; reg = <0xbd>; }; + +test_i2c_veml6046: veml6046@be { + compatible = "vishay,veml6046"; + reg = <0xbe>; +}; From a63b123a097a24277fe4cc41d4dfcac8b2d02ac8 Mon Sep 17 00:00:00 2001 From: Andreas Klinger Date: Sat, 17 May 2025 09:43:12 +0200 Subject: [PATCH 2/3] samples: sensor: veml6046: add attribution test application - Test all attribute combinations of Vishay RGBIR color sensor VEML6046. - Print OVERFLOW in case of saturation of sensor. - This small program is intended to be helping when finding appropriate attributes for an application of the sensor. Signed-off-by: Andreas Klinger --- samples/sensor/veml6046/CMakeLists.txt | 7 ++ samples/sensor/veml6046/README.rst | 61 ++++++++++ .../veml6046/boards/olimex_stm32_e407.overlay | 29 +++++ samples/sensor/veml6046/prj.conf | 1 + samples/sensor/veml6046/sample.yaml | 10 ++ samples/sensor/veml6046/src/main.c | 111 ++++++++++++++++++ 6 files changed, 219 insertions(+) create mode 100644 samples/sensor/veml6046/CMakeLists.txt create mode 100644 samples/sensor/veml6046/README.rst create mode 100644 samples/sensor/veml6046/boards/olimex_stm32_e407.overlay create mode 100644 samples/sensor/veml6046/prj.conf create mode 100644 samples/sensor/veml6046/sample.yaml create mode 100644 samples/sensor/veml6046/src/main.c diff --git a/samples/sensor/veml6046/CMakeLists.txt b/samples/sensor/veml6046/CMakeLists.txt new file mode 100644 index 0000000000000..4aa3e0a7b7408 --- /dev/null +++ b/samples/sensor/veml6046/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(veml6046) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/sensor/veml6046/README.rst b/samples/sensor/veml6046/README.rst new file mode 100644 index 0000000000000..12f34edfc1759 --- /dev/null +++ b/samples/sensor/veml6046/README.rst @@ -0,0 +1,61 @@ +.. zephyr:code-sample:: veml6046 + :name: VEML6046 RGBIR Color Sensor + :relevant-api: sensor_interface + + Get red, green, blue and IR light data from a VEML6046 sensor (polling + mode). + +Overview +******** + + This sample measures the red, green, blue and IR light for all possible + combinations of sensor attributes. They are: + + - integration time + - effective photodiode size + - gain + + These attributes can be used to put the sensor in an optimal working area. + When the light value reaches the maximum raw value (0xFFFF), an error is + returned to indicate the out of bounds situation to the user program. + With this program the raw value is also printed out together with the + attributes to be able to select good attribute values. + Interrupt and trigger modes are not supported so far, but planned for future + development. + +Requirements +************ + + This sample uses the VEML6046 sensor controlled using the I2C-2 interface of + the Olimex-STM32-E407 board on Feather connector pins PF0 and PF1. + +References +********** + + - VEML6046: https://www.vishay.com/docs/80173/veml6046x00.pdf + - Application note: https://www.vishay.com/docs/80410/designingveml6046x00.pdf + +Building and Running +******************** + + This project outputs sensor data to the console. It requires a VEML6046 + sensor to be connected to the desired board. + + .. zephyr-app-commands:: + :zephyr-app: samples/sensor/veml6046/ + :goals: build flash + :board: olimex_stm32_e407 + + +Sample Output +============= + + .. code-block:: console + + Test all attributes for a good guess of attribute usage away of saturation. + Red: 68 lx ( 51) green: 68 lx ( 84) blue: 68 lx ( 51) IR: 68 lx ( 27) it: 0 pdd: 0 gain: 0 -- + Red: 121 lx ( 181) green: 121 lx ( 347) blue: 121 lx ( 240) IR: 121 lx ( 53) it: 0 pdd: 0 gain: 1 -- + Red: 215 lx ( 106) green: 215 lx ( 226) blue: 215 lx ( 160) IR: 215 lx ( 19) it: 0 pdd: 0 gain: 2 -- + Red: 201 lx ( 75) green: 201 lx ( 156) blue: 201 lx ( 112) IR: 201 lx ( 14) it: 0 pdd: 0 gain: 3 -- + [...] + Test finished. diff --git a/samples/sensor/veml6046/boards/olimex_stm32_e407.overlay b/samples/sensor/veml6046/boards/olimex_stm32_e407.overlay new file mode 100644 index 0000000000000..1960223382389 --- /dev/null +++ b/samples/sensor/veml6046/boards/olimex_stm32_e407.overlay @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 Andreas Klinger + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + i2c2_sda_pf0: i2c2_sda_pf0 { + pinmux = < 0xa04 >; + bias-pull-up; + drive-open-drain; + }; + i2c2_scl_pf1: i2c2_scl_pf1 { + pinmux = < 0xa24 >; + bias-pull-up; + drive-open-drain; + }; +}; + +&i2c2 { + pinctrl-0 = < &i2c2_scl_pf1 &i2c2_sda_pf0 >; + pinctrl-names = "default"; + status = "okay"; + + rgbir: rgbir@29 { + compatible = "vishay,veml6046"; + reg = <0x29>; + }; +}; diff --git a/samples/sensor/veml6046/prj.conf b/samples/sensor/veml6046/prj.conf new file mode 100644 index 0000000000000..42fcd3c973bcb --- /dev/null +++ b/samples/sensor/veml6046/prj.conf @@ -0,0 +1 @@ +CONFIG_SENSOR=y diff --git a/samples/sensor/veml6046/sample.yaml b/samples/sensor/veml6046/sample.yaml new file mode 100644 index 0000000000000..fe2704301b11c --- /dev/null +++ b/samples/sensor/veml6046/sample.yaml @@ -0,0 +1,10 @@ +sample: + name: VEML6046 Sensor Sample +tests: + sample.sensor.veml6046: + harness: sensor + platform_allow: olimex_stm32_e407 + integration_platforms: + - olimex_stm32_e407 + tags: sensors + filter: dt_compat_enabled("vishay,veml6046") diff --git a/samples/sensor/veml6046/src/main.c b/samples/sensor/veml6046/src/main.c new file mode 100644 index 0000000000000..aec830e7d5177 --- /dev/null +++ b/samples/sensor/veml6046/src/main.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2025 Andreas Klinger + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include + +#include +#include + +#include + +static void read_with_attr(const struct device *dev, int it, int pdd, int gain) +{ + int ret; + struct sensor_value red, green, blue, ir; + struct sensor_value red_raw, green_raw, blue_raw, ir_raw; + struct sensor_value sen; + char result[10]; + + sen.val2 = 0; + + sen.val1 = it; + ret = sensor_attr_set(dev, SENSOR_CHAN_LIGHT, + (enum sensor_attribute)SENSOR_ATTR_VEML6046_IT, &sen); + if (ret) { + printf("Failed to set it attribute ret: %d\n", ret); + } + sen.val1 = pdd; + ret = sensor_attr_set(dev, SENSOR_CHAN_LIGHT, + (enum sensor_attribute)SENSOR_ATTR_VEML6046_PDD, &sen); + if (ret) { + printf("Failed to set pdd attribute ret: %d\n", ret); + } + sen.val1 = gain; + ret = sensor_attr_set(dev, SENSOR_CHAN_LIGHT, + (enum sensor_attribute)SENSOR_ATTR_VEML6046_GAIN, &sen); + if (ret) { + printf("Failed to set gain attribute ret: %d\n", ret); + } + + ret = sensor_sample_fetch(dev); + if ((ret < 0) && (ret != -E2BIG)) { + printf("sample update error. ret: %d\n", ret); + } + + sensor_channel_get(dev, SENSOR_CHAN_RED, &red); + sensor_channel_get(dev, (enum sensor_channel)SENSOR_CHAN_VEML6046_RED_RAW_COUNTS, + &red_raw); + + sensor_channel_get(dev, SENSOR_CHAN_GREEN, &green); + sensor_channel_get(dev, (enum sensor_channel)SENSOR_CHAN_VEML6046_GREEN_RAW_COUNTS, + &green_raw); + + sensor_channel_get(dev, SENSOR_CHAN_BLUE, &blue); + sensor_channel_get(dev, (enum sensor_channel)SENSOR_CHAN_VEML6046_BLUE_RAW_COUNTS, + &blue_raw); + + sensor_channel_get(dev, SENSOR_CHAN_IR, &ir); + sensor_channel_get(dev, (enum sensor_channel)SENSOR_CHAN_VEML6046_IR_RAW_COUNTS, + &ir_raw); + + if (ret == -E2BIG) { + snprintf(result, sizeof(result), "OVERFLOW"); + } else if (ret) { + snprintf(result, sizeof(result), "ERROR"); + } else { + snprintf(result, sizeof(result), ""); + } + + printf("Red: %6d lx (%6d) green: %6d lx (%6d) " + "blue: %6d lx (%6d) IR: %6d lx (%6d) " + " it: %d pdd: %d gain: %d -- %s\n", + red.val1, red_raw.val1, + green.val1, green_raw.val1, + blue.val1, blue_raw.val1, + ir.val1, ir_raw.val1, + it, pdd, gain, + result); +} + +static void read_with_all_attr(const struct device *dev) +{ + for (int it = VEML6046_IT_3_125; it <= VEML6046_IT_400; it++) { + for (int pdd = VEML6046_SIZE_2_2; pdd <= VEML6046_SIZE_1_2; pdd++) { + for (int gain = VEML6046_GAIN_1; gain <= VEML6046_GAIN_0_5; gain++) { + read_with_attr(dev, it, pdd, gain); + } + } + } +} + +int main(void) +{ + const struct device *const veml = DEVICE_DT_GET(DT_NODELABEL(rgbir)); + + if (!device_is_ready(veml)) { + printk("sensor: device not ready.\n"); + return 0; + } + + printf("Test all attributes for a good guess of attribute usage away of saturation.\n"); + read_with_all_attr(veml); + printf("Test finished.\n"); + + return 0; +} From d03591d6a9fe1f4fbedbb35c9087135a15e249a3 Mon Sep 17 00:00:00 2001 From: Andreas Klinger Date: Wed, 8 Oct 2025 13:18:04 +0200 Subject: [PATCH 3/3] drivers: sensor: remove redundancies in veml6031 and veml6046 - create common header file veml60xx-common.h for sensors VEML6031 and VEML6046. Signed-off-by: Andreas Klinger --- drivers/sensor/vishay/veml6031/veml6031.c | 98 +++----------- drivers/sensor/vishay/veml6046/veml6046.c | 80 +++--------- include/zephyr/drivers/sensor/veml6031.h | 46 +------ include/zephyr/drivers/sensor/veml6046.h | 48 +------ .../zephyr/drivers/sensor/veml60xx-common.h | 122 ++++++++++++++++++ samples/sensor/veml6031/src/main.c | 4 +- samples/sensor/veml6046/src/main.c | 4 +- 7 files changed, 167 insertions(+), 235 deletions(-) create mode 100644 include/zephyr/drivers/sensor/veml60xx-common.h diff --git a/drivers/sensor/vishay/veml6031/veml6031.c b/drivers/sensor/vishay/veml6031/veml6031.c index b36b0a4c8d909..b2511b2586f79 100644 --- a/drivers/sensor/vishay/veml6031/veml6031.c +++ b/drivers/sensor/vishay/veml6031/veml6031.c @@ -57,33 +57,6 @@ LOG_MODULE_REGISTER(VEML6031, CONFIG_SENSOR_LOG_LEVEL); #define VEML6031_CMDCODE_ID_H 0x15 #define VEML6031_CMDCODE_ALS_INT 0x17 -/* - * ALS integration time struct. - */ -struct veml6031_it_data { - enum veml6031_it num; - uint8_t val; - int us; -}; - -/* - * ALS integration time setting values. - * - * The enumerators of enum veml6031_it provide - * indices into this array to get the related value for the - * ALS_IT configuration bits. - */ -static const struct veml6031_it_data veml6031_it_values[] = { - {VEML6031_IT_3_125, 0x00, 3125}, /* 3.125 - 0b0000 */ - {VEML6031_IT_6_25, 0x01, 6250}, /* 6.25 - 0b0001 */ - {VEML6031_IT_12_5, 0x02, 12500}, /* 12.5 - 0b0010 */ - {VEML6031_IT_25, 0x03, 25000}, /* 25 - 0b0011 */ - {VEML6031_IT_50, 0x04, 50000}, /* 50 - 0b0100 */ - {VEML6031_IT_100, 0x05, 100000}, /* 100 - 0b0101 */ - {VEML6031_IT_200, 0x06, 200000}, /* 200 - 0b0110 */ - {VEML6031_IT_400, 0x07, 400000}, /* 400 - 0b0111 */ -}; - /* * Resolution matrix for values to convert between data provided * by the sensor ("counts") and lux. @@ -93,7 +66,7 @@ static const struct veml6031_it_data veml6031_it_values[] = { * and enum veml6031_als_it are used for indices into this matrix. */ static const float - veml6031_resolution[VEML6031_DIV4_COUNT][VEML6031_GAIN_COUNT][VEML6031_IT_COUNT] = { + veml6031_resolution[VEML6031_DIV4_COUNT][VEML60XX_GAIN_COUNT][VEML60XX_IT_COUNT] = { /*3.125ms 6.25ms 12.5ms 25ms 50ms 100ms 200ms 400ms IT */ /* size 4/4 */ { @@ -131,9 +104,9 @@ struct veml6031_data { uint8_t ir_sd; /* ALS and IR channel shutdown */ uint8_t cal; /* Power on ready */ enum veml6031_div4 div4; /* effective photodiode size */ - enum veml6031_gain gain; /* gain selection */ - enum veml6031_it itim; /* ALS integration time */ - enum veml6031_pers pers; /* ALS persistens protect */ + enum veml60xx_gain gain; /* gain selection */ + enum veml60xx_it itim; /* ALS integration time */ + enum veml60xx_pers pers; /* ALS persistence protect */ uint16_t thresh_high; uint16_t thresh_low; uint16_t als_data; @@ -142,30 +115,15 @@ struct veml6031_data { uint32_t int_flags; }; -static bool veml6031_gain_in_range(int32_t gain) -{ - return (gain >= VEML6031_GAIN_1) && (gain <= VEML6031_GAIN_0_5); -} - -static bool veml6031_itim_in_range(int32_t itim) -{ - return (itim >= VEML6031_IT_3_125) && (itim <= VEML6031_IT_400); -} - static bool veml6031_div4_in_range(int32_t div4) { return (div4 >= VEML6031_SIZE_4_4) && (div4 <= VEML6031_SIZE_1_4); } -static bool veml6031_pers_in_range(int32_t pers) -{ - return (pers >= VEML6031_PERS_1) && (pers <= VEML6031_PERS_8); -} - static void veml6031_sleep_by_integration_time(const struct veml6031_data *data) { - if (veml6031_itim_in_range(data->itim)) { - k_sleep(K_USEC(veml6031_it_values[data->itim].us)); + if (veml60xx_it_in_range(data->itim)) { + k_sleep(K_USEC(veml60xx_it_values[data->itim].us)); } else { LOG_WRN_ONCE("Wrong settings: itim:%d. Most likely an application bug!", data->itim); @@ -174,28 +132,8 @@ static void veml6031_sleep_by_integration_time(const struct veml6031_data *data) static int veml6031_check_settings(const struct veml6031_data *data) { - return veml6031_div4_in_range(data->div4) && veml6031_gain_in_range(data->gain) && - veml6031_itim_in_range(data->itim); -} - -static int veml6031_check_gain(const struct sensor_value *val) -{ - return veml6031_gain_in_range(val->val1); -} - -static int veml6031_check_it(const struct sensor_value *val) -{ - return veml6031_itim_in_range(val->val1); -} - -static int veml6031_check_div4(const struct sensor_value *val) -{ - return veml6031_div4_in_range(val->val1); -} - -static int veml6031_check_pers(const struct sensor_value *val) -{ - return veml6031_pers_in_range(val->val1); + return veml6031_div4_in_range(data->div4) && veml60xx_gain_in_range(data->gain) && + veml60xx_it_in_range(data->itim); } static int veml6031_read(const struct device *dev, uint8_t cmd, uint8_t *data) @@ -368,29 +306,29 @@ static int veml6031_attr_set(const struct device *dev, enum sensor_channel chan, /* SENSOR_ATTR_.*_THRESH are not in enum sensor_attribute_veml6031 */ switch ((int)attr) { case SENSOR_ATTR_VEML6031_IT: - if (veml6031_check_it(val)) { - data->itim = (enum veml6031_it)val->val1; + if (veml60xx_it_in_range(val->val1)) { + data->itim = (enum veml60xx_it)val->val1; } else { return -EINVAL; } break; case SENSOR_ATTR_VEML6031_DIV4: - if (veml6031_check_div4(val)) { + if (veml6031_div4_in_range(val->val1)) { data->div4 = (enum veml6031_div4)val->val1; } else { return -EINVAL; } break; case SENSOR_ATTR_VEML6031_GAIN: - if (veml6031_check_gain(val)) { - data->gain = (enum veml6031_gain)val->val1; + if (veml60xx_gain_in_range(val->val1)) { + data->gain = (enum veml60xx_gain)val->val1; } else { return -EINVAL; } break; case SENSOR_ATTR_VEML6031_PERS: - if (veml6031_check_pers(val)) { - data->pers = (enum veml6031_pers)val->val1; + if (veml60xx_pers_in_range(val->val1)) { + data->pers = (enum veml60xx_pers)val->val1; } else { return -EINVAL; } @@ -651,9 +589,9 @@ static DEVICE_API(sensor, veml6031_api) = { static struct veml6031_data veml6031_data_##n = {.trig = 1, \ .af = 1, \ .div4 = VEML6031_SIZE_4_4, \ - .gain = VEML6031_GAIN_1, \ - .itim = VEML6031_IT_100, \ - .pers = VEML6031_PERS_1, \ + .gain = VEML60XX_GAIN_1, \ + .itim = VEML60XX_IT_100, \ + .pers = VEML60XX_PERS_1, \ .thresh_high = 0xFFFF}; \ \ static const struct veml6031_config veml6031_config_##n = { \ diff --git a/drivers/sensor/vishay/veml6046/veml6046.c b/drivers/sensor/vishay/veml6046/veml6046.c index 9f0b54e1f27cd..89faea0e2d141 100644 --- a/drivers/sensor/vishay/veml6046/veml6046.c +++ b/drivers/sensor/vishay/veml6046/veml6046.c @@ -61,42 +61,16 @@ LOG_MODULE_REGISTER(VEML6046, CONFIG_SENSOR_LOG_LEVEL); #define VEML6046_CMDCODE_INT_L 0x1A #define VEML6046_CMDCODE_INT_H 0x1B -/* - * ALS integration time struct. - */ -struct veml6046_it_data { - enum veml6046_it num; - uint8_t val; - int us; -}; - -/* - * ALS integration time setting values. - * - * The enumerators of enum veml6046_it provide indices into this array to get - * the related value for the ALS_IT configuration bits. - */ -static const struct veml6046_it_data veml6046_it_values[VEML6046_IT_COUNT] = { - {VEML6046_IT_3_125, 0x00, 3125}, /* 3.125 - 0b0000 */ - {VEML6046_IT_6_25, 0x01, 6250}, /* 6.25 - 0b0001 */ - {VEML6046_IT_12_5, 0x02, 12500}, /* 12.5 - 0b0010 */ - {VEML6046_IT_25, 0x03, 25000}, /* 25 - 0b0011 */ - {VEML6046_IT_50, 0x04, 50000}, /* 50 - 0b0100 */ - {VEML6046_IT_100, 0x05, 100000}, /* 100 - 0b0101 */ - {VEML6046_IT_200, 0x06, 200000}, /* 200 - 0b0110 */ - {VEML6046_IT_400, 0x07, 400000}, /* 400 - 0b0111 */ -}; - /* * Resolution matrix for values to convert between data provided * by the sensor ("counts") and lux. * * These values depend on the current size, gain and integration time settings. - * The enumerators of enum veml6046_pdd, enum veml6046_gain and enum + * The enumerators of enum veml6046_pdd, enum veml60xx_gain and enum * veml6046_als_it are used for indices into this matrix. */ static const float - veml6046_resolution[VEML6046_PDD_COUNT][VEML6046_GAIN_COUNT][VEML6046_IT_COUNT] = { + veml6046_resolution[VEML6046_PDD_COUNT][VEML60XX_GAIN_COUNT][VEML60XX_IT_COUNT] = { /*3.125ms 6.25ms 12.5ms 25ms 50ms 100ms 200ms 400ms IT */ /* size 2/2 */ { @@ -131,9 +105,9 @@ struct veml6046_data { uint8_t int_en; /* ALS interrupt enable */ uint8_t trig; /* ALS active force trigger */ enum veml6046_pdd pdd; /* effective photodiode size divider */ - enum veml6046_gain gain; /* gain selection */ - enum veml6046_it itim; /* ALS integration time */ - enum veml6046_pers pers; /* ALS persistens protect */ + enum veml60xx_gain gain; /* gain selection */ + enum veml60xx_it itim; /* ALS integration time */ + enum veml60xx_pers pers; /* ALS persistens protect */ uint16_t thresh_high; uint16_t thresh_low; uint16_t red_data; @@ -146,24 +120,9 @@ struct veml6046_data { uint32_t ir_lux; }; -static int veml6046_check_gain(const struct sensor_value *val) +static bool veml6046_pdd_in_range(int32_t pdd) { - return val->val1 >= VEML6046_GAIN_1 && val->val1 <= VEML6046_GAIN_0_5; -} - -static int veml6046_check_it(const struct sensor_value *val) -{ - return val->val1 >= VEML6046_IT_3_125 && val->val1 <= VEML6046_IT_400; -} - -static int veml6046_check_pdd(const struct sensor_value *val) -{ - return val->val1 >= VEML6046_SIZE_2_2 && val->val1 <= VEML6046_SIZE_1_2; -} - -static int veml6046_check_pers(const struct sensor_value *val) -{ - return val->val1 >= VEML6046_PERS_1 && val->val1 <= VEML6046_PERS_8; + return pdd >= VEML6046_SIZE_2_2 && pdd <= VEML6046_SIZE_1_2; } static int veml6046_read16(const struct device *dev, uint8_t cmd, uint16_t *data) @@ -333,29 +292,29 @@ static int veml6046_attr_set(const struct device *dev, enum sensor_channel chan, /* SENSOR_ATTR_.*_THRESH are not in enum sensor_attribute_veml6046 */ switch ((int)attr) { case SENSOR_ATTR_VEML6046_IT: - if (veml6046_check_it(val)) { - data->itim = (enum veml6046_it)val->val1; + if (veml60xx_it_in_range(val->val1)) { + data->itim = (enum veml60xx_it)val->val1; } else { return -EINVAL; } break; case SENSOR_ATTR_VEML6046_PDD: - if (veml6046_check_pdd(val)) { + if (veml6046_pdd_in_range(val->val1)) { data->pdd = (enum veml6046_pdd)val->val1; } else { return -EINVAL; } break; case SENSOR_ATTR_VEML6046_GAIN: - if (veml6046_check_gain(val)) { - data->gain = (enum veml6046_gain)val->val1; + if (veml60xx_gain_in_range(val->val1)) { + data->gain = (enum veml60xx_gain)val->val1; } else { return -EINVAL; } break; case SENSOR_ATTR_VEML6046_PERS: - if (veml6046_check_pers(val)) { - data->pers = (enum veml6046_pers)val->val1; + if (veml60xx_pers_in_range(val->val1)) { + data->pers = (enum veml60xx_pers)val->val1; } else { return -EINVAL; } @@ -436,7 +395,7 @@ static int veml6046_perform_single_measurement(const struct device *dev) return ret; } - k_sleep(K_USEC(veml6046_it_values[data->itim].us)); + k_sleep(K_USEC(veml60xx_it_values[data->itim].us)); while (1) { ret = veml6046_read16(dev, VEML6046_CMDCODE_INT_L, &val); @@ -452,7 +411,7 @@ static int veml6046_perform_single_measurement(const struct device *dev) return -EAGAIN; } - k_sleep(K_USEC(veml6046_it_values[data->itim].us / 10)); + k_sleep(K_USEC(veml60xx_it_values[data->itim].us / 10)); cnt++; } @@ -552,7 +511,6 @@ static int veml6046_pm_action(const struct device *dev, enum pm_device_action ac default: return -ENOTSUP; } - return 0; } @@ -615,9 +573,9 @@ static DEVICE_API(sensor, veml6046_api) = { #define VEML6046_INIT(n) \ static struct veml6046_data veml6046_data_##n = {.trig = 0, \ .pdd = VEML6046_SIZE_2_2, \ - .gain = VEML6046_GAIN_1, \ - .itim = VEML6046_IT_100, \ - .pers = VEML6046_PERS_1, \ + .gain = VEML60XX_GAIN_1, \ + .itim = VEML60XX_IT_100, \ + .pers = VEML60XX_PERS_1, \ .thresh_high = 0xFFFF}; \ \ static const struct veml6046_config veml6046_config_##n = { \ diff --git a/include/zephyr/drivers/sensor/veml6031.h b/include/zephyr/drivers/sensor/veml6031.h index 8d9ee6874f01e..65808d5a79739 100644 --- a/include/zephyr/drivers/sensor/veml6031.h +++ b/include/zephyr/drivers/sensor/veml6031.h @@ -4,6 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include + /** * @file * @brief Header file for extended sensor API of VEML6031 sensor @@ -24,25 +26,6 @@ extern "C" { #endif -/** - * @brief VEML6031 integration time options for ambient light measurements. - * - * Possible values for @ref SENSOR_ATTR_VEML6031_IT custom attribute. - */ -enum veml6031_it { - VEML6031_IT_3_125, /**< 3.125 ms */ - VEML6031_IT_6_25, /**< 6.25 ms */ - VEML6031_IT_12_5, /**< 12.5 ms */ - VEML6031_IT_25, /**< 25 ms */ - VEML6031_IT_50, /**< 50 ms */ - VEML6031_IT_100, /**< 100 ms */ - VEML6031_IT_200, /**< 200 ms */ - VEML6031_IT_400, /**< 400 ms */ - /** @cond INTERNAL_HIDDEN */ - VEML6031_IT_COUNT, - /** @endcond */ -}; - /** * @brief VEML6031 size options for ambient light measurements. * @@ -56,31 +39,6 @@ enum veml6031_div4 { /** @endcond */ }; -/** - * @brief VEML6031 gain options for ambient light measurements. - */ -enum veml6031_gain { - VEML6031_GAIN_1 = 0x00, /**< 1x gain */ - VEML6031_GAIN_2 = 0x01, /**< 2x gain */ - VEML6031_GAIN_0_66 = 0x02, /**< 0.66x gain */ - VEML6031_GAIN_0_5 = 0x03, /**< 0.5x gain */ - /** @cond INTERNAL_HIDDEN */ - VEML6031_GAIN_COUNT = 4, - /** @endcond */ -}; - -/** - * @brief VEML6031 ALS interrupt persistence protect number options. - * - * Possible values for @ref SENSOR_ATTR_VEML6031_PERS custom attribute. - */ -enum veml6031_pers { - VEML6031_PERS_1 = 0x00, /**< 1 measurement */ - VEML6031_PERS_2 = 0x01, /**< 2 measurements */ - VEML6031_PERS_4 = 0x02, /**< 4 measurements */ - VEML6031_PERS_8 = 0x03, /**< 8 measurements */ -}; - /** * @brief Custom sensor attributes for VEML6031 sensor. * diff --git a/include/zephyr/drivers/sensor/veml6046.h b/include/zephyr/drivers/sensor/veml6046.h index c5641dccc791d..65b273b2fe3bd 100644 --- a/include/zephyr/drivers/sensor/veml6046.h +++ b/include/zephyr/drivers/sensor/veml6046.h @@ -4,6 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include + /** * @file * @brief Header file for extended sensor API of VEML6046 sensor @@ -24,25 +26,6 @@ extern "C" { #endif -/** - * @brief VEML6046 integration time options for light measurements. - * - * Possible values for @ref SENSOR_ATTR_VEML6046_IT custom attribute. - */ -enum veml6046_it { - VEML6046_IT_3_125, /**< 3.125 ms */ - VEML6046_IT_6_25, /**< 6.25 ms */ - VEML6046_IT_12_5, /**< 12.5 ms */ - VEML6046_IT_25, /**< 25 ms */ - VEML6046_IT_50, /**< 50 ms */ - VEML6046_IT_100, /**< 100 ms */ - VEML6046_IT_200, /**< 200 ms */ - VEML6046_IT_400, /**< 400 ms */ - /** @cond INTERNAL_HIDDEN */ - VEML6046_IT_COUNT, - /** @endcond */ -}; - /** * @brief VEML6046 size options for light measurements. * @@ -56,33 +39,6 @@ enum veml6046_pdd { /** @endcond */ }; -/** - * @brief VEML6046 gain options for light measurements. - * - * Possible values for @ref SENSOR_ATTR_VEML6046_GAIN custom attribute. - */ -enum veml6046_gain { - VEML6046_GAIN_1 = 0x00, /**< 1x gain */ - VEML6046_GAIN_2 = 0x01, /**< 2x gain */ - VEML6046_GAIN_0_66 = 0x02, /**< 0.66x gain */ - VEML6046_GAIN_0_5 = 0x03, /**< 0.5x gain */ - /** @cond INTERNAL_HIDDEN */ - VEML6046_GAIN_COUNT = 4, - /** @endcond */ -}; - -/** - * @brief VEML6046 interrupt persistence protect number options. - * - * Possible values for @ref SENSOR_ATTR_VEML6046_PERS custom attribute. - */ -enum veml6046_pers { - VEML6046_PERS_1 = 0x00, /**< 1 measurement */ - VEML6046_PERS_2 = 0x01, /**< 2 measurements */ - VEML6046_PERS_4 = 0x02, /**< 4 measurements */ - VEML6046_PERS_8 = 0x03, /**< 8 measurements */ -}; - /** * @brief VEML6046 specific sensor attributes. * diff --git a/include/zephyr/drivers/sensor/veml60xx-common.h b/include/zephyr/drivers/sensor/veml60xx-common.h new file mode 100644 index 0000000000000..0a9aea8dea99f --- /dev/null +++ b/include/zephyr/drivers/sensor/veml60xx-common.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2025 Andreas Klinger + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Header file for extended sensor API of VEML60xx sensor family + * @ingroup veml60xx_interface + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML60XX_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML60XX_H_ + +/** + * @defgroup veml60xx_interface VEML60XX + * @ingroup sensor_interface_ext + * @brief Vishay VEML60xx sensor family common attributes + * @{ + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief VEML60xx integration time options for ambient light measurements. + * + * Possible values for @ref SENSOR_ATTR_VEML6031_IT and + * @ref SENSOR_ATTR_VEML6046_IT custom attribute. + */ +enum veml60xx_it { + VEML60XX_IT_3_125, /**< 3.125 ms */ + VEML60XX_IT_6_25, /**< 6.25 ms */ + VEML60XX_IT_12_5, /**< 12.5 ms */ + VEML60XX_IT_25, /**< 25 ms */ + VEML60XX_IT_50, /**< 50 ms */ + VEML60XX_IT_100, /**< 100 ms */ + VEML60XX_IT_200, /**< 200 ms */ + VEML60XX_IT_400, /**< 400 ms */ + /** @cond INTERNAL_HIDDEN */ + VEML60XX_IT_COUNT, + /** @endcond */ +}; + +/* + * @brief VEML60xx integration time struct. + */ +struct veml60xx_it_data { + enum veml60xx_it num; + uint8_t val; + int us; +}; + +/* + * @brief VEML60xx integration time setting values. + * + * The enumerators of enum veml60xx_it provide indices into this array to get + * the related value for the ALS_IT configuration bits. + */ +static const struct veml60xx_it_data veml60xx_it_values[VEML60XX_IT_COUNT] = { + {VEML60XX_IT_3_125, 0x00, 3125}, /* 3.125 - 0b0000 */ + {VEML60XX_IT_6_25, 0x01, 6250}, /* 6.25 - 0b0001 */ + {VEML60XX_IT_12_5, 0x02, 12500}, /* 12.5 - 0b0010 */ + {VEML60XX_IT_25, 0x03, 25000}, /* 25 - 0b0011 */ + {VEML60XX_IT_50, 0x04, 50000}, /* 50 - 0b0100 */ + {VEML60XX_IT_100, 0x05, 100000}, /* 100 - 0b0101 */ + {VEML60XX_IT_200, 0x06, 200000}, /* 200 - 0b0110 */ + {VEML60XX_IT_400, 0x07, 400000}, /* 400 - 0b0111 */ +}; +/** + * @brief VEML60xx gain options for ambient light measurements. + */ +enum veml60xx_gain { + VEML60XX_GAIN_1 = 0x00, /**< 1x gain */ + VEML60XX_GAIN_2 = 0x01, /**< 2x gain */ + VEML60XX_GAIN_0_66 = 0x02, /**< 0.66x gain */ + VEML60XX_GAIN_0_5 = 0x03, /**< 0.5x gain */ + /** @cond INTERNAL_HIDDEN */ + VEML60XX_GAIN_COUNT = 4, + /** @endcond */ +}; + +/** + * @brief VEML60xx ALS interrupt persistence protect number options. + * + * Possible values for @ref SENSOR_ATTR_VEML6031_PERS and + * @ref SENSOR_ATTR_VEML6046_PERS custom attribute. + */ +enum veml60xx_pers { + VEML60XX_PERS_1 = 0x00, /**< 1 measurement */ + VEML60XX_PERS_2 = 0x01, /**< 2 measurements */ + VEML60XX_PERS_4 = 0x02, /**< 4 measurements */ + VEML60XX_PERS_8 = 0x03, /**< 8 measurements */ +}; + + +static inline bool veml60xx_gain_in_range(int32_t gain) +{ + return (gain >= VEML60XX_GAIN_1) && (gain <= VEML60XX_GAIN_0_5); +} + +static inline bool veml60xx_it_in_range(int32_t it) +{ + return (it >= VEML60XX_IT_3_125) && (it <= VEML60XX_IT_400); +} + +static inline bool veml60xx_pers_in_range(int32_t pers) +{ + return (pers >= VEML60XX_PERS_1) && (pers <= VEML60XX_PERS_8); +} + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_VEML60XX_H_ */ diff --git a/samples/sensor/veml6031/src/main.c b/samples/sensor/veml6031/src/main.c index 97239aa32a05d..06168918f9bf9 100644 --- a/samples/sensor/veml6031/src/main.c +++ b/samples/sensor/veml6031/src/main.c @@ -65,9 +65,9 @@ static void read_with_all_attr(const struct device *dev) { int it, div4, gain; - for (it = VEML6031_IT_3_125; it <= VEML6031_IT_400; it++) { + for (it = VEML60XX_IT_3_125; it <= VEML60XX_IT_400; it++) { for (div4 = VEML6031_SIZE_4_4; div4 <= VEML6031_SIZE_1_4; div4++) { - for (gain = VEML6031_GAIN_1; gain <= VEML6031_GAIN_0_5; gain++) { + for (gain = VEML60XX_GAIN_1; gain <= VEML60XX_GAIN_0_5; gain++) { read_with_attr(dev, it, div4, gain); } } diff --git a/samples/sensor/veml6046/src/main.c b/samples/sensor/veml6046/src/main.c index aec830e7d5177..7ac637e37f798 100644 --- a/samples/sensor/veml6046/src/main.c +++ b/samples/sensor/veml6046/src/main.c @@ -85,9 +85,9 @@ static void read_with_attr(const struct device *dev, int it, int pdd, int gain) static void read_with_all_attr(const struct device *dev) { - for (int it = VEML6046_IT_3_125; it <= VEML6046_IT_400; it++) { + for (int it = VEML60XX_IT_3_125; it <= VEML60XX_IT_400; it++) { for (int pdd = VEML6046_SIZE_2_2; pdd <= VEML6046_SIZE_1_2; pdd++) { - for (int gain = VEML6046_GAIN_1; gain <= VEML6046_GAIN_0_5; gain++) { + for (int gain = VEML60XX_GAIN_1; gain <= VEML60XX_GAIN_0_5; gain++) { read_with_attr(dev, it, pdd, gain); } }