1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
10462: drivers: add driver for ST VL6180X ranging and ambient light sensor r=benpicco a=gschorcht

### Contribution description

This PR adds a driver for the ST VL6180X ranging and ambient light sensor. The driver can be used in two variants which differ in functionality and size:

Module Name   | Driver
:-------------|:-------------------------------------------
vl6180x       | Driver with standard functionality and larger size
vl6180x_basic | Driver with only basic functionality and smaller size

Both driver variants provide

- continuous ranging and ambient light sensing,
- polling for new measurement results, and
- SAUL capabilies.

In addition to these basic functionalities, the standard driver `vl6180x`
 provides interrupt handling, single shot measurements, a number of
 configuration functions and power down / power up functionality.
 Features of the driver variants are summarized in the following comparison
 sheet. 

Feature                                 | vl6180x_basic | vl6180x
:---------------------------------------|:-------------:|:--------:
Range measurements [mm]                 | X             | X
Ambient light measurements [Lux, raw]   | X             | X
SAUL capability                         | X             | X
Continuous measurements                 | X             | X
Single shot measurements                |               | X
Start and stop measurements             |               | X
Polling for new measurement results     | X             | X
Data-ready interrupts                   |               | X
Threshold interrupts                    |               | X
Measurement error status                |               | X
Configuration during run-time           |               | X
Power down and power up                 |               | X
Size on reference platform              | 1.7 kByte     | 3.0 kByte

### Testing procedure

Using the make command
```
make flash -C tests/driver_vl6180x BOARD=...
```
the standard driver variant `vl6180x` is used with the following default
configuration parameters:

- periodic check of the availability of new data every 100 ms
- a ranging inter-measurement period of 100 ms
- a ranging maximum convergence time of 50 ms
- an ambient light sensing (ALS) inter-measurement period of 500 ms
- an ambient light sensing (ALS) integration time of 100 ms
- an ambient light sensing (ALS) analogue light channel gain of 1.0
- an ambient light sensing period of 500 ms
- a ranging period of 100 ms.

To use the basic driver variant, the `vl6180x_basic` module has
to be specified at make command line
```
USEMODULE=vl6180x_basic make flash -C tests/driver_vl6180x BOARD=...
```
The default configuration parameters used for the test application with the
basic driver variant are the same as for the standard driver variant.

To use interrupts to fetch new data instead of polling for new data
periodically, the standard driver variant `vl6180x` has to be used and the
pin connected with the interrupt signal GPIO1 of the sensor has to be
defined by configuration paramater `VL6180X_PARAM_PIN_INT`, e.g.,
```
CFLAGS="-DVL6180X_PARAM_PIN_INT=\(GPIO_PIN\(0,1\)\)" make flash -C tests/driver_vl6180x BOARD=...
```
To test the power down and power up functionality, the pin connected with
the signal GPIO0/CE of the sensor has to be defined by configuration
paramater `VL6180X_PARAM_PIN_SHUTDOWN
`, e.g.,
```
CFLAGS="-DVL6180X_PARAM_PIN_SHUTDOWN=\(GPIO_PIN\(0,2\)\)" make flash -C tests/driver_vl6180x BOARD=...
```


Co-authored-by: Gunar Schorcht <gunar@schorcht.net>
This commit is contained in:
bors[bot] 2023-02-02 09:23:45 +00:00 committed by GitHub
commit b0fc3e5814
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 3121 additions and 0 deletions

View File

@ -149,6 +149,7 @@ rsource "tsl2561/Kconfig"
rsource "tsl4531x/Kconfig"
rsource "vcnl40x0/Kconfig"
rsource "veml6070/Kconfig"
rsource "vl6180x/Kconfig"
endmenu # Sensor Device Drivers
menu "Storage Device Drivers"

View File

@ -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

