diff --git a/drivers/sensors/apds9960.c b/drivers/sensors/apds9960.c index 799b8499dd4..528f549d5d8 100644 --- a/drivers/sensors/apds9960.c +++ b/drivers/sensors/apds9960.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -71,16 +72,18 @@ 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 */ + 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 */ }; /**************************************************************************** @@ -99,6 +102,17 @@ static int apds9960_setdefault(FAR struct apds9960_dev_s *priv); 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, @@ -143,6 +157,24 @@ static const struct file_operations g_apds9960_fops = * 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 * @@ -153,11 +185,23 @@ static const struct file_operations g_apds9960_fops = 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); - snwarn("Interrupt on I2C add %d!\n", priv->config->i2c_addr); + /* 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; } @@ -185,7 +229,6 @@ static void apds9960_resetgesture(FAR struct apds9960_dev_s *priv) priv->gesture_far_count = 0; priv->gesture_state = 0; - priv->gesture_motion = DIR_NONE; } /**************************************************************************** @@ -587,7 +630,7 @@ static bool apds9960_isgestureavailable(FAR struct apds9960_dev_s *priv) * ****************************************************************************/ -bool apds9960_processgesture(FAR struct apds9960_dev_s *priv) +static bool apds9960_processgesture(FAR struct apds9960_dev_s *priv) { uint8_t u_first = 0; uint8_t d_first = 0; @@ -811,14 +854,14 @@ bool apds9960_processgesture(FAR struct apds9960_dev_s *priv) } /**************************************************************************** - * Name: apds9960_readgesture + * Name: apds9960_decodegesture * * Description: - * Read the photodiode data, process/decode it and return the guess + * Decode the sensor data and return true if there is some valid data * ****************************************************************************/ -bool apds9960_decodegesture(FAR struct apds9960_dev_s *priv) +static bool apds9960_decodegesture(FAR struct apds9960_dev_s *priv) { /* Return if near or far event is detected */ @@ -944,7 +987,7 @@ bool apds9960_decodegesture(FAR struct apds9960_dev_s *priv) * ****************************************************************************/ -int apds9960_readgesture(FAR struct apds9960_dev_s *priv) +static int apds9960_readgesture(FAR struct apds9960_dev_s *priv) { uint8_t fifo_level = 0; uint8_t bytes_read = 0; @@ -1086,6 +1129,10 @@ int apds9960_readgesture(FAR struct apds9960_dev_s *priv) snwarn("RESULT = DOWN\n"); } + /* Increase semaphore to indicate new data */ + + nxsem_post(&priv->sample_sem); + apds9960_resetgesture(priv); return motion; } @@ -1127,6 +1174,7 @@ static ssize_t apds9960_read(FAR struct file *filep, FAR char *buffer, { FAR struct inode *inode; FAR struct apds9960_dev_s *priv; + int ret; DEBUGASSERT(filep); inode = filep->f_inode; @@ -1136,21 +1184,28 @@ static ssize_t apds9960_read(FAR struct file *filep, FAR char *buffer, /* Check if the user is reading the right size */ - if (buflen != 4) + if (buflen < 1) { - snerr("ERROR: You need to read 4 bytes from this sensor!\n"); + snerr("ERROR: You need to read at least 1 byte from this sensor!\n"); return -EINVAL; } - /* Read Gesture */ + /* Wait for data available */ - apds9960_readgesture(priv); + do + { + ret = nxsem_wait(&priv->sample_sem); - /* Just return something, when done this driver will return keyboard arrows */ + /* The only case that an error should occur here is if the wait was + * awakened by a signal. + */ - buffer = (FAR char *) priv->gesture_data.u_data; + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); - nxsig_usleep(1000); + buffer[0] = (char) priv->gesture_motion; + buflen = 1; return buflen; } @@ -1206,6 +1261,8 @@ int apds9960_register(FAR const char *devpath, } priv->config = config; + nxsem_init(&priv->sample_sem, 0, 0); + priv->gesture_motion = DIR_NONE; /* Probe APDS9960 device */ diff --git a/include/nuttx/sensors/apds9960.h b/include/nuttx/sensors/apds9960.h index 6ed71d962db..7cbe27848aa 100644 --- a/include/nuttx/sensors/apds9960.h +++ b/include/nuttx/sensors/apds9960.h @@ -122,9 +122,9 @@ /* PERS Register */ -#define APERS_SHIFT 0 /* Bits 0-3: ALS Int. Persistence */ +#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_SHIFT 4 /* Bits 4-7: Prox. Int. Persistence */ #define PPERS_MASK (0xf << PERS_APERS_SHIFT) /* CONFIG1 Register */ @@ -134,10 +134,10 @@ /* PPULSE Register */ -#define PPULSE_SHIFT 0 /* Bits 0-5: Prox. Pulse Count (1 up to 64 pulses) */ +#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_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) @@ -146,19 +146,19 @@ /* CONTROL Register */ -#define AGAIN_SHIFT 0 /* Bits 0-1: ALS Gain Control */ +#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_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_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) @@ -167,7 +167,7 @@ /* CONFIG2 Register */ -#define LEDBOOST_SHIFT 4 /* Bits 4-5: Proximity/Gesture LED Boost */ +#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% */ @@ -195,15 +195,15 @@ /* GCONFIG1 Register */ -#define GEXPERS_SHIFT 0 /* Bits 0-1: Gesture Exit Persistence */ +#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_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_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 */ @@ -212,7 +212,7 @@ /* GCONFIG2 Register */ -#define GWTIME_SHIFT 0 /* Bits 0-2: Gesture Wait Time */ +#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) @@ -222,13 +222,13 @@ # 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_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_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) @@ -237,10 +237,10 @@ /* GPULSE Register */ -#define GPULSE_SHIFT 0 /* Bits 0-5: Pulse Count */ +#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_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) @@ -249,7 +249,7 @@ /* GCONFIG3 Register */ -#define GDIMS_SHIFT 0 /* Bits 0-1: Gesture Dimension Select */ +#define GDIMS_SHIFT 0 /* Bits 0-1: Gesture Dimension Select */ #define GDIMS_MASK (3 << GDIMS_SHIFT) /* GCONFIG4 Register */ @@ -278,7 +278,7 @@ #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_GCONFIG1 GFIFOTH_8DS /* 8 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 */