From 4a5e18a6db331b1a79ac1086e74c7d5063d3d8f6 Mon Sep 17 00:00:00 2001
From: Gregory Nutt <gnutt@nuttx.org>
Date: Mon, 18 Dec 2017 10:31:49 -0600
Subject: [PATCH] drivers/ft5x06.c:  Add additional configuration options: 
 Optimize if multi-touch capability is not used.  Add options to swap X/Y and
 thresholding to reduce the rate of false alarm reports (with no motion).

---
 configs/lpcxpresso-lpc54628/README.txt   |  24 +++-
 configs/lpcxpresso-lpc54628/fb/defconfig |   1 +
 drivers/input/Kconfig                    |  36 +++++-
 drivers/input/ft5x06.c                   | 134 ++++++++++++++++++++++-
 4 files changed, 183 insertions(+), 12 deletions(-)

diff --git a/configs/lpcxpresso-lpc54628/README.txt b/configs/lpcxpresso-lpc54628/README.txt
index f88a7553ad..a9a91f3c4e 100644
--- a/configs/lpcxpresso-lpc54628/README.txt
+++ b/configs/lpcxpresso-lpc54628/README.txt
@@ -67,10 +67,7 @@ STATUS
   2017-12-18:  Added an option to the FT5x06 driver to support a timer-
     based poll instead of interrupts.  This is very inefficient in that it
     will introduce delays in touchscreen response and will consume more CPU
-    bandwidth.
-
-    The FT5x06 driver is not, however, functional.  It is generating hard
-    faults.
+    bandwidth.  The driver appears to be functional.
 
 Configurations
 ==============
@@ -178,8 +175,23 @@ Configurations
          Touch panel I2C address:  0x38
 
     4. The touchscreen test program at apps/examples/touchscreen is also
-       included in this configuration.  As of this writing, touchscreen
-       is not yet functional, however.
+       included in this configuration.
+
+         nsh> tc 5
+         tc_main: nsamples: 2
+         tc_main: Initializing external touchscreen device
+         tc_main: Opening /dev/input0
+         Sample     :
+            npoints : 1
+         Point 1    :
+                 id : 0
+              flags : 1a
+                  x : 230
+                  y : 84
+                  h : 0
+                  w : 0
+           pressure : 0
+         etc.
 
   nsh:
 
diff --git a/configs/lpcxpresso-lpc54628/fb/defconfig b/configs/lpcxpresso-lpc54628/fb/defconfig
index a6e853d527..f24fb6216b 100644
--- a/configs/lpcxpresso-lpc54628/fb/defconfig
+++ b/configs/lpcxpresso-lpc54628/fb/defconfig
@@ -18,6 +18,7 @@ CONFIG_FAT_LFN=y
 CONFIG_FS_FAT=y
 CONFIG_FS_PROCFS=y
 CONFIG_FT5X06_POLLMODE=y
+CONFIG_FT5X06_SINGLEPOINT=y
 CONFIG_GRAPHICS_PDCURSES=y
 CONFIG_I2CTOOL_MAXBUS=9
 CONFIG_INPUT_FT5X06=y
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 477f66830a..a190c49428 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -78,7 +78,7 @@ config INPUT_FT5336
 if INPUT_FT5X06
 
 config FT5X06_POLLMODE
-	bool " FT5336/FT5x06 polled mode"
+	bool "Polled mode"
 	default n
 	---help---
 		Run the FT5x06 in a non-interrupt driven polled mode.  Events will
@@ -88,6 +88,40 @@ config FT5X06_POLLMODE
 		in detecting touch related events and (2) it will consume a
 		significant amount of CPU time to perform the polling.
 
