mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #10420 from gschorcht/drivers_apds99xx
drivers: add driver for APDS99XX ambient light and proximity sensors
This commit is contained in:
commit
1e8037670b
3
dist/tools/codespell/ignored_words.txt
vendored
3
dist/tools/codespell/ignored_words.txt
vendored
@ -13,3 +13,6 @@ od
|
||||
|
||||
# dout (Digital out) => doubt
|
||||
dout
|
||||
|
||||
# ALS (Ambient Light Sensing) => ALSO
|
||||
als
|
||||
|
@ -29,6 +29,15 @@ ifneq (,$(filter apa102,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_gpio
|
||||
endif
|
||||
|
||||
ifneq (,$(filter apds99%full,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_gpio_irq
|
||||
endif
|
||||
|
||||
ifneq (,$(filter apds99%,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_i2c
|
||||
USEMODULE += apds99xx
|
||||
endif
|
||||
|
||||
ifneq (,$(filter at,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_uart
|
||||
USEMODULE += fmt
|
||||
|
@ -20,6 +20,10 @@ ifneq (,$(filter apa102,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/apa102/include
|
||||
endif
|
||||
|
||||
ifneq (,$(filter apds99xx,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/apds99xx/include
|
||||
endif
|
||||
|
||||
ifneq (,$(filter at24cxxx,$(USEMODULE)))
|
||||
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/at24cxxx/include
|
||||
endif
|
||||
|
1
drivers/apds99xx/Makefile
Normal file
1
drivers/apds99xx/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
534
drivers/apds99xx/apds99xx.c
Normal file
534
drivers/apds99xx/apds99xx.c
Normal file
@ -0,0 +1,534 @@
|
||||
/*
|
||||
* 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_apds99xx
|
||||
* @{
|
||||
* @brief Device driver for the Broadcom APDS99XX proximity and ambient light sensor
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @file
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "apds99xx_regs.h"
|
||||
#include "apds99xx.h"
|
||||
|
||||
#include "irq.h"
|
||||
#include "log.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#if ENABLE_DEBUG
|
||||
|
||||
#define DEBUG_DEV(f, d, ...) \
|
||||
DEBUG("[apds99xx] %s i2c dev=%d addr=%02x: " f "\n", \
|
||||
__func__, d->params.dev, APDS99XX_I2C_ADDRESS, ## __VA_ARGS__);
|
||||
|
||||
#else /* ENABLE_DEBUG */
|
||||
|
||||
#define DEBUG_DEV(f, d, ...)
|
||||
|
||||
#endif /* ENABLE_DEBUG */
|
||||
|
||||
#define ERROR_DEV(f, d, ...) \
|
||||
LOG_ERROR("[apds99xx] %s i2c dev=%d addr=%02x: " f "\n", \
|
||||
__func__, d->params.dev, APDS99XX_I2C_ADDRESS, ## __VA_ARGS__);
|
||||
|
||||
/** Forward declaration of functions for internal use */
|
||||
|
||||
static int _is_available(const apds99xx_t *dev);
|
||||
static void _set_reg_bit(uint8_t *byte, uint8_t mask, uint8_t bit);
|
||||
static int _reg_read(const apds99xx_t *dev, uint8_t reg, uint8_t *data, uint16_t len);
|
||||
static int _reg_write(const apds99xx_t *dev, uint8_t reg, uint8_t *data, uint16_t len);
|
||||
static int _update_reg(const apds99xx_t *dev, uint8_t reg, uint8_t mask, uint8_t val);
|
||||
|
||||
int apds99xx_init(apds99xx_t *dev, const apds99xx_params_t *params)
|
||||
{
|
||||
/* some parameter sanity checks */
|
||||
assert(dev != NULL);
|
||||
assert(params != NULL);
|
||||
assert(params->als_steps <= 256);
|
||||
assert(params->wait_steps <= 256);
|
||||
#if MODULE_APDS9960
|
||||
assert(params->prx_pulses <= 15);
|
||||
#endif
|
||||
|
||||
DEBUG_DEV("params=%p", dev, params);
|
||||
|
||||
/* init sensor data structure */
|
||||
dev->params = *params;
|
||||
|
||||
#if MODULE_APDS99XX_FULL
|
||||
dev->isr = NULL;
|
||||
dev->isr_arg = NULL;
|
||||
dev->gpio_init = false;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* the sensor should be operational 5.7 ms after power on; try to check
|
||||
* its availability for some time (maximum 500 times/I2C address writes)
|
||||
*/
|
||||
int res = 0;
|
||||
int count = 500;
|
||||
while (count--) {
|
||||
res = _is_available(dev);
|
||||
if (res == APDS99XX_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (res != APDS99XX_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
uint8_t reg;
|
||||
|
||||
/* disable and power down the sensor */
|
||||
reg = 0;
|
||||
if (_reg_write(dev, APDS99XX_REG_ENABLE, ®, 1) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
/* write ALS integration time and gain parameter */
|
||||
uint8_t atime = 256 - dev->params.als_steps;
|
||||
if (_reg_write(dev, APDS99XX_REG_ATIME, &atime, 1) ||
|
||||
_update_reg(dev, APDS99XX_REG_CONTROL,
|
||||
APDS99XX_REG_AGAIN, dev->params.als_gain)) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
/* write PRX LED pulses LED drive strength and gain parameter */
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930
|
||||
uint8_t ptime = 0xff; /* PTIME is always 0xff as recommended in datasheet */
|
||||
if (_reg_write(dev, APDS99XX_REG_PTIME, &ptime, 1) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
#endif
|
||||
#if MODULE_APDS9960
|
||||
if (dev->params.prx_pulses > 0 &&
|
||||
_update_reg(dev, APDS99XX_REG_PPCOUNT,
|
||||
APDS99XX_REG_PPULSE,
|
||||
dev->params.prx_pulses-1) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
#else
|
||||
if (_reg_write(dev, APDS99XX_REG_PPCOUNT, &dev->params.prx_pulses, 1) ||
|
||||
_update_reg(dev, APDS99XX_REG_CONTROL, APDS99XX_REG_PDIODE, 2)) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
#endif
|
||||
if (_update_reg(dev, APDS99XX_REG_CONTROL, APDS99XX_REG_PDRIVE,
|
||||
dev->params.prx_drive) ||
|
||||
_update_reg(dev, APDS99XX_REG_CONTROL, APDS99XX_REG_PGAIN,
|
||||
dev->params.prx_gain)) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
/* write the waiting time */
|
||||
uint8_t wtime = 256 - dev->params.wait_steps;
|
||||
if (_reg_write(dev, APDS99XX_REG_WTIME, &wtime, 1) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
reg = 0;
|
||||
_set_reg_bit(®, APDS99XX_REG_PON, 1); /* power on */
|
||||
_set_reg_bit(®, APDS99XX_REG_AEN, dev->params.als_steps != 0); /* enable ALS */
|
||||
_set_reg_bit(®, APDS99XX_REG_PEN, dev->params.prx_pulses != 0); /* enable PRX */
|
||||
_set_reg_bit(®, APDS99XX_REG_WEN, dev->params.wait_steps != 0); /* enable Wait */
|
||||
if (_reg_write(dev, APDS99XX_REG_ENABLE, ®, 1) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
return APDS99XX_OK;
|
||||
}
|
||||
|
||||
int apds99xx_data_ready_als (const apds99xx_t *dev)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
DEBUG_DEV("", dev);
|
||||
|
||||
uint8_t reg;
|
||||
if (_reg_read(dev, APDS99XX_REG_STATUS, ®, 1) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
return (reg & APDS99XX_REG_AVALID) ? APDS99XX_OK : -APDS99XX_ERROR_NO_DATA;
|
||||
}
|
||||
|
||||
int apds99xx_read_als_raw(const apds99xx_t *dev, uint16_t *raw)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
assert(raw != NULL);
|
||||
DEBUG_DEV("raw=%p", dev, raw);
|
||||
|
||||
uint8_t data[2];
|
||||
|
||||
if (_reg_read(dev, APDS99XX_REG_CDATAL, data, 2) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_RAW_DATA;
|
||||
}
|
||||
|
||||
/* data LSB @ lower address */
|
||||
*raw = (data[1] << 8) | data[0];
|
||||
|
||||
return APDS99XX_OK;
|
||||
}
|
||||
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930
|
||||
static uint8_t apds99xx_gains[] = { 1, 8, 16, 120 };
|
||||
|
||||
int apds99xx_read_illuminance(const apds99xx_t *dev, uint16_t *lux)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
assert(lux != NULL);
|
||||
DEBUG_DEV("lux=%p", dev, lux);
|
||||
|
||||
uint8_t data[4];
|
||||
|
||||
if (_reg_read(dev, APDS99XX_REG_CDATAL, data, 4) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_RAW_DATA;
|
||||
}
|
||||
|
||||
/* data LSB @ lower address */
|
||||
uint16_t ch0 = (data[1] << 8) | data[0];
|
||||
uint16_t ch1 = (data[3] << 8) | data[2];
|
||||
|
||||
/* define some device dependent constants */
|
||||
double df = 52;
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901
|
||||
double ga = 0.48; /* glas or lens attenuation factor */
|
||||
double b = 2.23;
|
||||
double c = 0.7;
|
||||
double d = 1.42;
|
||||
#else
|
||||
/* APDS_9930 */
|
||||
double ga = 0.49; /* glas or lens attenuation factor */
|
||||
double b = 1.862;
|
||||
double c = 0.746;
|
||||
double d = 1.291;
|
||||
#endif
|
||||
|
||||
/* algorithm from datasheet */
|
||||
double iac1 = ch0 - b * ch1;
|
||||
double iac2 = c * ch0 - d * ch1;
|
||||
|
||||
/* iac = max(iac1, iac2, 0); */
|
||||
double iac = 0;
|
||||
iac = (iac1 > iac) ? iac1 : iac;
|
||||
iac = (iac2 > iac) ? iac2 : iac;
|
||||
|
||||
double lpc = ga * df / (apds99xx_gains[dev->params.als_gain] *
|
||||
dev->params.als_steps);
|
||||
double luxd = iac * lpc;
|
||||
*lux = luxd;
|
||||
|
||||
return APDS99XX_OK;
|
||||
}
|
||||
#endif /* MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930 */
|
||||
|
||||
#if MODULE_APDS9950 || MODULE_APDS9960
|
||||
int apds99xx_read_rgb_raw(const apds99xx_t *dev, apds99xx_rgb_t *rgb)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
assert(rgb != NULL);
|
||||
DEBUG_DEV("rgb=%p", dev, rgb);
|
||||
|
||||
uint8_t data[6] = { }; /* initialize with 0 */
|
||||
|
||||
if (_reg_read(dev, APDS99XX_REG_RDATAL, data, 6) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_RAW_DATA;
|
||||
}
|
||||
|
||||
/* data LSB @ lower address */
|
||||
rgb->val[0] = (data[1] << 8) | data[0];
|
||||
rgb->val[1] = (data[3] << 8) | data[2];
|
||||
rgb->val[2] = (data[5] << 8) | data[4];
|
||||
|
||||
return APDS99XX_OK;
|
||||
}
|
||||
#endif /* MODULE_APDS9950 || MODULE_APDS9960 */
|
||||
|
||||
int apds99xx_data_ready_prx (const apds99xx_t *dev)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
DEBUG_DEV("", dev);
|
||||
|
||||
uint8_t reg;
|
||||
|
||||
if (_reg_read(dev, APDS99XX_REG_STATUS, ®, 1) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
return (reg & APDS99XX_REG_PVALID) ? APDS99XX_OK : -APDS99XX_ERROR_NO_DATA;
|
||||
}
|
||||
|
||||
int apds99xx_read_prx_raw (const apds99xx_t *dev, uint16_t *prox)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
assert(prox != NULL);
|
||||
DEBUG_DEV("prox=%p", dev, prox);
|
||||
|
||||
uint8_t data[2] = { }; /* initialize with 0 */
|
||||
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930 || MODULE_APDS9950
|
||||
if (_reg_read(dev, APDS99XX_REG_PDATAL, data, 2) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_RAW_DATA;
|
||||
}
|
||||
#endif
|
||||
#if MODULE_APDS9960
|
||||
if (_reg_read(dev, APDS99XX_REG_PDATA, data, 1) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_RAW_DATA;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* data LSB @ lower address */
|
||||
*prox = (data[1] << 8) | data[0];
|
||||
|
||||
return APDS99XX_OK;
|
||||
}
|
||||
|
||||
int apds99xx_power_down(const apds99xx_t *dev)
|
||||
{
|
||||
return _update_reg(dev, APDS99XX_REG_ENABLE, APDS99XX_REG_PON, 0);
|
||||
}
|
||||
|
||||
int apds99xx_power_up(const apds99xx_t *dev)
|
||||
{
|
||||
return _update_reg(dev, APDS99XX_REG_ENABLE, APDS99XX_REG_PON, 1);
|
||||
}
|
||||
|
||||
#if MODULE_APDS99XX_FULL
|
||||
|
||||
void _apds99xx_isr(void *arg)
|
||||
{
|
||||
apds99xx_t* dev = (apds99xx_t*)arg;
|
||||
unsigned state = irq_disable();
|
||||
|
||||
DEBUG_DEV("", dev);
|
||||
|
||||
/* call registered interrupt service routine */
|
||||
if (dev->isr) {
|
||||
dev->isr(dev->isr_arg);
|
||||
}
|
||||
|
||||
irq_restore (state);
|
||||
}
|
||||
|
||||
int apds99xx_int_source(apds99xx_t *dev, apds99xx_int_source_t* source)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
assert(source != NULL);
|
||||
DEBUG_DEV("", dev);
|
||||
|
||||
uint8_t reg;
|
||||
|
||||
/* get interrupt status */
|
||||
if (_reg_read(dev, APDS99XX_REG_STATUS, ®, 1) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
/* set triggered interrupts */
|
||||
source->als_int = reg & APDS99XX_REG_AINT;
|
||||
source->prx_int = reg & APDS99XX_REG_PINT;
|
||||
|
||||
/* clear interrupt status */
|
||||
if (_reg_write(dev, APDS99XX_REG_CLI_CMD, 0, 0) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
return APDS99XX_OK;
|
||||
}
|
||||
|
||||
int apds99xx_int_config(apds99xx_t *dev, apds99xx_int_config_t* cfg,
|
||||
apds99xx_isr_t isr, void *isr_arg)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
assert(cfg != NULL);
|
||||
assert(dev->params.int_pin != GPIO_UNDEF);
|
||||
assert(cfg->als_pers <= 15);
|
||||
assert(cfg->prx_pers <= 15);
|
||||
|
||||
DEBUG_DEV("", dev);
|
||||
|
||||
if (!dev->gpio_init) {
|
||||
dev->gpio_init = true;
|
||||
gpio_init_int(dev->params.int_pin, GPIO_IN_PU, GPIO_FALLING,
|
||||
_apds99xx_isr, dev);
|
||||
}
|
||||
|
||||
/* LSB @ lower address */
|
||||
uint8_t ailtx[2] = { cfg->als_thresh_low & 0xff, cfg->als_thresh_low >> 8 };
|
||||
uint8_t aihtx[2] = { cfg->als_thresh_high & 0xff, cfg->als_thresh_high >> 8 };
|
||||
#if MODULE_APDS9960
|
||||
/* for APDS9960 the one byte thresholds is used for APDS99XX_REG_PIxTH */
|
||||
uint8_t pilth = cfg->prx_thresh_low & 0xff;
|
||||
uint8_t pihth = cfg->prx_thresh_high & 0xff;
|
||||
#else
|
||||
uint8_t piltx[2] = { cfg->prx_thresh_low & 0xff, cfg->prx_thresh_low >> 8 };
|
||||
uint8_t pihtx[2] = { cfg->prx_thresh_high & 0xff, cfg->prx_thresh_high >> 8 };
|
||||
#endif
|
||||
|
||||
if (_reg_write(dev, APDS99XX_REG_AILTL, ailtx, 2) ||
|
||||
_reg_write(dev, APDS99XX_REG_AIHTL, aihtx, 2) ||
|
||||
#if MODULE_APDS9960
|
||||
_reg_write(dev, APDS99XX_REG_PILTH, &pilth, 1) ||
|
||||
_reg_write(dev, APDS99XX_REG_PIHTH, &pihth, 1) ||
|
||||
#else
|
||||
_reg_write(dev, APDS99XX_REG_PILTL, piltx, 2) ||
|
||||
_reg_write(dev, APDS99XX_REG_PIHTL, pihtx, 2) ||
|
||||
#endif
|
||||
_update_reg(dev, APDS99XX_REG_PERS, APDS99XX_REG_APERS, cfg->als_pers) ||
|
||||
_update_reg(dev, APDS99XX_REG_PERS, APDS99XX_REG_PPERS, cfg->prx_pers) ||
|
||||
_update_reg(dev, APDS99XX_REG_ENABLE, APDS99XX_REG_AIEN, cfg->als_int_en) ||
|
||||
_reg_write(dev, APDS99XX_REG_CLI_CMD, 0, 0) ||
|
||||
_update_reg(dev, APDS99XX_REG_ENABLE, APDS99XX_REG_PIEN, cfg->prx_int_en) ||
|
||||
_reg_write(dev, APDS99XX_REG_CLI_CMD, 0, 0)) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
dev->isr = isr;
|
||||
dev->isr_arg = isr_arg;
|
||||
|
||||
return APDS99XX_OK;
|
||||
}
|
||||
|
||||
#endif /* MODULE_APDS99XX_FULL */
|
||||
|
||||
/** Functions for internal use only */
|
||||
|
||||
/**
|
||||
* @brief Check the chip ID to test whether sensor is available
|
||||
*/
|
||||
static int _is_available(const apds99xx_t *dev)
|
||||
{
|
||||
DEBUG_DEV("", dev);
|
||||
|
||||
uint8_t reg;
|
||||
|
||||
/* read the chip id from APDS99XX_REG_ID_X */
|
||||
if (_reg_read(dev, APDS99XX_REG_ID, ®, 1) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
if (reg != APDS99XX_ID) {
|
||||
DEBUG_DEV("sensor is not available, wrong device id %02x, "
|
||||
"should be %02x", dev, reg, APDS99XX_ID);
|
||||
return -APDS99XX_ERROR_WRONG_ID;
|
||||
}
|
||||
|
||||
return APDS99XX_OK;
|
||||
}
|
||||
|
||||
static void _set_reg_bit(uint8_t *byte, uint8_t mask, uint8_t bit)
|
||||
{
|
||||
assert(byte != NULL);
|
||||
|
||||
uint8_t shift = 0;
|
||||
while (!((mask >> shift) & 0x01)) {
|
||||
shift++;
|
||||
}
|
||||
*byte = ((*byte & ~mask) | ((bit << shift) & mask));
|
||||
}
|
||||
|
||||
static int _update_reg(const apds99xx_t *dev, uint8_t reg, uint8_t mask, uint8_t val)
|
||||
{
|
||||
DEBUG_DEV("reg=%02x mask=%02x val=%02x", dev, reg, mask, val);
|
||||
|
||||
uint8_t reg_val;
|
||||
uint8_t shift = 0;
|
||||
|
||||
while (!((mask >> shift) & 0x01)) {
|
||||
shift++;
|
||||
}
|
||||
|
||||
/* read current register value */
|
||||
if (_reg_read(dev, reg, ®_val, 1) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
/* set masked bits to the given value */
|
||||
reg_val = (reg_val & ~mask) | ((val << shift) & mask);
|
||||
|
||||
/* write back new register value */
|
||||
if (_reg_write(dev, reg, ®_val, 1) != APDS99XX_OK) {
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
return APDS99XX_OK;
|
||||
}
|
||||
|
||||
static int _reg_read(const apds99xx_t *dev, uint8_t reg, uint8_t *data, uint16_t len)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
assert(data != NULL);
|
||||
assert(len != 0);
|
||||
|
||||
if (i2c_acquire(dev->params.dev)) {
|
||||
DEBUG_DEV("could not acquire I2C bus", dev);
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
int res = i2c_read_regs(dev->params.dev, APDS99XX_I2C_ADDRESS, reg, data, len, 0);
|
||||
i2c_release(dev->params.dev);
|
||||
|
||||
if (res != 0) {
|
||||
DEBUG_DEV("could not read %d bytes from sensor registers "
|
||||
"starting at addr %02x, reason %d",
|
||||
dev, len, reg, res);
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
if (ENABLE_DEBUG) {
|
||||
printf("[apds99xx] %s i2c dev=%d addr=%02x: read from reg 0x%02x: ",
|
||||
__func__, dev->params.dev, APDS99XX_I2C_ADDRESS, reg);
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
printf("%02x ", data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _reg_write(const apds99xx_t *dev, uint8_t reg, uint8_t *data, uint16_t len)
|
||||
{
|
||||
assert(dev != NULL);
|
||||
|
||||
if (ENABLE_DEBUG) {
|
||||
printf("[apds99xx] %s i2c dev=%d addr=%02x: write to reg 0x%02x: ",
|
||||
__func__, dev->params.dev, APDS99XX_I2C_ADDRESS, reg);
|
||||
for (uint16_t i = 0; i < len; i++) {
|
||||
printf("%02x ", data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (i2c_acquire(dev->params.dev)) {
|
||||
DEBUG_DEV("could not acquire I2C bus", dev);
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
int res;
|
||||
|
||||
if (!data || !len) {
|
||||
res = i2c_write_byte(dev->params.dev, APDS99XX_I2C_ADDRESS, reg, 0);
|
||||
}
|
||||
else {
|
||||
res = i2c_write_regs(dev->params.dev, APDS99XX_I2C_ADDRESS, reg, data, len, 0);
|
||||
}
|
||||
i2c_release(dev->params.dev);
|
||||
|
||||
if (res != 0) {
|
||||
DEBUG_DEV("could not write %d bytes to sensor registers "
|
||||
"starting at addr 0x%02x, reason %d",
|
||||
dev, len, reg, res);
|
||||
return -APDS99XX_ERROR_I2C;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
95
drivers/apds99xx/apds99xx_saul.c
Normal file
95
drivers/apds99xx/apds99xx_saul.c
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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_apds99xx
|
||||
* @brief APDS99XX adaption to the RIOT actuator/sensor interface
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @file
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "saul.h"
|
||||
#include "apds99xx.h"
|
||||
|
||||
static int read_prx(const void *dev, phydat_t *res)
|
||||
{
|
||||
if (apds99xx_read_prx_raw((const apds99xx_t *)dev, (uint16_t*)res) == 0) {
|
||||
res->unit = UNIT_CTS;
|
||||
res->scale = 0;
|
||||
return 1;
|
||||
}
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
static int read_als(const void *dev, phydat_t *res)
|
||||
{
|
||||
if (apds99xx_read_als_raw((const apds99xx_t *)dev, (uint16_t*)res) == 0) {
|
||||
res->unit = UNIT_CTS;
|
||||
res->scale = 0;
|
||||
return 1;
|
||||
}
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930
|
||||
static int read_lux(const void *dev, phydat_t *res)
|
||||
{
|
||||
if (apds99xx_read_illuminance((const apds99xx_t *)dev, (uint16_t*)res) == 0) {
|
||||
res->unit = UNIT_LUX;
|
||||
res->scale = 0;
|
||||
return 1;
|
||||
}
|
||||
return -ECANCELED;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MODULE_APDS9950 || MODULE_APDS9960
|
||||
static int read_rgb(const void *dev, phydat_t *res)
|
||||
{
|
||||
apds99xx_rgb_t rgb;
|
||||
if (apds99xx_read_rgb_raw((const apds99xx_t *)dev, &rgb) == 0) {
|
||||
res->val[0] = (int16_t)rgb.red;
|
||||
res->val[1] = (int16_t)rgb.green;
|
||||
res->val[2] = (int16_t)rgb.blue;
|
||||
res->unit = UNIT_CTS;
|
||||
res->scale = 0;
|
||||
return 3;
|
||||
}
|
||||
return -ECANCELED;
|
||||
}
|
||||
#endif
|
||||
|
||||
const saul_driver_t apds99xx_saul_prx_driver = {
|
||||
.read = read_prx,
|
||||
.write = saul_notsup,
|
||||
.type = SAUL_SENSE_PROXIMITY,
|
||||
};
|
||||
|
||||
const saul_driver_t apds99xx_saul_als_driver = {
|
||||
.read = read_als,
|
||||
.write = saul_notsup,
|
||||
.type = SAUL_SENSE_LIGHT,
|
||||
};
|
||||
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930
|
||||
const saul_driver_t apds99xx_saul_lux_driver = {
|
||||
.read = read_lux,
|
||||
.write = saul_notsup,
|
||||
.type = SAUL_SENSE_LIGHT,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if MODULE_APDS9950 || MODULE_APDS9960
|
||||
const saul_driver_t apds99xx_saul_rgb_driver = {
|
||||
.read = read_rgb,
|
||||
.write = saul_notsup,
|
||||
.type = SAUL_SENSE_LIGHT,
|
||||
};
|
||||
#endif
|
112
drivers/apds99xx/include/apds99xx_params.h
Normal file
112
drivers/apds99xx/include/apds99xx_params.h
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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_apds99xx
|
||||
* @brief Default configuration for Broadcom APDS99XX proximity and ambient light sensor
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @file
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef APDS99XX_PARAMS_H
|
||||
#define APDS99XX_PARAMS_H
|
||||
|
||||
#include "board.h"
|
||||
#include "apds99xx.h"
|
||||
#include "saul_reg.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Set default configuration parameters
|
||||
* @{
|
||||
*/
|
||||
#ifndef APDS99XX_PARAM_DEV
|
||||
/** device is I2C_DEV(0) */
|
||||
#define APDS99XX_PARAM_DEV I2C_DEV(0)
|
||||
#endif
|
||||
|
||||
#ifndef APDS99XX_PARAM_ALS_STEPS
|
||||
/** ALS measurement: enabled with integration time of 64 steps */
|
||||
#define APDS99XX_PARAM_ALS_STEPS (64)
|
||||
#endif
|
||||
#ifndef APDS99XX_PARAM_ALS_GAIN
|
||||
/** ALS gain: 1 x gain */
|
||||
#define APDS99XX_PARAM_ALS_GAIN (APDS99XX_ALS_GAIN_1)
|
||||
#endif
|
||||
|
||||
#ifndef APDS99XX_PARAM_PRX_PULSES
|
||||
/** PRX LED pulse count: 8 pulses as recommended in datasheet */
|
||||
#define APDS99XX_PARAM_PRX_PULSES (8)
|
||||
#endif
|
||||
#ifndef APDS99XX_PARAM_PRX_DRIVE
|
||||
/** PRX LED drive strength: 100 mA as recommended in datasheet */
|
||||
#define APDS99XX_PARAM_PRX_DRIVE (APDS99XX_PRX_DRIVE_100)
|
||||
#endif
|
||||
#ifndef APDS99XX_PARAM_PRX_GAIN
|
||||
/** PRX gain: 1 x gain */
|
||||
#define APDS99XX_PARAM_PRX_GAIN (APDS99XX_PRX_GAIN_1)
|
||||
#endif
|
||||
#ifndef APDS99XX_PARAM_WAIT_STEPS
|
||||
/** Waiting time: disabled */
|
||||
#define APDS99XX_PARAM_WAIT_STEPS (0)
|
||||
#endif
|
||||
#ifndef APDS99XX_PARAM_WAIT_LONG
|
||||
/** Wait long: false */
|
||||
#define APDS99XX_PARAM_WAIT_LONG (false)
|
||||
#endif
|
||||
|
||||
#ifndef APDS99XX_PARAM_INT_PIN
|
||||
/** Interrupt pin */
|
||||
#define APDS99XX_PARAM_INT_PIN (GPIO_PIN(0, 0))
|
||||
#endif
|
||||
|
||||
#ifndef APDS99XX_PARAMS
|
||||
#define APDS99XX_PARAMS { \
|
||||
.dev = APDS99XX_PARAM_DEV, \
|
||||
.als_steps = APDS99XX_PARAM_ALS_STEPS, \
|
||||
.als_gain = APDS99XX_PARAM_ALS_GAIN, \
|
||||
.prx_pulses = APDS99XX_PARAM_PRX_PULSES, \
|
||||
.prx_gain = APDS99XX_PARAM_PRX_GAIN, \
|
||||
.prx_drive = APDS99XX_PARAM_PRX_DRIVE, \
|
||||
.wait_steps = APDS99XX_PARAM_WAIT_STEPS, \
|
||||
.wait_long = APDS99XX_PARAM_WAIT_LONG, \
|
||||
.int_pin = APDS99XX_PARAM_INT_PIN, \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef APDS99XX_SAUL_INFO
|
||||
#define APDS99XX_SAUL_INFO { .name = "apds99xx" }
|
||||
#endif
|
||||
/**@}*/
|
||||
|
||||
/**
|
||||
* @brief Allocate some memory to store the actual configuration
|
||||
*/
|
||||
static const apds99xx_params_t apds99xx_params[] =
|
||||
{
|
||||
APDS99XX_PARAMS
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Additional meta information to keep in the SAUL registry
|
||||
*/
|
||||
static const saul_reg_info_t apds99xx_saul_info[] =
|
||||
{
|
||||
APDS99XX_SAUL_INFO
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* APDS99XX_PARAMS_H */
|
||||
/** @} */
|
138
drivers/apds99xx/include/apds99xx_regs.h
Normal file
138
drivers/apds99xx/include/apds99xx_regs.h
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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_apds99xx
|
||||
* @brief Register definitions for Broadcom APDS99XX proximity and ambient light sensor
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @file
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef APDS99XX_REGS_H
|
||||
#define APDS99XX_REGS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Register addresses
|
||||
* @{
|
||||
*/
|
||||
#if MODULE_APDS9960
|
||||
#define APDS99XX_REG_BASE (0x80) /**< Register base address is 0x80 */
|
||||
#else
|
||||
#define APDS99XX_REG_BASE (0xa0) /**< Register base address for autoincrement is 0xa0 */
|
||||
#endif
|
||||
|
||||
#define APDS99XX_REG_ENABLE (APDS99XX_REG_BASE + 0x00) /**< Enable states and interrupts */
|
||||
#define APDS99XX_REG_ATIME (APDS99XX_REG_BASE + 0x01) /**< ALS timing register */
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930
|
||||
#define APDS99XX_REG_PTIME (APDS99XX_REG_BASE + 0x02) /**< Proximity timing register */
|
||||
#endif
|
||||
#define APDS99XX_REG_WTIME (APDS99XX_REG_BASE + 0x03) /**< Wait timing register */
|
||||
#define APDS99XX_REG_AILTL (APDS99XX_REG_BASE + 0x04) /**< ALS interrupt low threshold low byte */
|
||||
#define APDS99XX_REG_AILTH (APDS99XX_REG_BASE + 0x05) /**< ALS interrupt low threshold high byte */
|
||||
#define APDS99XX_REG_AIHTL (APDS99XX_REG_BASE + 0x06) /**< ALS interrupt high threshold low byte */
|
||||
#define APDS99XX_REG_AIHTH (APDS99XX_REG_BASE + 0x07) /**< ALS interrupt high threshold high byte */
|
||||
#if !MODULE_APDS9960
|
||||
#define APDS99XX_REG_PILTL (APDS99XX_REG_BASE + 0x08) /**< PRX interrupt low threshold low byte */
|
||||
#endif
|
||||
#define APDS99XX_REG_PILTH (APDS99XX_REG_BASE + 0x09) /**< PRX interrupt low threshold high byte */
|
||||
#if !MODULE_APDS9960
|
||||
#define APDS99XX_REG_PIHTL (APDS99XX_REG_BASE + 0x0a) /**< PRX interrupt high threshold low byte */
|
||||
#endif
|
||||
#define APDS99XX_REG_PIHTH (APDS99XX_REG_BASE + 0x0b) /**< PRX interrupt high threshold high byte */
|
||||
#define APDS99XX_REG_PERS (APDS99XX_REG_BASE + 0x0c) /**< Interrupt persistence filters */
|
||||
#define APDS99XX_REG_CONFIG (APDS99XX_REG_BASE + 0x0d) /**< Configuration register (one) */
|
||||
#define APDS99XX_REG_PPCOUNT (APDS99XX_REG_BASE + 0x0e) /**< Proximity pulse count */
|
||||
#define APDS99XX_REG_CONTROL (APDS99XX_REG_BASE + 0x0f) /**< Control */
|
||||
#define APDS99XX_REG_ID (APDS99XX_REG_BASE + 0x12) /**< Device ID */
|
||||
#define APDS99XX_REG_STATUS (APDS99XX_REG_BASE + 0x13) /**< Device status */
|
||||
|
||||
#define APDS99XX_REG_CDATAL (APDS99XX_REG_BASE + 0x14) /**< Clear channel / Ch0 ADC data low byte */
|
||||
#define APDS99XX_REG_CDATAH (APDS99XX_REG_BASE + 0x15) /**< Clear channel / Ch0 ADC data high byte */
|
||||
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930
|
||||
#define APDS99XX_REG_IRDATAL (APDS99XX_REG_BASE + 0x16) /**< Ch1(IR) ADC data low byte */
|
||||
#define APDS99XX_REG_IRDATAH (APDS99XX_REG_BASE + 0x17) /**< Ch1(IR) ADC data high byte */
|
||||
#define APDS99XX_REG_PDATAL (APDS99XX_REG_BASE + 0x18) /**< Proximity ADC data low byte */
|
||||
#define APDS99XX_REG_PDATAH (APDS99XX_REG_BASE + 0x19) /**< Proximity ADC data high byte */
|
||||
#endif
|
||||
#if MODULE_APDS9950 || MODULE_APDS9960
|
||||
#define APDS99XX_REG_RDATAL (APDS99XX_REG_BASE + 0x16) /**< Red channel data low byte */
|
||||
#define APDS99XX_REG_RDATAH (APDS99XX_REG_BASE + 0x17) /**< Red channel data high byte */
|
||||
#define APDS99XX_REG_GDATAL (APDS99XX_REG_BASE + 0x18) /**< Green channel data low byte */
|
||||
#define APDS99XX_REG_GDATAH (APDS99XX_REG_BASE + 0x19) /**< Green channel data high byte */
|
||||
#define APDS99XX_REG_BDATAL (APDS99XX_REG_BASE + 0x1a) /**< Blue channel data low byte */
|
||||
#define APDS99XX_REG_BDATAH (APDS99XX_REG_BASE + 0x1b) /**< Blue channel data high byte */
|
||||
#endif
|
||||
#if MODULE_APDS9950
|
||||
#define APDS99XX_REG_PDATAL (APDS99XX_REG_BASE + 0x1c) /**< Proximity ADC data low byte */
|
||||
#define APDS99XX_REG_PDATAH (APDS99XX_REG_BASE + 0x1d) /**< Proximity ADC data high byte */
|
||||
#endif
|
||||
#if MODULE_APDS9960
|
||||
#define APDS99XX_REG_PDATA (APDS99XX_REG_BASE + 0x1c) /**< Proximity ADC data (only one byte) */
|
||||
#endif
|
||||
|
||||
#define APDS99XX_REG_CLI_CMD (0xe7) /**< Clear ALS and proximity interrupt command */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Register structure definitions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* Enable states and interrupts register (APDS99XX_REG_ENABLE) */
|
||||
#define APDS99XX_REG_GEN (0x40) /**< Gesture Enable */
|
||||
#define APDS99XX_REG_PIEN (0x20) /**< Proximity Interrupt Enable */
|
||||
#define APDS99XX_REG_AIEN (0x10) /**< ALS Interrupt Enable */
|
||||
#define APDS99XX_REG_WEN (0x08) /**< Wait Enable */
|
||||
#define APDS99XX_REG_PEN (0x04) /**< Proximity Detect Enable */
|
||||
#define APDS99XX_REG_AEN (0x02) /**< ALS Enable */
|
||||
#define APDS99XX_REG_PON (0x01) /**< Power ON */
|
||||
|
||||
/* Device status register (APDS99XX_REG_STATUS) */
|
||||
#define APDS99XX_REG_CPSAT (0x80) /**< Clear Photodiode Saturation */
|
||||
#define APDS99XX_REG_PGSAT (0x40) /**< Analog saturation event occurred */
|
||||
#define APDS99XX_REG_PINT (0x20) /**< Proximity Interrupt */
|
||||
#define APDS99XX_REG_AINT (0x10) /**< ALS Interrupt */
|
||||
#define APDS99XX_REG_GINT (0x04) /**< Gesture Interrupt */
|
||||
#define APDS99XX_REG_PVALID (0x02) /**< Proximity Valid */
|
||||
#define APDS99XX_REG_AVALID (0x01) /**< ALS Valid */
|
||||
|
||||
/** Control register (APDS99XX_REG_CONTROL) */
|
||||
#define APDS99XX_REG_PDRIVE (0xc0) /**< LED Drive Strength */
|
||||
#if !MODULE_APDS9960
|
||||
#define APDS99XX_REG_PDIODE (0x30) /**< Proximity Diode Select */
|
||||
#endif
|
||||
#define APDS99XX_REG_PGAIN (0x0c) /**< Proximity Gain Control */
|
||||
#define APDS99XX_REG_AGAIN (0x03) /**< ALS (and Color) Gain Control */
|
||||
|
||||
/** Configuration register (APDS99XX_REG_CONFIG) */
|
||||
#define APDS99XX_REG_WLONG (0x02) /**< Wait Long */
|
||||
|
||||
/** Interrupt persistence filter register (APDS99XX_REG_PERS) */
|
||||
#define APDS99XX_REG_PPERS (0xf0) /**< Proximity Interrupt persistence. */
|
||||
#define APDS99XX_REG_APERS (0x0f) /**< ALS Interrupt persistence. */
|
||||
|
||||
#if MODULE_APDS9960
|
||||
/** Proximity pulse count register (APDS99XX_REG_PPCOUNT) for APDS9960 only */
|
||||
#define APDS99XX_REG_PPLEN (0xc0) /**< Proximity Pulse Length */
|
||||
#define APDS99XX_REG_PPULSE (0x3f) /**< Proximity Pulse Count */
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* APDS99XX_REGS_H */
|
||||
/** @} */
|
687
drivers/include/apds99xx.h
Normal file
687
drivers/include/apds99xx.h
Normal file
@ -0,0 +1,687 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup drivers_apds99xx APDS99XX proximity and ambient light sensors
|
||||
* @ingroup drivers_sensors
|
||||
* @ingroup drivers_saul
|
||||
* @brief Device driver for Broadcom APDS99XX proximity and ambient light sensors
|
||||
*
|
||||
* The driver can be used with following Broadcom sensors:
|
||||
* APDS9900, APDS9901, APDS9930, APDS9950, APDS9960
|
||||
*
|
||||
* The driver is divided into two parts:
|
||||
*
|
||||
* - **Basic driver** ```apds99xx```
|
||||
*
|
||||
* The base driver ```apds99xx``` only supports a basic set of functions and
|
||||
* has therefore a small size. The procedure for retrieving new data is
|
||||
* polling. Ambient light and proximity sensing are supported.
|
||||
*
|
||||
* - **Fully functional driver** ```apds99xx_full```
|
||||
*
|
||||
* The fully functional driver ```apds99xx_full``` supports all the features
|
||||
* supported by the base driver, as well as all other sensor features,
|
||||
* including interrupts and their configuration. Data-ready interrupts can be
|
||||
* used to retrieve data. In addition, threshold interrupts can be used and
|
||||
* configured.
|
||||
*
|
||||
* @note In addition to specifying whether the base driver ```apds99xx``` or
|
||||
* the fully featured driver ```apds99xx_full``` should be used, the
|
||||
* application has to declare used sensor by means of various
|
||||
* pseudomodules as follows:
|
||||
*
|
||||
* APDS9900: USEMODULE=apds9900
|
||||
* APDS9901: USEMODULE=apds9901
|
||||
* APDS9930: USEMODULE=apds9930
|
||||
* APDS9950: USEMODULE=apds9950
|
||||
* APDS9960: USEMODULE=apds9960
|
||||
*
|
||||
* This driver provides @ref drivers_saul capabilities.
|
||||
*
|
||||
* # Measurement Cycle
|
||||
*
|
||||
* APDS99XX sensor **measurement cycles** consists of the
|
||||
* following **three steps** in the given order:
|
||||
*
|
||||
* - **Proximity Sensing**
|
||||
*
|
||||
* The sensor generates a number of IR LED pulses and measures the amount
|
||||
* of the IR energy reflected by an object to determine its distance. The
|
||||
* time required for proximity sensing (```t_prx``` ) results from the
|
||||
* time it takes to generate the IR LED pulses and to accumulate the
|
||||
* reflected IR energy (```t_prx_acc```) as well as the time for the ADC
|
||||
* conversion (```t_prx_cnv```).
|
||||
*
|
||||
* t_prx = t_prx_acc + t_prx_cnv
|
||||
*
|
||||
* The time to generate the IR LED pulses and accumulate reflected IR
|
||||
* energy ```t_prx_acc``` is defined by the number of pulses (parameter
|
||||
* apds99xx_params_t::prx_pulses) and the period of one pulse ```t_prx_pulse```.
|
||||
*
|
||||
* t_prx_acc = prx_pulses * t_prx_pulse
|
||||
*
|
||||
* The ADC conversion time ```t_prx_cnv``` and the period of one
|
||||
* pulse ```t_prx_pulse``` depend on used sensor and are available as the
|
||||
* defines #APDS99XX_T_PRX_CNV and #APDS99XX_T_PRX_PULSE for calculations
|
||||
* by the application.
|
||||
* Sensor<br> | t_prx_pulse<br>APDS99XX_T_PRX_PULSE | t_prx_cnv<br>APDS99XX_T_PRX_CNV
|
||||
* ---------- | -------- | -----------------------
|
||||
* APDS9900 | 0.016 ms | 2.720 ms
|
||||
* APDS9901 | 0.016 ms | 2.720 ms
|
||||
* APDS9930 | 0.016 ms | 2.730 ms
|
||||
* APDS9950 | 0.014 ms | 2.400 ms
|
||||
* APDS9960 | 0.036 ms | 0.814 ms
|
||||
* <br>
|
||||
*
|
||||
* Proximity sensing uses the gain specified by parameter
|
||||
* apds99xx_params_t::prx_gain and the LED current specified by
|
||||
* parameter apds99xx_params_t::prx_drive.
|
||||
*
|
||||
* - **Wait**
|
||||
*
|
||||
* The sensor waits for ```t_wait``` which is defined by the number of
|
||||
* waiting steps ```wait_steps``` (apds99xx_params_t::wait_steps), the time
|
||||
* per step ```t_step```, and the wait long flag
|
||||
* (apds99xx_params_t::wait_long);
|
||||
*
|
||||
* t_wait = wait_steps * t_wait_step (if wait_long is false)
|
||||
* t_wait = wait_steps * t_wait_step * 12 (if wait_long is true)
|
||||
*
|
||||
* Parameter apds99xx_params_t::wait_steps can range from 0 to 256.
|
||||
* If apds99xx_params_t::wait_steps is 0, waiting is disabled. The time per
|
||||
* step ```t_wait_step``` depends on used sensor and is available as the
|
||||
* define #APDS99XX_T_WAIT_STEP for calculations by the application.
|
||||
* Sensor<br> | t_als_step<br>APDS99XX_T_WAIT_STEP | t_wait (wait_long=0) <br> | t_wait (wait_long=1) <br>
|
||||
* ---------- | ------- | ---------------------------| ------------------------
|
||||
* APDS9900 | 2.72 ms | ```wait_steps``` * 2.72 ms | ```wait_steps``` * 12 * 2.72 ms
|
||||
* APDS9901 | 2.72 ms | ```wait_steps``` * 2.72 ms | ```wait_steps``` * 12 * 2.72 ms
|
||||
* APDS9930 | 2.73 ms | ```wait_steps``` * 2.73 ms | ```wait_steps``` * 12 * 2.73 ms
|
||||
* APDS9950 | 2.40 ms | ```wait_steps``` * 2.40 ms | ```wait_steps``` * 12 * 2.40 ms
|
||||
* APDS9960 | 2.78 ms | ```wait_steps``` * 2.78 ms | ```wait_steps``` * 12 * 2.78 ms
|
||||
* <br>
|
||||
*
|
||||
* - **Ambient Light Sensing (ALS)**
|
||||
*
|
||||
* The sensor converts the amplified photodiode currents using integrating
|
||||
* ADCs. The time required for ALS is determined by the ALS integration
|
||||
* time ```t_als_int```, which is defined as the number of integration
|
||||
* steps ```als_steps``` (parameter apds99xx_params_t::als_steps) and the
|
||||
* time per step ```t_als_step```.
|
||||
*
|
||||
* t_als_int = als_steps * t_als_step
|
||||
*
|
||||
* Parameter apds99xx_params_t::als_steps can range from 0 to 256.
|
||||
* If apds99xx_params_t::als_steps is 0, ALS is disabled. The time per
|
||||
* step ```t_als_step``` depends on used sensor and is available as the
|
||||
* define #APDS99XX_T_ALS_STEP for calculations by the application.
|
||||
*
|
||||
* The ALS integration time in steps ```als_steps``` (parameter
|
||||
* apds99xx_params_t::als_steps) affects the resolution and the full scale
|
||||
* range ```cnt_als_max``` of ALS data.
|
||||
*
|
||||
* cnt_als_max = als_steps * cnts_p_step
|
||||
*
|
||||
* The counts per step ```cnts_p_step``` depend on used sensor and is
|
||||
* available as defines #APDS99XX_CNTS_P_STEP for calculations by the
|
||||
* application
|
||||
*
|
||||
* ALS uses the gain specified by parameter apds99xx_params_t::als_gain.
|
||||
* Sensor<br> | t_als_step<br>APDS99XX_T_ALS_STEP | cnts_p_step<br>APDS99XX_CNTS_P_STEP | t_als_int<br> | cnt_als_max<br>
|
||||
* -------- | ------- | ---- | ----------------------- | ----------------
|
||||
* APDS9900 | 2.72 ms | 1023 | ```als_steps``` * 2.72 ms | ```als_steps``` * 1023
|
||||
* APDS9901 | 2.72 ms | 1023 | ```als_steps``` * 2.72 ms | ```als_steps``` * 1023
|
||||
* APDS9930 | 2.73 ms | 1023 | ```als_steps``` * 2.73 ms | ```als_steps``` * 1023
|
||||
* APDS9950 | 2.40 ms | 1024 | ```als_steps``` * 2.40 ms | ```als_steps``` * 1024
|
||||
* APDS9960 | 2.78 ms | 1025 | ```als_steps``` * 2.78 ms | ```als_steps``` * 1025
|
||||
*
|
||||
*
|
||||
* The overall measurement cycle time is given by:
|
||||
*
|
||||
* t_cycle = t_prx + t_wait + t_als_int
|
||||
*
|
||||
* For a given ALS integration time and a given proximity sensing time,
|
||||
* the waiting time can be used to tune the overall measurement cycle time.
|
||||
*
|
||||
*
|
||||
* # Interrupts
|
||||
*
|
||||
* With the ```apds99xx_full``` driver, interrupts can be used either to
|
||||
* fetch ALS and proximity data or to detect when these data exceed
|
||||
* defined thresholds.
|
||||
*
|
||||
* To use interrupts, the application must call the #apds99xx_int_config
|
||||
* function to specify an #apds99xx_int_config_t interrupt configuration and
|
||||
* an ISR with an optional argument that is invoked when an interrupt is
|
||||
* raised. For details about configuring and enabling the interrupts, see
|
||||
* #apds99xx_int_config_t.
|
||||
*
|
||||
* @note The ISR of the application is executed in the interrupt context.
|
||||
* Therefore, it must not be blocking or time consuming. In addition, it
|
||||
* must not access the sensor directly via I2C. It should only indicate to
|
||||
* the waiting application that an interrupt has occurred, which is then
|
||||
* handled in the thread context.
|
||||
*
|
||||
* While an interrupt is being serviced, the application can use the
|
||||
* #apds99xx_int_source function to query the type of interrupts triggered
|
||||
* by the sensor. In addition, the function resets the interrupt signal.
|
||||
*
|
||||
* @note Since the interrupt signal is only reset by the function
|
||||
* #apds99xx_int_source, this function #apds99xx_int_source must be executed
|
||||
* by application, even if the type of the triggered interrupt is not of
|
||||
* interest.
|
||||
*
|
||||
* For using interrupts, the GPIO to which the sensor's **INT** output pin
|
||||
* is connected has to be defined by the application in configuration
|
||||
* parameters. The GPIO is initialized by the driver as soon as the interrupt
|
||||
* configuration with the function # apds99xx_int_config is specified.
|
||||
*
|
||||
* # Illuminance in Lux and RGB count values
|
||||
*
|
||||
* For all sensors, the clear channel and the RGB channels provide only count
|
||||
* values. APDS9900, APDS9901, and APDS9930 have an IR photodiode in addition
|
||||
* to the clear channel photodiode, which can be used to determine the
|
||||
* illuminance in Lux with an algorithm described in their datasheets.
|
||||
*
|
||||
* Unfortunately, APDS9950 and APDS9960 do not have such an additional IR
|
||||
* photodiode, and there is no document which describes an approach to
|
||||
* calculate the illuminance from the RGB channels. Therefore, it is not
|
||||
* possible to determine the illuminance for these sensors.
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @file
|
||||
*/
|
||||
|
||||
#ifndef APDS99XX_H
|
||||
#define APDS99XX_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "periph/gpio.h"
|
||||
#include "periph/i2c.h"
|
||||
|
||||
/**
|
||||
* @name APDS99XX device properties
|
||||
*
|
||||
* Defines for different device properties for supported sensor types. These
|
||||
* properties can be used by the application for calculations.
|
||||
* @{
|
||||
*/
|
||||
#if MODULE_APDS9900
|
||||
#define APDS99XX_ID (0x29) /**< Device ID of ADPS-9900 */
|
||||
#define APDS99XX_T_PRX_PULSE (16) /**< LED IR pulse period ```t_pulse``` in us */
|
||||
#define APDS99XX_T_PRX_CNV (2720) /**< Proximity ADC conversion time ```t_prx_conv``` in us */
|
||||
#define APDS99XX_T_WAIT_STEP (2720) /**< Wait time step size ```t_step``` in us */
|
||||
#define APDS99XX_T_ALS_STEP (2720) /**< ALS integration time step size ```t_step``` in */
|
||||
#define APDS99XX_CNTS_P_STEP (1023) /**< Counts per step ```cnts_p_step``` */
|
||||
|
||||
#elif MODULE_APDS9901
|
||||
#define APDS99XX_ID (0x20) /**< Device ID of ADPS-9901 */
|
||||
#define APDS99XX_T_PRX_PULSE (16) /**< LED IR pulse period ```t_pulse``` in us */
|
||||
#define APDS99XX_T_PRX_CNV (2720) /**< Proximity ADC conversion time ```t_prx_conv``` in us */
|
||||
#define APDS99XX_T_WAIT_STEP (2720) /**< Wait time step size ```t_step``` in us */
|
||||
#define APDS99XX_T_ALS_STEP (2720) /**< ALS integration time step size ```t_step``` in */
|
||||
#define APDS99XX_CNTS_P_STEP (1023) /**< Counts per step ```cnts_p_step``` */
|
||||
|
||||
#elif MODULE_APDS9930
|
||||
#define APDS99XX_ID (0x39) /**< Device ID of ADPS-9930 */
|
||||
#define APDS99XX_T_PRX_PULSE (16) /**< LED IR pulse period ```t_pulse``` in us */
|
||||
#define APDS99XX_T_PRX_CNV (2730) /**< Proximity ADC conversion time ```t_prx_conv``` in us */
|
||||
#define APDS99XX_T_WAIT_STEP (2730) /**< Wait time step size ```t_step``` in us */
|
||||
#define APDS99XX_T_ALS_STEP (2730) /**< ALS integration time step size ```t_step``` in */
|
||||
#define APDS99XX_CNTS_P_STEP (1023) /**< Counts per step ```cnts_p_step``` */
|
||||
|
||||
#elif MODULE_APDS9950
|
||||
#define APDS99XX_ID (0x69) /**< Device ID of ADPS-9950 */
|
||||
#define APDS99XX_T_PRX_PULSE (14) /**< LED IR pulse period ```t_pulse``` in us */
|
||||
#define APDS99XX_T_PRX_CNV (2400) /**< Proximity ADC conversion time ```t_prx_conv``` in us */
|
||||
#define APDS99XX_T_WAIT_STEP (2400) /**< Wait time step size ```t_step``` in us */
|
||||
#define APDS99XX_T_ALS_STEP (2400) /**< ALS integration time step size ```t_step``` in */
|
||||
#define APDS99XX_CNTS_P_STEP (1024) /**< Counts per step ```cnts_p_step``` */
|
||||
|
||||
#elif MODULE_APDS9960 || DOXYGEN
|
||||
#define APDS99XX_ID (0xab) /**< Device ID of ADPS-9960 */
|
||||
#define APDS99XX_T_PRX_PULSE (36) /**< LED IR pulse period ```t_pulse``` in us
|
||||
(for PPLEN=8 us) */
|
||||
#define APDS99XX_T_PRX_CNV (841) /**< Proximity ADC conversion time ```t_prx_conv``` in us
|
||||
(tINIT + tCNVT for PPLEN=8 us) */
|
||||
#define APDS99XX_T_WAIT_STEP (2780) /**< Wait time step size ```t_step``` in us */
|
||||
#define APDS99XX_T_ALS_STEP (2780) /**< ALS integration time step size ```t_step``` in */
|
||||
#define APDS99XX_CNTS_P_STEP (1025) /**< Counts per step ```cnts_p_step``` */
|
||||
|
||||
#else
|
||||
#error "Please provide a valid aps99xx variant (apds9900, apds9901, adps9930, apds9950, apds9960)"
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief APDS99XX I2C addresses
|
||||
*/
|
||||
#define APDS99XX_I2C_ADDRESS (0x39)
|
||||
|
||||
/**
|
||||
* @brief Definition of error codes
|
||||
*/
|
||||
typedef enum {
|
||||
APDS99XX_OK, /**< success */
|
||||
APDS99XX_ERROR_I2C, /**< I2C communication error */
|
||||
APDS99XX_ERROR_WRONG_ID, /**< wrong id read */
|
||||
APDS99XX_ERROR_NO_DATA, /**< no data are available */
|
||||
APDS99XX_ERROR_RAW_DATA, /**< reading raw data failed */
|
||||
} apds99xx_error_codes_t;
|
||||
|
||||
/**
|
||||
* @brief Ambient light sensing (ALS) gain
|
||||
*/
|
||||
typedef enum {
|
||||
APDS99XX_ALS_GAIN_1 = 0, /**< 1 x gain (default) */
|
||||
APDS99XX_ALS_GAIN_8, /**< 8 x gain */
|
||||
APDS99XX_ALS_GAIN_16, /**< 16 x gain */
|
||||
#if MODULE_APDS9950 || MODULE_APDS9960 || DOXYGEN
|
||||
APDS99XX_ALS_GAIN_64, /**< 64 x gain (APDS9950, APDS9960 only) */
|
||||
#endif /* MODULE_APDS9950 || MODULE_APDS9960 || DOXYGEN */
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || APDS9930 || DOXYGEN
|
||||
APDS99XX_ALS_GAIN_120, /**< 120 x gain (APDS9900, APDS9901, APDS9930 only) */
|
||||
#endif /* MODULE_APDS9900 || MODULE_APDS9901 || APDS9930 || DOXYGEN */
|
||||
} apds99xx_als_gain_t;
|
||||
|
||||
/**
|
||||
* @brief Proximity sensing (PRX) gain
|
||||
*/
|
||||
typedef enum {
|
||||
APDS99XX_PRX_GAIN_1 = 0, /**< 1 x gain (default) */
|
||||
#if MODULE_APDS9930 || MODULE_APDS9960 || DOXYGEN
|
||||
APDS99XX_PRX_GAIN_2, /**< 2 x gain (APDS9930, APDS9960 only) */
|
||||
APDS99XX_PRX_GAIN_4, /**< 4 x gain (APDS9930, APDS9960 only) */
|
||||
APDS99XX_PRX_GAIN_8, /**< 8 x gain (APDS9930, APDS9960 only) */
|
||||
#endif /* MODULE_APDS9930 || MODULE_APDS9960 || DOXYGEN */
|
||||
} apds99xx_prx_gain_t;
|
||||
|
||||
/**
|
||||
* @brief Proximity sensing (PRX) LED drive strength
|
||||
*/
|
||||
typedef enum {
|
||||
APDS99XX_PRX_DRIVE_100 = 0, /**< 100.0 mA (default) */
|
||||
APDS99XX_PRX_DRIVE_50, /**< 50.0 mA */
|
||||
APDS99XX_PRX_DRIVE_25, /**< 25.0 mA */
|
||||
APDS99XX_PRX_DRIVE_12_5, /**< 12.5 mA */
|
||||
} apds99xx_prx_drive_t;
|
||||
|
||||
/**
|
||||
* @brief APDS99XX device initialization parameters
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
unsigned dev; /**< I2C device (default I2C_DEV(0)) */
|
||||
|
||||
uint16_t als_steps; /**< ALS integration time in steps. If 0,
|
||||
ALS is disabled. (default 64) */
|
||||
apds99xx_als_gain_t als_gain; /**< Gain used for ALS.
|
||||
(default #APDS99XX_ALS_GAIN_1) */
|
||||
|
||||
uint8_t prx_pulses; /**< IR LED pulses for proximity sensing.
|
||||
If 0, proximity sensing is disabled.
|
||||
(default 8 as recommended) */
|
||||
apds99xx_prx_drive_t prx_drive; /**< IR LED current for proximity sensing
|
||||
(default #APDS99XX_PRX_DRIVE_100) */
|
||||
apds99xx_prx_gain_t prx_gain; /**< Gain used for proximity sensing.
|
||||
(default #APDS99XX_PRX_GAIN_1) */
|
||||
|
||||
uint16_t wait_steps; /**< Waiting time in steps. If 0, waiting is
|
||||
disabled. (default 0) */
|
||||
bool wait_long; /**< Long waiting time. If true, waitng time is
|
||||
increased by a factor 12. (default false) */
|
||||
|
||||
gpio_t int_pin; /**< interrupt pin: #GPIO_UNDEF if not used */
|
||||
|
||||
} apds99xx_params_t;
|
||||
|
||||
#if MODULE_APDS99XX_FULL || DOXYGEN
|
||||
|
||||
/**
|
||||
* @brief Interrupt configuration
|
||||
*
|
||||
* The interrupt enable flags apds99xx_int_config_t::als_int_en and
|
||||
* apds99xx_int_config_t::prx_int_en control whether ALS and proximity sensor
|
||||
* interrupts are enable.
|
||||
*
|
||||
* The persistence values apds99xx_int_config_t::als_pers and
|
||||
* apds99xx_int_config_t::prx_pers specify how many ALS or proximity
|
||||
* values have to be outside of the thresholds defined by
|
||||
* apds99xx_int_config_t::als_thresh_low and
|
||||
* apds99xx_int_config_t::als_thresh_high or
|
||||
* apds99xx_int_config_t::prx_thresh_low and
|
||||
* apds99xx_int_config_t::prx_thresh_high.
|
||||
*
|
||||
* @note If the persistence values are 0, an interrupt is generated in each
|
||||
* cycle at the end of the corresponding measurement step, regardless of the
|
||||
* values and the defined threshold. The corresponding interrupt is thus used
|
||||
* as a data-ready interrupt.
|
||||
*/
|
||||
typedef struct {
|
||||
bool als_int_en; /**< ALS interrupt enabled */
|
||||
uint8_t als_pers; /**< Number of consecutive ALS values that have to be
|
||||
outside the thresholds to generate an interrupt:
|
||||
Value | Interrupt is generated
|
||||
--------| -------
|
||||
0 | every cycle (ALS data-ready interrupt)
|
||||
1...15 | when n values are outside the thresholds */
|
||||
uint16_t als_thresh_low; /**< Low threshold value for ALS interrupts */
|
||||
uint16_t als_thresh_high; /**< High threshold value for ALS interrupts */
|
||||
|
||||
bool prx_int_en; /**< Proximity interrupt enabled */
|
||||
uint8_t prx_pers; /**< Number of consecutive proximity values that have
|
||||
to be outside the thresholds to generate an
|
||||
interrupt:
|
||||
Value | Interrupt is generated
|
||||
--------| -------
|
||||
0 | every cycle (PRX data-ready interrupt)
|
||||
1, 2, 3 | when 1, 2, or 3 values are outside the thresholds
|
||||
4...15 | when (n - 3) * 5 values are outside the thresholds */
|
||||
|
||||
uint16_t prx_thresh_low; /**< Low threshold for proximity values
|
||||
(only the low byte is used for APDS9960) */
|
||||
uint16_t prx_thresh_high; /**< High threshold for proximity values
|
||||
(only the low byte is used for APDS9960) */
|
||||
|
||||
} apds99xx_int_config_t;
|
||||
|
||||
/**
|
||||
* @brief Interrupt source
|
||||
*
|
||||
* The type is used to
|
||||
*/
|
||||
typedef struct {
|
||||
bool als_int; /**< ALS interrupt happened */
|
||||
bool prx_int; /**< Proximity interrupt happened */
|
||||
} apds99xx_int_source_t;
|
||||
|
||||
/**
|
||||
* @brief Interrupt service routine function prototype
|
||||
*/
|
||||
typedef void (*apds99xx_isr_t)(void *arg);
|
||||
|
||||
#endif /* MODULE_APDS99XX_FULL */
|
||||
|
||||
/**
|
||||
* @brief APDS99XX sensor device data structure type
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
apds99xx_params_t params; /**< device initialization parameters */
|
||||
|
||||
#if MODULE_APDS99XX_FULL || DOXYGEN
|
||||
apds99xx_isr_t isr; /**< user ISR */
|
||||
void* isr_arg; /**< user ISR argument */
|
||||
|
||||
bool gpio_init; /**< GPIO is already initialized */
|
||||
#endif /* MODULE_APDS99XX_FULL */
|
||||
|
||||
} apds99xx_t;
|
||||
|
||||
#if MODULE_APDS9950 || MODULE_APDS9960 || DOXYGEN
|
||||
/**
|
||||
* @brief RGB count value data structure (APDS9950 and APDS9960 only)
|
||||
*/
|
||||
typedef union {
|
||||
|
||||
struct {
|
||||
uint16_t red; /**< R photodiode count value (red) */
|
||||
uint16_t green; /**< G photodiode count value (green) */
|
||||
uint16_t blue; /**< B photodiode count value (blue) */
|
||||
};
|
||||
uint16_t val[3]; /**< RGB count values as array */
|
||||
|
||||
} apds99xx_rgb_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize the APDS99XX sensor device
|
||||
*
|
||||
* This function resets the sensor and initializes the sensor according to
|
||||
* given initialization parameters. All registers are reset to default values.
|
||||
*
|
||||
* @param[in] dev device descriptor of APDS99XX sensor to be initialized
|
||||
* @param[in] params configuration parameters, see #apds99xx_params_t
|
||||
*
|
||||
* @retval APDS99XX_OK on success
|
||||
* @retval APDS99XX_ERROR_* a negative error code on error,
|
||||
* see #apds99xx_error_codes_t
|
||||
*/
|
||||
int apds99xx_init(apds99xx_t *dev, const apds99xx_params_t *params);
|
||||
|
||||
/**
|
||||
* @brief Ambient light sensing (ALS) data-ready status function
|
||||
*
|
||||
* The function reads the ALS valid flag in status register and returns.
|
||||
* It can be used for polling new ambient light sensing data.
|
||||
*
|
||||
* @param[in] dev device descriptor of APDS99XX sensor
|
||||
*
|
||||
* @retval APDS99XX_OK new abmient light data available
|
||||
* @retval APDS99XX_ERROR_NO_DATA no new abmient light data available
|
||||
* @retval APDS99XX_ERROR_* negative error code,
|
||||
* see #apds99xx_error_codes_t
|
||||
*/
|
||||
int apds99xx_data_ready_als(const apds99xx_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Read one raw data sample of ambient light sensing (ALS)
|
||||
*
|
||||
* Raw ambient light sensing (ALS) data are available as 16-bit count values
|
||||
* (cnt_als). The range of these count values depends on the ALS integration
|
||||
* time apds99xx_params_t::als_steps and the ALS gain
|
||||
* apds99xx_params_t::als_gain. The maximum count value (cnt_als_max) is:
|
||||
*
|
||||
* cnt_als_max = APDS99XX_CNTS_P_STEP * als_steps * als_gain
|
||||
*
|
||||
* If there are no new data ready to read, last valid data sample is returned.
|
||||
* Function #apds99xx_data_ready_als could be used to check whether new data
|
||||
* are available before this function is called.
|
||||
*
|
||||
* @param[in] dev device descriptor of APDS99XX sensor
|
||||
* @param[out] raw raw ambient light sensing data as count value
|
||||
*
|
||||
* @retval APDS99XX_OK on success
|
||||
* @retval APDS99XX_ERROR_* negative error code,
|
||||
* see #apds99xx_error_codes_t
|
||||
*/
|
||||
int apds99xx_read_als_raw(const apds99xx_t *dev, uint16_t *raw);
|
||||
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930 || DOXYGEN
|
||||
/**
|
||||
* @brief Read one data sample of illuminance in lux
|
||||
*
|
||||
* Illuminance in lux is computed from raw ambient light sensing (ALS) data
|
||||
* which are measured in counts. Since the range of ALS data depend on ALS
|
||||
* integration time apds99xx_params_t::als_steps and the ALS gain
|
||||
* apds99xx_params_t::als_gain, these parameters also affect the sensitivity
|
||||
* of the illuminance.
|
||||
*
|
||||
* @note This function is only available for APDS9900, APDS9901 and APD9930.
|
||||
*
|
||||
* @param[in] dev device descriptor of APDS99XX sensor
|
||||
* @param[out] lux illuminance in lux
|
||||
*
|
||||
* @retval APDS99XX_OK on success
|
||||
* @retval APDS99XX_ERROR_* negative error code,
|
||||
* see #apds99xx_error_codes_t
|
||||
*/
|
||||
int apds99xx_read_illuminance(const apds99xx_t *dev, uint16_t *lux);
|
||||
|
||||
#endif /* MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930 || DOXYGEN */
|
||||
|
||||
#if MODULE_APDS9950 || MODULE_APDS9960 || DOXYGEN
|
||||
/**
|
||||
* @brief Read one raw RGB color data sample (APDS9950 and APDS9960 only)
|
||||
*
|
||||
* In APDS9950 and APDS9960 sensors, ambient light sensing (ALS) also detects
|
||||
* spectral components of the light as RGB count values. This function can
|
||||
* be used to fetch raw RGB data.
|
||||
*
|
||||
* Raw RGB data are available as 16-bit count values (cnt_als).
|
||||
* The range of these count values depends on the ALS integration time
|
||||
* apds99xx_params_t::als_steps and the ALS gain apds99xx_params_t::als_gain.
|
||||
* The maximum count value (cnt_rgb_max) is:
|
||||
*
|
||||
* cnt_rgb_max = APDS99XX_CNTS_P_STEP * als_steps * als_gain
|
||||
*
|
||||
* If there are no data ready to read, last valid data sample is returned.
|
||||
* Function #apds99xx_data_ready_als could be used to check whether new data
|
||||
* are available before this function is called.
|
||||
*
|
||||
* @note This function is only available for APDS9950 and APD9960.
|
||||
*
|
||||
* @param[in] dev device descriptor of APDS99XX sensor
|
||||
* @param[out] rgb RGB color data sample as count values
|
||||
*
|
||||
* @retval APDS99XX_OK on success
|
||||
* @retval APDS99XX_ERROR_* negative error code,
|
||||
* see #apds99xx_error_codes_t
|
||||
*/
|
||||
int apds99xx_read_rgb_raw(const apds99xx_t *dev, apds99xx_rgb_t *rgb);
|
||||
|
||||
#endif /* MODULE_APDS9950 || MODULE_APDS9960 || DOXYGEN */
|
||||
|
||||
/**
|
||||
* @brief Proximity sensing (PRX) data-ready status function
|
||||
*
|
||||
* The function reads the proximity valid flag in status register and returns.
|
||||
* It can be used for polling new proximity sensing data.
|
||||
*
|
||||
* @param[in] dev device descriptor of APDS99XX sensor
|
||||
*
|
||||
* @retval APDS99XX_OK new proximity data available
|
||||
* @retval APDS99XX_ERROR_NO_DATA no new proximity data available
|
||||
* @retval APDS99XX_ERROR_* negative error code,
|
||||
* see #apds99xx_error_codes_t
|
||||
*/
|
||||
int apds99xx_data_ready_prx(const apds99xx_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Read one data sample of proximity sensing (PRX)
|
||||
*
|
||||
* Proximity data samples are given as a 16-bit count values (cnt_prx).
|
||||
*
|
||||
* @note APDS9960 returns only 8-bit values in the range of 0 to 255.
|
||||
*
|
||||
* The range of the count values depends on the PRX LED drive strength
|
||||
* apds99xx_params_t::prx_drive the PRX gain apds99xx_params_t::prx_gain,
|
||||
* and if used, the PRX integration time apds99xx_params_t::prx_time.
|
||||
*
|
||||
* @note A number of disturbing effects such as DC noise, sensor coverage,
|
||||
* or surrounding objects cause an offset in the measured proximity values.
|
||||
* The application should remove this offset, for example, by finding the
|
||||
* minimum value ever measured and subtracting it from the current reading.
|
||||
* The minimum value is then assumed to be 0 (no proximity).
|
||||
*
|
||||
* @param[in] dev device descriptor of APDS99XX sensor
|
||||
* @param[out] prx proximity sensing data as count value
|
||||
*
|
||||
* @retval APDS99XX_OK on success
|
||||
* @retval APDS99XX_ERROR_* a negative error code on error,
|
||||
* see #apds99xx_error_codes_t
|
||||
*/
|
||||
int apds99xx_read_prx_raw(const apds99xx_t *dev, uint16_t *prx);
|
||||
|
||||
/**
|
||||
* @brief Power down the sensor
|
||||
*
|
||||
* The sensor is switched into sleep mode. It remains operational on the I2C
|
||||
* interface. Depending on the sensor used, it consumes only about 1 to 3 uA
|
||||
* in this mode.
|
||||
*
|
||||
* @param[in] dev device descriptor of APDS99XX sensor
|
||||
*
|
||||
* @retval APDS99XX_OK on success
|
||||
* @retval APDS99XX_ERROR_* a negative error code on error,
|
||||
* see #apds99xx_error_codes_t
|
||||
*/
|
||||
int apds99xx_power_down(const apds99xx_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Power up the sensor
|
||||
*
|
||||
* The sensor is woken up from sleep mode.
|
||||
*
|
||||
* @param[in] dev device descriptor of APDS99XX sensor
|
||||
*
|
||||
* @retval APDS99XX_OK on success
|
||||
* @retval APDS99XX_ERROR_* a negative error code on error,
|
||||
* see #apds99xx_error_codes_t
|
||||
*/
|
||||
int apds99xx_power_up(const apds99xx_t *dev);
|
||||
|
||||
#if MODULE_APDS99XX_FULL || DOXYGEN
|
||||
|
||||
/**
|
||||
* @brief Configure the interrupts of the sensor
|
||||
*
|
||||
* The function configures the interrupts of the sensor and sets the ISR
|
||||
* as well as its argument for handling the interrupts.
|
||||
*
|
||||
* If any interrupt is enabled by the configuration
|
||||
* (apds99xx_int_config_t::als_int_en or apds99xx_int_config_t::als_int_en are
|
||||
* set), the function
|
||||
*
|
||||
* - initializes the GPIO defined by apds99xx_params_t::int_pin, and
|
||||
* - attaches the ISR specified by the @p isr parameter to the interrupt.
|
||||
*
|
||||
* @note Since the ISR is executed in the interrupt context, it must not be
|
||||
* blocking or time consuming. In addition, it must not access the sensor
|
||||
* directly via I2C. It should only indicate to a waiting thread that an
|
||||
* interrupt has occurred, which is then handled in the thread context.
|
||||
*
|
||||
* @param[in] dev device descriptor of APDS99XX sensor
|
||||
* @param[in] cfg interrupt configuration, see #apds99xx_int_config_t
|
||||
* @param[in] isr ISR called for all types of interrupts
|
||||
* @param[in] isr_arg ISR argument, can be NULL
|
||||
*
|
||||
* @retval APDS99XX_OK on success
|
||||
* @retval APDS99XX_ERROR_* negative error code on error,
|
||||
* see #apds99xx_error_codes_t
|
||||
*/
|
||||
int apds99xx_int_config(apds99xx_t *dev, apds99xx_int_config_t* cfg,
|
||||
apds99xx_isr_t isr, void *isr_arg);
|
||||
|
||||
/**
|
||||
* @brief Get the source of an interrupt
|
||||
*
|
||||
* The function clears the interrupt signal and returns the source of the
|
||||
* interrupt. Since the interrupt signal is only reset by this function,
|
||||
* it must be executed to reset the interrupt signal, even if the type of the
|
||||
* triggered interrupt is not of interest.
|
||||
*
|
||||
* @note It must not be called from the ISR to avoid I2C bus accesses in
|
||||
* the interrupt context.
|
||||
*
|
||||
* @param[in] dev device descriptor of APDS99XX sensor
|
||||
* @param[out] src interrupt source, see #apds99xx_int_source_t
|
||||
*
|
||||
* @retval APDS99XX_OK on success
|
||||
* @retval APDS99XX_ERROR_* negative error code on error,
|
||||
* see #apds99xx_error_codes_t
|
||||
*/
|
||||
int apds99xx_int_source(apds99xx_t *dev, apds99xx_int_source_t* src);
|
||||
|
||||
#endif /* MODULE_APDS99XX_FULL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* APDS99XX_H */
|
||||
/** @} */
|
108
drivers/saul/init_devs/auto_init_apds99xx.c
Normal file
108
drivers/saul/init_devs/auto_init_apds99xx.c
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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_apds99xx
|
||||
* @{
|
||||
* @ingroup sys_auto_init_saul
|
||||
* @brief Auto initialization of Broadcom APDS99XX proximity and ambient light sensor
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @file
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef MODULE_APDS99XX
|
||||
|
||||
#include "assert.h"
|
||||
#include "log.h"
|
||||
#include "saul_reg.h"
|
||||
#include "apds99xx.h"
|
||||
#include "apds99xx_params.h"
|
||||
|
||||
/**
|
||||
* @name Reference the driver structs
|
||||
* @{
|
||||
*/
|
||||
extern saul_driver_t apds99xx_saul_prx_driver;
|
||||
extern saul_driver_t apds99xx_saul_als_driver;
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930
|
||||
extern saul_driver_t apds99xx_saul_lux_driver;
|
||||
#endif
|
||||
#if MODULE_APDS9950 || MODULE_APDS9960
|
||||
extern saul_driver_t apds99xx_saul_rgb_driver;
|
||||
#endif
|
||||
|
||||
#define APDS99XX_SAUL_ENTRY_NUM 3
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Define the number of configured sensors
|
||||
*/
|
||||
#define APDS99XX_NUM (sizeof(apds99xx_params) / sizeof(apds99xx_params[0]))
|
||||
|
||||
/**
|
||||
* @brief Allocate memory for the device descriptors
|
||||
*/
|
||||
static apds99xx_t apds99xx_devs[APDS99XX_NUM];
|
||||
|
||||
/**
|
||||
* @brief Memory for the SAUL registry entries
|
||||
*/
|
||||
static saul_reg_t saul_entries[APDS99XX_NUM * APDS99XX_SAUL_ENTRY_NUM];
|
||||
|
||||
/**
|
||||
* @brief Define the number of saul info
|
||||
*/
|
||||
#define APDS99XX_INFO_NUM (sizeof(apds99xx_saul_info) / sizeof(apds99xx_saul_info[0]))
|
||||
|
||||
void auto_init_apds99xx(void)
|
||||
{
|
||||
assert(APDS99XX_NUM == APDS99XX_INFO_NUM);
|
||||
|
||||
for (unsigned int i = 0; i < APDS99XX_NUM; i++) {
|
||||
LOG_DEBUG("[auto_init_saul] initializing apds99xx #%u\n", i);
|
||||
|
||||
if (apds99xx_init(&apds99xx_devs[i], &apds99xx_params[i]) < 0) {
|
||||
LOG_ERROR("[auto_init_saul] error initializing apds99xx #%u\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* proximity */
|
||||
saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM)].dev = &(apds99xx_devs[i]);
|
||||
saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM)].name = apds99xx_saul_info[i].name;
|
||||
saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM)].driver = &apds99xx_saul_prx_driver;
|
||||
saul_reg_add(&(saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM)]));
|
||||
|
||||
/* ambient light sensing */
|
||||
saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM) + 1].dev = &(apds99xx_devs[i]);
|
||||
saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM) + 1].name = apds99xx_saul_info[i].name;
|
||||
saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM) + 1].driver = &apds99xx_saul_als_driver;
|
||||
saul_reg_add(&(saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM) + 1]));
|
||||
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930
|
||||
/* illuminance */
|
||||
saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM) + 2].dev = &(apds99xx_devs[i]);
|
||||
saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM) + 2].name = apds99xx_saul_info[i].name;
|
||||
saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM) + 2].driver = &apds99xx_saul_lux_driver;
|
||||
saul_reg_add(&(saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM) + 2]));
|
||||
#endif
|
||||
|
||||
#if MODULE_APDS9950 || MODULE_APDS9960
|
||||
/* RGB sensing */
|
||||
saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM) + 2].dev = &(apds99xx_devs[i]);
|
||||
saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM) + 2].name = apds99xx_saul_info[i].name;
|
||||
saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM) + 2].driver = &apds99xx_saul_rgb_driver;
|
||||
saul_reg_add(&(saul_entries[(i * APDS99XX_SAUL_ENTRY_NUM) + 2]));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
typedef int dont_be_pedantic;
|
||||
#endif /* MODULE_APDS99XX */
|
@ -55,6 +55,10 @@ void saul_init_devs(void)
|
||||
extern void auto_init_adxl345(void);
|
||||
auto_init_adxl345();
|
||||
}
|
||||
if (IS_USED(MODULE_APDS99XX)) {
|
||||
extern void auto_init_apds99xx(void);
|
||||
auto_init_apds99xx();
|
||||
}
|
||||
if (IS_USED(MODULE_BMP180)) {
|
||||
extern void auto_init_bmp180(void);
|
||||
auto_init_bmp180();
|
||||
|
@ -128,6 +128,14 @@ PSEUDOMODULES += adc081c
|
||||
PSEUDOMODULES += adc101c
|
||||
PSEUDOMODULES += adc121c
|
||||
|
||||
# include variants of APDS99XX drivers as pseudo modules
|
||||
PSEUDOMODULES += apds9900
|
||||
PSEUDOMODULES += apds9901
|
||||
PSEUDOMODULES += apds9930
|
||||
PSEUDOMODULES += apds9950
|
||||
PSEUDOMODULES += apds9960
|
||||
PSEUDOMODULES += apds99xx_full
|
||||
|
||||
# full featured version of CCS811 driver as pseudo module
|
||||
PSEUDOMODULES += ccs811_full
|
||||
|
||||
|
8
tests/driver_apds99xx/Makefile
Normal file
8
tests/driver_apds99xx/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
DRIVER ?= apds9960
|
||||
|
||||
USEMODULE += $(DRIVER)
|
||||
USEMODULE += xtimer
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
94
tests/driver_apds99xx/main.c
Normal file
94
tests/driver_apds99xx/main.c
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 tests
|
||||
* @brief Test application for Vishay APDS99XX proximity and ambient light sensor
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @file
|
||||
*
|
||||
* The test application demonstrates the use of the APDS99XX driver and
|
||||
* polling the data every 200 ms.
|
||||
*
|
||||
* The application uses the following configurations:
|
||||
*
|
||||
* - 1 x ALS gain,
|
||||
* - 64 steps as ALS integration time to use the full range of uint16_t,
|
||||
* - 8 IR LED pulses at 100 mA current for proximity sensing,
|
||||
* - 1 x proximity sensing gain, and,
|
||||
* - no waits.
|
||||
*
|
||||
* Depending on the sensor, a measurement cycle takes from 156 ms (APDS9950)
|
||||
* to 179 ms (APDS9960).
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "thread.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
#include "apds99xx.h"
|
||||
#include "apds99xx_params.h"
|
||||
|
||||
#define APDS99XX_SLEEP (200 * US_PER_MS)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
apds99xx_t dev;
|
||||
|
||||
puts("APDS99XX proximity and ambient light sensor driver test application\n");
|
||||
puts("Initializing APDS99XX sensor");
|
||||
|
||||
/* initialize the sensor with default configuration parameters */
|
||||
if (apds99xx_init(&dev, &apds99xx_params[0]) == APDS99XX_OK) {
|
||||
puts("[OK]\n");
|
||||
}
|
||||
else {
|
||||
puts("[Failed]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
|
||||
/* wait for 200 ms */
|
||||
xtimer_usleep(APDS99XX_SLEEP);
|
||||
|
||||
/* check whether ambient light and proximity data are available */
|
||||
if (apds99xx_data_ready_als(&dev) == APDS99XX_OK &&
|
||||
apds99xx_data_ready_prx(&dev) == APDS99XX_OK) {
|
||||
|
||||
uint16_t _als;
|
||||
uint16_t _prx;
|
||||
|
||||
if (apds99xx_read_prx_raw(&dev, &_prx) == APDS99XX_OK) {
|
||||
printf("proximity = %d [cnts]\n", _prx);
|
||||
}
|
||||
|
||||
if (apds99xx_read_als_raw(&dev, &_als) == APDS99XX_OK) {
|
||||
printf("ambient = %d [cnts]\n", _als);
|
||||
}
|
||||
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930
|
||||
if (apds99xx_read_illuminance(&dev, &_als) == APDS99XX_OK) {
|
||||
printf("illuminance = %d [lux]\n", _als);
|
||||
}
|
||||
#endif
|
||||
#if MODULE_APDS9950 || MODULE_APDS9960
|
||||
apds99xx_rgb_t rgb;
|
||||
|
||||
if (apds99xx_read_rgb_raw(&dev, &rgb) == APDS99XX_OK) {
|
||||
printf("red = %d [cnts], green = %d [cnts], blue = %d [cnts]\n",
|
||||
rgb.red, rgb.green, rgb.blue);
|
||||
}
|
||||
#endif
|
||||
|
||||
printf("+-------------------------------------+\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
28
tests/driver_apds99xx/tests/01-run.py
Executable file
28
tests/driver_apds99xx/tests/01-run.py
Executable file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2020 Gunar Schorcht <gunar@schorcht.net>
|
||||
#
|
||||
# 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.
|
||||
|
||||
import sys
|
||||
from testrunner import run
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
child.expect('Initializing APDS99XX sensor')
|
||||
i = child.expect(['[OK]', '[Failed]'])
|
||||
if i == 1:
|
||||
print('FAILED')
|
||||
return
|
||||
child.expect(r'proximity = \d+ \[cnts\]')
|
||||
child.expect(r'ambient = \d+ \[cnts\]')
|
||||
child.expect([r'red = \d+ \[cnts\], green = \d+ \[cnts\], blue = \d+ \[cnts\]',
|
||||
r'illuminance = %d [lux]'])
|
||||
print('SUCCESS')
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(run(testfunc))
|
9
tests/driver_apds99xx_full/Makefile
Normal file
9
tests/driver_apds99xx_full/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
DRIVER ?= apds9960
|
||||
|
||||
USEMODULE += $(DRIVER)
|
||||
USEMODULE += apds99xx_full
|
||||
USEMODULE += core_thread_flags
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
145
tests/driver_apds99xx_full/main.c
Normal file
145
tests/driver_apds99xx_full/main.c
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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 tests
|
||||
* @brief Test application for Vishay APDS99XX proximity and ambient light sensor
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @file
|
||||
*
|
||||
* The test application demonstrates the use of the APDS99XX driver with
|
||||
* interrupts.
|
||||
*
|
||||
* - Ambient light sensing (ALS) interrupt is used as data-ready interrupt
|
||||
* which generates an interrupt in each cycle when the ALS has been finished.
|
||||
*
|
||||
* - Proximity sensing (PRX) interrupt is generated when the count value
|
||||
* exceeds the threshold of 200. It doesn't use the low threshold.
|
||||
*
|
||||
* The application uses the following configurations:
|
||||
*
|
||||
* - 1 x ALS gain,
|
||||
* - 64 steps as ALS integration time to use the full range of uint16_t,
|
||||
* - 8 IR LED pulses at 100 mA current for proximity sensing,
|
||||
* - 1 x proximity sensing gain, and,
|
||||
* - no waits.
|
||||
*
|
||||
* Depending on the sensor, a measurement cycle takes from 156 ms (APDS9950)
|
||||
* to 179 ms (APDS9960).
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "thread.h"
|
||||
#include "thread_flags.h"
|
||||
#include "xtimer.h"
|
||||
|
||||
#include "apds99xx.h"
|
||||
#include "apds99xx_params.h"
|
||||
|
||||
/* thread flags are used to indicate interrupt events to a waiting thread */
|
||||
#define APDS99XX_IRQ_FLAG 0x1000
|
||||
|
||||
static void apds99xx_isr (void *arg)
|
||||
{
|
||||
/*
|
||||
* This ISR function is executed in the interrupt context. It must not be
|
||||
* blocking or time-consuming and must not access the sensor directly
|
||||
* via I2C.
|
||||
*
|
||||
* Therefore, the ISR function only indicates to the waiting thread that
|
||||
* an interrupt has occurred which needs to be handled in the thread
|
||||
* context.
|
||||
*
|
||||
* For this purpose, a message could be sent to the waiting thread.
|
||||
* However, sending a message in an ISR requires a message queue in
|
||||
* the waiting thread. Since it is not relevant how many interrupts
|
||||
* have occurred since last interrupt handling, but only that an
|
||||
* interrupt has occurred, we simply use a mutex instead of
|
||||
* a message for simplicity.
|
||||
*/
|
||||
mutex_unlock(arg);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
apds99xx_t dev;
|
||||
|
||||
/* use a locked mutex for interrupt */
|
||||
mutex_t _isr_mtx = MUTEX_INIT_LOCKED;
|
||||
|
||||
/* initialize the sensor with default configuration parameters */
|
||||
puts("APDS99XX proximity and ambient light sensor driver test application\n");
|
||||
puts("Initializing APDS99XX sensor");
|
||||
|
||||
if (apds99xx_init(&dev, &apds99xx_params[0]) == APDS99XX_OK) {
|
||||
puts("[OK]\n");
|
||||
}
|
||||
else {
|
||||
puts("[Failed]");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* configure the interrupts */
|
||||
apds99xx_int_config_t int_cfg = {
|
||||
.als_int_en = true, /* ALS interrupt enabled */
|
||||
.als_pers = 0, /* ALS interrupt in each cycle (data-ready) */
|
||||
.prx_int_en = true, /* PRX interrupt enabled */
|
||||
.prx_pers = 1, /* PRX interrupt when 1 value is above threshold */
|
||||
.prx_thresh_low = 0, /* PRX low threshold, cannot be exceeded */
|
||||
.prx_thresh_high = 200, /* PRX high threshold */
|
||||
};
|
||||
apds99xx_int_config(&dev, &int_cfg, apds99xx_isr, &_isr_mtx);
|
||||
|
||||
while (1) {
|
||||
|
||||
/* wait for an interrupt which simply unlocks the mutex */
|
||||
mutex_lock(&_isr_mtx);
|
||||
|
||||
/* get the interrupt source (resets the interrupt line) */
|
||||
apds99xx_int_source_t int_src;
|
||||
apds99xx_int_source(&dev, &int_src);
|
||||
|
||||
/* on proximity sensing interrupt */
|
||||
if (int_src.prx_int) {
|
||||
uint16_t _prx;
|
||||
if (apds99xx_read_prx_raw(&dev, &_prx) == APDS99XX_OK) {
|
||||
printf("proximity = %d [cnts]\n", _prx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* on ambient light sensing interrupt */
|
||||
if (int_src.als_int) {
|
||||
uint16_t _als;
|
||||
if (apds99xx_read_als_raw(&dev, &_als) == APDS99XX_OK) {
|
||||
printf("ambient = %d [cnts]\n", _als);
|
||||
}
|
||||
|
||||
#if MODULE_APDS9900 || MODULE_APDS9901 || MODULE_APDS9930
|
||||
if (apds99xx_read_illuminance(&dev, &_als) == APDS99XX_OK) {
|
||||
printf("illuminance = %d [lux]\n", _als);
|
||||
}
|
||||
#endif
|
||||
#if MODULE_APDS9950 || MODULE_APDS9960
|
||||
apds99xx_rgb_t rgb;
|
||||
|
||||
if (apds99xx_read_rgb_raw(&dev, &rgb) == APDS99XX_OK) {
|
||||
printf("red = %d [cnts], green = %d [cnts], blue = %d [cnts]\n",
|
||||
rgb.red, rgb.green, rgb.blue);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* print final line only if there was at least one interrupt */
|
||||
if (int_src.prx_int || int_src.als_int) {
|
||||
printf("+-------------------------------------+\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
27
tests/driver_apds99xx_full/tests/01-run.py
Executable file
27
tests/driver_apds99xx_full/tests/01-run.py
Executable file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2020 Gunar Schorcht <gunar@schorcht.net>
|
||||
#
|
||||
# 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.
|
||||
|
||||
import sys
|
||||
from testrunner import run
|
||||
|
||||
|
||||
def testfunc(child):
|
||||
child.expect('Initializing APDS99XX sensor')
|
||||
i = child.expect(['[OK]', '[Failed]'])
|
||||
if i == 1:
|
||||
print('FAILED')
|
||||
return
|
||||
child.expect(r'ambient = \d+ \[cnts\]')
|
||||
child.expect([r'red = \d+ \[cnts\], green = \d+ \[cnts\], blue = \d+ \[cnts\]',
|
||||
r'illuminance = %d [lux]'])
|
||||
print('SUCCESS')
|
||||
return
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(run(testfunc))
|
Loading…
Reference in New Issue
Block a user