diff --git a/configs/stm32f103-minimum/src/Makefile b/configs/stm32f103-minimum/src/Makefile index b9062f9b5e7..1101523b7ad 100644 --- a/configs/stm32f103-minimum/src/Makefile +++ b/configs/stm32f103-minimum/src/Makefile @@ -109,6 +109,10 @@ ifeq ($(CONFIG_LCD_PCD8544),y) CSRCS += stm32_pcd8544.c endif +ifeq ($(CONFIG_SENSORS_APDS9960),y) + CSRCS += stm32_apds9960.c +endif + ifeq ($(CONFIG_SENSORS_QENCODER),y) CSRCS += stm32_qencoder.c endif diff --git a/configs/stm32f103-minimum/src/stm32_apds9960.c b/configs/stm32f103-minimum/src/stm32_apds9960.c new file mode 100644 index 00000000000..1174ebf3ab2 --- /dev/null +++ b/configs/stm32f103-minimum/src/stm32_apds9960.c @@ -0,0 +1,184 @@ +/************************************************************************************ + * configs/stm32f103-minimum/src/stm32_apds9960.c + * + * Copyright (C) 2017 Alan Carvalho de Assis. All rights reserved. + * Author: Alan Carvalho de Assis + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include +#include + +#include +#include + +#include "stm32.h" +#include "stm32_i2c.h" +#include "stm32f103_minimum.h" + +#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_APDS9960) + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ + +#define APDS9960_I2C_PORTNO 1 /* On I2C1 */ + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +struct stm32_apds9960config_s +{ + /* Configuration structure as seen by the APDS-9960 driver */ + + struct apds9960_config_s config; + + /* Additional private definitions only known to this driver */ + + FAR void *arg; /* Argument to pass to the interrupt handler */ + FAR xcpt_t isr; /* ISR Handler */ +}; + +/************************************************************************************ + * Private Function Prototypes + ************************************************************************************/ + +static int apds9960_irq_attach(FAR struct apds9960_config_s *state, xcpt_t isr, + FAR void *arg); + +/************************************************************************************ + * Private Data + ************************************************************************************/ + +/* A reference to a structure of this type must be passed to the APDS-9960 + * driver. This structure provides information about the configuration + * of the APDS-9960 and provides some board-specific hooks. + * + * Memory for this structure is provided by the caller. It is not copied + * by the driver and is presumed to persist while the driver is active. The + * memory must be writable because, under certain circumstances, the driver + * may modify frequency or X plate resistance values. + */ + +static struct stm32_apds9960config_s g_apds9960config = +{ + .config = + { + .irq_attach = apds9960_irq_attach, + }, +}; + +/************************************************************************************ + * Private Functions + ************************************************************************************/ + +/* Attach the APDS-9960 interrupt handler to the GPIO interrupt */ + +static int apds9960_irq_attach(FAR struct apds9960_config_s *state, xcpt_t isr, + FAR void *arg) +{ + FAR struct stm32_apds9960config_s *priv = + (FAR struct stm32_apds9960config_s *)state; + irqstate_t flags; + + sninfo("apds9960_irq_attach\n"); + + flags = enter_critical_section(); + + /* Setup interrupt for Falling Edge */ + + (void)stm32_gpiosetevent(GPIO_APDS9960_INT, false, true, true, isr, arg); + + leave_critical_section(flags); + + return OK; +} + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/************************************************************************************ + * Name: stm32_apds9960initialize + * + * Description: + * Initialize and register the APDS9960 gesture sensor. + * + * Input parameters: + * devpath - The full path to the driver to register. E.g., "/dev/gest0" + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ************************************************************************************/ + +int stm32_apds9960initialize(FAR const char *devpath) +{ + FAR struct i2c_master_s *i2c; + int ret; + + sninfo("Initializing APDS9960!\n"); + + /* Configure the GPIO interrupt */ + + stm32_configgpio(GPIO_APDS9960_INT); + + /* Initialize I2C */ + + i2c = stm32_i2cbus_initialize(APDS9960_I2C_PORTNO); + if (i2c == NULL) + { + return -ENODEV; + } + + /* Save this i2c in the config */ + + g_apds9960config.config.i2c_dev = i2c; + g_apds9960config.config.i2c_addr = APDS9960_I2C_ADDR; + + /* Then register the gesture sensor */ + + ret = apds9960_register(devpath, &g_apds9960config.config); + if (ret < 0) + { + snerr("ERROR: Failed registering APDS-9960!\n"); + } + + return ret; +} + +#endif /* CONFIG_I2C && CONFIG_SENSORS_APDS9960 && CONFIG_STM32_I2C1 */ diff --git a/configs/stm32f103-minimum/src/stm32_bringup.c b/configs/stm32f103-minimum/src/stm32_bringup.c index fcd02bc641f..3794698a466 100644 --- a/configs/stm32f103-minimum/src/stm32_bringup.c +++ b/configs/stm32f103-minimum/src/stm32_bringup.c @@ -292,6 +292,16 @@ int stm32_bringup(void) } #endif +#ifdef CONFIG_SENSORS_APDS9960 + /* Register the APDS-9960 gesture sensor */ + + ret = stm32_apds9960initialize("/dev/gest0"); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: stm32_apds9960initialize() failed: %d\n", ret); + } +#endif + #ifdef CONFIG_SENSORS_VEML6070 /* Register the UV-A light sensor */ diff --git a/configs/stm32f103-minimum/src/stm32f103_minimum.h b/configs/stm32f103-minimum/src/stm32f103_minimum.h index 1f9b66f933d..e6f3698ad3e 100644 --- a/configs/stm32f103-minimum/src/stm32f103_minimum.h +++ b/configs/stm32f103-minimum/src/stm32f103_minimum.h @@ -83,6 +83,10 @@ #define GPIO_HCSR04_TRIG (GPIO_OUTPUT|GPIO_CNF_OUTPP|GPIO_MODE_50MHz|\ GPIO_OUTPUT_CLEAR|GPIO_PORTA|GPIO_PIN1) +/* Pin for APDS-9960 sensor */ + +#define GPIO_APDS9960_INT (GPIO_INPUT|GPIO_CNF_INFLOAT|GPIO_PORTA|GPIO_PIN0) + /* SPI chip selects */ #define FLASH_SPI1_CS (GPIO_OUTPUT|GPIO_CNF_OUTPP|GPIO_MODE_50MHz|\ diff --git a/drivers/sensors/Kconfig b/drivers/sensors/Kconfig index 285116ff79c..688d9d4113c 100644 --- a/drivers/sensors/Kconfig +++ b/drivers/sensors/Kconfig @@ -3,6 +3,18 @@ # see the file kconfig-language.txt in the NuttX tools repository. # +config SENSORS_APDS9960 + bool "Avago APDS-9960 Gesture Sensor support" + default n + select I2C + ---help--- + Enable driver support for the Avago APDS-9960 gesture sensor. + +config APDS9960_I2C_FREQUENCY + int "APDS-9960 I2C frequency" + default 400000 + depends on SENSORS_APDS9960 + config SENSORS_AS5048B bool "AMS AS5048B Magnetic Rotary Encoder support" default n diff --git a/drivers/sensors/Make.defs b/drivers/sensors/Make.defs index 22c7703fb72..42b6e6f0b8f 100644 --- a/drivers/sensors/Make.defs +++ b/drivers/sensors/Make.defs @@ -49,6 +49,10 @@ endif ifeq ($(CONFIG_I2C),y) +ifeq ($(CONFIG_SENSORS_APDS9960),y) + CSRCS += apds9960.c +endif + ifeq ($(CONFIG_SENSORS_AS5048B),y) CSRCS += as5048b.c endif diff --git a/drivers/sensors/apds9960.c b/drivers/sensors/apds9960.c new file mode 100644 index 00000000000..799b8499dd4 --- /dev/null +++ b/drivers/sensors/apds9960.c @@ -0,0 +1,1288 @@ +/**************************************************************************** + * drivers/sensors/apds9960.c + * Character driver for the APDS9960 Gesture Sensor + * + * Copyright (C) 2017 Alan Carvalho de Assis. All rights reserved. + * Author: Alan Carvalho de Assis + * + * This driver is based on APDS-9960 Arduino library developed by + * Shawn Hymel from SparkFun Electronics and released under public + * domain. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_APDS9960) + +/**************************************************************************** + * Pre-process Definitions + ****************************************************************************/ + +#ifndef CONFIG_APDS9960_I2C_FREQUENCY +# define CONFIG_APDS9960_I2C_FREQUENCY 400000 +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct apds9960_dev_s +{ + FAR struct apds9960_config_s *config; /* Hardware Configuration */ + struct gesture_data_s gesture_data; /* Gesture data container */ + int gesture_ud_delta; /* UP/DOWN delta */ + int gesture_lr_delta; /* LEFT/RIGHT delta */ + int gesture_ud_count; /* UP/DOWN counter */ + int gesture_lr_count; /* LEFT/RIGHT counter */ + int gesture_near_count; /* Near distance counter */ + int gesture_far_count; /* Far distance counter */ + int gesture_state; /* Gesture machine state */ + int gesture_motion; /* Gesture motion direction */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Reset gesture values */ + +static void apds9960_resetgesture(FAR struct apds9960_dev_s *priv); + +/* Setup default initial values */ + +static int apds9960_setdefault(FAR struct apds9960_dev_s *priv); + +/* Probe function to verify if sensor is present */ + +static int apds9960_probe(FAR struct apds9960_dev_s *priv); + +/* I2C Helpers */ + +static int apds9960_i2c_read(FAR struct apds9960_dev_s *priv, + uint8_t const regaddr, FAR uint8_t *regval, int len); +static int apds9960_i2c_read8(FAR struct apds9960_dev_s *priv, + uint8_t const regaddr, FAR uint8_t *regval); +static int apds9960_i2c_write(FAR struct apds9960_dev_s *priv, + uint8_t const *data, int len); +static int apds9960_i2c_write8(FAR struct apds9960_dev_s *priv, + uint8_t const regaddr, uint8_t regval); + +/* Character driver methods */ + +static int apds9960_open(FAR struct file *filep); +static int apds9960_close(FAR struct file *filep); +static ssize_t apds9960_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static ssize_t apds9960_write(FAR struct file *filep, + FAR const char *buffer, size_t buflen); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_apds9960_fops = +{ + apds9960_open, /* open */ + apds9960_close, /* close */ + apds9960_read, /* read */ + apds9960_write, /* write */ + NULL, /* seek */ + NULL /* ioctl */ +#ifndef CONFIG_DISABLE_POLL + , NULL /* poll */ +#endif +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + , NULL /* unlink */ +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: apds9960_int_handler + * + * Description: + * Interrupt handler (ISR) for APDS-99600 INT pin. + * + ****************************************************************************/ + +static int apds9960_int_handler(int irq, FAR void *context, FAR void *arg) +{ + FAR struct apds9960_dev_s *priv = (FAR struct apds9960_dev_s *)arg; + + DEBUGASSERT(priv != NULL); + + snwarn("Interrupt on I2C add %d!\n", priv->config->i2c_addr); + + return OK; +} + +/**************************************************************************** + * Name: apds9960_resetgesture + * + * Description: + * Reset gesture values + * + ****************************************************************************/ + +static void apds9960_resetgesture(FAR struct apds9960_dev_s *priv) +{ + priv->gesture_data.index = 0; + priv->gesture_data.total_gestures = 0; + + priv->gesture_ud_delta = 0; + priv->gesture_lr_delta = 0; + + priv->gesture_ud_count = 0; + priv->gesture_lr_count = 0; + + priv->gesture_near_count = 0; + priv->gesture_far_count = 0; + + priv->gesture_state = 0; + priv->gesture_motion = DIR_NONE; +} + +/**************************************************************************** + * Name: apds9960_setdefault + * + * Description: + * Verify if sensor is present. Check if ID is 0xAB. + * + ****************************************************************************/ + +static int apds9960_setdefault(FAR struct apds9960_dev_s *priv) +{ + int ret; + + /* Set default values for ambient light and proximity registers */ + + ret = apds9960_i2c_write8(priv, APDS9960_ATIME, DEFAULT_ATIME); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_ATIME!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_WTIME, DEFAULT_WTIME); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_WTIME!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_PPULSE, DEFAULT_PPULSE); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_PPULSE!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_POFFSET_UR, DEFAULT_POFFSET_UR); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_POFFSET_UR!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_POFFSET_DL, DEFAULT_POFFSET_DL); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_POFFSET_DL!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_CONFIG1, DEFAULT_CONFIG1); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_CONFIG1!\n"); + return ret; + } + + /* Set LED driver strength to 100mA, AGAIN 4X and PGAIN 4X */ + + ret = apds9960_i2c_write8(priv, APDS9960_CONTROL, DEFAULT_CONTROL); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_CONTROL!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_PILT, DEFAULT_PILT); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_PILT!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_PIHT, DEFAULT_PIHT); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_PIHT!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_AILTL, DEFAULT_AILTL); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_AILTL!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_AILTH, DEFAULT_AILTH); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_AILTH!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_AIHTL, DEFAULT_AIHTL); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_AIHTL!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_AIHTH, DEFAULT_AIHTH); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_AIHTH!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_PERS, DEFAULT_PERS); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_PERS!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_CONFIG2, DEFAULT_CONFIG2); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_CONFIG2!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_CONFIG3, DEFAULT_CONFIG3); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_CONFIG3!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_GPENTH, DEFAULT_GPENTH); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_GPENTH!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_GEXTH, DEFAULT_GEXTH); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_GEXTH!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_GCONFIG1, DEFAULT_GCONFIG1); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_GCONFIG1!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_GCONFIG2, DEFAULT_GCONFIG2); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_GCONFIG2!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_GOFFSET_U, DEFAULT_GOFFSET_U); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_GOFFSET_U!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_GOFFSET_D, DEFAULT_GOFFSET_D); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_GOFFSET_D!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_GOFFSET_L, DEFAULT_GOFFSET_L); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_GOFFSET_L!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_GOFFSET_R, DEFAULT_GOFFSET_R); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_GOFFSET_R!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_GPULSE, DEFAULT_GPULSE); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_GPULSE!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_GCONFIG3, DEFAULT_GCONFIG3); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_GCONFIG3!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_GCONFIG4, DEFAULT_GCONFIG4); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_GCONFIG3!\n"); + return ret; + } + + ret = apds9960_i2c_write8(priv, APDS9960_WTIME, 0xff); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_WTIME!\n"); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: apds9960_probe + * + * Description: + * Verify if sensor is present. Check if ID is 0xAB. + * + ****************************************************************************/ + +static int apds9960_probe(FAR struct apds9960_dev_s *priv) +{ + int ret; + uint8_t id; + + ret = apds9960_i2c_read8(priv, APDS9960_ID, &id); + if (ret < 0) + { + snerr("ERROR: Failed to initialize the APDS9960!\n"); + return ret; + } + + if (id != APDS9960_ID_VAL) + { + return -ENODEV; + } + + return OK; +} + +/**************************************************************************** + * Name: apds9960_i2c_read + * + * Description: + * Read an arbitrary number of bytes starting at regaddr + * + ****************************************************************************/ + +static int apds9960_i2c_read(FAR struct apds9960_dev_s *priv, + uint8_t const regaddr, FAR uint8_t *regval, int len) +{ + struct i2c_config_s config; + int ret = -1; + + /* Set up the I2C configuration */ + + config.frequency = CONFIG_APDS9960_I2C_FREQUENCY; + config.address = priv->config->i2c_addr; + config.addrlen = 7; + + /* Write the register address to read from */ + + ret = i2c_write(priv->config->i2c_dev, &config, ®addr, 1); + if (ret < 0) + { + snerr ("i2c_write failed: %d\n", ret); + return ret; + } + + /* Read "len" bytes from regaddr */ + + ret = i2c_read(priv->config->i2c_dev, &config, regval, len); + if (ret < 0) + { + snerr ("i2c_read failed: %d\n", ret); + return ret; + } + + return OK; +} + +/**************************************************************************** + * Name: apds9960_i2c_read8 + * + * Description: + * Read 8-bit register + * + ****************************************************************************/ + +static int apds9960_i2c_read8(FAR struct apds9960_dev_s *priv, + uint8_t const regaddr, FAR uint8_t *regval) +{ + int ret; + + ret = apds9960_i2c_read(priv, regaddr, regval, 1); + + return ret; +} + +/**************************************************************************** + * Name: apds9960_i2c_write + * + * Description: + * Write an arbitrary number of bytes starting at regaddr. + * + ****************************************************************************/ + +static int apds9960_i2c_write(FAR struct apds9960_dev_s *priv, + uint8_t const *data, int len) +{ + struct i2c_config_s config; + int ret; + + /* Set up the I2C configuration */ + + config.frequency = CONFIG_APDS9960_I2C_FREQUENCY; + config.address = priv->config->i2c_addr; + config.addrlen = 7; + + /* Write the data */ + + ret = i2c_write(priv->config->i2c_dev, &config, data, len); + if (ret < 0) + { + snerr("ERROR: i2c_write failed: %d\n", ret); + } + + return ret; +} + +/**************************************************************************** + * Name: apds9960_i2c_write8 + * + * Description: + * Write an arbitrary number of bytes starting at regaddr. + * + ****************************************************************************/ + +static int apds9960_i2c_write8(FAR struct apds9960_dev_s *priv, + uint8_t const regaddr, uint8_t regval) +{ + int ret; + uint8_t data[2]; + + /* Create the addr:val data */ + + data[0] = regaddr; + data[1] = regval; + + ret = apds9960_i2c_write(priv, data, 2); + + return ret; +} + +/**************************************************************************** + * Name: apds9960_isgestureavailable + * + * Description: + * Return true is gesture data is valid. + * + ****************************************************************************/ + +static bool apds9960_isgestureavailable(FAR struct apds9960_dev_s *priv) +{ + int ret; + uint8_t val; + + /* Read value from GSTATUS register */ + + ret = apds9960_i2c_read8(priv, APDS9960_GSTATUS, &val); + if (ret < 0) + { + snerr("ERROR: Failed to read APDS9960_GSTATUS!\n"); + return ret; + } + + /* Return true/false based on GVALID bit */ + + if ((val & GVALID) == GVALID) + { + return true; + } + else + { + return false; + } +} + +/**************************************************************************** + * Name: apds9960_processgesture + * + * Description: + * Process the data read from the photodiodes + * + ****************************************************************************/ + +bool apds9960_processgesture(FAR struct apds9960_dev_s *priv) +{ + uint8_t u_first = 0; + uint8_t d_first = 0; + uint8_t l_first = 0; + uint8_t r_first = 0; + uint8_t u_last = 0; + uint8_t d_last = 0; + uint8_t l_last = 0; + uint8_t r_last = 0; + int ud_ratio_first; + int lr_ratio_first; + int ud_ratio_last; + int lr_ratio_last; + int ud_delta; + int lr_delta; + int i; + + /* If we have less than 4 total gestures, that's not enough */ + + if (priv->gesture_data.total_gestures <= 4) + { + snerr("ERROR: We don't have enough gesture: %d\n", + priv->gesture_data.total_gestures); + return false; + } + + /* Check to make sure our data isn't out of bounds */ + + if ((priv->gesture_data.total_gestures <= 32) && \ + (priv->gesture_data.total_gestures > 0)) + { + /* Find the first value in U/D/L/R above the threshold */ + + for (i = 0; i < priv->gesture_data.total_gestures; i++) + { + if ((priv->gesture_data.u_data[i] > GESTURE_THRESHOLD_OUT) && \ + (priv->gesture_data.d_data[i] > GESTURE_THRESHOLD_OUT) && \ + (priv->gesture_data.l_data[i] > GESTURE_THRESHOLD_OUT) && \ + (priv->gesture_data.r_data[i] > GESTURE_THRESHOLD_OUT)) + { + u_first = priv->gesture_data.u_data[i]; + d_first = priv->gesture_data.d_data[i]; + l_first = priv->gesture_data.l_data[i]; + r_first = priv->gesture_data.r_data[i]; + break; + } + } + + /* If one of the _first values is 0, then there is no good data */ + + if ((u_first == 0) || (d_first == 0) || \ + (l_first == 0) || (r_first == 0)) + { + snerr("ERROR: First value is zero! U=%d, D=%d, L=%d, R=%d\n", \ + u_first, d_first, l_first, r_first); + return false; + } + + /* Find the last value in U/D/L/R above the threshold */ + + for (i = priv->gesture_data.total_gestures - 1; i >= 0; i--) + { + sninfo("Finding last: \n"); + sninfo("U: %03d\n", priv->gesture_data.u_data[i]); + sninfo("D: %03d\n", priv->gesture_data.d_data[i]); + sninfo("L: %03d\n", priv->gesture_data.l_data[i]); + sninfo("R: %03d\n", priv->gesture_data.r_data[i]); + + if ((priv->gesture_data.u_data[i] > GESTURE_THRESHOLD_OUT) && + (priv->gesture_data.d_data[i] > GESTURE_THRESHOLD_OUT) && + (priv->gesture_data.l_data[i] > GESTURE_THRESHOLD_OUT) && + (priv->gesture_data.r_data[i] > GESTURE_THRESHOLD_OUT)) + { + u_last = priv->gesture_data.u_data[i]; + d_last = priv->gesture_data.d_data[i]; + l_last = priv->gesture_data.l_data[i]; + r_last = priv->gesture_data.r_data[i]; + break; + } + } + } + + /* Calculate the first vs. last ratio of up/down and left/right */ + + ud_ratio_first = ((u_first - d_first) * 100) / (u_first + d_first); + lr_ratio_first = ((l_first - r_first) * 100) / (l_first + r_first); + ud_ratio_last = ((u_last - d_last) * 100) / (u_last + d_last); + lr_ratio_last = ((l_last - r_last) * 100) / (l_last + r_last); + + sninfo("Last Values: \n"); + sninfo("U: %03d\n", u_last); + sninfo("D: %03d\n", d_last); + sninfo("L: %03d\n", l_last); + sninfo("R: %03d\n", r_last); + + sninfo("Ratios: \n"); + sninfo("UD Fi: %03d\n", ud_ratio_first); + sninfo("UD La: %03d\n", ud_ratio_last); + sninfo("LR Fi: %03d\n", lr_ratio_first); + sninfo("LR La: %03d\n", lr_ratio_last); + + /* Determine the difference between the first and last ratios */ + + ud_delta = ud_ratio_last - ud_ratio_first; + lr_delta = lr_ratio_last - lr_ratio_first; + + sninfo("Deltas: \n"); + sninfo("UD: %03d\n", ud_delta); + sninfo("LR: %03d\n", lr_delta); + + /* Accumulate the UD and LR delta values */ + + priv->gesture_ud_delta += ud_delta; + priv->gesture_lr_delta += lr_delta; + + sninfo("Accumulations: \n"); + sninfo("UD: %03d\n", priv->gesture_ud_delta); + sninfo("LR: %03d\n", priv->gesture_lr_delta); + + /* Determine U/D gesture */ + + if (priv->gesture_ud_delta >= GESTURE_SENSITIVITY_1) + { + priv->gesture_ud_count = 1; + } + else + { + if (priv->gesture_ud_delta <= -GESTURE_SENSITIVITY_1) + { + priv->gesture_ud_count = -1; + } + else + { + priv->gesture_ud_count = 0; + } + } + + /* Determine L/R gesture */ + + if (priv->gesture_lr_delta >= GESTURE_SENSITIVITY_1) + { + priv->gesture_lr_count = 1; + } + else + { + if (priv->gesture_lr_delta <= -GESTURE_SENSITIVITY_1) + { + priv->gesture_lr_count = -1; + } + else + { + priv->gesture_lr_count = 0; + } + } + + /* Determine Near/Far gesture */ + + if ((priv->gesture_ud_count == 0) && (priv->gesture_lr_count == 0)) + { + if ((abs(ud_delta) < GESTURE_SENSITIVITY_2) && \ + (abs(lr_delta) < GESTURE_SENSITIVITY_2)) + { + if ((ud_delta == 0) && (lr_delta == 0)) + { + priv->gesture_near_count++; + } + else + { + if ((ud_delta != 0) || (lr_delta != 0)) + { + priv->gesture_far_count++; + } + } + + if ((priv->gesture_near_count >= 10) && \ + (priv->gesture_far_count >= 2)) + { + if ((ud_delta == 0) && (lr_delta == 0)) + { + priv->gesture_state = NEAR_STATE; + } + else + { + if ( (ud_delta != 0) && (lr_delta != 0)) + { + priv->gesture_state = FAR_STATE; + } + } + + return true; + } + } + } + else + { + if ((abs(ud_delta) < GESTURE_SENSITIVITY_2) && \ + (abs(lr_delta) < GESTURE_SENSITIVITY_2)) + { + if ((ud_delta == 0) && (lr_delta == 0)) + { + priv->gesture_near_count++; + } + + if (priv->gesture_near_count >= 10) + { + priv->gesture_ud_count = 0; + priv->gesture_lr_count = 0; + priv->gesture_ud_delta = 0; + priv->gesture_lr_delta = 0; + } + } + } + + sninfo(" UD_CT: %03d\n", priv->gesture_ud_count); + sninfo(" LR_CT: %03d\n", priv->gesture_lr_count); + sninfo(" NEAR_CT: %03d\n", priv->gesture_near_count); + sninfo(" FAR_CT: %03d\n", priv->gesture_far_count); + sninfo("----------------------\n"); + + return false; +} + +/**************************************************************************** + * Name: apds9960_readgesture + * + * Description: + * Read the photodiode data, process/decode it and return the guess + * + ****************************************************************************/ + +bool apds9960_decodegesture(FAR struct apds9960_dev_s *priv) +{ + /* Return if near or far event is detected */ + + if (priv->gesture_state == NEAR_STATE) + { + priv->gesture_motion = DIR_NEAR; + return true; + } + else + { + if (priv->gesture_state == FAR_STATE) + { + priv->gesture_motion = DIR_FAR; + return true; + } + } + + /* Determine swipe direction */ + + if ((priv->gesture_ud_count == -1) && (priv->gesture_lr_count == 0)) + { + priv->gesture_motion = DIR_UP; + } + else + { + if ((priv->gesture_ud_count == 1) && (priv->gesture_lr_count == 0)) + { + priv->gesture_motion = DIR_DOWN; + } + else + { + if ((priv->gesture_ud_count == 0) && (priv->gesture_lr_count == 1)) + { + priv->gesture_motion = DIR_RIGHT; + } + else + { + if ((priv->gesture_ud_count == 0) && + (priv->gesture_lr_count == -1)) + { + priv->gesture_motion = DIR_LEFT; + } + else + { + if ((priv->gesture_ud_count == -1) && + (priv->gesture_lr_count == 1)) + { + if (abs(priv->gesture_ud_delta) > \ + abs(priv->gesture_lr_delta)) + { + priv->gesture_motion = DIR_UP; + } + else + { + priv->gesture_motion = DIR_RIGHT; + } + } + else + { + if ((priv->gesture_ud_count == 1) && \ + (priv->gesture_lr_count == -1)) + { + if (abs(priv->gesture_ud_delta) > \ + abs(priv->gesture_lr_delta)) + { + priv->gesture_motion = DIR_DOWN; + } + else + { + priv->gesture_motion = DIR_LEFT; + } + } + else + { + if ((priv->gesture_ud_count == -1) && \ + (priv->gesture_lr_count == -1)) + { + if (abs(priv->gesture_ud_delta) > \ + abs(priv->gesture_lr_delta)) + { + priv->gesture_motion = DIR_UP; + } + else + { + priv->gesture_motion = DIR_LEFT; + } + } + else + { + if ((priv->gesture_ud_count == 1) && \ + (priv->gesture_lr_count == 1)) + { + if (abs(priv->gesture_ud_delta) > \ + abs(priv->gesture_lr_delta)) + { + priv->gesture_motion = DIR_DOWN; + } + else + { + priv->gesture_motion = DIR_RIGHT; + } + } + else + { + return false; + } + } + } + } + } + } + } + } + + return true; +} + +/**************************************************************************** + * Name: apds9960_readgesture + * + * Description: + * Read the photodiode data, process/decode it and return the guess + * + ****************************************************************************/ + +int apds9960_readgesture(FAR struct apds9960_dev_s *priv) +{ + uint8_t fifo_level = 0; + uint8_t bytes_read = 0; + uint8_t fifo_data[128]; + uint8_t gstatus; + int motion; + int ret; + int i; + + /* Make sure that power and gesture is on and data is valid */ + + if (!apds9960_isgestureavailable(priv)) + { + return DIR_NONE; + } + + /* Keep looping as long as gesture data is valid */ + + while(1) + { + /* Wait some time to collect next batch of FIFO data */ + + nxsig_usleep(FIFO_PAUSE_TIME); + + /* Get the contents of the STATUS register. Is data still valid? */ + + ret = apds9960_i2c_read8(priv, APDS9960_GSTATUS, &gstatus); + if (ret < 0) + { + snerr("ERROR: Failed to read APDS9960_GSTATUS!\n"); + return ret; + } + + /* If we have valid data, read in FIFO */ + + if ((gstatus & GVALID) == GVALID) + { + /* Read the current FIFO level */ + + ret = apds9960_i2c_read8(priv, APDS9960_GFLVL, &fifo_level); + if (ret < 0) + { + snerr("ERROR: Failed to read APDS9960_GFLVL!\n"); + return ret; + } + + sninfo("FIFO Level: %d\n", fifo_level); + + /* If there's stuff in the FIFO, read it into our data block */ + + if (fifo_level > 0) + { + bytes_read = fifo_level * 4; + ret = apds9960_i2c_read(priv, APDS9960_GFIFO_U, + (uint8_t *) fifo_data, bytes_read); + if (ret < 0) + { + snerr("ERROR: Failed to read APDS9960_GFIFO_U!\n"); + return ret; + } + + sninfo("\nFIFO Dump:\n"); + for (i = 0; i < fifo_level; i++) + { + sninfo("U: %03d | D: %03d | L: %03d | R: %03d\n", + fifo_data[i], fifo_data[i+1], + fifo_data[i+2], fifo_data[i+3]); + } + + sninfo("\n"); + + /* If at least 1 set of data, sort the data into U/D/L/R */ + + if (bytes_read >= 4) + { + for (i = 0; i < bytes_read; i += 4) + { + priv->gesture_data.u_data[priv->gesture_data.index] = fifo_data[i + 0]; + priv->gesture_data.d_data[priv->gesture_data.index] = fifo_data[i + 1]; + priv->gesture_data.l_data[priv->gesture_data.index] = fifo_data[i + 2]; + priv->gesture_data.r_data[priv->gesture_data.index] = fifo_data[i + 3]; + priv->gesture_data.index++; + priv->gesture_data.total_gestures++; + } + + sninfo("Up Data:\n"); + for (i = 0; i < priv->gesture_data.total_gestures; i++) + { + sninfo("%03d\n", priv->gesture_data.u_data[i]); + } + + sninfo("\n"); + + /* Filter and process gesture data. Decode near/far state */ + + if (apds9960_processgesture(priv)) + { + if (apds9960_decodegesture(priv)) + { + //***TODO: U-Turn Gestures + //sninfo("gesture_motion = %d\n", gesture_motion); + } + } + + /* Reset data */ + + priv->gesture_data.index = 0; + priv->gesture_data.total_gestures = 0; + } + } + } + else + { + /* Determine best guessed gesture and clean up */ + + nxsig_usleep(FIFO_PAUSE_TIME); + apds9960_decodegesture(priv); + motion = priv->gesture_motion; + + snwarn("END: %d\n", priv->gesture_motion); + + if (motion == DIR_LEFT) + { + snwarn("RESULT = LEFT\n"); + } + + if (motion == DIR_RIGHT) + { + snwarn("RESULT = RIGHT\n"); + } + + if (motion == DIR_UP) + { + snwarn("RESULT = UP\n"); + } + + if (motion == DIR_DOWN) + { + snwarn("RESULT = DOWN\n"); + } + + apds9960_resetgesture(priv); + return motion; + } + } +} + +/**************************************************************************** + * Name: apds9960_open + * + * Description: + * This function is called whenever the APDS9960 device is opened. + * + ****************************************************************************/ + +static int apds9960_open(FAR struct file *filep) +{ + return OK; +} + +/**************************************************************************** + * Name: apds9960_close + * + * Description: + * This routine is called when the APDS9960 device is closed. + * + ****************************************************************************/ + +static int apds9960_close(FAR struct file *filep) +{ + return OK; +} + +/**************************************************************************** + * Name: apds9960_read + ****************************************************************************/ + +static ssize_t apds9960_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct inode *inode; + FAR struct apds9960_dev_s *priv; + + DEBUGASSERT(filep); + inode = filep->f_inode; + + DEBUGASSERT(inode && inode->i_private); + priv = (FAR struct apds9960_dev_s *)inode->i_private; + + /* Check if the user is reading the right size */ + + if (buflen != 4) + { + snerr("ERROR: You need to read 4 bytes from this sensor!\n"); + return -EINVAL; + } + + /* Read Gesture */ + + apds9960_readgesture(priv); + + /* Just return something, when done this driver will return keyboard arrows */ + + buffer = (FAR char *) priv->gesture_data.u_data; + + nxsig_usleep(1000); + + return buflen; +} + +/**************************************************************************** + * Name: apds9960_write + ****************************************************************************/ + +static ssize_t apds9960_write(FAR struct file *filep, + FAR const char *buffer, size_t buflen) +{ + return -ENOSYS; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: apds9960_register + * + * Description: + * Register the APDS9960 character device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/gest0" + * i2c - An instance of the I2C interface to use to communicate with APDS9960 + * addr - The I2C address of the APDS9960. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int apds9960_register(FAR const char *devpath, + FAR struct apds9960_config_s *config) +{ + int ret; + + /* Sanity check */ + + DEBUGASSERT(i2c != NULL); + + /* Initialize the APDS9960 device structure */ + + FAR struct apds9960_dev_s *priv = + (FAR struct apds9960_dev_s *)kmm_malloc(sizeof(struct apds9960_dev_s)); + + if (priv == NULL) + { + snerr("ERROR: Failed to allocate instance\n"); + return -ENOMEM; + } + + priv->config = config; + + /* Probe APDS9960 device */ + + ret = apds9960_probe(priv); + if (ret != OK) + { + snerr("ERROR: APDS-9960 is not responding!\n"); + return ret; + } + + /* Turn the device OFF to make it sane */ + + ret = apds9960_i2c_write8(priv, APDS9960_ENABLE, 0); + if (ret < 0) + { + snerr("ERROR: Failed to initialize the APDS9960!\n"); + return ret; + } + + /* Wait 100ms */ + + nxsig_usleep(100000); + + /* Initialize the device (leave RESET) */ + + ret = apds9960_i2c_write8(priv, APDS9960_ENABLE, PON); + if (ret < 0) + { + snerr("ERROR: Failed to initialize the APDS9960!\n"); + return ret; + } + + /* Set default initial register values */ + + ret = apds9960_setdefault(priv); + if (ret < 0) + { + snerr("ERROR: Failed to initialize the APDS9960!\n"); + return ret; + } + + /* Reset gesture values */ + + apds9960_resetgesture(priv); + + /* Enable the Gesture mode and interruptions */ + + ret = apds9960_i2c_write8(priv, APDS9960_GCONFIG4, (GMODE | GIEN)); + if (ret < 0) + { + snerr("ERROR: Failed to write APDS9960_GCONFIG4!\n"); + return ret; + } + + /* Enable the Gesture mode (Proximity mode is needed for gesture mode) */ + + ret = apds9960_i2c_write8(priv, APDS9960_ENABLE, PON | PEN | GEN | WEN); + if (ret < 0) + { + snerr("ERROR: Failed to initialize the APDS9960!\n"); + return ret; + } + + /* Register the character driver */ + + ret = register_driver(devpath, &g_apds9960_fops, 0666, priv); + if (ret < 0) + { + snerr("ERROR: Failed to register driver: %d\n", ret); + kmm_free(priv); + } + + /* Attach to the interrupt */ + + priv->config->irq_attach(priv->config, apds9960_int_handler, priv); + + return ret; +} + +#endif /* CONFIG_I2C && CONFIG_SENSORS_APDS9960 */ diff --git a/include/nuttx/sensors/apds9960.h b/include/nuttx/sensors/apds9960.h new file mode 100644 index 00000000000..6ed71d962db --- /dev/null +++ b/include/nuttx/sensors/apds9960.h @@ -0,0 +1,396 @@ +/******************************************************************************************** + * drivers/sensors/apds9960.h + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Alan Carvalho de Assis + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ********************************************************************************************/ + +#ifndef __INCLUDE_NUTTX_SENSORS_APDS9960_H +#define __INCLUDE_NUTTX_SENSORS_APDS9960_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#if defined(CONFIG_SENSORS_APDS9960) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Default APDS-9960 Identification value */ + +#define APDS9960_ID_VAL 0xab + +/* Device I2C Address */ + +#define APDS9960_I2C_ADDR 0x39 + +/* Registers mapping */ + +#define APDS9960_ENABLE 0x80 /* Power ON, Prox./ALS/Gest (Int.) Enable */ +#define APDS9960_ATIME 0x82 /* ALS ADC Integration Time */ +#define APDS9960_WTIME 0x83 /* Wait Time */ +#define APDS9960_AILTL 0x84 /* ALS Low Threshold, lower byte */ +#define APDS9960_AILTH 0x85 /* ALS Low Threshold, higher byte */ +#define APDS9960_AIHTL 0x86 /* ALS High Threshold, lower byte */ +#define APDS9960_AIHTH 0x87 /* ALS High Threshold, higher byte */ +#define APDS9960_PILT 0x89 /* Proximity Low Threshold */ +#define APDS9960_PIHT 0x8b /* Proximity High Threshold */ +#define APDS9960_PERS 0x8c /* Proximity Persistence */ +#define APDS9960_CONFIG1 0x8d /* Wait Long Enable */ +#define APDS9960_PPULSE 0x8e /* Proximity Pulse */ +#define APDS9960_CONTROL 0x8f /* Gain and LED control */ +#define APDS9960_CONFIG2 0x90 /* Saturation interrupt and LED Boost config. */ +#define APDS9960_ID 0x92 /* APDS-9960 Identification register */ +#define APDS9960_STATUS 0x93 /* Proximity Saturation/Inter/Valid status */ +#define APDS9960_CDATAL 0x94 /* Clear Data, Low byte */ +#define APDS9960_CDATAH 0x95 /* Clear Data, High byte */ +#define APDS9960_RDATAL 0x96 /* Red Data, Low byte */ +#define APDS9960_RDATAH 0x97 /* Red Data, High byte */ +#define APDS9960_GDATAL 0x98 /* Green Data, Low byte */ +#define APDS9960_GDATAH 0x99 /* Green Data, High byte */ +#define APDS9960_BDATAL 0x9a /* Blue Data, Low byte */ +#define APDS9960_BDATAH 0x9b /* Blue Data, High byte */ +#define APDS9960_PDATA 0x9c /* Proximity data */ +#define APDS9960_POFFSET_UR 0x9d /* Prox. offset UP/RIGHT */ +#define APDS9960_POFFSET_DL 0x9e /* Prox. offset DOWN/LEFT */ +#define APDS9960_CONFIG3 0x9f /* Prox. gain compensation U/D/L/R Mask config */ +#define APDS9960_GPENTH 0xa0 /* Gesture Prox. Entry Threshold */ +#define APDS9960_GEXTH 0xa1 /* Gesture Exit Threshold */ +#define APDS9960_GCONFIG1 0xa2 /* Gesture Config1 register */ +#define APDS9960_GCONFIG2 0xa3 /* Gesture Config2 register */ +#define APDS9960_GOFFSET_U 0xa4 /* Gesture Offset Up */ +#define APDS9960_GOFFSET_D 0xa5 /* Gesture Offset Down */ +#define APDS9960_GPULSE 0xa6 /* Gesture Pulse config */ +#define APDS9960_GOFFSET_L 0xa7 /* Gesture Offset Left */ +#define APDS9960_GOFFSET_R 0xa9 /* Gesture Offset Up */ +#define APDS9960_GCONFIG3 0xaa /* Gesture Config3 register */ +#define APDS9960_GCONFIG4 0xab /* Gesture Config4 register */ +#define APDS9960_GFLVL 0xae /* Gesture FIFO level */ +#define APDS9960_GSTATUS 0xaf /* Gesture Status */ +#define APDS9960_PICLEAR 0xe5 /* Proximity Interrupt Clear */ +#define APDS9960_CICLEAR 0xe5 /* Clear Channel Interrupt Clear */ +#define APDS9960_AICLEAR 0xe7 /* All Non-Gesture Interrupt Clear */ +#define APDS9960_GFIFO_U 0xfc /* Gesture FIFO Data UP */ +#define APDS9960_GFIFO_D 0xfd /* Gesture FIFO Data DOWN */ +#define APDS9960_GFIFO_L 0xfe /* Gesture FIFO Data LEFT */ +#define APDS9960_GFIFO_R 0xff /* Gesture FIFO Data RIGHT */ + +/* ENABLE Register */ + +#define PON (1 << 0) /* Bit 0: Power ON */ +#define AEN (1 << 1) /* Bit 1: ALS Enable ??? */ +#define PEN (1 << 2) /* Bit 2: Prox. Enable */ +#define WEN (1 << 3) /* Bit 3: Wait Enable */ +#define AIEN (1 << 4) /* Bit 4: ALS Int. Enable */ +#define PIEN (1 << 5) /* Bit 5: Prox. Int. Enable */ +#define GEN (1 << 6) /* Bit 6: Gesture Enable */ + +/* PERS Register */ + +#define APERS_SHIFT 0 /* Bits 0-3: ALS Int. Persistence */ +#define APERS_MASK (0xf << PERS_APERS_SHIFT) +#define PPERS_SHIFT 4 /* Bits 4-7: Prox. Int. Persistence */ +#define PPERS_MASK (0xf << PERS_APERS_SHIFT) + +/* CONFIG1 Register */ + +#define LOWPOW (1 << 0) /* Bit 0: Low Power (Gesture) */ +#define WLONG (1 << 1) /* Bit 1: Wait Long Enable (ALS) */ + +/* PPULSE Register */ + +#define PPULSE_SHIFT 0 /* Bits 0-5: Prox. Pulse Count (1 up to 64 pulses) */ +#define PPULSE_MASK (0x3f << PPULSE_SHIFT) +# define PPULSE_NUM(n) ((n-1) << PPULSE_SHIFT) +#define PPLEN_SHIFT 6 /* Bits 6-7: Prox. Pulse Lenght */ +#define PPLEN_MASK (3 << PPLEN_SHIFT) +# define PPLEN_4US (0 << PPLEN_SHIFT) +# define PPLEN_8US (1 << PPLEN_SHIFT) +# define PPLEN_16US (2 << PPLEN_SHIFT) +# define PPLEN_32US (3 << PPLEN_SHIFT) + +/* CONTROL Register */ + +#define AGAIN_SHIFT 0 /* Bits 0-1: ALS Gain Control */ +#define AGAIN_MASK (3 << AGAIN_SHIFT) +# define AGAIN_1X (0 << AGAIN_SHIFT) +# define AGAIN_2X (1 << AGAIN_SHIFT) +# define AGAIN_4X (2 << AGAIN_SHIFT) +# define AGAIN_8X (3 << AGAIN_SHIFT) +#define PGAIN_SHIFT 2 /* Bits 2-3: Proximity Gain Control */ +#define PGAIN_MASK (3 << PGAIN_SHIFT) +# define PGAIN_1X (0 << PGAIN_SHIFT) +# define PGAIN_2X (1 << PGAIN_SHIFT) +# define PGAIN_4X (2 << PGAIN_SHIFT) +# define PGAIN_8X (3 << PGAIN_SHIFT) +#define LDRIVE_SHIFT 6 /* Bits 6-7: LED Drive Strength */ +#define LDRIVE_MASK (3 << LDRIVE_SHIFT) +# define LDRIVE_100MA (0 << LDRIVE_SHIFT) +# define LDRIVE_50MA (1 << LDRIVE_SHIFT) +# define LDRIVE_25MA (2 << LDRIVE_SHIFT) +# define LDRIVE_12p5MA (3 << LDRIVE_SHIFT) + +/* CONFIG2 Register */ + +#define LEDBOOST_SHIFT 4 /* Bits 4-5: Proximity/Gesture LED Boost */ +#define LEDBOOST_MASK (3 << LEDBOOST_SHIFT) +# define LEDBOOST_100 (0 << LEDBOOST_SHIFT) /* Boost LED 100% */ +# define LEDBOOST_150 (1 << LEDBOOST_SHIFT) /* Boost LED 150% */ +# define LEDBOOST_200 (2 << LEDBOOST_SHIFT) /* Boost LED 200% */ +# define LEDBOOST_300 (3 << LEDBOOST_SHIFT) /* Boost LED 300% */ +#define CPSIEN (1 << 6) /* Bit 6: Clear diode Saturation Interrupt Enable */ +#define PSIEN (1 << 7) /* Bit 7: Proximity Saturation Interrupt Enable */ + +/* STATUS Regiser */ + +#define AVALID (1 << 0) /* Bit 0: ALS Valid */ +#define PVALID (1 << 1) /* Bit 1: Proximity Valid */ +#define AINT (1 << 4) /* Bit 4: ALS Interrupt */ +#define PINT (1 << 5) /* Bit 5: Proximity Interrupt */ +#define PGSAT (1 << 6) /* Bit 6: Proximity Saturation */ +#define CPSAT (1 << 7) /* Bit 7: ALS Interrupt */ + +/* CONFIG3 Register */ + +#define PMSK_R (1 << 0) /* Bit 0: Proximity Mask RIGHT Enable */ +#define PMSK_L (1 << 1) /* Bit 1: Proximity Mask LEFT Enable */ +#define PMSK_D (1 << 2) /* Bit 2: Proximity Mask DOWN Enable */ +#define PMSK_U (1 << 3) /* Bit 3: Proximity Mask UP Enable */ +#define PCMP (1 << 5) /* Bit 5: Proximity Gain Compensation Enable */ + +/* GCONFIG1 Register */ + +#define GEXPERS_SHIFT 0 /* Bits 0-1: Gesture Exit Persistence */ +#define GEXPERS_MASK (3 << GEXPERS_SHIFT) +# define GEXPERS_1ST (0 << GEXPERS_SHIFT) /* 1st 'gesture end' exits */ +# define GEXPERS_2ND (1 << GEXPERS_SHIFT) /* 2nd 'gesture end' exits */ +# define GEXPERS_4TH (2 << GEXPERS_SHIFT) /* 4th 'gesture end' exits */ +# define GEXPERS_7TH (3 << GEXPERS_SHIFT) /* 7th 'gesture end' exits */ +#define GEXMSK_SHIFT 2 /* Bits 2-5: Gesture Exit Mask */ +#define GEXMSK_MASK (0xf << GEXMSK_SHIFT) +#define GFIFOTH_SHIFT 6 /* Bits 6-7: Gesture FIFO Threshold */ +#define GFIFOTH_MASK (3 << GFIFOTH_SHIFT) +# define GFIFOTH_1DS (0 << GFIFOTH_SHIFT) /* Interrupt after 1 dataset */ +# define GFIFOTH_4DS (1 << GFIFOTH_SHIFT) /* Interrupt after 4 datasets */ +# define GFIFOTH_8DS (2 << GFIFOTH_SHIFT) /* Interrupt after 8 datasets */ +# define GFIFOTH_16DS (3 << GFIFOTH_SHIFT) /* Interrupt after 8 datasets */ + +/* GCONFIG2 Register */ + +#define GWTIME_SHIFT 0 /* Bits 0-2: Gesture Wait Time */ +#define GWTIME_MASK (7 << GWTIME_SHIFT) +# define GWTIME_0MS (0 << GWTIME_SHIFT) +# define GWTIME_2p8MS (1 << GWTIME_SHIFT) +# define GWTIME_5p6MS (2 << GWTIME_SHIFT) +# define GWTIME_8p4MS (3 << GWTIME_SHIFT) +# define GWTIME_14MS (4 << GWTIME_SHIFT) +# define GWTIME_22p4MS (5 << GWTIME_SHIFT) +# define GWTIME_30p8MS (6 << GWTIME_SHIFT) +# define GWTIME_39p2MS (7 << GWTIME_SHIFT) +#define GLDRIVE_SHIFT 3 /* Bits 3-4: Gesture LED Drive Strength */ +#define GLDRIVE_MASK (3 << GLDRIVE_SHIFT) +# define GLDRIVE_100MA (0 << GLDRIVE_SHIFT) +# define GLDRIVE_50MA (1 << GLDRIVE_SHIFT) +# define GLDRIVE_25MA (2 << GLDRIVE_SHIFT) +# define GLDRIVE_12p5MA (3 << GLDRIVE_SHIFT) +#define GGAIN_SHIFT 5 /* Bits 5-6: Gesture Gain Control */ +#define GGAIN_MASK (3 << GGAIN_SHIFT) +# define GGAIN_1X (0 << GGAIN_SHIFT) +# define GGAIN_2X (1 << GGAIN_SHIFT) +# define GGAIN_4X (2 << GGAIN_SHIFT) +# define GGAIN_8X (3 << GGAIN_SHIFT) + +/* GPULSE Register */ + +#define GPULSE_SHIFT 0 /* Bits 0-5: Pulse Count */ +#define GPULSE_MASK (0x3f << GPULSE_SHIFT) +# define GPULSE_NUM(n) ((n-1) << GPULSE_SHIFT) +#define GPLEN_SHIFT 6 /* Bit 6-7: Gesture Pulse Length */ +#define GPLEN_MASK (3 << GPLEN_SHIFT) +# define GPLEN_4US (0 << GPLEN_SHIFT) +# define GPLEN_8US (1 << GPLEN_SHIFT) +# define GPLEN_16US (2 << GPLEN_SHIFT) +# define GPLEN_32US (3 << GPLEN_SHIFT) + +/* GCONFIG3 Register */ + +#define GDIMS_SHIFT 0 /* Bits 0-1: Gesture Dimension Select */ +#define GDIMS_MASK (3 << GDIMS_SHIFT) + +/* GCONFIG4 Register */ + +#define GMODE (1 << 0) /* Bit 0: Gesture Mode */ +#define GIEN (1 << 1) /* Bit 1: Gesture Interrupt Enable */ + +/* GSTATUS Register */ + +#define GVALID (1 << 0) /* Bit 0: Gesture Valid */ +#define GFOV (1 << 1) /* Bit 1: Gesture FIFO Overflow */ + +/* Default values for device initialization */ + +#define DEFAULT_ATIME 219 /* 103ms */ +#define DEFAULT_WTIME 246 /* 27ms */ +#define DEFAULT_PPULSE (PPLEN_16US | PPULSE_NUM(8)) /* 16us and 8 pulses */ +#define DEFAULT_POFFSET_UR 0 /* 0 offset */ +#define DEFAULT_POFFSET_DL 0 /* 0 offset */ +#define DEFAULT_CONFIG1 0x60 /* No 12x wait (WTIME) factor */ +#define DEFAULT_PILT 0 /* Low proximity threshold */ +#define DEFAULT_PIHT 50 /* High proximity threshold */ +#define DEFAULT_PERS 0x11 /* 2 consecutive prox or ALS for int. */ +#define DEFAULT_CONTROL (AGAIN_4X | PGAIN_4X | LDRIVE_100MA) +#define DEFAULT_CONFIG2 0x01 /* No saturation interrupts or LED boost */ +#define DEFAULT_CONFIG3 0 /* Enable all photodiodes, no SAI */ +#define DEFAULT_GPENTH 40 /* Threshold for entering gesture mode */ +#define DEFAULT_GEXTH 30 /* Threshold for exiting gesture mode */ +#define DEFAULT_GCONFIG1 GFIFOTH_4DS /* 4 gesture events for int., 1 for exit */ +#define DEFAULT_GCONFIG2 (GGAIN_4X | GLDRIVE_100MA /*| GWTIME_2p8MS*/ ) +#define DEFAULT_GOFFSET_U 0 /* No offset scaling for gesture mode */ +#define DEFAULT_GOFFSET_D 0 /* No offset scaling for gesture mode */ +#define DEFAULT_GOFFSET_L 0 /* No offset scaling for gesture mode */ +#define DEFAULT_GOFFSET_R 0 /* No offset scaling for gesture mode */ +#define DEFAULT_GPULSE (GPLEN_32US | GPULSE_NUM(10)) /* 32us, 10 pulses */ +#define DEFAULT_GCONFIG3 0 /* All photodiodes active during gesture */ +#define DEFAULT_GCONFIG4 0 /* Disable gesture interrupts */ +#define DEFAULT_AILTL 0xFF /* Force interrupt for calibration */ +#define DEFAULT_AILTH 0xFF /* Force interrupt for calibration */ +#define DEFAULT_AIHTL 0 +#define DEFAULT_AIHTH 0 +#define FIFO_PAUSE_TIME 30000 /* Wait period (ms) between FIFO reads */ + +/* Gesture parameters */ + +#define GESTURE_THRESHOLD_OUT 10 +#define GESTURE_SENSITIVITY_1 50 +#define GESTURE_SENSITIVITY_2 20 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Direction definitions */ + +enum +{ + DIR_NONE, + DIR_LEFT, + DIR_RIGHT, + DIR_UP, + DIR_DOWN, + DIR_NEAR, + DIR_FAR, + DIR_ALL +}; + +/* State definitions */ + +enum +{ + NA_STATE, + NEAR_STATE, + FAR_STATE, + ALL_STATE +}; + +/* Interrupt configuration data structure */ + +struct apds9960_config_s +{ + CODE int (*irq_attach)(FAR struct apds9960_config_s * state, xcpt_t isr, + FAR void *arg); + FAR struct i2c_master_s *i2c_dev; + uint8_t i2c_addr; +}; + +/* Container for gesture data */ + +struct gesture_data_s +{ + uint8_t u_data[32]; + uint8_t d_data[32]; + uint8_t l_data[32]; + uint8_t r_data[32]; + uint8_t index; + uint8_t total_gestures; + uint8_t in_threshold; + uint8_t out_threshold; +}; + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: apds9960_register + * + * Description: + * Register the APDS9960 character device as 'devpath' + * + * Input Parameters: + * devpath - The full path to the driver to register. E.g., "/dev/gest0" + * i2c - An instance of the I2C interface to use to communicate with + * APDS9960 + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +struct i2c_master_s; +int apds9960_register(FAR const char *devpath, + FAR struct apds9960_config_s *config); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_SENSORS_APDS9960 */ +#endif /* __INCLUDE_NUTTX_SENSORS_APDS9960_H */