1076
drivers/include/vl6180x.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2018 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
* @ingroup sys_auto_init_saul
* @brief Auto initialization of ST VL6180X Ranging and Ambient Light Sensor
* @author Gunar Schorcht <gunar@schorcht.net>
* @file
*/
#ifdef MODULE_VL6180X
#include "assert.h"
#include "log.h"
#include "saul_reg.h"
#include "vl6180x.h"
#include "vl6180x_params.h"
/**
* @brief Define the number of configured sensors
*/
#define VL6180X_NUM ARRAY_SIZE(vl6180x_params)
/**
* @brief Allocate memory for the device descriptors
*/
static vl6180x_t vl6180x_devs[VL6180X_NUM];
/**
* @brief Memory for the SAUL registry entries
*/
static saul_reg_t saul_entries[VL6180X_NUM * 2];
/**
* @brief Define the number of saul info
*/
#define VL6180X_INFO_NUM ARRAY_SIZE(vl6180x_saul_info)
/**
* @brief Reference the driver structs
*/
extern saul_driver_t vl6180x_saul_als_driver;
extern saul_driver_t vl6180x_saul_rng_driver;
void auto_init_vl6180x(void)
{
assert(VL6180X_INFO_NUM == VL6180X_NUM);
for (unsigned i = 0; i < VL6180X_NUM; i++) {
LOG_DEBUG("[auto_init_saul] initializing vl6180x #%u\n", i);
if (vl6180x_init(&vl6180x_devs[i], &vl6180x_params[i]) != VL6180X_OK) {
LOG_ERROR("[auto_init_saul] error initializing vl6180x #%u\n", i);
continue;
}
saul_entries[(i * 2)].dev = &(vl6180x_devs[i]);
saul_entries[(i * 2)].name = vl6180x_saul_info[i].name;
saul_entries[(i * 2)].driver = &vl6180x_saul_als_driver;
saul_reg_add(&(saul_entries[(i * 2)]));
saul_entries[(i * 2) + 1].dev = &(vl6180x_devs[i]);
saul_entries[(i * 2) + 1].name = vl6180x_saul_info[i].name;
saul_entries[(i * 2) + 1].driver = &vl6180x_saul_rng_driver;
saul_reg_add(&(saul_entries[(i * 2) + 1]));
}
}
#else
typedef int dont_be_pedantic;
#endif /* MODULE_VL6180X */

View File

@ -335,4 +335,8 @@ void saul_init_devs(void)
extern void auto_init_veml6070(void);
auto_init_veml6070();
}
if (IS_USED(MODULE_VL6180X)) {
extern void auto_init_vl6180x(void);
auto_init_vl6180x();
}
}

211
drivers/vl6180x/Kconfig Normal file
View File

