diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep
index 421f98adad..7f169427a4 100644
--- a/drivers/Makefile.dep
+++ b/drivers/Makefile.dep
@@ -215,6 +215,10 @@ ifneq (,$(filter vcnl40%0,$(USEMODULE)))
USEMODULE += vcnl40x0
endif
+ifneq (,$(filter vl6180x_%,$(USEMODULE)))
+ USEMODULE += vl6180x
+endif
+
ifneq (,$(filter ws281x_%,$(USEMODULE)))
USEMODULE += ws281x
endif
diff --git a/drivers/include/vl6180x.h b/drivers/include/vl6180x.h
new file mode 100644
index 0000000000..0d409c2bf5
--- /dev/null
+++ b/drivers/include/vl6180x.h
@@ -0,0 +1,1076 @@
+/*
+ * Copyright (C) 2021 Gunar Schorcht
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @defgroup drivers_vl6180x VL6180X Ranging and Ambient Light Sensing (ALS) module
+ * @ingroup drivers_sensors
+ * @ingroup drivers_saul
+ * @brief Device driver for the ST VL6180X Ranging and Ambient Light Sensing (ALS) module
+ *
+ * # Overview {#vl6180x_overview}
+ *
+ * ## About the sensor {#vl6180x_about}
+ *
+ * The ST VL6180X is a low-power **proximity** and **ambient light** sensor
+ * with an I2C interface that uses time-to-flight technology for distance
+ * measurements. It can be used for ranging and/or ambient light sensing (ALS).
+ * Measurements can be automatically performed at user-defined intervals.
+ *
+ * To minimize host operations, interrupts can be used either when
+ * new sensor data are ready to be read or when sensor values exceed
+ * configured thresholds.
+ *
+ * ## Supported Features {#vl6180x_supported}
+ *
+ * The driver supports different levels of functionality, which can be
+ * enabled by using pseudomodules according to the requirements of the
+ * application. This ensures that the driver only uses as much ROM/RAM
+ * as really needed.
+ *
+ * As basic functionality the driver supports:
+ *
+ * - Ranging and ambient light sensing (ALS) in single-shot or
+ * continuous mode with polling for new sensor data
+ * - Fixed configuration of the sensor by a default parameter set of
+ * type #vl6180x_params_t as defined in the file `vl6180x_params.h
+ * - SAUL sensor interface
+ *
+ * The following pseudomodules are used to enable additional functionalities:
+ *
+ * Pseudomodule | Functionality
+ * :-------------------|:-------------------------------------------------------
+ * `vl6180x_irq` | Data ready and event interrupt handling
+ * `vl6180x_suhtdown` | Power-down and power-up functions
+ * `vl6180x_config` | Functions for changing configurations at runtime
+ *
+ *
+ *
+ * The following table shows the mapping of which modules have to be used
+ * to enable which functions of the VL6180X.
+ *
+ *
+ * Feature | Module
+ * :------------------------------------------------------------ |:-------------
+ * Ranging in single-shot or continuous mode | `vl6180x_rng`
+ * Ambient light sensing (ALS) in single-shot or continuous mode | `vl6180x_als`
+ * Data ready and event interrupt handling | `vl6180x_irq`
+ * Power-down and power-up functions | `vl6180x_shutdown`
+ * Configuration of the sensor at runtime | `vl6180x_config`
+ *
+ *
+ * @note
+ * - If the handling of interrupts for data ready and event interrupts
+ * is enabled by module `vl6180x_irq`, the GPIO pin for the interrupt
+ * signal (sensor pin GPIO1) must be defined by the configuration parameter
+ * vl6180x_params_t::int_pin. The default configuration of this GPIO pin
+ * is defined by #VL6180X_PARAM_INT_PIN that can be overridden by the
+ * board definition. The interrupt signal is LOW active.
+ * - If power-down and power-up functions are enabled by module
+ * `vl6180x_shutdown`, the GPIO pin for the shutdown signal (sensor pin
+ * GPIO0/CE) must be defined by the configuration parameter
+ * vl6180x_params_t::shutdown_pin. The default configuration of this GPIO pin
+ * is defined by #VL6180X_PARAM_SHUTDOWN_PIN that can be overridden by the
+ * board definition. The shutdown signal is LOW active.
+ *
+ * # Using the driver {#vl6180x_using_driver}
+ *
+ * ## Initialization {#vl6180x_initialization}
+ *
+ * The **easiest way to use the driver** is simply to initialize the sensor
+ * with function #vl6180x_init using the default configuration parameter set
+ * #vl6180x_params as defined in file vl6180x_params.h.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
+ * static vl6180x_t dev;
+ *
+ * if (vl6180x_init(&dev, &vl6180x_params[0]) != VL6180X_OK) {
+ * ... // error handling
+ * }
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * After initialization, the sensor is configured according to the standard
+ * configuration parameters and is fully operational.
+ *
+ * ## Operation modes {#vl6180x_operation_mode}
+ *
+ * The VL6180X can be used in two modes
+ *
+ * - **Single-shot mode**
+ * In this mode the sensor is in software standby and a single measurement
+ * is started explicitly either with the function #vl6180x_rng_start_single
+ * or the function #vl6180x_als_start_single. After finishing the single
+ * measurement the sensor returns to software standby. In software standby,
+ * the power consumption of the sensor is less than 1 uA.
+ * - **Continuous mode**
+ * In this mode the sensor automatically performs measurements with the
+ * measurement period configured by parameter vl6180x_params_t::period.
+ * Between these measurements it returns to the software standby.
+ * If range and ALS measurements are used (the modules `vl6180x_rng` and
+ * `vl6180x_als` are both used), the so-called **interleaved mode** is
+ * automatically used, where an ALS measurement is immediately followed
+ * by a range measurement and repeated with the defined period.
+ * The continuous mode can be stopped with function #vl6180x_stop_cont,
+ * for example to start single measurements. It is also possible to
+ * restart it using function #vl6180x_start_cont.
+ *
+ * @note If the configured measurement period is 0, the single-shot mode
+ * is enabled after initialization for both the range and ALS measurements.
+ * The functions #vl6180x_rng_start_single and #vl6180x_als_start_single must
+ * then be used to start a single measurement.
+ * Otherwise, the continuous mode is activated for both measurements and
+ * continuous measurements started automatically after sensor initialization
+ * with the configured measurement period.
+ *
+ * ## Fetching data {#vl6180x_fetching_data}
+ *
+ * To get data, the user task can use either
+ *
+ * - the #vl6180x_rng_data_ready and #vl6180x_als_data_ready functions to
+ * periodically check if new data are ready to be read, and the
+ * #vl6180x_rng_read and #vl6180x_als_read functions to read the data
+ * (following example), or
+ * - the data ready interrupt which is triggered as soon as new output data
+ * are available, see section [Using Interrupts](#vl6180x_using_interrupts).
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
+ * while (1)
+ * {
+ * uint16_t als;
+ * uint16_t lux;
+ * uint8_t rng;
+ *
+ * // execute task every 20 ms
+ * xtimer_usleep(20 * US_PER_MS);
+ * ...
+ * // test for new data and fetch them if available
+ * if (vl6180x_als_data_ready(&dev) == VL6180X_OK &&
+ * vl6180x_als_read(&dev, &als, &lux) == VL6180X_OK) {
+ * ...
+ * }
+ * if (vl6180x_rng_data_ready(&dev) == VL6180X_OK) {
+ * if (vl6180x_rng_read(&dev, &rng) == VL6180X_OK) {
+ * ...
+ * }
+ * }
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ## Output data format
+ *
+ * Function #vl6180x_als_read returns one ALS data sample as raw count value
+ * and, if required, as illuminance in Lux. The range of the count value
+ * depends on
+ *
+ * - the ALS analog gain defined by vl6180x_params_t::als_gain,
+ * - the integration time defined by vl6180x_params_t::als_int_time, and
+ * - the lux resolution defined by vl6180x_params_t::als_lux_res.
+ *
+ * The count value is returned in parameter \p raw while the illuminance
+ * is returned in parameter \p lux. For either \p raw or \p lux also `NULL`
+ * can be passed, if only one value is of interest.
+ *
+ * If ALS value is invalid because of a measurement error, #VL6180X_ERROR_ALS
+ * is returned. The #vl6180x_als_status function can then be used to get an
+ * error code of type #vl6180x_als_status_t.
+ *
+ * Function vl6180x_rng_read returns the ranging data in millimeters. If
+ * ranging value is invalid because of a measurement error, #VL6180X_ERROR_RNG
+ * is returned and function #vl6180x_rng_status function can then be used to
+ * get an error code of type #vl6180x_rng_status_t.
+ *
+ * # Using Interrupts {#vl6180x_using_interrupts}
+ *
+ * The VL6180X sensor allows the use of different types of interrupts on signal
+ * GPIO1 for range and ALS measurements:
+ *
+ * - data ready interrupts when data become available
+ * - different event interrupts when sensor data cross configured thresholds
+ *
+ * @note Interrupts are only supported when module `vl6180x_irq` is used.
+ *
+ * ## Interrupt configuration {#vl6180x_interrupt_configuration}
+ *
+ * These interrupts can be enabled separately for the range and ALS
+ * measurements by the interrupt mode of type #vl6180x_int_mode_t
+ *
+ *
+ * | Interrupt mode | Driver symbol |
+ * |:-----------------------------------------------------------------------|:------------------|
+ * | New data are ready to be read | #VL6180X_INT_DRDY |
+ * | Sensor data are below the lower threshold | #VL6180X_INT_LOW |
+ * | Sensor data are above the upper threshold | #VL6180X_INT_HIGH |
+ * | Sensor data are below the lower threshold or above the upper threshold | #VL6180X_INT_OUT |
+ *
+ *
+ * @warning Only one of the interrupt modes must be enabled at the same time
+ * for the same measurement.
+ *
+ * For event interrupts, upper and lower thresholds have to be defined,
+ * with the upper and lower thresholds defining a threshold window of type
+ * #vl6180x_int_thresh_t.
+ *
+ * In default configuration, #VL6180X_INT_DRDY is used both for range and
+ * ALS measurements if module `vl6180x_irq` is used.
+ *
+ * The enabled interrupts can be changed with the #vl6180x_int_enable
+ * function which takes a parameter of type #vl6180x_int_config_t which
+ * simply contains the interrupt mode of type #vl6180x_int_mode_t for range
+ * and ALS measurements.
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
+ * vl6180x_int_config_t mode = { .rng_int = VL6180X_INT_OUT,
+ * .als_int = VL6180X_INT_DRDY };
+ * vl6180x_int_enable(&dev, mode);
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * If module `vl6180x_config` is used, the thresholds for event interrupts
+ * can be changed using function #vl6180x_int_config, for example:
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
+ * vl6180x_int_thresh_t thresh;
+ *
+ * thresh.rng_low = 30;
+ * thresh.rng_high = 100;
+ * thresh.als_low = 10;
+ * thresh.als_high = 1000;
+ *
+ * vl6180x_int_config(&dev, thresh);
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ## Interrupt usage {#vl6180x_interrupt_sources}
+ *
+ * All functions of the driver require direct access to the sensor via
+ * I2C which does not work in interrupt context.
+ *
+ * Therefore, the driver prevents the direct use of the interrupts and
+ * application specific ISRs. The only way to use interrupts is to call
+ * the function #vl6180x_int_wait which enables the interrupt signal
+ * for the configured MCU GPIO and then blocks the calling thread
+ * until an interrupt is triggered.
+ *
+ * Once an interrupt is triggered, the driver handles the interrupt with
+ * an internal ISR and then returns from the #vl6180x_int_wait function
+ * with the interrupt source. The interrupt mode of type #vl6180x_int_mode_t
+ * respectively the composite type #vl6180x_int_config_t which is used for
+ * defining enabled interrupts is also used for specifying the interrupt
+ * source. It contains a flag for each possible interrupt source which
+ * can be tested for true.
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
+ * vl6180x_int_config_t src;
+ *
+ * vl6180x_int_wait(&dev, &src);
+ *
+ * if (src.rng_int == VL6180X_INT_DRDY) {
+ * vl6180x_rng_read(&dev, &rng);
+ * printf("RNG: %u [mm]\n", rng);
+ * }
+ * else if (src.rng_int == VL6180X_INT_OUT) {
+ * printf("RNG: out of window\n");
+ * }
+ * else if (src.rng_int == VL6180X_INT_RNG_LOW) {
+ * printf("RNG: low level\n");
+ * }
+ * else if (src.rng_int == VL6180X_INT_RNG_HIGH) {
+ * printf("RNG: high level\n");
+ * }
+ *
+ * if (src.als_int == VL6180X_INT_DRDY) {
+ * vl6180x_als_read(&dev, &als, &lux);
+ * printf("ALS: %u [cnts], %u [lux]\n", als, lux);
+ * }
+ * else if (src.als_int == VL6180X_INT_OUT) {
+ * printf("ALS: out of window\n");
+ * }
+ * else if (src.als_int == VL6180X_INT_LOW) {
+ * printf("ALS: low level\n");
+ * }
+ * else if (src.als_int == VL6180X_INT_HIGH) {
+ * printf("ALS: high level\n");
+ * }
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * # Power Saving {#vl6180x_power_saving}
+ *
+ * If module `vl6180x_shutdown` is used, the VL6180X sensor can be shutdown
+ * when no measurements are required using the function #vl6180x_power_down.
+ * The power consumption is then reduced to less than 1 uA. To restart the
+ * VL6180X in previous measurement mode, the #vl6180x_power_up function can
+ * be used.
+ *
+ * @note To use these functions, the MCU GPIO pin connected to the GPIO0/CE
+ * pin of the sensor has to be defined by the vl6180x_params_t::pin_shutdown
+ * parameter.
+ *
+ * # Low level functions {#vl6180x_low_level}
+ *
+ * Low level level interface functions that allow direct read and write
+ * access to the registers of the sensor.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
+ * bool vl6180x_reg_read(const vl6180x_t* dev, uint16_t reg, uint8_t *data, uint8_t len);
+ * bool vl6180x_reg_write(const vl6180x_t* dev, uint16_t reg, const uint8_t *data, uint8_t len);
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * @warning
+ * These functions should only be used to do something special that
+ * is not covered by the high level interface AND if you exactly
+ * know what you do and what it might affect. Please be aware that
+ * it might affect the high level interface.
+ *
+ * # Configuration
+ *
+ * ## Default configuration
+ *
+ * Default sensor hardware configurations are set in file `vl6180x_params.h`
+ * using the following defines:
+ *
+ *
+ * | Hardware configuration | Driver name | Default Value |
+ * |:-----------------------|:----------------------------|:------------------|
+ * | I2C device | #VL6180X_PARAM_DEV | I2C_DEV(0) |
+ * | I2C address | #VL6180X_PARAM_ADDR | #VL6180X_I2C_ADDR |
+ * | Interrupt pin | #VL6180X_PARAM_INT_PIN | GPIO_PIN(0,1) |
+ * | Shutdown pin | #VL6180X_PARAM_SHUTDOWN_PIN | GPIO_PIN(0,2) |
+ *
+ *
+ * These hardware configurations can be overridden either by the board
+ * definition or by defining them in the `CFLAGS` variable in the make
+ * command, for example:
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * USEMODULE='vl6180x_rng vl6180x_als vl6180x_irq` \
+ * CLFAGS='-DVL6180X_PARAM_INT_PIN=GPIO_PIN\(0,5\)' \
+ * BOARD=... make -C tests/driver_vl6180x
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * The default configuration of the sensor is defined in file
+ * `vl6180x_params.h` using the following defines:
+ *
+ * Parameter | Default Value | Define to be overridden
+ * :----------------------------------|:-------------------|:---------------------------
+ * Period of continuous measurements | 200 ms | #CONFIG_VL6180X_MEAS_PERIOD
+ * Ranging maximum convergence time | 50 ms | #CONFIG_VL6180X_RNG_MAX_TIME
+ * Ranging interrupt mode | VL6180X_INT_DRDY | #CONFIG_VL6180X_RNG_INT
+ * Ranging lower threshold | 20 mm | #CONFIG_VL6180X_RNG_THRESH_LOW
+ * Ranging upper threshold | 90 mm | #CONFIG_VL6180X_RNG_THRESH_HIGH
+ * ALS integration time | 100 ms | #CONFIG_VL6180X_ALS_INT_TIME
+ * ALS analogue gain | VL6180X_ALS_GAIN_1 | #CONFIG_VL6180X_ALS_GAIN
+ * ALS lux resolution lux/count*1000 | 320 | #CONFIG_VL6180X_ALS_LUX_RES
+ * ALS interrupt mode | VL6180X_INT_DRDY | #CONFIG_VL6180X_ALS_INT
+ * ALS lower threshold | 50 counts | #CONFIG_VL6180X_ALS_THRESH_LOW
+ * ALS upper threshold | 2000 counts | #CONFIG_VL6180X_ALS_THRESH_HIGH
+ *
+ * Single or all parameters of the default configuration can be overridden
+ * either by placing a modified file `vl6180x_params.h` in the application
+ * directory or by defining them in the variable `CFLAGS` in the make command
+ * line. For example to configure a measurement period of 500 ms and a maximum
+ * convergence time for ranging of 60 ms, the following command could be used:
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * USEMODULE='vl6180x_rng vl6180x_als` \
+ * CLFAGS='-DCONFIG_VL6180X_MEAS_PERIOD=50 -DCONFIG_VL6180X_RNG_MAX_TIME=60' \
+ * BOARD=... make -C tests/driver_vl6180x
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ## Configuration at runtime
+ *
+ * If module `vl6180x_config` is used, the following functions can be used to
+ * change the default sensor configuration at runtime.
+ *
+ * | Function | Functionality |
+ * |:--------------------|:------------------------------------------------------|
+ * | #vl6180x_rng_config | Changes the range measurement parameter configuration |
+ * | #vl6180x_als_config | Changes the ALS measurement parameter configuration |
+ * | #vl6180x_int_config | Changes the thresholds for event interrupts |
+ *
+ * @{
+ *
+ * @author Gunar Schorcht
+ * @file
+ */
+
+#ifndef VL6180X_H
+#define VL6180X_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include
+#include
+
+#include "periph/gpio.h"
+#include "periph/i2c.h"
+
+#include "vl6180x_regs.h"
+
+#define VL6180X_I2C_ADDR (0x29) /**< VNCL6180 default I2C slave address */
+
+/**
+ * @brief Error codes
+ */
+typedef enum {
+ VL6180X_OK = 0, /**< Success */
+ VL6180X_ERROR_I2C = 1, /**< I2C communication error */
+ VL6180X_ERROR_WRONG_ID = 2, /**< Wrong id read */
+ VL6180X_ERROR_NO_PIN = 3, /**< Pin not defined */
+ VL6180X_ERROR_NO_DATA = 4, /**< No data available */
+ VL6180X_ERROR_RNG = 5, /**< Ranging error */
+ VL6180X_ERROR_ALS = 6, /**< Ambient light sensing (ALS) error */
+ VL6180X_ERROR_NOT_READY = 7, /**< Device not ready */
+} vl6180x_error_t;
+
+/**
+ * @brief Analogue gain for ALS measurements
+ */
+typedef enum {
+ VL6180X_ALS_GAIN_20 = 0, /**< 20 x gain (actual analogue gain of 20) */
+ VL6180X_ALS_GAIN_10 = 1, /**< 10 x gain (actual analogue gain of 10.32) */
+ VL6180X_ALS_GAIN_5 = 2, /**< 5 x gain (actual analogue gain of 5.21) */
+ VL6180X_ALS_GAIN_2_5 = 3, /**< 2.5 x gain (actual analogue gain of 2.6) */
+ VL6180X_ALS_GAIN_1_67 = 4, /**< 1.67 x gain (actual analogue gain of 1.72) */
+ VL6180X_ALS_GAIN_1_25 = 5, /**< 1.25 x gain (actual analogue gain of 1.28) */
+ VL6180X_ALS_GAIN_1 = 6, /**< 1 x gain (actual analogue gain of 1.01), default */
+ VL6180X_ALS_GAIN_40 = 7, /**< 40 x gain (actual analogue gain of 40) */
+} vl6180x_als_gain_t;
+
+/**
+ * @brief Range measurement status
+ */
+typedef enum {
+ VL6180X_RNG_OK = 0, /**< No error */
+ VL6180X_RNG_VCSEL_CONT_TEST = 1, /**< VCSEL continuity Test */
+ VL6180X_RNG_VCSEL_WD_TEST = 2, /**< VCSEL watchdog test */
+ VL6180X_RNG_VCSEL_WD = 3, /**< VCSEL watchdog */
+ VL6180X_RNG_PLL1_LOCK = 4, /**< PLL1 lock */
+ VL6180X_RNG_PLL2_LOCK = 5, /**< PLL2 lock */
+ VL6180X_RNG_EARLY_CONV_EST = 6, /**< Early convergence estimate */
+ VL6180X_RNG_MAX_CONV = 7, /**< Maximum convergence time reached */
+ VL6180X_RNG_NO_TARGET = 8, /**< No target, ignore */
+ VL6180X_RNG_MAX_SNR = 11, /**< Maximum SNR reached */
+ VL6180X_RNG_RAW_ALGO_UNDERFLOW = 12, /**< Raw ranging algorithm underflow */
+ VL6180X_RNG_RAW_ALGO_OVERFLOW = 13, /**< Raw ranging algorithn overflow */
+ VL6180X_RNG_ALGO_UNDERFLOW = 14, /**< Ranging algorithm underflow */
+ VL6180X_RNG_ALGO_OVERFLOW = 15, /**< Ranging algorithm overflow */
+} vl6180x_rng_status_t;
+
+/**
+ * @brief Ambient light sensing (ALS) status
+ */
+typedef enum {
+ VL6180X_ALS_OK = 0, /**< No error */
+ VL6180X_ALS_OVERFLOW = 1, /**< ALS measurement overflow */
+ VL6180X_ALS_UNDERFLOW = 2, /**< ALS measurement underflow */
+} vl6180x_als_status_t;
+
+/**
+ * @brief Interrupt mode
+ *
+ * The interrupt mode defines the different sources that can trigger an
+ * interrupt on the GPIO1 pin of the sensor. The interrupt mode is defined for
+ * range and ALS measurements separately. Interrupts can be triggered either
+ *
+ * - in each measurement cycle when new data become available (data ready interrupts) or
+ * - only when values exceed a threshold configured (event interrupts).
+ *
+ * For threshold interrupts, upper and lower thresholds have to be defined,
+ * with the upper and lower thresholds defining a threshold window, see
+ * also #vl6180x_int_thresh_t.
+ *
+ * @note Interrupts are only supported when module `vl6180x_irq` is used.
+ * @warning Only one of the interrupt modes must be enabled at the same time.
+ */
+typedef enum {
+ VL6180X_INT_NONE = 0, /**< Interrupt is disabled */
+
+ VL6180X_INT_LOW = 1, /**< Interrupt is triggered when values are below
+ the lower threshold */
+ VL6180X_INT_HIGH = 2, /**< Interrupt is triggered when values are above
+ the upper threshold */
+ VL6180X_INT_OUT = 3, /**< Interrupt is triggered when values are below
+ the lower threshold or above the upper threshold
+ (value leave the threshold window) */
+ VL6180X_INT_DRDY = 4, /**< Interrupt is triggered when new data are ready
+ to be read */
+} vl6180x_int_mode_t;
+
+/**
+ * @brief Interrupt config
+ *
+ * This type defines the interrupt mode for both range measurements and
+ * ALS measurements. It is used on the one hand as parameter \p mode in the
+ * function #vl6180x_int_enable to enable the interrupt for the respective
+ * measurement and on the other hand to return the source of an interrupt
+ * in function #vl6180x_int_wait when an interrupt was triggered.
+ *
+ * The interrupt mode is defined for range and ALS measurements separately.
+ *
+ * @note Interrupts are only supported when module `vl6180x_irq` is used.
+ */
+typedef struct {
+#if IS_USED(MODULE_VL6180X_RNG) || DOXYGEN
+ vl6180x_int_mode_t rng_int; /**< Interrupt mode for range measurements */
+#endif
+#if IS_USED(MODULE_VL6180X_ALS) || DOXYGEN
+ vl6180x_int_mode_t als_int; /**< Interrupt mode for ALS measurements */
+#endif
+} vl6180x_int_config_t;
+
+/**
+ * @brief Interrupt threshold configuration
+ *
+ * Threshold configurations are used for event interrupts only.
+ * If event interrupts are enabled by the corresponding interrupt
+ * mode for range and/or ALS measurements, the lower and/or upper threshold
+ * values are used to generate an interrupt if the values of the respective
+ * measurement exceed these threshold values.
+ *
+ * The unit of threshold values for range measurements is millimeters.
+ * The unit of threshold values for ALS measurements is counts.
+ *
+ * @note Interrupts are only supported when module `vl6180x_irq` is used.
+ */
+typedef struct {
+#if IS_USED(MODULE_VL6180X_RNG) || DOXYGEN
+ uint8_t rng_high; /**< upper threshold for range values */
+ uint8_t rng_low; /**< lower threshold for range values */
+#endif
+#if IS_USED(MODULE_VL6180X_ALS) || DOXYGEN
+ uint16_t als_high; /**< upper threshold for ALS values */
+ uint16_t als_low; /**< lower threshold for ALS values */
+#endif
+} vl6180x_int_thresh_t;
+
+/**
+ * @brief VL6180X device configuration
+ */
+typedef struct {
+ /**
+ * @name Hardware configuration
+ * @{
+ */
+ unsigned i2c_dev; /**< I2C device, default I2C_DEV(0) */
+ uint8_t i2c_addr; /**< I2C slave address */
+
+#if IS_USED(MODULE_VL6180X_SHUTDOWN) || DOXYGEN
+ gpio_t shutdown_pin; /**< Shutdown pin, LOW active */
+#endif /* IS_USED(MODULE_VL6180X_SHUTDOWN) || DOXYGEN */
+
+#if IS_USED(MODULE_VL6180X_IRQ) || DOXYGEN
+ gpio_t int_pin; /**< Interrupt pin, LOW active */
+ vl6180x_int_config_t int_cfg; /**< Interrupt mode configuration */
+ vl6180x_int_thresh_t int_thresh; /**< Interrupt threshold configuration */
+#endif /* IS_USED(MODULE_VL6180X_IRQ) || DOXYGEN */
+
+ /** @} */
+
+ /**
+ * @brief Measurement period in continuous mode in
+ * steps of 10 ms (default 20 = 200 ms).
+ *
+ * The measurement period also controls the measurement mode used after
+ * sensor initialization. If the configured measurement period is 0,
+ * the single-shot mode is enabled for both the range and ALS
+ * measurements. The functions vl6180x_rng_start_single and
+ * vl6180x_als_start_single must then be used to start a single measurement.
+ * Otherwise, the continuous mode is activated for both measurements,
+ * which are started immediately after sensor initialization with the
+ * configured measurement period. This also applies to the initialization
+ * after a power-down and power-up cycle.
+ *
+ * @note When ALS and range measurements are used in continuous mode,
+ * the so-called interleaved mode is used automatically, where an ALS
+ * measurement is immediately followed by a range measurement and
+ * repeated with the defined period.
+ */
+ uint8_t period;
+
+#if IS_USED(MODULE_VL6180X_RNG) || DOXYGEN
+ /**
+ * @name Range measurement configuration
+ * @{
+ */
+ uint8_t rng_max_time; /**< Maximum convergence time in ms [1...63] given
+ to the sensor to perform a range measurement
+ (default 50 = 50 ms) */
+ /** @} */
+#endif /* IS_USED(MODULE_VL6180X_RNG) || DOXYGEN */
+
+#if IS_USED(MODULE_VL6180X_ALS) || DOXYGEN
+ /**
+ * @name ALS measurement configuration
+ * @{
+ */
+ uint16_t als_int_time; /**< ALS integration time in ms [1...512]
+ (default 100 = 100 ms, **recommended**) */
+ uint16_t als_lux_res; /**< ALS lux resolution multiplied by factor 1000
+ (default 0.32 lux/count is the factory
+ calibrated lux resolution without cover glas) */
+ vl6180x_als_gain_t als_gain; /**< ALS analogue gain for light channel
+ (default VL6180X_ALS_GAIN_1_0) */
+ /** @} */
+#endif /* IS_USED(MODULE_VL6180X_ALS) || DOXYGEN */
+
+} vl6180x_params_t;
+
+/**
+ * @brief VL6180X sensor device data structure type
+ */
+typedef struct {
+
+ vl6180x_params_t params; /**< Device initialization parameters */
+ bool cont_meas; /**< Continuous mode running */
+#if IS_USED(MODULE_VL6180X_RNG) || DOXYGEN
+ vl6180x_rng_status_t rng_status; /**< Status of last range measurement */
+#endif /* IS_USED(MODULE_VL6180X_RNG) || DOXYGEN */
+#if IS_USED(MODULE_VL6180X_ALS) || DOXYGEN
+ vl6180x_als_status_t als_status; /**< Status of last ALS measurement */
+#endif /* IS_USED(MODULE_VL6180X_ALS) || DOXYGEN */
+} vl6180x_t;
+
+/**
+ * @brief Initialize the VL6180X sensor device
+ *
+ * After initialization, the sensor is configured according to the standard
+ * configuration parameters and is fully functional.
+ *
+ * If the configured measurement period is 0, the single-shot mode
+ * is enabled for both the range and ALS measurements. The functions
+ * vl6180x_rng_start_single and vl6180x_als_start_single must then be used
+ * to start a single measurement. Otherwise, the continuous mode is activated
+ * for both measurements, which are started immediately after sensor
+ * initialization with the configured measurement period.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor to be initialized
+ * @param[in] params Configuration parameters, see #vl6180x_params_t
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_init(vl6180x_t *dev, const vl6180x_params_t *params);
+
+/**
+ * @brief Start measurements in continuous mode
+ *
+ * Range and/or ALS measurements are started in continuous mode with same
+ * measurement period as defined in configuration parameter
+ * vl6180x_params_t::period.
+ *
+ * @note Continuous mode cannot be started separately for range and ALS
+ * measurements.
+ *
+ * @pre
+ * - Measurement period vl6180x_params_t::period must not be zero.
+ * - Measurements must not yet be started in continuous mode when called.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor to be initialized
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_start_cont(vl6180x_t *dev);
+
+/**
+ * @brief Stop measurements in continuous mode
+ *
+ * Continuous range and ALS measurements are stopped. Once continuous
+ * measurements are stopped, vl6180x_rng_start_single or
+ * vl6180x_als_start_single can be used to start single-shot measurements
+ * separately.
+ *
+ * @pre
+ * - Measurement period vl6180x_params_t::period must not be zero.
+ * - Measurements must be started in continuous mode when called.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor to be initialized
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_stop_cont(vl6180x_t *dev);
+
+#if IS_USED(MODULE_VL6180X_RNG) || DOXYGEN
+/**
+ * @brief Range data ready status function
+ *
+ * The function can be used for polling to know when new ranging data are ready.
+ *
+ * @note This function is only available when module `vl6180x_rng` is used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ *
+ * @retval VL6180X_OK new ranging data are ready
+ * @retval VL6180X_ERROR_NO_DATA no new ranging data available
+ * @retval VL6180X_ERROR_* a negative error code on any other error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_rng_data_ready(const vl6180x_t *dev);
+
+/**
+ * @brief Read one ranging data sample in mm
+ *
+ * This function returns the ranging data in millimeters. If ranging value
+ * is invalid because of a measurement error, #VL6180X_ERROR_RNG is returned.
+ * The #vl6180x_rng_status function can then be used to get an error code of
+ * type #vl6180x_rng_status_t.
+ *
+ * @note
+ * - This function is only available when module `vl6180x_rng` is used.
+ * - The function clears the interrupt if ambient light sensing interrupts
+ * are used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ * @param[out] mm Ranging data in mm [0...100]
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_RNG on error during range measurement
+ * @retval VL6180X_ERROR_* a negative error code on any other error
+ * see #vl6180x_error_t
+ */
+int vl6180x_rng_read(vl6180x_t *dev, uint8_t *mm);
+
+/**
+ * @brief Get status of last range measurement
+ *
+ * @note This function is only available when module `vl6180x_rng` is used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ * @retval status of type vl6180x_rng_status_t
+ */
+vl6180x_rng_status_t vl6180x_rng_status(const vl6180x_t *dev);
+
+/**
+ * @brief Start a single-shot range measurement
+ *
+ * @pre Measurements must not be started in continuous mode when called.
+ *
+ * @note This function is only available when module `vl6180x_rng` is used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_rng_start_single(const vl6180x_t *dev);
+
+#if IS_USED(MODULE_VL6180X_CONFIG) || DOXYGEN
+/**
+ * @brief Reconfigure range measurements at runtime
+ *
+ * This function can be used to overwrite the default configuration of range
+ * measurements defined by #vl6180x_params_t at runtime.
+ *
+ * For this purpose, the running range measurement is stopped and restarted
+ * after the reconfiguration if continuous mode is used (\p period is not 0).
+ *
+ * @note
+ * - This function is only available when modules `vl6180x_rng` and
+ * `vl6180x_config` are used.
+ * - Since parameter \p period is used for continuous mode, in which
+ * measurements are performed in interleaved mode, setting the period
+ * with this function also affects the ALS measurements in continuous mode.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ * @param[in] period Period in continuous measurement mode in steps
+ * of 10 ms. It controls also the measurement mode
+ * enabled after the initialization. If 0, single-shot
+ * mode is enabled, otherwise the continuous
+ * mode is enabled and measurement are started
+ * automatically.
+ * @param[in] max_time Maximum convergence time in ms [1...63] given
+ * to the sensor to perform range measurements
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_rng_config(vl6180x_t *dev, uint8_t period, uint8_t max_time);
+
+#endif /* IS_USED(MODULE_VL6180X_CONFIG) || DOXYGEN */
+#endif /* IS_USED(MODULE_VL6180X_RNG) || DOXYGEN */
+
+#if IS_USED(MODULE_VL6180X_ALS) || DOXYGEN
+/**
+ * @brief ALS data ready status function
+ *
+ * @note This function is only available when module `vl6180x_als` is used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ *
+ * @retval VL6180X_OK new ALS data are ready
+ * @retval VL6180X_ERROR_NO_DATA no new ALS data available
+ * @retval VL6180X_ERROR_* a negative error code on any other error,
+ * see #vl6180x_error_t
+ */
+int vl6180x_als_data_ready(const vl6180x_t *dev);
+
+/**
+ * @brief Read one ambient light sensing (ALS) data sample
+ *
+ * This function returns one ALS data sample as raw count value and, if
+ * required, as illuminance in Lux. The range of the count value
+ * depends on
+ *
+ * - the ALS analog gain defined by vl6180x_params_t::als_gain,
+ * - the integration time defined by vl6180x_params_t::als_int_time, and
+ * - the lux resolution defined by vl6180x_params_t::als_res.
+ *
+ * The count value is returned in parameter \p raw while the illuminance
+ * is returned in parameter \p lux. For either \p raw or \p lux also `NULL`
+ * can be passed, if only one value is of interest.
+ *
+ * If ALS value is invalid because of a measurement error, #VL6180X_ERROR_ALS
+ * is returned. The #vl6180x_als_status function can then be used to get an
+ * error code of type #vl6180x_als_status_t.
+ *
+ * @note
+ * - This function is only available when module `vl6180x_als` is used.
+ * - The function clears the interrupt if ambient light sensing interrupts
+ * are used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ * @param[out] raw Ambient light raw data as count value
+ * @param[out] lux Ambient light in Lux
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_als_read(vl6180x_t *dev, uint16_t *raw, uint16_t *lux);
+
+/**
+ * @brief Get status of last ALS measurement
+ *
+ * @note This function is only available when module `vl6180x_als` is used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ * @retval status of type vl6180x_als_status_t
+ */
+vl6180x_als_status_t vl6180x_als_status(const vl6180x_t *dev);
+
+/**
+ * @brief Start a single-shot ALS measurement
+ *
+ * @pre Measurements must not be started in continuous mode when called.
+ *
+ * @note This function is only available when module `vl6180x_als` is used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_als_start_single(const vl6180x_t *dev);
+
+#if IS_USED(MODULE_VL6180X_CONFIG) || DOXYGEN
+/**
+ * @brief Reconfigure ambient light sensing (ALS) during runtime
+ *
+ * This function can be used to overwrite the default configuration of ambient
+ * light sensing defined by #vl6180x_params_t during runtime.
+ *
+ * For this purpose, the running ambient light sensing (ALS) is stopped and
+ * restarted after the reconfiguration if continuous mode is used
+ * (\p period is not 0).
+ *
+ * @note
+ * - This function is only available when modules `vl6180x_als` and
+ * `vl6180x_config` are used.
+ * - Since parameter \p period is used for continuous mode, in which
+ * measurements are performed in interleaved mode, setting the period
+ * with this function also affects the range measurements in continuous mode.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ * @param[in] period Period in continuous measurement mode in steps
+ * of 10 ms. It controls also the measurement mode
+ * enabled after the initialization. If 0, single-shot
+ * mode is enabled, otherwise the continuous
+ * mode is enabled and measurement are started
+ * automatically.
+ * @param[in] int_time ALS integration time in ms [0...511]
+ * @param[in] gain ALS analogue gain for light channel
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_als_config(vl6180x_t *dev, uint8_t period, uint8_t int_time,
+ vl6180x_als_gain_t gain);
+
+#endif /* IS_USED(MODULE_VL6180X_CONFIG) || DOXYGEN */
+#endif /* IS_USED(MODULE_VL6180X_ALS) || DOXYGEN */
+
+#if IS_USED(MODULE_VL6180X_SHUTDOWN) || DOXYGEN
+/**
+ * @brief Power down the sensor
+ *
+ * @pre This function requires that a GPIO connected to sensor's GPIO0/CE pin is
+ * defined by parameter vl6180x_params_t::pin_shutdown.
+ *
+ * @note This function is only available if the `vl6180x_shutdown` module
+ * is used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_power_down(const vl6180x_t *dev);
+
+/**
+ * @brief Power down the sensor
+ *
+ * @pre This function requires that a GPIO connected to sensor's GPIO0/CE pin is
+ * defined by parameter vl6180x_params_t::pin_shutdown.
+ *
+ * @note This function is only available if the `vl6180x_shutdown` module
+ * is used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_power_up(vl6180x_t *dev);
+
+#endif /* IS_USED(MODULE_VL6180X_SHUTDOWN) || DOXYGEN */
+
+#if IS_USED(MODULE_VL6180X_IRQ) || DOXYGEN
+
+/**
+ * @brief Wait for configured interrupts and return the interrupt sources
+ *
+ * To avoid I2C bus access in interrupt context, the driver prevents the
+ * direct use of interrupts and application specific ISRs. Rather, this
+ * function has to be used to wait for an interrupt. It enables the interrupt
+ * signal for the configured MCU GPIO and then blocks the calling thread
+ * until an interrupt is triggered.
+ *
+ * Once an interrupt is triggered, the driver handles the interrupt with an
+ * internal ISR and then returns. When the function returns, the data structure
+ * of type vl6180x_int_config_t to which the \p src parameter points contains
+ * the source of the triggered interrupt. It contains a flag for each possible
+ * interrupt source which can be tested for true.
+ *
+ * @pre
+ * - Configuration parameter vl6180x_params_t::int_pin has to be defined.
+ * - vl6180x_int_config_t::rng_int and vl6180x_int_config_t::als_int
+ * must only define one interrupt mode each.
+ * - If threshold interrupts are enabled, thresholds have to be valid.
+ *
+ * @note This function is only available when module `vl6180x_irq` is used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ * @param[out] src Interrupt sources, see #vl6180x_int_config_t
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_int_wait(const vl6180x_t *dev, vl6180x_int_config_t *src);
+
+/**
+ * @brief Enable and disable interrupts
+ *
+ * Configured interrupts can be enabled or disabled with this function.
+ *
+ * @pre
+ * - Configuration parameter vl6180x_params_t::int_pin has to be defined
+ * - vl6180x_int_config_t::rng_int and vl6180x_int_config_t::als_int
+ * must only define one interrupt mode each.
+ *
+ * @note
+ * - To disable intertupts, set vl6180x_int_config_t::rng_int and
+ * vl6180x_int_config_t::als_int to #VL6180X_INT_NONE.
+ * - This function is only available when module `vl6180x_irq` is used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ * @param[in] mode Interrupts to be enabled, must be only one for each
+ * measurement type (range and ALS)
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_int_enable(vl6180x_t *dev, vl6180x_int_config_t mode);
+
+#if IS_USED(MODULE_VL6180X_CONFIG) || DOXYGEN
+/**
+ * @brief Configure thresholds for event interrupts at runtime
+ *
+ * @note This function is only available when modules `vl6180x_irq` and
+ * `vl6180x_config` are used.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ * @param[in] thresh Threshold configuration for event interrupts,
+ * see #vl6180x_int_thresh_t
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_int_config(vl6180x_t *dev, vl6180x_int_thresh_t thresh);
+
+#endif /* IS_USED(MODULE_VL6180X_CONFIG) || DOXYGEN */
+#endif /* IS_USED(MODULE_VL6180X_IRQ) || DOXYGEN */
+
+/**
+ * @name Low level interface functions
+ * @{
+ */
+
+/**
+ * @brief Direct write to register
+ *
+ * @note This function should only be used to do something special that
+ * is not covered by the high level interface AND if you exactly know what you
+ * do and what effects it might have. Please be aware that it might affect the
+ * high level interface.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ * @param[in] reg Address of the first register to be changed
+ * @param[in] data Pointer to the data to be written to the register
+ * @param[in] len Number of bytes to be written to the register
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_reg_write(const vl6180x_t *dev,
+ uint16_t reg, const uint8_t *data, uint8_t len);
+
+/**
+ * @brief Direct read from register
+ *
+ * @note This function should only be used to do something special that
+ * is not covered by the high level interface AND if you exactly know what you
+ * do and what effects it might have. Please be aware that it might affect the
+ * high level interface.
+ *
+ * @param[in] dev Device descriptor of VL6180X sensor
+ * @param[in] reg address of the first register to be read
+ * @param[out] data pointer to the data to be read from the register
+ * @param[in] len number of bytes to be read from the register
+ *
+ * @retval VL6180X_OK on success
+ * @retval VL6180X_ERROR_* a negative error code on error, see
+ * #vl6180x_error_t
+ */
+int vl6180x_reg_read(const vl6180x_t *dev,
+ uint16_t reg, uint8_t *data, uint8_t len);
+/** @} */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VL6180X_H */
+/** @} */
diff --git a/drivers/vl6180x/Makefile b/drivers/vl6180x/Makefile
new file mode 100644
index 0000000000..48422e909a
--- /dev/null
+++ b/drivers/vl6180x/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/drivers/vl6180x/Makefile.dep b/drivers/vl6180x/Makefile.dep
new file mode 100644
index 0000000000..f0875e9371
--- /dev/null
+++ b/drivers/vl6180x/Makefile.dep
@@ -0,0 +1,17 @@
+ifneq (,$(filter vl6180x_irq_%,$(USEMODULE)))
+ USEMODULE += vl6180x_irq
+endif
+
+ifneq (,$(filter vl6180x,$(USEMODULE)))
+ FEATURES_REQUIRED += periph_i2c
+ USEMODULE += ztimer_msec
+endif
+
+ifneq (,$(filter vl6180x_shutdown,$(USEMODULE)))
+ FEATURES_REQUIRED += periph_gpio
+endif
+
+ifneq (,$(filter vl6180x_irq,$(USEMODULE)))
+ FEATURES_REQUIRED += periph_gpio
+ FEATURES_REQUIRED += periph_gpio_irq
+endif
diff --git a/drivers/vl6180x/Makefile.include b/drivers/vl6180x/Makefile.include
new file mode 100644
index 0000000000..701100d760
--- /dev/null
+++ b/drivers/vl6180x/Makefile.include
@@ -0,0 +1,9 @@
+# include variants of VL6180x drivers as pseudomodules
+PSEUDOMODULES += vl6180x_rng
+PSEUDOMODULES += vl6180x_als
+PSEUDOMODULES += vl6180x_irq
+PSEUDOMODULES += vl6180x_shutdown
+PSEUDOMODULES += vl6180x_config
+
+USEMODULE_INCLUDES_vl6180x:= $(LAST_MAKEFILEDIR)/include
+USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_vl6180x)
diff --git a/drivers/vl6180x/include/vl6180x_params.h b/drivers/vl6180x/include/vl6180x_params.h
new file mode 100644
index 0000000000..7e47f9bc23
--- /dev/null
+++ b/drivers/vl6180x/include/vl6180x_params.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2021 Gunar Schorcht
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup drivers_vl6180x
+ * @brief Default configuration for ST VL6180X Ranging and Ambient Light Sensing (ALS) module
+ * @author Gunar Schorcht
+ * @file
+ * @{
+ */
+
+#ifndef VL6180X_PARAMS_H
+#define VL6180X_PARAMS_H
+
+#include "board.h"
+#include "saul_reg.h"
+#include "vl6180x.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @name Default hardware configuration
+ * @{
+ */
+#ifndef VL6180X_PARAM_DEV
+/** Default I2C_DEV(0) device */
+#define VL6180X_PARAM_DEV I2C_DEV(0)
+#endif
+
+#ifndef VL6180X_PARAM_ADDR
+/** Default I2C device address */
+#define VL6180X_PARAM_ADDR (VL6180X_I2C_ADDR)
+#endif
+
+#ifndef VL6180X_PARAM_INT_PIN
+/** Default interrupt pin */
+#define VL6180X_PARAM_INT_PIN (GPIO_PIN(0, 1))
+#endif
+
+#ifndef VL6180X_PARAM_SHUTDOWN_PIN
+/** Default shutdown pin */
+#define VL6180X_PARAM_SHUTDOWN_PIN (GPIO_PIN(0, 2))
+#endif
+/** @} */
+
+/**
+ * @name Default sensor configuration parameters
+ * @{
+ */
+#if !DOXYGEN
+/* Mapping of Kconfig defines to the respective driver enumeration values */
+
+#ifdef CONFIG_VL6180X_ALS_GAIN_1
+#define CONFIG_VL6180X_ALS_GAIN (VL6180X_ALS_GAIN_1)
+#elif CONFIG_VL6180X_ALS_GAIN_1_25
+#define CONFIG_VL6180X_ALS_GAIN (VL6180X_ALS_GAIN_1_25)
+#elif CONFIG_VL6180X_ALS_GAIN_1_67
+#define CONFIG_VL6180X_ALS_GAIN (VL6180X_ALS_GAIN_1_67)
+#elif CONFIG_VL6180X_ALS_GAIN_2_5
+#define CONFIG_VL6180X_ALS_GAIN (VL6180X_ALS_GAIN_2_5)
+#elif CONFIG_VL6180X_ALS_GAIN_5
+#define CONFIG_VL6180X_ALS_GAIN (VL6180X_ALS_GAIN_5)
+#elif CONFIG_VL6180X_ALS_GAIN_10
+#define CONFIG_VL6180X_ALS_GAIN (VL6180X_ALS_GAIN_10)
+#elif CONFIG_VL6180X_ALS_GAIN_20
+#define CONFIG_VL6180X_ALS_GAIN (VL6180X_ALS_GAIN_20)
+#elif CONFIG_VL6180X_ALS_GAIN_40
+#define CONFIG_VL6180X_ALS_GAIN (VL6180X_ALS_GAIN_40)
+#endif
+
+#ifdef CONFIG_VL6180X_RNG_INT_DRDY
+#define CONFIG_VL6180X_RNG_INT (VL6180X_INT_DRDY)
+#elif CONFIG_VL6180X_RNG_INT_LOW
+#define CONFIG_VL6180X_RNG_INT (VL6180X_INT_LOW)
+#elif CONFIG_VL6180X_RNG_INT_HIGH
+#define CONFIG_VL6180X_RNG_INT (VL6180X_INT_HIGH)
+#elif CONFIG_VL6180X_RNG_INT_OUT
+#define CONFIG_VL6180X_RNG_INT (VL6180X_INT_OUT)
+#endif
+
+#ifdef CONFIG_VL6180X_ALS_INT_DRDY
+#define CONFIG_VL6180X_ALS_INT (VL6180X_INT_DRDY)
+#elif CONFIG_VL6180X_ALS_INT_LOW
+#define CONFIG_VL6180X_ALS_INT (VL6180X_INT_LOW)
+#elif CONFIG_VL6180X_ALS_INT_HIGH
+#define CONFIG_VL6180X_ALS_INT (VL6180X_INT_HIGH)
+#elif CONFIG_VL6180X_ALS_INT_OUT
+#define CONFIG_VL6180X_ALS_INT (VL6180X_INT_OUT)
+#endif
+
+#endif /* !DOXYGEN */
+
+#ifndef CONFIG_VL6180X_MEAS_PERIOD
+/** Default period for range and ALS measurements in steps of 10 ms: 200 ms */
+#define CONFIG_VL6180X_MEAS_PERIOD (20)
+#endif
+
+#ifndef CONFIG_VL6180X_RNG_MAX_TIME
+/** Default ranging maximum convergence time: 50 ms */
+#define CONFIG_VL6180X_RNG_MAX_TIME (50)
+#endif
+
+#ifndef CONFIG_VL6180X_RNG_INT
+/** Default interrupt mode for ranging: VL6180X_INT_DRDY */
+#define CONFIG_VL6180X_RNG_INT (VL6180X_INT_DRDY)
+#endif
+
+#ifndef CONFIG_VL6180X_RNG_THRESH_LOW
+/** Default low threshold value for ranging comparison: 20 mm */
+#define CONFIG_VL6180X_RNG_THRESH_LOW (20)
+#endif
+
+#ifndef CONFIG_VL6180X_RNG_THRESH_HIGH
+/** Default high threshold value for ranging comparison: 90 mm */
+#define CONFIG_VL6180X_RNG_THRESH_HIGH (90)
+#endif
+
+#ifndef CONFIG_VL6180X_ALS_INT_TIME
+/** Default ALS integration time: 100 ms (recommended by the datasheet) */
+#define CONFIG_VL6180X_ALS_INT_TIME (100)
+#endif
+
+#ifndef CONFIG_VL6180X_ALS_GAIN
+/** Default ALS analogue light channel gain: 1.0 */
+#define CONFIG_VL6180X_ALS_GAIN (VL6180X_ALS_GAIN_1)
+#endif
+
+#ifndef CONFIG_VL6180X_ALS_LUX_RES
+/** Default ALS lux resolution specified as lux/count*1000: 0.32 count/lux is factory calibrated */
+#define CONFIG_VL6180X_ALS_LUX_RES 320
+#endif
+
+#ifndef CONFIG_VL6180X_ALS_INT
+/** Default interrupt mode for ranging: VL6180X_INT_DRDY */
+#define CONFIG_VL6180X_ALS_INT (VL6180X_INT_DRDY)
+#endif
+
+#ifndef CONFIG_VL6180X_ALS_THRESH_LOW
+/** Default low threshold value for ALS comparison: 50 counts */
+#define CONFIG_VL6180X_ALS_THRESH_LOW (50)
+#endif
+
+#ifndef CONFIG_VL6180X_ALS_THRESH_HIGH
+/** Default high threshold value for ALS comparison: 2000 counts */
+#define CONFIG_VL6180X_ALS_THRESH_HIGH (2000)
+#endif
+
+#if IS_USED(MODULE_VL6180X_RNG) || DOXYGEN
+/** Range measurement configuration parameters */
+#define VL6180X_PARAM_RANGE .rng_max_time = CONFIG_VL6180X_RNG_MAX_TIME,
+#else
+#define VL6180X_PARAM_RANGE
+#endif
+
+#if IS_USED(MODULE_VL6180X_ALS) || DOXYGEN
+/** ALS measurement configuration parameters */
+#define VL6180X_PARAM_ALS .als_int_time = CONFIG_VL6180X_ALS_INT_TIME, \
+ .als_gain = CONFIG_VL6180X_ALS_GAIN, \
+ .als_lux_res = CONFIG_VL6180X_ALS_LUX_RES,
+#else
+#define VL6180X_PARAM_ALS
+#endif
+
+#if IS_USED(MODULE_VL6180X_SHUTDOWN) || DOXYGEN
+/** Shutdown hardware configuration */
+#define VL6180X_PARAM_SHUTDOWN .shutdown_pin = VL6180X_PARAM_SHUTDOWN_PIN,
+#else
+#define VL6180X_PARAM_SHUTDOWN
+#endif
+
+#if IS_USED(MODULE_VL6180X_IRQ) || DOXYGEN
+/** Interrupt pin configuration */
+#define VL6180X_PARAM_INT .int_pin = VL6180X_PARAM_INT_PIN,
+
+#if IS_USED(MODULE_VL6180X_RNG) || DOXYGEN
+/** Interrupt configuration for ranging */
+#define VL6180X_PARAM_INT_RNG_CFG .int_cfg.rng_int = CONFIG_VL6180X_RNG_INT, \
+ .int_thresh.rng_low = CONFIG_VL6180X_RNG_THRESH_LOW, \
+ .int_thresh.rng_high = CONFIG_VL6180X_RNG_THRESH_HIGH,
+#else /* IS_USED(MODULE_VL6180X_RNG) || DOXYGEN */
+#define VL6180X_PARAM_INT_RNG_CFG
+#endif /* IS_USED(MODULE_VL6180X_RNG) || DOXYGEN */
+
+#if IS_USED(MODULE_VL6180X_ALS) || DOXYGEN
+/** Interrupt configuration for ALS */
+#define VL6180X_PARAM_INT_ALS_CFG .int_cfg.als_int = CONFIG_VL6180X_ALS_INT, \
+ .int_thresh.als_low = CONFIG_VL6180X_ALS_THRESH_LOW, \
+ .int_thresh.als_high = CONFIG_VL6180X_ALS_THRESH_HIGH,
+#else /* IS_USED(MODULE_VL6180X_ALS) || DOXYGEN */
+#define VL6180X_PARAM_INT_ALS_CFG
+#endif /* IS_USED(MODULE_VL6180X_ALS) || DOXYGEN */
+
+#else /* IS_USED(MODULE_VL6180X_IRQ) || DOXYGEN */
+#define VL6180X_PARAM_INT
+#define VL6180X_PARAM_INT_RNG_CFG
+#define VL6180X_PARAM_INT_ALS_CFG
+#endif /* IS_USED(MODULE_VL6180X_IRQ) || DOXYGEN */
+
+#if !VL6180X_PARAMS || DOXYGEN
+/** Default configuration parameter set */
+#define VL6180X_PARAMS { \
+ .i2c_dev = VL6180X_PARAM_DEV, \
+ .i2c_addr = VL6180X_PARAM_ADDR, \
+ .period = CONFIG_VL6180X_MEAS_PERIOD, \
+ VL6180X_PARAM_RANGE \
+ VL6180X_PARAM_ALS \
+ VL6180X_PARAM_SHUTDOWN \
+ VL6180X_PARAM_INT \
+ VL6180X_PARAM_INT_RNG_CFG \
+ VL6180X_PARAM_INT_ALS_CFG \
+ }
+#endif /* !VL6180X_PARAMS */
+
+#if !defined(VL6180X_SAUL_INFO) || DOXYGEN
+/** Default SAUL information */
+#define VL6180X_SAUL_INFO { .name = "vl6180x" }
+#endif
+/**@}*/
+
+/**
+ * @brief Allocate some memory to store the actual configuration
+ */
+static const vl6180x_params_t vl6180x_params[] =
+{
+ VL6180X_PARAMS
+};
+
+/**
+ * @brief Additional meta information to keep in the SAUL registry
+ */
+static const saul_reg_info_t vl6180x_saul_info[] =
+{
+ VL6180X_SAUL_INFO
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VL6180X_PARAMS_H */
+/** @} */
diff --git a/drivers/vl6180x/include/vl6180x_regs.h b/drivers/vl6180x/include/vl6180x_regs.h
new file mode 100644
index 0000000000..067cf02045
--- /dev/null
+++ b/drivers/vl6180x/include/vl6180x_regs.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2021 Gunar Schorcht
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup drivers_vl6180x
+ * @brief Register definitions for ST VL6180X Ranging and Ambient Light Sensing (ALS) module
+ * @author Gunar Schorcht
+ * @file
+ * @{
+ */
+
+#ifndef VL6180X_REGS_H
+#define VL6180X_REGS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * @name Register addresses
+ * @{
+ */
+#define VL6180X_REG_MODEL_ID (0x00) /**< Device ID */
+#define VL6180X_REG_MODEL_REV_MAJOR (0x01) /**< Device revision (major) */
+#define VL6180X_REG_MODEL_REV_MINOR (0x02) /**< Device revision (minor) */
+#define VL6180X_REG_MODULE_REV_MAJOR (0x03) /**< Module revision (major) */
+#define VL6180X_REG_MODULE_REV_MINOR (0x04) /**< Module revision (minor) */
+
+#define VL6180X_REG_GPIO0_MODE (0x10) /**< GPIO0 mode definition */
+#define VL6180X_REG_GPIO1_MODE (0x11) /**< GPIO1 mode definition */
+#define VL6180X_REG_HISTORY_CTRL (0x12) /**< ALS and Ranging history control */
+#define VL6180X_REG_INT_CONFIG (0x14) /**< Interrupt mode config */
+#define VL6180X_REG_INT_CLR (0x15) /**< Interrupt clear */
+#define VL6180X_REG_FRESH_RST (0x16) /**< Fresh out of reset bit */
+
+#define VL6180X_REG_RNG_START (0x18) /**< Range measurement start */
+#define VL6180X_REG_RNG_THRESH_HI (0x19) /**< Range measurement high threshold */
+#define VL6180X_REG_RNG_THRESH_LO (0x1a) /**< Range measurement low threshold */
+#define VL6180X_REG_RNG_PERIOD (0x1b) /**< Range measurement period in continuous mode */
+#define VL6180X_REG_RNG_MAX_TIME (0x1c) /**< Range measurement time limit */
+#define VL6180X_REG_RNG_VALUE (0x62) /**< Range 8-bit value in mm */
+
+#define VL6180X_REG_ALS_START (0x38) /**< ALS measurement start */
+#define VL6180X_REG_ALS_THRESH_HI (0x3a) /**< ALS measurement high threshold */
+#define VL6180X_REG_ALS_THRESH_LO (0x3c) /**< ALS measurement low threshold */
+#define VL6180X_REG_ALS_PERIOD (0x3e) /**< ALS measurement period in continuous mode */
+#define VL6180X_REG_ALS_GAIN (0x3f) /**< ALS analogue gain */
+#define VL6180X_REG_ALS_INT_TIME (0x40) /**< ALS integration time */
+#define VL6180X_REG_ALS_VALUE (0x50) /**< ALS 16-bit count value */
+
+#define VL6180X_REG_RNG_STATUS (0x4d) /**< Range measurement status */
+#define VL6180X_REG_ALS_STATUS (0x4e) /**< ALS measurement status */
+#define VL6180X_REG_INT_STATUS (0x4f) /**< Interrupt status */
+
+#define VL6180X_REG_I2C_ADDR (0x212) /**< Programmable device address */
+#define VL6180X_REG_INTERLEAVED_MODE (0x2a3) /**< Interleaved mode enable */
+/** @} */
+
+/**
+ * @name Register structures
+ * @{
+ */
+
+/* VL6180X_REG_RNG_START */
+#define VL6180X_RNG_MODE_CONT (0x02) /**< Continuous range measurement mode */
+#define VL6180X_RNG_START_STOP (0x01) /**< Start/stop range measurement */
+
+/* VL6180X_REG_ALS_START */
+#define VL6180X_ALS_MODE_CONT (0x02) /**< ALS measurement mode */
+#define VL6180X_ALS_START_STOP (0x01) /**< Start/stop ALS measurement */
+
+/* VL6180X_REG_RNG_STATUS */
+#define VL6180X_RNG_ERR_CODE (0xf0) /**< Range measurement error code mask */
+#define VL6180X_RNG_ERR_CODE_S (4) /**< Range measurement error code shift */
+#define VL6180X_RNG_DEVICE_RDY (0x01) /**< Range device ready */
+
+/* VL6180X_REG_ALS_STATUS */
+#define VL6180X_ALS_ERR_CODE (0xf0) /**< ALS measurement error code mask */
+#define VL6180X_ALS_ERR_CODE_S (4) /**< ALS measurement error code shift */
+#define VL6180X_ALS_DEVICE_RDY (0x01) /**< ALS device ready */
+
+/* VL6180X_REG_ALS_GAIN */
+#define VL6180X_ALS_GAIN_LIGHT (0x07) /**< ALS analogue gain mask (light channel) */
+
+/* VL6180X_REG_INT_CONFIG and VL6180X_REG_INT_STATUS */
+#define VL6180X_INT_RNG (0x07) /**< RNG interrupt mask */
+#define VL6180X_INT_RNG_S (0) /**< RNG interrupt shift */
+#define VL6180X_INT_ALS (0x38) /**< ALS interrupt mask */
+#define VL6180X_INT_ALS_S (3) /**< ALS interrupt shift */
+#define VL6180X_ERR_INT (0xc0) /**< Error interrupt mask (VL6180X_REG_INT_STATUS only) */
+#define VL6180X_ERR_INT_S (6) /**< Error interrupt shift */
+
+#define VL6180X_INT_RNG_LOW (0x01) /**< range < lower threshold */
+#define VL6180X_INT_RNG_HIGH (0x02) /**< range > upper threshold */
+#define VL6180X_INT_RNG_OUT (0x03) /**< range < lower threshold or range > upper threshold */
+#define VL6180X_INT_RNG_DRDY (0x04) /**< new range data are ready to be read */
+#define VL6180X_INT_ALS_LOW (0x08) /**< ALS < lower threshold */
+#define VL6180X_INT_ALS_HIGH (0x10) /**< ALS > upper threshold */
+#define VL6180X_INT_ALS_OUT (0x18) /**< ALS < lower threshold or ALS > upper threshold */
+#define VL6180X_INT_ALS_DRDY (0x20) /**< new ALS data are ready to be read */
+#define VL6180X_INT_ERR_LASER (0x40) /**< Laser safety error */
+#define VL6180X_INT_ERR_PLL (0x80) /**< PLL error */
+
+/* VL6180X_REG_INT_CLR */
+#define VL6180X_CLR_ERR_INT (0x04) /**< Clear error interrupt */
+#define VL6180X_CLR_ALS_INT (0x02) /**< Clear ALS interrupt */
+#define VL6180X_CLR_RNG_INT (0x01) /**< Clear range interrupt */
+#define VL6180X_CLR_ALL_INT (0x07) /**< Clear all interrupts */
+
+/* VL6180X_REG_GPIO0_MODE */
+#define VL6180X_GPIO0_SHUT (0x40) /**< GPIO0 shutdown function mask */
+#define VL6180X_GPIO0_SHUT_ON (0x40) /**< GPIO0 shutdown function enabled */
+#define VL6180X_GPIO0_SHUT_OFF (0x00) /**< GPIO0 shutdown function disabled */
+#define VL6180X_GPIO0_POL (0x20) /**< GPIO0 polarity mask */
+#define VL6180X_GPIO0_POL_LOW (0x00) /**< GPIO0 polarity is low */
+#define VL6180X_GPIO0_POL_HIGH (0x20) /**< GPIO0 polarity is high */
+#define VL6180X_GPIO0_FUNC (0x1e) /**< GPIO0 function mask */
+#define VL6180X_GPIO0_FUNC_OFF (0x00) /**< GPIO0 function off*/
+#define VL6180X_GPIO0_FUNC_ON (0x10) /**< GPIO0 function on */
+
+/* VL6180X_REG_GPIO1_MODE */
+#define VL6180X_GPIO1_POL (0x20) /**< GPIO1 polarity mask */
+#define VL6180X_GPIO1_POL_LOW (0x00) /**< GPIO1 polarity is low */
+#define VL6180X_GPIO1_POL_HIGH (0x20) /**< GPIO1 polarity is high */
+#define VL6180X_GPIO1_FUNC (0x1e) /**< GPIO1 function mask */
+#define VL6180X_GPIO1_FUNC_OFF (0x00) /**< GPIO1 function off */
+#define VL6180X_GPIO1_FUNC_ON (0x10) /**< GPIO1 function on */
+
+/** @} */
+
+/**
+ * @name Default register values
+ *
+ * These values are the register values after reset or overwritten at
+ * boot-up by NVM contents.
+ * @{
+ */
+#define VL6180X_MODEL_ID (0xb4) /**< VNCL6180 Device ID */
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VL6180X_REGS_H */
+/** @} */
diff --git a/drivers/vl6180x/vl6180x.c b/drivers/vl6180x/vl6180x.c
new file mode 100644
index 0000000000..5941a99953
--- /dev/null
+++ b/drivers/vl6180x/vl6180x.c
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2021 Gunar Schorcht
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup drivers_vl6180x
+ * @brief Device driver for the ST VL6180X Ranging and Ambient Light Sensing (ALS) module
+ * @author Gunar Schorcht
+ * @file
+ * @{
+ */
+
+#include
+#include
+
+#include "vl6180x_regs.h"
+#include "vl6180x.h"
+
+#include "irq.h"
+#include "log.h"
+#include "ztimer.h"
+
+#define ENABLE_DEBUG 0
+#include "debug.h"
+
+#if ENABLE_DEBUG
+
+#define ASSERT_PARAM(cond) \
+ do { \
+ if (!(cond)) { \
+ DEBUG("[vl6180x] %s: %s\n", \
+ __func__, "parameter condition (" # cond ") not fulfilled"); \
+ assert(cond); \
+ } \
+ } while (0)
+
+#define DEBUG_DEV(f, d, ...) \
+ DEBUG("[vl6180x] %s i2c dev=%d addr=%02x: " f "\n", \
+ __func__, d->params.i2c_dev, d->params.i2c_addr, ## __VA_ARGS__)
+
+#else /* ENABLE_DEBUG */
+
+#define ASSERT_PARAM(cond) assert(cond)
+#define DEBUG_DEV(f, d, ...)
+
+#endif /* ENABLE_DEBUG */
+
+#define ERROR_DEV(f, d, ...) \
+ LOG_ERROR("[vl6180x] %s i2c dev=%d addr=%02x: " f "\n", \
+ __func__, d->params.i2c_dev, d->params.i2c_addr, ## __VA_ARGS__)
+
+#define _SET_REG_VALUE(r, m, v) r = (r & ~m) | (((v) << m ## _S) & m)
+#define _GET_REG_VALUE(r, m) ((r & ~m) >> m ## _S)
+
+/** Forward declaration of functions for internal use */
+static int _is_available(const vl6180x_t *dev);
+static int _reset(const vl6180x_t *dev);
+static int _init(vl6180x_t *dev);
+
+inline static void _acquire(const vl6180x_t *dev);
+inline static void _release(const vl6180x_t *dev);
+
+static int _read(const vl6180x_t *dev,
+ uint16_t reg, uint8_t *pdata, uint8_t len);
+static int _write(const vl6180x_t *dev,
+ uint16_t reg, const uint8_t *pdata, uint8_t len);
+
+inline static int _read_word(const vl6180x_t *dev, uint16_t reg, uint16_t *word);
+inline static int _write_byte(const vl6180x_t *dev, uint16_t reg, uint8_t byte);
+inline static int _write_word(const vl6180x_t *dev, uint16_t reg, uint16_t word);
+
+inline static int _read_word_x(const vl6180x_t *dev, uint16_t reg, uint16_t *word);
+inline static int _write_byte_x(const vl6180x_t *dev, uint16_t reg, uint8_t byte);
+inline static int _write_word_x(const vl6180x_t *dev, uint16_t reg, uint16_t word);
+
+int vl6180x_init(vl6180x_t *dev, const vl6180x_params_t *params)
+{
+ /* some parameter sanity checks */
+ ASSERT_PARAM(dev != NULL);
+ ASSERT_PARAM(params != NULL);
+
+ DEBUG_DEV("params=%p", dev, params);
+
+#if IS_USED(MODULE_VL6180X_RNG)
+ ASSERT_PARAM(params->rng_max_time > 0 && params->rng_max_time < 64);
+#endif
+#if IS_USED(MODULE_VL6180X_ALS)
+ ASSERT_PARAM(params->als_int_time > 0 && params->als_int_time < 512);
+#endif
+
+ /* init sensor data structure */
+ dev->params = *params;
+
+#if IS_USED(MODULE_VL6180X_SHUTDOWN)
+ /* if shutdown is used, the pin nust not be undefined */
+ ASSERT_PARAM(gpio_is_valid(params->shutdown_pin));
+
+ /* shutdown pin is initialized and set to HIGH */
+ gpio_init(params->shutdown_pin, GPIO_OUT);
+ gpio_write(params->shutdown_pin, 1);
+#endif /* IS_USED(MODULE_VL6180X_SHUTDOWN) */
+
+ /* init the sensor and start measurement if periodic measurements used */
+ return _init(dev);
+}
+
+int vl6180x_start_cont(vl6180x_t *dev)
+{
+ ASSERT_PARAM(dev != NULL);
+ ASSERT_PARAM(dev->params.period != 0); /* only possible of period != 0 */
+ ASSERT_PARAM(!dev->cont_meas); /* continuous mode is not yet started */
+
+ /* cppcheck-suppress unusedVariable
+ * (reason: only unused if neither vl6180x_rng nor vl6180x_als is used) */
+ uint8_t status;
+ int res = VL6180X_OK;
+
+ _acquire(dev);
+
+#if IS_USED(MODULE_VL6180X_ALS)
+ res |= _read(dev, VL6180X_REG_ALS_STATUS, &status, 1);
+ if ((status & VL6180X_ALS_DEVICE_RDY) == 0) {
+ res |= _write_byte(dev, VL6180X_REG_ALS_START, VL6180X_ALS_START_STOP);
+ }
+ while ((status & VL6180X_ALS_DEVICE_RDY) == 0) {
+ /* start measurement only when device is ready to start*/
+ res |= _read(dev, VL6180X_REG_ALS_STATUS, &status, 1);
+ }
+ /*
+ * Start either only ALS measurements or both ALS and range
+ * measurements in continuous mode. According to the data sheet,
+ * the interleaved mode should be used in latter case. This means
+ * that the INTERLEAVED_MODE__ENABLE register must be set to 0x01
+ * and the ALS measurement must be started as configured. The same
+ * period is then used for both measurements.
+ */
+ res |= _write_byte(dev, VL6180X_REG_INTERLEAVED_MODE,
+ IS_USED(MODULE_VL6180X_RNG));
+ res |= _write_byte(dev, VL6180X_REG_ALS_START,
+ VL6180X_ALS_START_STOP | VL6180X_ALS_MODE_CONT);
+#elif IS_USED(MODULE_VL6180X_RNG)
+ res |= _read(dev, VL6180X_REG_RNG_STATUS, &status, 1);
+ if ((status & VL6180X_RNG_DEVICE_RDY) == 0) {
+ res |= _write_byte(dev, VL6180X_REG_RNG_START, VL6180X_RNG_START_STOP);
+ }
+ while ((status & VL6180X_RNG_DEVICE_RDY) == 0) {
+ /* start measurement only when device is ready to start*/
+ res |= _read(dev, VL6180X_REG_RNG_STATUS, &status, 1);
+ }
+ if ((status & VL6180X_RNG_DEVICE_RDY) == 0) {
+ /* start measurement only when device is ready to start*/
+ return -VL6180X_ERROR_NOT_READY;
+ }
+
+ /* start only range measurements in continuous mode */
+ res |= _write_byte(dev, VL6180X_REG_INTERLEAVED_MODE, 0);
+ res |= _write_byte(dev, VL6180X_REG_RNG_START,
+ VL6180X_RNG_START_STOP | VL6180X_RNG_MODE_CONT);
+#endif /* IS_USED(MODULE_VL6180X_ALS) */
+
+ _release(dev);
+
+ dev->cont_meas = (res == VL6180X_OK) ? true : dev->cont_meas;
+
+ return res;
+}
+
+int vl6180x_stop_cont(vl6180x_t *dev)
+{
+ ASSERT_PARAM(dev != NULL);
+ ASSERT_PARAM(dev->params.period != 0); /* operation is only valid if period != 0 */
+ ASSERT_PARAM(dev->cont_meas); /* continuous mode is started */
+
+ /* cppcheck-suppress unusedVariable
+ * (reason: only unused if neither vl6180x_rng nor vl6180x_als is used) */
+ uint8_t status;
+ int res = VL6180X_OK;
+
+ _acquire(dev);
+
+#if IS_USED(MODULE_VL6180X_ALS)
+ res |= _read(dev, VL6180X_REG_ALS_STATUS, &status, 1);
+ if ((status & VL6180X_ALS_DEVICE_RDY) == 0) {
+ /* stop only when device is not ready, otherwise it is already stopped */
+ res |= _write_byte(dev, VL6180X_REG_ALS_START, VL6180X_ALS_START_STOP);
+ }
+ /* wait that the device becomes ready */
+ do {
+ res |= _read(dev, VL6180X_REG_ALS_STATUS, &status, 1);
+ } while ((res != VL6180X_OK) || ((status & VL6180X_ALS_DEVICE_RDY) == 0));
+
+#elif IS_USED(MODULE_VL6180X_RNG)
+ res |= _read(dev, VL6180X_REG_RNG_STATUS, &status, 1);
+ if ((status & VL6180X_RNG_DEVICE_RDY) == 0) {
+ /* stop only when device is not ready, otherwise it is already stopped */
+ res |= _write_byte(dev, VL6180X_REG_RNG_START, VL6180X_RNG_START_STOP);
+ }
+ /* wait that the device becomes ready */
+ do {
+ res |= _read(dev, VL6180X_REG_RNG_STATUS, &status, 1);
+ } while ((res != VL6180X_OK) || ((status & VL6180X_RNG_DEVICE_RDY) == 0));
+#endif
+
+ res |= _write_byte(dev, VL6180X_REG_INTERLEAVED_MODE, 0);
+ _release(dev);
+
+ dev->cont_meas = (res == VL6180X_OK) ? false : dev->cont_meas;
+
+ return res;
+}
+
+#if IS_USED(MODULE_VL6180X_RNG)
+
+int vl6180x_rng_data_ready (const vl6180x_t *dev)
+{
+ ASSERT_PARAM(dev != NULL);
+ DEBUG_DEV("", dev);
+
+ uint8_t byte;
+
+ int res = vl6180x_reg_read(dev, VL6180X_REG_INT_STATUS, &byte, 1);
+ if (res != VL6180X_OK) {
+ return res;
+ }
+
+ return ((byte & VL6180X_INT_RNG_DRDY) != 0) ? VL6180X_OK
+ : -VL6180X_ERROR_NO_DATA;
+}
+
+int vl6180x_rng_read(vl6180x_t *dev, uint8_t *mm)
+{
+ ASSERT_PARAM(dev != NULL);
+ ASSERT_PARAM(mm != NULL);
+ DEBUG_DEV("mm=%p", dev, mm);
+
+ _acquire(dev);
+
+ /* read range results */
+ int res;
+ res = _read(dev, VL6180X_REG_RNG_VALUE, mm, 1);
+
+ /* read range measurement status */
+ uint8_t status;
+ res |= _read(dev, VL6180X_REG_RNG_STATUS, &status, 1);
+
+ _release(dev);
+
+ dev->rng_status = (status & VL6180X_ALS_ERR_CODE) >> VL6180X_RNG_ERR_CODE_S;
+
+ if (res != VL6180X_OK) {
+ return res;
+ }
+ else {
+ /* return with error code if status isn't zero */
+ return (dev->rng_status) ? -VL6180X_ERROR_RNG : VL6180X_OK;
+ }
+}
+
+vl6180x_rng_status_t vl6180x_rng_status(const vl6180x_t *dev)
+{
+ ASSERT_PARAM(dev != NULL);
+ DEBUG_DEV("", dev);
+ return dev->rng_status;
+}
+
+int vl6180x_rng_start_single(const vl6180x_t *dev)
+{
+ ASSERT_PARAM(dev != NULL);
+ ASSERT_PARAM(!dev->cont_meas); /* continuous mode is not started */
+
+ uint8_t status;
+ int res;
+
+ _acquire(dev);
+ res = _read(dev, VL6180X_REG_RNG_STATUS, &status, 1);
+ if ((res == VL6180X_OK) && ((status & VL6180X_RNG_DEVICE_RDY) == 1)) {
+ /* start measurement only when device is ready to start*/
+ res = _write_byte_x(dev, VL6180X_REG_RNG_START, VL6180X_RNG_START_STOP);
+ }
+ _release(dev);
+
+ return res;
+}
+
+#if IS_USED(MODULE_VL6180X_CONFIG)
+
+int vl6180x_rng_config(vl6180x_t *dev, uint8_t period, uint8_t max_time)
+{
+ /* some parameter sanity checks */
+ ASSERT_PARAM(dev != NULL);
+ ASSERT_PARAM(max_time < 64);
+
+ DEBUG_DEV("period=%u max_time=%u", dev, period, max_time);
+
+ int res;
+
+ /* write new ranging configuration */
+ dev->params.period = period;
+ dev->params.rng_max_time = max_time;
+ period -= 1; /* register value is decremented by 1 */
+
+ _acquire(dev);
+ res = _write(dev, VL6180X_REG_RNG_PERIOD, &period, 1);
+ res |= _write(dev, VL6180X_REG_RNG_MAX_TIME, &max_time, 1);
+ _release(dev);
+
+ /* if running in continuous mode, stop and restart measurements */
+ if ((res == VL6180X_OK) && dev->cont_meas) {
+ res = vl6180x_stop_cont(dev);
+ if ((res == VL6180X_OK) && period) {
+ res = vl6180x_start_cont(dev);
+ }
+ }
+
+ return res;
+}
+
+#endif /* IS_USED(MODULE_VL6180X_CONFIG) */
+#endif /* IS_USED(MODULE_VL6180X_RNG) */
+
+#if IS_USED(MODULE_VL6180X_ALS)
+
+int vl6180x_als_data_ready(const vl6180x_t *dev)
+{
+ ASSERT_PARAM(dev != NULL);
+ DEBUG_DEV("", dev);
+
+ uint8_t byte;
+
+ int res = vl6180x_reg_read(dev, VL6180X_REG_INT_STATUS, &byte, 1);
+ if (res != VL6180X_OK) {
+ return res;
+ }
+
+ return ((byte & VL6180X_INT_ALS_DRDY) != 0) ? VL6180X_OK
+ : -VL6180X_ERROR_NO_DATA;
+}
+
+static const uint16_t _als_gains[] = {
+/* 20 10.32 5.21 2.56 1.72 1.28 1.01 40 actual analogue gain */
+ 20000, 10320, 5210, 2560, 1720, 1280, 1010, 40000
+};
+
+int vl6180x_als_read(vl6180x_t *dev, uint16_t *raw, uint16_t *lux)
+{
+ ASSERT_PARAM(dev != NULL);
+ ASSERT_PARAM(raw != NULL || lux != NULL);
+ DEBUG_DEV("raw=%p lux=%p", dev, raw, lux);
+
+ int res;
+
+ _acquire(dev);
+
+ /* read raw ALS results */
+ uint16_t cnt = 0;
+ res = _read_word(dev, VL6180X_REG_ALS_VALUE, &cnt);
+
+ /* read range measurement status */
+ uint8_t status;
+ res |= _read(dev, VL6180X_REG_ALS_STATUS, &status, 1);
+
+ _release(dev);
+
+ if (res) {
+ return res;
+ }
+
+ dev->als_status = (status & VL6180X_ALS_ERR_CODE_S) >> VL6180X_ALS_ERR_CODE_S;
+
+ if (dev->als_status == VL6180X_ALS_OK) {
+ if (raw) {
+ *raw = cnt;
+ }
+ if (lux) {
+ /* lux = lux_res * (cnt / als_gain) * (100 / als_int_time) */
+ uint32_t lux_tmp;
+ lux_tmp = dev->params.als_lux_res * cnt * 100;
+ lux_tmp /= _als_gains[dev->params.als_gain];
+ lux_tmp /= dev->params.als_int_time;
+ *lux = lux_tmp;
+ }
+
+ }
+
+ /* return with error code if status isn't zero */
+ return (dev->als_status) ? -VL6180X_ERROR_ALS : VL6180X_OK;
+}
+
+vl6180x_als_status_t vl6180x_als_status(const vl6180x_t *dev)
+{
+ ASSERT_PARAM(dev != NULL);
+ DEBUG_DEV("", dev);
+ return dev->als_status;
+}
+
+int vl6180x_als_start_single(const vl6180x_t *dev)
+{
+ ASSERT_PARAM(dev != NULL);
+ ASSERT_PARAM(!dev->cont_meas); /* continuous mode is not started */
+
+ uint8_t status;
+ int res;
+
+ _acquire(dev);
+
+ res = _read(dev, VL6180X_REG_ALS_STATUS, &status, 1);
+ if ((res == VL6180X_OK) && ((status & VL6180X_ALS_DEVICE_RDY) == 1)) {
+ /* start measurement only when device is ready to start*/
+ res = _write_byte(dev, VL6180X_REG_ALS_START, VL6180X_ALS_START_STOP);
+ }
+
+ _release(dev);
+
+ return res;
+}
+
+#if IS_USED(MODULE_VL6180X_CONFIG)
+
+int vl6180x_als_config(vl6180x_t *dev, uint8_t period, uint8_t int_time,
+ vl6180x_als_gain_t gain)
+{
+ /* some parameter sanity checks */
+ ASSERT_PARAM(dev != NULL);
+
+ DEBUG_DEV("period=%u int_time=%u gain=%u", dev, period, int_time, gain);
+
+ /* write new ambient light sensing (ALS) configuration */
+ dev->params.period = period;
+ dev->params.als_int_time = int_time;
+ dev->params.als_gain = gain;
+
+ int res;
+
+ _acquire(dev);
+ res = _write_byte(dev, VL6180X_REG_ALS_PERIOD, period - 1);
+ res |= _write_word(dev, VL6180X_REG_ALS_INT_TIME, int_time - 1);
+ res |= _write_byte(dev, VL6180X_REG_ALS_GAIN, 0x40 + gain);
+ _release(dev);
+
+ /* if running in continuous mode, stop and restart measurements */
+ if ((res == VL6180X_OK) && dev->cont_meas) {
+ res = vl6180x_stop_cont(dev);
+ if ((res == VL6180X_OK) && period) {
+ res = vl6180x_start_cont(dev);
+ }
+ }
+
+ return res;
+}
+#endif /* IS_USED(MODULE_VL6180X_CONFIG) */
+#endif /* IS_USED(MODULE_VL6180X_ALS) */
+
+#if IS_USED(MODULE_VL6180X_SHUTDOWN)
+int vl6180x_power_down(const vl6180x_t *dev)
+{
+ ASSERT_PARAM(dev != NULL);
+ DEBUG_DEV("", dev);
+
+ /* assert shutdown pin */
+ gpio_clear(dev->params.shutdown_pin);
+ return VL6180X_OK;
+}
+
+int vl6180x_power_up(vl6180x_t *dev)
+{
+ ASSERT_PARAM(dev != NULL);
+ DEBUG_DEV("", dev);
+
+ /* deactivate shutdown pin */
+ gpio_set(dev->params.shutdown_pin);
+
+ /* init the sensor and start measurement */
+ _init(dev);
+
+ return VL6180X_OK;
+}
+#endif /* IS_USED(MODULE_VL6180X_SHUTDOWN) */
+
+#if IS_USED(MODULE_VL6180X_IRQ)
+
+static void _isr(void *lock)
+{
+ mutex_unlock(lock);
+}
+
+int vl6180x_int_wait(const vl6180x_t *dev, vl6180x_int_config_t *src)
+{
+ ASSERT_PARAM(dev != NULL);
+ ASSERT_PARAM(src != NULL);
+ ASSERT_PARAM(gpio_is_valid(dev->params.int_pin));
+
+ mutex_t lock = MUTEX_INIT_LOCKED;
+
+ /* enable interrupt pin */
+ gpio_init_int(dev->params.int_pin, GPIO_IN_PU, GPIO_FALLING, _isr, &lock);
+
+ /* wait for interrupt */
+ mutex_lock(&lock);
+
+ /* disable interrupt pin */
+ gpio_irq_disable(dev->params.int_pin);
+
+ /* read interrupt sources */
+ uint8_t byte;
+ int res;
+
+ _acquire(dev);
+ res = _read(dev, VL6180X_REG_INT_STATUS, &byte, 1);
+#if IS_USED(MODULE_VL6180X_RNG)
+ src->rng_int = (byte & VL6180X_INT_RNG) >> VL6180X_INT_RNG_S;
+#endif
+#if IS_USED(MODULE_VL6180X_ALS)
+ src->als_int = (byte & VL6180X_INT_ALS) >> VL6180X_INT_ALS_S;
+#endif
+
+ /* clear all interrupt flags */
+ res |= _write_byte(dev, VL6180X_REG_INT_CLR, VL6180X_CLR_ALL_INT);
+ _release(dev);
+
+ return res;
+}
+
+#if IS_USED(MODULE_VL6180X_CONFIG)
+int vl6180x_int_enable(vl6180x_t *dev, vl6180x_int_config_t mode)
+{
+ ASSERT_PARAM(dev != NULL);
+ DEBUG_DEV("", dev);
+
+ dev->params.int_cfg = mode;
+
+ uint8_t byte = 0;
+#if IS_USED(MODULE_VL6180X_RNG)
+ byte |= (mode.rng_int << VL6180X_INT_RNG_S) & VL6180X_INT_RNG;
+#endif
+#if IS_USED(MODULE_VL6180X_ALS)
+ byte |= (mode.als_int << VL6180X_INT_ALS_S) & VL6180X_INT_ALS;
+#endif
+
+ int res;
+ _acquire(dev);
+ res = _write_byte(dev, VL6180X_REG_INT_CONFIG, byte);
+ res |= _write_byte(dev, VL6180X_REG_GPIO1_MODE,
+ VL6180X_GPIO1_POL_LOW | VL6180X_GPIO1_FUNC_ON);
+ res |= _write_byte(dev, VL6180X_REG_INT_CLR, VL6180X_CLR_ALL_INT);
+ _release(dev);
+
+ return res;
+}
+
+int vl6180x_int_config(vl6180x_t *dev, vl6180x_int_thresh_t thresh)
+{
+ ASSERT_PARAM(dev != NULL);
+ DEBUG_DEV("", dev);
+
+ dev->params.int_thresh = thresh;
+
+ int res = VL6180X_OK;
+
+ _acquire(dev);
+#if IS_USED(MODULE_VL6180X_RNG)
+ res |= _write_byte(dev, VL6180X_REG_RNG_THRESH_HI, thresh.rng_high);
+ res |= _write_byte(dev, VL6180X_REG_RNG_THRESH_LO, thresh.rng_low);
+#endif
+
+#if IS_USED(MODULE_VL6180X_ALS)
+ res |= _write_word(dev, VL6180X_REG_ALS_THRESH_HI, thresh.als_high);
+ res |= _write_word(dev, VL6180X_REG_ALS_THRESH_LO, thresh.als_low);
+#endif
+ _release(dev);
+
+ return res;
+}
+#endif /* IS_USED(MODULE_VL6180X_CONFIG) */
+
+#endif /* IS_USED(MODULE_VL6180X_IRQ) */
+
+int vl6180x_reg_read(const vl6180x_t *dev,
+ uint16_t reg, uint8_t *pdata, uint8_t len)
+{
+ ASSERT_PARAM(dev != NULL);
+ DEBUG_DEV("reg=%04x pdata=%p len=%u", dev, reg, pdata, len);
+
+ int res;
+
+ _acquire(dev);
+ res = _read(dev, reg, pdata, len);
+ _release(dev);
+
+ return res;
+}
+
+int vl6180x_reg_write(const vl6180x_t *dev,
+ uint16_t reg, const uint8_t *pdata, uint8_t len)
+{
+ ASSERT_PARAM(dev != NULL);
+ DEBUG_DEV("reg=%04x pdata=%p len=%u", dev, reg, pdata, len);
+
+ int res;
+
+ _acquire(dev);
+ res = _write(dev, reg, pdata, len);
+ _release(dev);
+
+ return res;
+}
+
+/** Functions for internal use only */
+
+/**
+ * @brief Check the chip ID to test whether sensor is available
+ */
+static int _is_available(const vl6180x_t *dev)
+{
+ DEBUG_DEV("", dev);
+
+ uint8_t data[5];
+
+ /* read the chip id from VL6180X_REG_ID_X */
+ if (_read(dev, VL6180X_REG_MODEL_ID, data, 5) != VL6180X_OK) {
+ return VL6180X_ERROR_I2C;
+ }
+
+ if (data[0] != VL6180X_MODEL_ID) {
+ DEBUG_DEV("sensor is not available, wrong device id %02x, "
+ "should be %02x", dev, data[0], VL6180X_MODEL_ID);
+ return -VL6180X_ERROR_WRONG_ID;
+ }
+
+ DEBUG_DEV("rev=%u.%u, module rev=%d.%d",
+ dev, data[1], data[2], data[3], data[4]);
+
+ return VL6180X_OK;
+}
+
+static int _init(vl6180x_t *dev)
+{
+#if IS_USED(MODULE_VL6180X_RNG)
+ dev->rng_status = VL6180X_RNG_OK;
+#endif /* IS_USED(MODULE_VL6180X_RNG) */
+#if IS_USED(MODULE_VL6180X_ALS)
+ dev->als_status = VL6180X_ALS_OK;
+#endif /* IS_USED(MODULE_VL6180X_ALS) */
+
+ dev->cont_meas = false;
+
+ /* we have to wait at least 400 us after power on, with msec timer 1 ms */
+ ztimer_sleep(ZTIMER_MSEC, 1);
+
+ int res;
+
+ _acquire(dev);
+
+ /* check availability of the sensor */
+ res = _is_available(dev);
+ if (res != VL6180X_OK) {
+ _release(dev);
+ return res;
+ }
+
+ /* reset the device to recommended configuration */
+ res = _reset(dev);
+
+ /* clear all interrupt flags */
+ res |= _write_byte(dev, VL6180X_REG_INT_CLR, VL6180X_CLR_ALL_INT);
+
+ /* set range measurement configuration */
+ uint8_t rng_int = 0;
+
+#if IS_USED(MODULE_VL6180X_RNG)
+#if IS_USED(MODULE_VL6180X_IRQ)
+ res |= _write_byte(dev, VL6180X_REG_RNG_THRESH_HI, dev->params.int_thresh.rng_high);
+ res |= _write_byte(dev, VL6180X_REG_RNG_THRESH_LO, dev->params.int_thresh.rng_low);
+ rng_int = (dev->params.int_cfg.rng_int << VL6180X_INT_RNG_S) & VL6180X_INT_RNG;
+#else
+ rng_int = VL6180X_INT_RNG_DRDY;
+#endif /* IS_USED(MODULE_VL6180X_IRQ) */
+ res |= _write_byte(dev, VL6180X_REG_RNG_PERIOD, dev->params.period - 1);
+ res |= _write_byte(dev, VL6180X_REG_RNG_MAX_TIME, dev->params.rng_max_time);
+#endif /* IS_USED(MODULE_VL6180X_RNG) */
+
+ /* set ALS measurement configuration */
+ uint8_t als_int = 0;
+
+#if IS_USED(MODULE_VL6180X_ALS)
+#if IS_USED(MODULE_VL6180X_IRQ)
+ res |= _write_byte(dev, VL6180X_REG_ALS_THRESH_HI, dev->params.int_thresh.als_high);
+ res |= _write_byte(dev, VL6180X_REG_ALS_THRESH_LO, dev->params.int_thresh.als_low);
+ als_int = (dev->params.int_cfg.als_int << VL6180X_INT_ALS_S) & VL6180X_INT_ALS;
+#else /* IS_USED(MODULE_VL6180X_IRQ) */
+ als_int = VL6180X_INT_ALS_DRDY;
+#endif /* IS_USED(MODULE_VL6180X_IRQ) */
+ res |= _write_byte(dev, VL6180X_REG_ALS_PERIOD, dev->params.period - 1);
+ res |= _write_byte(dev, VL6180X_REG_ALS_INT_TIME, dev->params.als_int_time - 1);
+ res |= _write_byte(dev, VL6180X_REG_ALS_GAIN, 0x40 + dev->params.als_gain);
+#endif /* IS_USED(MODULE_VL6180X_ALS) */
+
+#if IS_USED(MODULE_VL6180X_IRQ)
+ res |= _write_byte(dev, VL6180X_REG_GPIO1_MODE,
+ VL6180X_GPIO1_POL_LOW | VL6180X_GPIO1_FUNC_ON);
+#endif
+ /* cppcheck-suppress knownConditionTrueFalse
+ * (reason: it is not a condition but a bitwise OR) */
+ res |= _write_byte(dev, VL6180X_REG_INT_CONFIG, rng_int | als_int);
+ _release(dev);
+
+ if (res != VL6180X_OK) {
+ return res;
+ }
+
+ /* if period is defined, continuous mode measurements are started */
+ if (dev->params.period) {
+ return vl6180x_start_cont(dev);
+ }
+
+ return VL6180X_OK;
+}
+
+typedef struct {
+ uint16_t reg;
+ uint8_t value;
+} _vl6180x_reg_value_t;
+
+/*
+ * Private registers and standard register initialization values that have to
+ * be set after power on reset, see [Application Note AN4545, section 9]
+ * (https://www.st.com/resource/en/application_note/dm00122600.pdf)
+ */
+static const _vl6180x_reg_value_t _init_values [] = {
+ /* Mandatory : private registers */
+ { .reg = 0x0207, .value = 0x01 },
+ { .reg = 0x0208, .value = 0x01 },
+ { .reg = 0x0096, .value = 0x00 },
+ { .reg = 0x0097, .value = 0xfd },
+ { .reg = 0x00e3, .value = 0x01 },
+ { .reg = 0x00e4, .value = 0x03 },
+ { .reg = 0x00e5, .value = 0x02 },
+ { .reg = 0x00e6, .value = 0x01 },
+ { .reg = 0x00e7, .value = 0x03 },
+ { .reg = 0x00f5, .value = 0x02 },
+ { .reg = 0x00d9, .value = 0x05 },
+ { .reg = 0x00db, .value = 0xce },
+ { .reg = 0x00dc, .value = 0x03 },
+ { .reg = 0x00dd, .value = 0xf8 },
+ { .reg = 0x009f, .value = 0x00 },
+ { .reg = 0x00a3, .value = 0x3c },
+ { .reg = 0x00b7, .value = 0x00 },
+ { .reg = 0x00bb, .value = 0x3c },
+ { .reg = 0x00b2, .value = 0x09 },
+ { .reg = 0x00ca, .value = 0x09 },
+ { .reg = 0x0198, .value = 0x01 },
+ { .reg = 0x01b0, .value = 0x17 },
+ { .reg = 0x01ad, .value = 0x00 },
+ { .reg = 0x00ff, .value = 0x05 },
+ { .reg = 0x0100, .value = 0x05 },
+ { .reg = 0x0199, .value = 0x05 },
+ { .reg = 0x01a6, .value = 0x1b },
+ { .reg = 0x01ac, .value = 0x3e },
+ { .reg = 0x01a7, .value = 0x1f },
+ { .reg = 0x0030, .value = 0x00 },
+
+ /* Recommended : Public registers - See data sheet for more detail */
+ { .reg = 0x0011, .value = 0x10 }, /* GPIO1 is HIGH active and OFF (Hi-Z), polling enabled */
+ { .reg = 0x010a, .value = 0x30 }, /* Range averaging period is 4.3 ms*/
+ { .reg = 0x003f, .value = 0x46 }, /* ALS analogue gain is 1.01, dark gain upper nibble */
+ { .reg = 0x0031, .value = 0xff }, /* Calibration every 255 measurements */
+ { .reg = 0x0041, .value = 0x63 }, /* ALS integration time is 100 ms */
+ { .reg = 0x002e, .value = 0x01 }, /* Perform temperature calibration */
+
+ /* Optional: Public registers - See data sheet for more detail */
+ { .reg = 0x001b, .value = 0x13 }, /* Range measurement period is 200 ms */
+ { .reg = 0x003e, .value = 0x13 }, /* ALS measurement period is 200 ms */
+ { .reg = 0x0014, .value = 0x00 }, /* Interrupts disabled */
+ { .reg = 0x02a3, .value = 0x00 }, /* Interleave mode disabled */
+
+ /* change fresh out of status to 0 */
+ { .reg = 0x0016, .value = 0x00 },
+};
+
+static int _reset(const vl6180x_t *dev)
+{
+ uint8_t byte;
+ int res = VL6180X_OK;
+
+ /* read the fresh out from reset status */
+ res |= _read(dev, VL6180X_REG_FRESH_RST, &byte, 1);
+
+ /* if not fresh out from reset, stop measurements if necessary */
+ if ((byte & 0x01) == 0) {
+ uint8_t status;
+ res |= _read(dev, VL6180X_REG_RNG_STATUS, &status, 1);
+ if ((status & VL6180X_RNG_DEVICE_RDY) == 0) {
+ /* stop measurement when device is not ready */
+ res |= _write_byte(dev, VL6180X_REG_RNG_START, VL6180X_RNG_START_STOP);
+ }
+ }
+ /* load initial register value set */
+ for (unsigned i = 0; i < ARRAY_SIZE(_init_values); i++) {
+ res |= _write_byte(dev, _init_values[i].reg, _init_values[i].value);
+ }
+
+ return res;
+}
+
+inline static void _acquire(const vl6180x_t *dev)
+{
+ i2c_acquire(dev->params.i2c_dev);
+}
+
+inline static void _release(const vl6180x_t *dev)
+{
+ i2c_release(dev->params.i2c_dev);
+}
+
+#define VL6180X_BUFSIZ 32
+static uint8_t _buffer[VL6180X_BUFSIZ] = {};
+
+static int _write(const vl6180x_t *dev,
+ uint16_t reg, const uint8_t *pdata, uint8_t len)
+{
+ ASSERT_PARAM(dev != NULL);
+ ASSERT_PARAM(pdata != NULL);
+ ASSERT_PARAM(len <= (VL6180X_BUFSIZ - 2));
+ DEBUG_DEV("reg=%04x pdata=%p len=%u", dev, reg, pdata, len);
+
+#if ENABLE_DEBUG
+ printf("[vl6180x] %s i2c dev=%d addr=%02x: ",
+ __func__, dev->params.i2c_dev, dev->params.i2c_addr);
+ for (uint8_t i = 0; i < len; i++) {
+ printf("%02x ", pdata[i]);
+ }
+ printf("\n");
+#endif /* ENABLE_DEBUG */
+
+ /* fill the first 2 bytes of the buffer with the reg */
+ _buffer[0] = (reg >> 8) & 0xff;
+ _buffer[1] = reg & 0xff;
+
+ /* fill the buffer with data bytes */
+ memcpy(_buffer + 2, pdata, len);
+
+ int res = i2c_write_bytes(dev->params.i2c_dev,
+ dev->params.i2c_addr, _buffer, len + 2, 0);
+ if (res) {
+ DEBUG_DEV("I2C communication error: %d", dev, res);
+ return -VL6180X_ERROR_I2C;
+ }
+
+ return VL6180X_OK;
+}
+
+inline static int _write_byte(const vl6180x_t *dev, uint16_t reg, uint8_t byte)
+{
+ return _write(dev, reg, &byte, 1);
+}
+
+inline static int _write_word(const vl6180x_t *dev, uint16_t reg, uint16_t data)
+{
+ uint8_t bytes[2] = { (data >> 8) & 0xff, data & 0xff };
+ return _write(dev, reg, (uint8_t*)&bytes, 2);
+}
+
+static int _read(const vl6180x_t *dev,
+ uint16_t reg, uint8_t *pdata, uint8_t len)
+{
+ ASSERT_PARAM(dev != NULL);
+ ASSERT_PARAM(pdata != NULL);
+ ASSERT_PARAM(len <= VL6180X_BUFSIZ);
+ DEBUG_DEV("reg=%04x pdata=%p len=%u", dev, reg, pdata, len);
+
+ uint8_t bytes[2] = { (reg >> 8) & 0xff, reg & 0xff };
+
+ /* write the register reg and if successful, read the data */
+ if (i2c_write_bytes(dev->params.i2c_dev, dev->params.i2c_addr, bytes, 2, 0) ||
+ i2c_read_bytes(dev->params.i2c_dev, dev->params.i2c_addr, pdata, len, 0)) {
+ DEBUG_DEV("I2C communication error", dev);
+ return -VL6180X_ERROR_I2C;
+ }
+
+#if ENABLE_DEBUG
+ printf("[vl6180x] %s i2c dev=%d addr=%02x: ",
+ __func__, dev->params.i2c_dev, dev->params.i2c_addr);
+ for (uint8_t i = 0; i < len; i++) {
+ printf("%02x ", pdata[i]);
+ }
+ printf("\n");
+#endif /* ENABLE_DEBUG */
+
+ return VL6180X_OK;
+}
+
+inline static int _read_word(const vl6180x_t *dev, uint16_t reg, uint16_t *word)
+{
+ uint8_t bytes[2];
+ int res = _read(dev, reg, bytes, 2);
+ *word = (bytes[0] << 8) + (bytes[1]);
+ return res;
+}
+
+inline static int _read_word_x(const vl6180x_t *dev, uint16_t reg, uint16_t *word)
+{
+ uint8_t bytes[2];
+ int res = vl6180x_reg_read(dev, reg, bytes, 2);
+ *word = (bytes[0] << 8) + (bytes[1]);
+ return res;
+}
+
+inline static int _write_byte_x(const vl6180x_t *dev, uint16_t reg, uint8_t byte)
+{
+ return vl6180x_reg_write(dev, reg, &byte, 1);
+}
+
+inline static int _write_word_x(const vl6180x_t *dev, uint16_t reg, uint16_t data)
+{
+ uint8_t bytes[2] = { (data >> 8) & 0xff, data & 0xff };
+ return vl6180x_reg_write(dev, reg, (uint8_t*)&bytes, 2);
+}