+config FT5X06_SWAPXY
+	bool "Swap X/Y"
+	default n
+	---help---
+		Reverse the meaning of X and Y to handle different LCD orientations.
+
+config FT5X06_SINGLEPOINT
+	bool "Single point"
+	default n
+	---help---
+		Do no report multi-touch events
+
+if FT5X06_SINGLEPOINT
+
+config FT5X06_THRESHX
+	int "X threshold"
+	default 12
+	---help---
+		New touch positions will only be reported when the X or Y data changes by these
+		thresholds. This trades reduced data rates for some loss in dragging accuracy.  For
+		12-bit values the raw ranges are 0-4095. So for example, if your display is
+		320x240, then THRESHX=13 and THRESHY=17 would correspond to one pixel.  Default: 12
+
+config FT5X06_THRESHY
+	int "Y threshold"
+	default 12
+	---help---
+		New touch positions will only be reported when the X or Y data changes by these
+		thresholds. This trades reduced data rates for some loss in dragging accuracy.  For
+		12-bit values the raw ranges are 0-4095. So for example, if your display is
+		320x240, then THRESHX=13 and THRESHY=17 would correspond to one pixel.  Default: 12
+
+endif # FT5X06_SINGLEPOINT
+
 config FT5X06_NPOLLWAITERS
 	int "Number FT5336/FT5x06 poll waiters"
 	default 4
diff --git a/drivers/input/ft5x06.c b/drivers/input/ft5x06.c
index ed7e4b9224..9ce827e1ef 100644
--- a/drivers/input/ft5x06.c
+++ b/drivers/input/ft5x06.c
@@ -120,6 +120,12 @@ struct ft5x06_dev_s
                                              * FT5x06 data */
   volatile bool valid;                      /* True:  New, valid touch data in
                                              * touchbuf[] */
+#ifdef CONFIG_FT5X06_SINGLEPOINT
+  uint8_t lastid;                           /* Last reported touch id */
+  uint8_t lastevent;                        /* Last reported event */
+  int16_t lastx;                            /* Last reported X position */
+  int16_t lasty;                            /* Last reported Y position */
+#endif
   sem_t devsem;                             /* Manages exclusive access to this
                                              * structure */
   sem_t waitsem;                            /* Used to wait for the
@@ -299,7 +305,12 @@ static void ft5x06_data_worker(FAR void *arg)
   msg[0].buffer    = &regaddr;              /* Send one byte of data (no STOP) */
   msg[0].length    = 1;
 
-  /* Set up the data read operation */
+  /* Set up the data read operation.
+   *
+   * REVISIT:  If CONFIG_FT5X06_SINGLEPOINT is selected, we we not just
+   * set the length for one sample?  Or is there some reason why we have to
+   * read all of the points?
+   */
 
   msg[1].frequency = priv->frequency;       /* I2C frequency */
   msg[1].addr      = config->address;       /* 7-bit address */
@@ -427,6 +438,113 @@ static int ft5x06_data_interrupt(int irq, FAR void *context, FAR void *arg)
  * Name: ft5x06_sample
  ****************************************************************************/
 