@ -0,0 +1,211 @@
# 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.
#
menuconfig MODULE_VL6180X
bool "VL6180X Ranging and Ambient Light Sensing (ALS) module"
depends on TEST_KCONFIG
depends on HAS_PERIPH_I2C
select MODULE_PERIPH_I2C
select MODULE_ZTIMER_MSEC
help
Driver for the ST VL6180X Ranging and Ambient Light Sensing (ALS) module
if MODULE_VL6180X
config MODULE_VL6180X_RNG
bool "Ranging enabled"
config VL6180X_RNG_MAX_TIME
int "Ranging maximum convergence time [ms]"
range 1 63
default 50
depends on MODULE_VL6180X_RNG
config MODULE_VL6180X_ALS
bool "Ambient light sensing (ALS) enabled"
if MODULE_VL6180X_ALS
config VL6180X_ALS_INT_TIME
int "ALS integration time [ms]"
range 0 511
default 100
choice VL6180X_ALS_GAIN
bool "ALS analogue light channel gain"
default VL6180X_ALS_GAIN_1
config VL6180X_ALS_GAIN_1
bool "ALS gain setting 1 (actual analogue gain of 1.01)"
config VL6180X_ALS_GAIN_1_25
bool "ALS gain setting 1.25 (actual analogue gain of 1.28)"
config VL6180X_ALS_GAIN_1_67
bool "ALS gain setting 1.67 (actual analogue gain of 1.72)"
config VL6180X_ALS_GAIN_2_5
bool "ALS gain setting 2.5 (actual analogue gain of 2.60)"
config VL6180X_ALS_GAIN_5
bool "ALS gain setting 5 (actual analogue gain of 5.21)"
config VL6180X_ALS_GAIN_10
bool "ALS gain setting 10 (actual analogue gain of 10.32)"
config VL6180X_ALS_GAIN_20
bool "ALS gain setting 20 (actual analogue gain of 20)"
config VL6180X_ALS_GAIN_40
bool "ALS gain setting 40 (actual analogue gain of 40)"
endchoice
config VL6180X_ALS_LUX_RES
int "ALS lux resolution [lux/count*1000]"
range 1 320
default 320
help
ALS lux resolution is used to convert count values from ambient
light sensing to lux values. It is specified as lux/count*1000.
The factory calibrated lux resolution is 0.32 lux/count.
endif # MODULE_VL6180X_ALS
config VL6180X_MEAS_PERIOD
int "Measurement period in continuous mode [10 ms]"
range 0 255
default 20
help
The measurement period is defined as multiple of 10 ms. It is used for
both range measurements and ambient light sensing (ALS) in
continuous mode.
The measurement period also controls the measurement mode used after
sensor initialization. If the measurement period is 0, the single-shot
mode is used for both the range and ALS measurements, and functions
vl6180x_rng_start_single and vl6180x_als_start_single have to be used
to start a single measurement. Otherwise the continuous mode is
used for both measurements which are started immediatly after sensor
initialization.
config MODULE_VL6180X_IRQ
bool "Support for interrupts"
depends on HAS_PERIPH_GPIO_IRQ
select MODULE_PERIPH_GPIO_IRQ
help
Interrupts can be used either when new sensor data are ready to be
read or when sensor values exceed configured thresholds.
If interrupt handling is enabled, the interrupt signal
(sensor pin GPIO1) has to be connected to a MCU GPIO pin which
has to be defined in the board definition by VL6180X_PARAM_INT_PIN.
if MODULE_VL6180X_IRQ
choice
bool "Ranging interrupt mode"
depends on MODULE_VL6180X_RNG
default VL6180X_RNG_INT_DRDY
help
Interrupt defines the interrupt that is enabled for ranging.
config VL6180X_RNG_INT_DRDY
bool "Data Ready"
help
Interrupt is triggered when new ranging data are ready to be read
config VL6180X_RNG_INT_LOW
bool "Level Low"
help
Interrupt is triggered when ranging values are below lower threshold
config VL6180X_RNG_INT_HIGH
bool "Level High"
help
Interrupt is triggered when ranging values are above upper threshold
config VL6180X_RNG_INT_OUT
bool "Out of Window"
help
Interrupt is triggered when ranging values are below the lower
threshold or above the upper threshold (out of threshold window)
endchoice
config VL6180X_RNG_THRESH_LOW
int "Ranging lower threshold [mm]"
depends on VL6180X_RNG_INT_LOW || VL6180X_RNG_INT_OUT
range 0 255
default 20
help
Interrupt is triggered when ranging values are below this threshold
config VL6180X_RNG_THRESH_HIGH
int "Ranging upper threshold [mm]"
depends on VL6180X_RNG_INT_HIGH || VL6180X_RNG_INT_OUT
range 0 255
default 90
help
Interrupt is triggered when ranging values are above this threshold
choice
bool "ALS interrupt mode"
depends on MODULE_VL6180X_ALS
default VL6180X_ALS_INT_DRDY
help
Interrupt defines the interrupt that is enabled for ALS.
config VL6180X_ALS_INT_DRDY
bool "Data Ready"
help
Interrupt is triggered when new ALS data are ready to be read
config VL6180X_ALS_INT_LOW
bool "Level Low"
help
Interrupt is triggered when ALS values are below lower threshold
config VL6180X_ALS_INT_HIGH
bool "Level High"
help
Interrupt is triggered when ALS values are above upper threshold
config VL6180X_ALS_INT_OUT
bool "Out of Window"
help
Interrupt is triggered when ALS values are below the lower
threshold or above the upper threshold (out of threshold window)
endchoice
config VL6180X_ALS_THRESH_LOW
int "ALS lower threshold [counts]"
depends on VL6180X_ALS_INT_LOW || VL6180X_ALS_INT_OUT
range 0 65535
default 50
help
Interrupt is triggered when ALS values are below this threshold
config VL6180X_ALS_THRESH_HIGH
int "ALS upper threshold [counts]"
depends on VL6180X_ALS_INT_HIGH || VL6180X_ALS_INT_OUT
range 0 65535
default 2000
help
Interrupt is triggered when ALS values are above this threshold
endif # MODULE_VL6180X_IRQ
config MODULE_VL6180X_SHUTDOWN
bool "Support for power-down and power-up"
depends on HAS_PERIPH_GPIO
select MODULE_PERIPH_GPIO
help
Enable the power-down and power-up functions. If power-down and
power-up functions are enabled by module, the shutdown signal
(sensor pin GPIO0/CE) has to be connected to a MCU GPIO pin which
has to be defined in the board definition by VL6180X_PARAM_SHUTDOWN_PIN.
config MODULE_VL6180X_CONFIG
bool "Configuration of the sensor at runtime"
help
Enables the functions that can be used to reconfigure the sensor at
runtime.
endif # MODULE_VL6180X

