1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

drivers/vl6180x: driver for VL6180X ranging and ALS

This commit is contained in:
Gunar Schorcht 2021-11-28 12:16:22 +01:00
parent 12a05b8fa2
commit 51bf9d4d7d
8 changed files with 2427 additions and 0 deletions

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

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);
}