+#ifdef CONFIG_FT5X06_SINGLEPOINT
+static ssize_t ft5x06_sample(FAR struct ft5x06_dev_s *priv, FAR char *buffer,
+                             size_t len)
+{
+  FAR struct ft5x06_touch_data_s *raw;
+  FAR struct ft5x06_touch_point_s *touch;
+  FAR struct touch_sample_s *sample;
+  FAR struct touch_point_s *point;
+  int16_t x;
+  int16_t y;
+  uint8_t event;
+  uint8_t id;
+
+  if (!priv->valid)
+    {
+      return 0;  /* Nothing to read */
+    }
+
+  /* Raw data pointers (source) */
+
+  raw = (FAR struct ft5x06_touch_data_s *)priv->touchbuf;
+
+  if (raw->tdstatus != 1)
+    {
+      priv->valid = false;
+      return 0;  /* No touches read. */
+    }
+
+  /* Get the reported X and Y positions */
+
+  touch = raw->touch;
+
+  #ifdef CONFIG_FT5X06_SWAPXY
+  y = TOUCH_POINT_GET_X(touch[0]);
+  x = TOUCH_POINT_GET_Y(touch[0]);
+#else
+  x = TOUCH_POINT_GET_X(touch[0]);
+  y = TOUCH_POINT_GET_Y(touch[0]);
+#endif
+
+  /* Get the touch point ID and event */
+
+  event = TOUCH_POINT_GET_EVENT(touch[0]);
+  id    = TOUCH_POINT_GET_ID(touch[0]);
+
+  if (id == priv->lastid && event == priv->lastevent)
+    {
+      int16_t deltax;
+      int16_t deltay;
+
+      /* Same ID and event.. Compare the change in position from the last
+       * report.
+       */
+
+      deltax = (x - priv->lastx);
+      if (deltax < 0)
+        {
+          deltax = -deltax;
+        }
+
+      if (deltax < CONFIG_FT5X06_THRESHX)
+        {
+          /* There as been no significant change in X, try Y */
+
+          deltay = (y - priv->lasty);
+          if (deltay < 0)
+            {
+              deltay = -deltay;
+            }
+
+          if (deltax < CONFIG_FT5X06_THRESHX)
+            {
+             /* Ignore... no significant change in Y either */
+
+              priv->valid = false;
+              return 0;  /* No new touches read. */
+            }
+        }
+    }
+
+  priv->lastid      = id;
+  priv->lastevent   = event;
+  priv->lastx       = x;
+  priv->lasty       = y;
+
+  /* User data buffer points (sink) */
+
+  /* Return the number of touches read */
+
+  sample            = (FAR struct touch_sample_s *)buffer;
+  sample->npoints   = 1;
+
+  /* Decode and return the single touch point */
+
+  point             = sample->point;
+  point[0].id       = id;
+  point[0].flags    = g_event_map[event];
+  point[0].x        = x;
+  point[0].y        = y;
+  point[0].h        = 0;
+  point[0].w        = 0;
+  point[0].pressure = 0;
+
+  priv->valid       = false;
+  return SIZEOF_TOUCH_SAMPLE_S(1);
+}
+#else
 static ssize_t ft5x06_sample(FAR struct ft5x06_dev_s *priv, FAR char *buffer,
                              size_t len)
 {
@@ -434,8 +552,8 @@ static ssize_t ft5x06_sample(FAR struct ft5x06_dev_s *priv, FAR char *buffer,
   FAR struct ft5x06_touch_point_s *touch;
   FAR struct touch_sample_s *sample;
   FAR struct touch_point_s *point;
-  unsigned int ntouches;
   unsigned int maxtouches;
+  unsigned int ntouches;
   int i;
 
   maxtouches = (len - sizeof(int)) / sizeof(struct touch_point_s);
@@ -454,6 +572,8 @@ static ssize_t ft5x06_sample(FAR struct ft5x06_dev_s *priv, FAR char *buffer,
   /* Decode number of touches */
 
   ntouches = raw->tdstatus;
+  DEBUGASSERT(ntouches <= FT5x06_MAX_TOUCHES);
+
   if (ntouches > maxtouches)
     {
       ntouches = maxtouches;
@@ -465,8 +585,6 @@ static ssize_t ft5x06_sample(FAR struct ft5x06_dev_s *priv, FAR char *buffer,
       return 0;  /* No touches read. */
     }
 
-  DEBUGASSERT(ntouches <= FT5x06_MAX_TOUCHES);
-
   /* User data buffer points (sink) */
 
   sample = (FAR struct touch_sample_s *)buffer;
@@ -478,14 +596,19 @@ static ssize_t ft5x06_sample(FAR struct ft5x06_dev_s *priv, FAR char *buffer,
 
   /* Decode and return the touch points */
 
-  for (i = 0; i < raw->tdstatus; i++)
+  for (i = 0; i < ntouches; i++)
     {
       int event         = TOUCH_POINT_GET_EVENT(touch[i]);
 
       point[i].id       = TOUCH_POINT_GET_ID(touch[i]);
       point[i].flags    = g_event_map[event];
+#ifdef CONFIG_FT5X06_SWAPXY
+      point[i].y        = TOUCH_POINT_GET_X(touch[i]);
+      point[i].x        = TOUCH_POINT_GET_Y(touch[i]);
+#else
       point[i].x        = TOUCH_POINT_GET_X(touch[i]);
       point[i].y        = TOUCH_POINT_GET_Y(touch[i]);
+#endif
       point[i].h        = 0;
       point[i].w        = 0;
       point[i].pressure = 0;
@@ -494,6 +617,7 @@ static ssize_t ft5x06_sample(FAR struct ft5x06_dev_s *priv, FAR char *buffer,
   priv->valid = false;
   return SIZEOF_TOUCH_SAMPLE_S(ntouches);
 }
+#endif /* CONFIG_FT5X06_SINGLEPOINT */
 
 /****************************************************************************
  * Name: ft5x06_waitsample
-- 
GitLab