1
drivers/vl6180x/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View File

@ -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

View File

@ -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)

View File

@ -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 <gunar@schorcht.net>
* @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 */
/** @} */

View File

@ -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 <gunar@schorcht.net>
* @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 */
/** @} */

920
drivers/vl6180x/vl6180x.c Normal file
View File

@ -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 <gunar@schorcht.net>
* @file
* @{
*/
#include <string.h>
#include <stdlib.h>
#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);
}

View File

@ -0,0 +1,64 @@
/*
* 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 VL6180X adaption to the RIOT actuator/sensor interface
* @author Gunar Schorcht <gunar@schorcht.net>
* @file
*/
#include <string.h>
#include "saul.h"
#include "vl6180x.h"
#if IS_USED(MODULE_VL6180X_ALS)
static int read_als(const void *dev, phydat_t *res)
{
if (vl6180x_als_data_ready((vl6180x_t*)dev) == VL6180X_OK &&
vl6180x_als_read((vl6180x_t*)dev, NULL,
(uint16_t*)&res->val[0]) == VL6180X_OK) {
res->unit = UNIT_LUX;
res->scale = 0;
return 1;
}
return -ECANCELED;
}
const saul_driver_t vl6180x_saul_als_driver = {
.read = read_als,
.write = saul_write_notsup,
.type = SAUL_SENSE_LIGHT,
};
#endif /* IS_USED(MODULE_VL6180X_ALS) */
#if IS_USED(MODULE_VL6180X_RNG)
static int read_rng(const void *dev, phydat_t *res)
{
uint8_t mm;
if (vl6180x_rng_data_ready((vl6180x_t*)dev) == VL6180X_OK) {
vl6180x_rng_read((vl6180x_t*)dev, &mm);
res->val[0] = mm;
res->unit = UNIT_M;
res->scale = -3;
return 1;
}
return -ECANCELED;
}
const saul_driver_t vl6180x_saul_rng_driver = {
.read = read_rng,
.write = saul_write_notsup,
.type = SAUL_SENSE_DISTANCE,
};
#endif /* IS_USED(MODULE_VL6180X_RNG) */

View File

