mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
0337d08bcc
Some of the `static inline` functions are only used in some configurations, depending on the preprocessor. Explicitly disable the warning to allow compilation with clang.
925 lines
27 KiB
C
925 lines
27 KiB
C
/*
|
|
* 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);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
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;
|
|
}
|
|
|
|
__attribute__((unused))
|
|
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;
|
|
}
|
|
|
|
__attribute__((unused))
|
|
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);
|
|
}
|
|
|
|
__attribute__((unused))
|
|
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);
|
|
}
|