nuttx/drivers/sensors/apds9960.c

1347 lines
38 KiB
C

/****************************************************************************
* 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 <acassis@gmail.com>
*
* 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 <nuttx/config.h>
#include <errno.h>
#include <debug.h>
#include <stdlib.h>
#include <nuttx/kmalloc.h>
#include <nuttx/signal.h>
#include <nuttx/random.h>
#include <nuttx/wqueue.h>
#include <nuttx/fs/fs.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/sensors/apds9960.h>
#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 work_s work; /* Supports ISR "bottom half" */
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 */
sem_t sample_sem; /* Semaphore for sample data */
};
/****************************************************************************
* 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);
/* Work queue */
static void apds9960_worker(FAR void *arg);
/* Gesture processing/decoding functions */
static int apds9960_readgesture(FAR struct apds9960_dev_s *priv);
static bool apds9960_decodegesture(FAR struct apds9960_dev_s *priv);
static bool apds9960_processgesture(FAR struct apds9960_dev_s *priv);
static bool apds9960_isgestureavailable(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_worker
****************************************************************************/
static void apds9960_worker(FAR void *arg)
{
FAR struct apds9960_dev_s *priv = (FAR struct apds9960_dev_s *)arg;
int ret;
DEBUGASSERT(priv != NULL);
ret = apds9960_readgesture(priv);
if (ret != DIR_NONE)
{
sninfo("Got a valid gesture!\n");
}
}
/****************************************************************************
* 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)
{
int ret;
FAR struct apds9960_dev_s *priv = (FAR struct apds9960_dev_s *)arg;
DEBUGASSERT(priv != NULL);
/* Transfer processing to the worker thread. Since APDS-9960 interrupts
* are disabled while the work is pending, no special action should be
* required to protect the work queue.
*/
DEBUGASSERT(priv->work.worker == NULL);
ret = work_queue(HPWORK, &priv->work, apds9960_worker, priv, 0);
if (ret != 0)
{
snerr("ERROR: Failed to queue work: %d\n", ret);
}
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;
}
/****************************************************************************
* 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, &regaddr, 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
*
****************************************************************************/
static 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_decodegesture
*
* Description:
* Decode the sensor data and return true if there is some valid data
*
****************************************************************************/
static 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
*
****************************************************************************/
static 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");
}
/* Increase semaphore to indicate new data */
nxsem_post(&priv->sample_sem);
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;
int ret;
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 < 1)
{
snerr("ERROR: You need to read at least 1 byte from this sensor!\n");
return -EINVAL;
}
/* Wait for data available */
do
{
ret = nxsem_wait(&priv->sample_sem);
/* The only case that an error should occur here is if the wait was
* awakened by a signal.
*/
DEBUGASSERT(ret == OK || ret == -EINTR);
}
while (ret == -EINTR);
buffer[0] = (char) priv->gesture_motion;
buflen = 1;
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;
priv->work.worker = NULL;
priv->gesture_motion = DIR_NONE;
nxsem_init(&priv->sample_sem, 0, 0);
/* 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 */