@ -0,0 +1,7 @@
include ../Makefile.tests_common
USEMODULE += vl6180x_rng
USEMODULE += vl6180x_als
USEMODULE += ztimer_sec
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,55 @@
# Test application for ST VL6180X Ranging and Ambient Light Sensing (ALS) module
## About
The test application demonstrates the use of different functions of
the ST VL6180X sensor driver depending on used pseudomodules:
Module Name | Functionality used
:------------------|:-------------------------------------------
`vl6180x_rng` | Periodic range measurements enabled
`vl6180x_als` | Periodic ambient light sensing (ALS) enabled
`vl6180x_irq` | Data ready interrupt enabled for range and ALS measurements
`vl6180x_config` | Event interrupt enabled and configured for range measurements
`vl6180x_shutdown` | Power-down and power-up functions used
## Usage
To compile and execute the test application, use command:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
make BOARD=... -C tests/driver_vl6180x flash term
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By default, the `vl6180x_rng` and `vl6180x_als` modules are enabled.
This is, the standard variant of the test application performs periodic
range and ALS measurements.
To use data ready interrupts for range and ALS measurements instead of
polling for new data, the `vl6180x_irq` module has to be used:
The MCU GPIO pin connected with the interrupt signal GPIO1 of the sensor
has to be defined by the configuration parameter `VL6180X_PARAM_PIN_INT`,
for example:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CFLAGS="-DVL6180X_PARAM_INT_PIN=\(GPIO_PIN\(0,5\)\)" \
USEMODULE=vl6180x_irq \
make BOARD=... -C tests/driver_vl6180x flash term
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the module `vl6180x_config` is used additionally, the event interrupt
is configured and used for range measurements:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CFLAGS="-DVL6180X_PARAM_INT_PIN=\(GPIO_PIN\(0,5\)\)" \
USEMODULE='vl6180x_irq vl6180x_config'\
make BOARD=... -C tests/driver_vl6180x flash term
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To test the power-down and power-up functionality, the `vl6180x_shutdown`
module has to be used. The MCU GPIO pin connected with the signal GPIO0/CE
of the sensor has to be defined by configuration parameter
`VL6180X_PARAM_PIN_SHUTDOWN`, for example:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CFLAGS="-VL6180X_PARAM_SHUTDOWN_PIN=\(GPIO_PIN\(0,6\)\)" \
USEMODULE=vl6180x_shutdown \
make BOARD=... -C tests/driver_vl6180x flash term
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,6 @@
# this file enables modules defined in Kconfig. Do not use this file for
# application configuration. This is only needed during migration.
CONFIG_MODULE_VL6180X=y
CONFIG_MODULE_VL6180X_RNG=y
CONFIG_MODULE_VL6180X_ALS=y
CONFIG_MODULE_ZTIMER_SEC=y

264
tests/driver_vl6180x/main.c Normal file
View File

@ -0,0 +1,264 @@
/*
* 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 tests
* @brief Test application for ST VL6180X Ranging and Ambient Light Sensing (ALS) module
* @author Gunar Schorcht <gunar@schorcht.net>
* @file
*
* ## About
*
* The test application demonstrates the use of different functions of
* the ST VL6180X sensor driver depending on used pseudomodules:
*
* Module Name | Functionality used
* :------------------|:-------------------------------------------
* `vl6180x_rng` | Periodic range measurements enabled
* `vl6180x_als` | Periodic ambient light sensing (ALS) enabled
* `vl6180x_irq` | Data ready interrupt enabled for range and ALS measurements
* `vl6180x_config` | Event interrupt enabled and configured for range measurements
* `vl6180x_shutdown` | Power-down and power-up functions used
*
* ## Usage
*
* To compile and execute the test application, use command:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* make BOARD=... -C tests/driver_vl6180x flash term
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* By default, the `vl6180x_rng` and `vl6180x_als` modules are enabled.
* This is, the standard variant of the test application performs periodic
* range and ALS measurements.
*
* To use data ready interrupts for range and ALS measurements instead of
* polling for new data, the `vl6180x_irq` module has to be used:
*
* The MCU GPIO pin connected with the interrupt signal GPIO1 of the sensor
* has to be defined by the configuration parameter `VL6180X_PARAM_PIN_INT`,
* for example:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* CFLAGS="-DVL6180X_PARAM_INT_PIN=\(GPIO_PIN\(0,5\)\)" \
* USEMODULE=vl6180x_irq \
* make BOARD=... -C tests/driver_vl6180x flash term
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* If the module `vl6180x_config` is used additionally, the event interrupt
* is configured and used for range measurements:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* CFLAGS="-DVL6180X_PARAM_INT_PIN=\(GPIO_PIN\(0,5\)\)" \
* USEMODULE='vl6180x_irq vl6180x_config'\
* make BOARD=... -C tests/driver_vl6180x flash term
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* To test the power-down and power-up functionality, the `vl6180x_shutdown`
* module has to be used. The MCU GPIO pin connected with the signal GPIO0/CE
* of the sensor has to be defined by configuration parameter
* `VL6180X_PARAM_PIN_SHUTDOWN`, for example:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* CFLAGS="-VL6180X_PARAM_SHUTDOWN_PIN=\(GPIO_PIN\(0,6\)\)" \
* USEMODULE=vl6180x_shutdown \
* make BOARD=... -C tests/driver_vl6180x flash term
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include "thread.h"
#include "ztimer.h"
#include "vl6180x.h"
#include "vl6180x_params.h"
#if IS_USED(MODULE_VL6180X_IRQ)
#if IS_USED(MODULE_VL6180X_RNG)
static const char *rng_errors[] = {
"No error", /**< VL6180X_RNG_OK */
"VCSEL continuity Test", /**< VL6180X_RNG_VCSEL_CONT_TEST */
"VCSEL watchdog test", /**< VL6180X_RNG_VCSEL_WD_TEST */
"VCSEL watchdog", /**< VL6180X_RNG_VCSEL_WD */
"PLL1 lock", /**< VL6180X_RNG_PLL1_LOCK */
"PLL2 lock", /**< VL6180X_RNG_PLL2_LOCK */
"Early convergence estimate", /**< VL6180X_RNG_EARLY_CONV_EST */
"Maximum convergence time reached", /**< VL6180X_RNG_MAX_CONV */
"No target, ignore", /**< VL6180X_RNG_NO_TARGET */
"Unknown",
"Unknown",
"Maximum SNR reached", /**< VL6180X_RNG_MAX_SNR */
"Raw ranging algorithm underflow", /**< VL6180X_RNG_RAW_ALGO_UNDERFLOW */
"Raw ranging algorithn overflow", /**< VL6180X_RNG_RAW_ALGO_OVERFLOW */
"Ranging algorithm underflow", /**< VL6180X_RNG_ALGO_UNDERFLOW */
"Ranging algorithm overflow", /**< VL6180X_RNG_ALGO_OVERFLOW */
};
#endif
#if IS_USED(MODULE_VL6180X_ALS)
static const char *als_errors[] = {
"No error", /**< VL6180X_ALS_OK */
"ALS measurement overflow", /**< VL6180X_ALS_OVERFLOW */
"ALS measurement underflow", /**< VL6180X_ALS_UNDERFLOW */
};
#endif
#endif /* IS_USED(MODULE_VL6180X_IRQ) */
int main(void)
{
/* Initialize the sensor */
vl6180x_t dev;
#if IS_USED(MODULE_VL6180X_RNG)
uint8_t rng;
#endif
#if IS_USED(MODULE_VL6180X_ALS)
uint16_t als;
uint16_t lux;
#endif
/* initialize the sensor */
puts("VL6180X Time-of-Flight distance sensor\n");
puts("Initializing VL6180X sensor");
if (vl6180x_init(&dev, &vl6180x_params[0]) == VL6180X_OK) {
puts("[OK]");
}
else {
puts("[Failed]");
return 1;
}
#if IS_USED(MODULE_VL6180X_SHUTDOWN)
unsigned count = 0;
#endif
#if IS_USED(MODULE_VL6180X_IRQ) && IS_USED(MODULE_VL6180X_CONFIG)
vl6180x_int_config_t mode;
vl6180x_int_thresh_t thresh;
#if IS_USED(MODULE_VL6180X_RNG)
/* interrupt when distance is less than 30 mm or greater than 100 mm */
mode.rng_int = VL6180X_INT_OUT;
thresh.rng_low = 30;
thresh.rng_high = 100;
#endif /* IS_USED(MODULE_VL6180X_RNG) */
#if IS_USED(MODULE_VL6180X_ALS)
/* interrupts when ALS data are ready */
mode.als_int = VL6180X_INT_DRDY;
#endif /* IS_USED(MODULE_VL6180X_RNG) */
vl6180x_int_config(&dev, thresh);
vl6180x_int_enable(&dev, mode);
#endif /* IS_USED(MODULE_VL6180X_CONFIG) && IS_USED(MODULE_VL6180X_IRQ) */
while (1) {
#if IS_USED(MODULE_VL6180X_IRQ)
vl6180x_int_config_t src;
vl6180x_int_wait(&dev, &src);
#if IS_USED(MODULE_VL6180X_RNG)
if (src.rng_int == VL6180X_INT_DRDY) {
int status = vl6180x_rng_read(&dev, &rng);
switch (status) {
case VL6180X_RNG_OK:
printf("RNG: %u [mm]\n", rng);
break;
case -VL6180X_ERROR_RNG:
printf("RNG: %s\n", rng_errors[vl6180x_rng_status(&dev)]);
break;
default:
printf("RNG: error %d\n", status);
}
puts("+-----------------------------------------+");
}
else if (src.rng_int == VL6180X_INT_OUT) {
puts("RNG: out of window");
}
else if (src.rng_int == VL6180X_INT_RNG_LOW) {
puts("RNG: low level");
}
else if (src.rng_int == VL6180X_INT_RNG_HIGH) {
puts("RNG: high level");
}
#endif /* IS_USED(MODULE_VL6180X_RNG) */
#if IS_USED(MODULE_VL6180X_ALS)
if (src.als_int == VL6180X_INT_DRDY) {
int status = vl6180x_als_read(&dev, &als, &lux);
switch (status) {
case VL6180X_OK:
printf("ALS: %u [cnts], %u [lux]\n", als, lux);
break;
case -VL6180X_ERROR_ALS:
printf("ALS: %s\n", als_errors[vl6180x_als_status(&dev)]);
break;
default:
printf("ALS: error %d\n", status);
}
}
else if (src.als_int == VL6180X_INT_OUT) {
puts("ALS: out of window");
}
else if (src.als_int == VL6180X_INT_LOW) {
puts("ALS: low level");
}
else if (src.als_int == VL6180X_INT_HIGH) {
puts("ALS: high level");
}
#endif /* IS_USED(MODULE_VL6180X_ALS) */
#else /* IS_USED(MODULE_VL6180X_IRQ) */
/* just wait 250 ms if module vl6180x_basic is used */
ztimer_sleep(ZTIMER_MSEC, 250);
#if IS_USED(MODULE_VL6180X_ALS)
if (vl6180x_als_data_ready(&dev) == VL6180X_OK &&
vl6180x_als_read(&dev, &als, &lux) == VL6180X_OK) {
printf("ALS: %u [cnts], %u [lux]\n", als, lux);
}
#endif /* IS_USED(MODULE_VL6180X_ALS) */
#if IS_USED(MODULE_VL6180X_RNG)
if (vl6180x_rng_data_ready(&dev) == VL6180X_OK) {
if (vl6180x_rng_read(&dev, &rng) == VL6180X_OK) {
printf("RNG: %u [mm]\n", rng);
}
else {
puts("RNG: inf");
}
puts("+-----------------------------------------+");
}
#endif /* IS_USED(MODULE_VL6180X_RNG) */
#endif /* IS_USED(MODULE_VL6180X_IRQ) */
#if IS_USED(MODULE_VL6180X_SHUTDOWN)
/*
* if shutdown pin is defined, the sensor is powered down and up
* again after 5 seconds every 50 cycles
*/
count++;
if ((count % 50) == 0) {
vl6180x_power_down(&dev);
puts("Sensor powered down");
ztimer_sleep(ZTIMER_SEC, 5);
vl6180x_power_up(&dev);
puts("Sensor powered up");
}
#endif /* IS_USED(MODULE_VL6180X_SHUTDOWN) */
}
return 0;
}

View File

@ -46,6 +46,10 @@ endif
ifneq (,$(filter tmp00x,$(DRIVERS)))
USEMODULE += tmp006
endif
ifneq (,$(filter vl6180x,$(DRIVERS)))
USEMODULE += vl6180x_als
USEMODULE += vl6180x_rng
endif
USEMODULE += saul_default