drivers/ft5x06: Add a polled mode of operation for the FT5x06 in attempt to work around the fact that the LPCXpresso-LPC54628 chose a non-interrupt pin for the FT5x06 interrupt. Driver is still not yet functional.

This commit is contained in:
Gregory Nutt 2017-12-18 08:58:07 -06:00
parent 783d2cabee
commit 23dfc0bf06
6 changed files with 218 additions and 23 deletions

View file

@ -63,10 +63,14 @@ STATUS
system asserts during boot up. This is because the FT5x06 interrupt
is on pin P4.0 but pin interrupts are only supported on P0.m and
P1.m, m=0..31. Does this mean that TSC interrupts are not supported?
I think so! I think that a polled solution will have to be used.
I think so!
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.
If you want to use the fb configuration in the near future, you should
disable CONFIG_INPUT_FT5x06 to prevent this assertion.
The FT5x06 driver is not, however, functional. It is generating hard
faults.
Configurations
==============
@ -154,6 +158,29 @@ Configurations
is not yet usable. Others work fine with no user include: charset,
xmas, firework, worms, rain, for examples.
3. I2C2 is enabled (will be used with the capacitive touchscreen). In
order to verify I2C functionality, the I2C tool at apps/system/i2ctool
is enabled in this configuration.
nsh> i2c dev -b 2 3 77
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- 1a -- -- 1d -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- 38 -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
Codec I2C address: 0x1a
Accel I2C address: 0x1d
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.
nsh:
Configures the NuttShell (nsh) application located at examples/nsh.

View file

@ -17,6 +17,7 @@ CONFIG_FAT_LCNAMES=y
CONFIG_FAT_LFN=y
CONFIG_FS_FAT=y
CONFIG_FS_PROCFS=y
CONFIG_FT5X06_POLLMODE=y
CONFIG_GRAPHICS_PDCURSES=y
CONFIG_I2CTOOL_MAXBUS=9
CONFIG_INPUT_FT5X06=y

View file

@ -62,11 +62,14 @@
* Private Function Ptototypes
****************************************************************************/
#ifndef CONFIG_FT5X06_POLLMODE
static int lpc54_ft5x06_attach(FAR const struct ft5x06_config_s *config,
xcpt_t isr, FAR void *arg);
static void lpc54_ft5x06_enable(FAR const struct ft5x06_config_s *config,
bool enable);
static void lpc54_ft5x06_clear(FAR const struct ft5x06_config_s *config);
#endif
static void lpc54_ft5x06_wakeup(FAR const struct ft5x06_config_s *config);
static void lpc54_ft5x06_nreset(FAR const struct ft5x06_config_s *config,
bool state);
@ -79,14 +82,18 @@ static const struct ft5x06_config_s g_ft5x06_config =
{
.address = FT5x06_I2C_ADDRESS,
.frequency = FT5x06_FREQUENCY,
#ifndef CONFIG_FT5X06_POLLMODE
.attach = lpc54_ft5x06_attach,
.enable = lpc54_ft5x06_enable,
.clear = lpc54_ft5x06_clear,
#endif
.wakeup = lpc54_ft5x06_wakeup,
.nreset = lpc54_ft5x06_nreset
};
#ifndef CONFIG_FT5X06_POLLMODE
static uint8_t g_ft5x06_irq;
#endif
/****************************************************************************
* Private Functions
@ -100,11 +107,13 @@ static uint8_t g_ft5x06_irq;
*
****************************************************************************/
#ifndef CONFIG_FT5X06_POLLMODE
static int lpc54_ft5x06_attach(FAR const struct ft5x06_config_s *config,
xcpt_t isr, FAR void *arg)
{
return irq_attach(g_ft5x06_irq, isr, arg);
}
#endif
/****************************************************************************
* Name: lpc54_ft5x06_enable
@ -114,6 +123,7 @@ static int lpc54_ft5x06_attach(FAR const struct ft5x06_config_s *config,
*
****************************************************************************/
#ifndef CONFIG_FT5X06_POLLMODE
static void lpc54_ft5x06_enable(FAR const struct ft5x06_config_s *config,
bool enable)
{
@ -126,6 +136,7 @@ static void lpc54_ft5x06_enable(FAR const struct ft5x06_config_s *config,
up_disable_irq(g_ft5x06_irq);
}
}
#endif
/****************************************************************************
* Name: lpc54_ft5x06_clear
@ -135,10 +146,12 @@ static void lpc54_ft5x06_enable(FAR const struct ft5x06_config_s *config,
*
****************************************************************************/
#ifndef CONFIG_FT5X06_POLLMODE
static void lpc54_ft5x06_clear(FAR const struct ft5x06_config_s *config)
{
(void)lpc54_gpio_ackedge(g_ft5x06_irq);
}
#endif
/****************************************************************************
* Name: lpc54_ft5x06_wakeup
@ -183,9 +196,11 @@ static void lpc54_ft5x06_nreset(FAR const struct ft5x06_config_s *config,
int lpc54_ft5x06_register(void)
{
FAR struct i2c_master_s *i2c;
int irq;
int ret;
#ifndef CONFIG_FT5X06_POLLMODE
int irq;
/* Initialize GPIO pins. NOTE: The nRST pin was already configured during
* early LCD initialization. The Part is in reset now.
*/
@ -199,6 +214,7 @@ int lpc54_ft5x06_register(void)
lpc54_gpio_ackedge(irq);
up_disable_irq(irq);
#endif
/* Take the FT5x06 out of reset */

View file

@ -75,13 +75,28 @@ config INPUT_FT5336
Enable support for the FocalTech FT5x06 multi-touch, capacitive
touch panel controller
if INPUT_FT5X06
config FT5X06_POLLMODE
bool " FT5336/FT5x06 polled mode"
default n
---help---
Run the FT5x06 in a non-interrupt driven polled mode. Events will
not be driven by interrupts but rather based on a timed poll.
This is a non-optimal design both because (1) it will lead to delays
in detecting touch related events and (2) it will consume a
significant amount of CPU time to perform the polling.
config FT5X06_NPOLLWAITERS
int "Number FT5336/FT5x06 poll waiters"
default 4
depends on !DISABLE_POLL && INPUT_FT5X06
depends on !DISABLE_POLL
---help---
Maximum number of threads that can be waiting on poll()
endif # INPUT_FT5X06
config INPUT_ADS7843E
bool "TI ADS7843/TSC2046 touchscreen controller"
default n

View file

@ -78,9 +78,8 @@
#include <nuttx/fs/fs.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/wqueue.h>
#include <nuttx/random.h>
#include <nuttx/wdog.h>
#include <nuttx/signal.h>
#include <nuttx/input/touchscreen.h>
#include <nuttx/input/ft5x06.h>
@ -95,8 +94,17 @@
* defined here so that it will be used consistently in all places.
*/
#define DEV_FORMAT "/dev/input%d"
#define DEV_NAMELEN 16
#define DEV_FORMAT "/dev/input%d"
#define DEV_NAMELEN 16
/* In polled mode, the polling rate will decrease when there is no touch
* activity. These definitions represent the maximum and the minimum
* polling rates.
*/
#define POLL_MINDELAY MSEC2TICK(50)
#define POLL_MAXDELAY MSEC2TICK(200)
#define POLL_INCREMENT MSEC2TICK(10)
/****************************************************************************
* Private Types
@ -117,11 +125,17 @@ struct ft5x06_dev_s
sem_t waitsem; /* Used to wait for the
* availability of data */
uint32_t frequency; /* Current I2C frequency */
#ifdef CONFIG_FT5X06_POLLMODE
uint32_t delay; /* Current poll delay */
#endif
FAR const struct ft5x06_config_s *config; /* Board configuration data */
FAR struct i2c_master_s *i2c; /* Saved I2C driver instance */
struct work_s work; /* Supports the interrupt handling
* "bottom half" */
#ifdef CONFIG_FT5X06_POLLMODE
WDOG_ID polltimer; /* Poll timer */
#endif
uint8_t touchbuf[FT5x06_TOUCH_DATA_LEN]; /* Raw touch data */
#ifndef CONFIG_DISABLE_POLL
@ -140,7 +154,11 @@ struct ft5x06_dev_s
static void ft5x06_notify(FAR struct ft5x06_dev_s *priv);
static void ft5x06_data_worker(FAR void *arg);
#ifdef CONFIG_FT5X06_POLLMODE
static void ft5x06_poll_timeout(int argc, wdparm_t arg1, ...);
#else
static int ft5x06_data_interrupt(int irq, FAR void *context, FAR void *arg);
#endif
static ssize_t ft5x06_sample(FAR struct ft5x06_dev_s *priv, FAR char *buffer,
size_t len);
static ssize_t ft5x06_waitsample(FAR struct ft5x06_dev_s *priv,
@ -248,8 +266,10 @@ static void ft5x06_data_worker(FAR void *arg)
{
FAR struct ft5x06_dev_s *priv = (FAR struct ft5x06_dev_s *)arg;
FAR const struct ft5x06_config_s *config;
FAR struct ft5x06_touch_data_s *sample;
struct i2c_msg_s msg[2];
uint8_t regaddr;
uint8_t ntouches;
int ret;
/* Get a pointer the callbacks for convenience */
@ -281,32 +301,92 @@ static void ft5x06_data_worker(FAR void *arg)
/* Set up the data read operation */
msg[0].frequency = priv->frequency; /* I2C frequency */
msg[0].addr = config->address; /* 7-bit address */
msg[0].flags = I2C_M_READ; /* Read transaction with Re-START */
msg[0].buffer = priv->touchbuf; /* Read all touch data */
msg[0].length = FT5x06_TOUCH_DATA_LEN;
msg[1].frequency = priv->frequency; /* I2C frequency */
msg[1].addr = config->address; /* 7-bit address */
msg[1].flags = I2C_M_READ; /* Read transaction with Re-START */
msg[1].buffer = priv->touchbuf; /* Read all touch data */
msg[1].length = FT5x06_TOUCH_DATA_LEN;
ret = I2C_TRANSFER(priv->i2c, msg, 2);
if (ret >= 0)
sample = (FAR struct ft5x06_touch_data_s *)priv->touchbuf;
ntouches = sample->tdstatus;
if (ret >= 0 && ntouches > 0)
{
/* Notify any waiters that new FT5x06 data is available */
priv->valid = true;
ft5x06_notify(priv);
}
#ifdef CONFIG_FT5X06_POLLMODE
else
{
ntouches = 0;
}
/* Update the poll rate */
if (ntouches > 0)
{
priv->delay = POLL_MINDELAY;
}
else if (priv->delay < POLL_MAXDELAY)
{
priv->delay += POLL_INCREMENT;
}
/* Exit, re-starting the poll (unless there is no longer any task waiting
* for touch data).
*/
if (priv->nwaiters > 0)
{
(void)wd_start(priv->polltimer, priv->delay, ft5x06_poll_timeout, 1,
priv);
}
#else
/* Exit, re-enabling FT5x06 interrupts */
config->enable(config, true);
#endif
nxsem_post(&priv->devsem);
}
/****************************************************************************
* Name: ft5x06_poll_timeout
****************************************************************************/
#ifdef CONFIG_FT5X06_POLLMODE
static void ft5x06_poll_timeout(int argc, wdparm_t arg1, ...)
{
FAR struct ft5x06_dev_s *priv = (FAR struct ft5x06_dev_s *)arg1;
int ret;
/* Transfer processing to the worker thread. Since FT5x06 poll timer is
* disabled while the work is pending, no special action should be
* required to protected the work queue.
*/
if (priv->nwaiters > 0)
{
DEBUGASSERT(priv->work.worker == NULL);
ret = work_queue(HPWORK, &priv->work, ft5x06_data_worker, priv, 0);
if (ret != 0)
{
ierr("ERROR: Failed to queue work: %d\n", ret);
}
}
}
#endif
/****************************************************************************
* Name: ft5x06_data_interrupt
****************************************************************************/
#ifndef CONFIG_FT5X06_POLLMODE
static int ft5x06_data_interrupt(int irq, FAR void *context, FAR void *arg)
{
FAR struct ft5x06_dev_s *priv = (FAR struct ft5x06_dev_s *)arg;
@ -341,6 +421,7 @@ static int ft5x06_data_interrupt(int irq, FAR void *context, FAR void *arg)
config->clear(config);
return OK;
}
#endif
/****************************************************************************
* Name: ft5x06_sample
@ -442,9 +523,31 @@ static ssize_t ft5x06_waitsample(FAR struct ft5x06_dev_s *priv,
while (!priv->valid)
{
/* Wait for a change in the FT5x06 state */
/* Increment the count of waiters */
priv->nwaiters++;
#ifdef CONFIG_FT5X06_POLLMODE
/* The poll timer is stopped when there are no waiters. So we may
* need to restart with at the maximum rate.
*/
if (priv->nwaiters == 1)
{
priv->delay = POLL_MINDELAY;
ret = wd_start(priv->polltimer, priv->delay, ft5x06_poll_timeout,
1, priv);
if (ret < 0)
{
ierr("ERROR: nxsem_wait failed: %d\n", ret);
goto errout;
}
}
#endif
/* Wait for a change in the FT5x06 state */
ret = nxsem_wait(&priv->waitsem);
priv->nwaiters--;
@ -523,10 +626,12 @@ static int ft5x06_bringup(FAR struct ft5x06_dev_s *priv)
return ret;
}
#ifndef CONFIG_FT5X06_POLLMODE
/* Enable FT5x06 interrupts */
config->clear(config);
config->enable(config, true);
#endif
return OK;
}
@ -536,6 +641,12 @@ static int ft5x06_bringup(FAR struct ft5x06_dev_s *priv)
static void ft5x06_shutdown(FAR struct ft5x06_dev_s *priv)
{
#ifdef CONFIG_FT5X06_POLLMODE
/* Stop the poll timer */
(void)wd_cancel(priv->polltimer);
#else
FAR const struct ft5x06_config_s *config = priv->config;
/* Make sure that the FT5x06 interrupt is disabled */
@ -546,6 +657,7 @@ static void ft5x06_shutdown(FAR struct ft5x06_dev_s *priv)
/* Attach the interrupt handler */
(void)config->attach(config, NULL, NULL);
#endif
}
/****************************************************************************
@ -597,7 +709,7 @@ static int ft5x06_open(FAR struct file *filep)
ret = ft5x06_bringup(priv);
if (ret < 0)
{
ierr("ERROR: nxsem_wait failed: %d\n", ret);
ierr("ERROR: ft5x06_bringup failed: %d\n", ret);
goto errout_with_sem;
}
}
@ -929,8 +1041,13 @@ int ft5x06_register(FAR struct i2c_master_s *i2c,
/* Debug-only sanity checks */
DEBUGASSERT(i2c != NULL && config != NULL && minor >= 0 && minor < 100);
DEBUGASSERT(config->attach != NULL && config->enable != NULL &&
config->clear != NULL && config->nreset != NULL);
#ifdef CONFIG_FT5X06_POLLMODE
DEBUGASSERT(config->wakeup != NULL && config->nreset != NULL);
#else
DEBUGASSERT(config->attach != NULL && config->enable != NULL &&
config->clear != NULL && config->wakeup != NULL &&
config->nreset != NULL);
#endif
/* Create and initialize a FT5x06 device driver instance */
@ -956,6 +1073,17 @@ int ft5x06_register(FAR struct i2c_master_s *i2c,
nxsem_setprotocol(&priv->waitsem, SEM_PRIO_NONE);
#ifdef CONFIG_FT5X06_POLLMODE
/* Allocate a timer for polling the FT5x06 */
priv->delay = POLL_MAXDELAY;
priv->polltimer = wd_create();
if (priv->polltimer == NULL)
{
ierr("ERROR: Failed to allocate polltimer\n");
goto errout_with_priv;
}
#else
/* Make sure that the FT5x06 interrupt interrupt is disabled */
config->clear(config);
@ -968,8 +1096,9 @@ int ft5x06_register(FAR struct i2c_master_s *i2c,
if (ret < 0)
{
ierr("ERROR: Failed to attach interrupt\n");
goto errout_with_priv;
goto errout_with_timer;
}
#endif
/* Register the device as an input device */
@ -980,7 +1109,7 @@ int ft5x06_register(FAR struct i2c_master_s *i2c,
if (ret < 0)
{
ierr("ERROR: register_driver() failed: %d\n", ret);
goto errout_with_priv;
goto errout_with_timer;
}
/* Schedule work to perform the initial sampling and to set the data
@ -991,13 +1120,18 @@ int ft5x06_register(FAR struct i2c_master_s *i2c,
if (ret != 0)
{
ierr("ERROR: Failed to queue work: %d\n", ret);
goto errout_with_priv;
goto errout_with_timer;
}
/* And return success */
return OK;
errout_with_timer:
#ifdef CONFIG_FT5X06_POLLMODE
(void)wd_delete(priv->polltimer);
#endif
errout_with_priv:
nxsem_destroy(&priv->devsem);
kmm_free(priv);

View file

@ -116,10 +116,12 @@ struct ft5x06_config_s
*/
#ifndef CONFIG_FT5X06_POLLMODE
int (*attach)(FAR const struct ft5x06_config_s *config, xcpt_t isr,
FAR void *arg);
void (*enable)(FAR const struct ft5x06_config_s *config, bool enable);
void (*clear)(FAR const struct ft5x06_config_s *config);
#endif
void (*wakeup)(FAR const struct ft5x06_config_s *config);
void (*nreset)(FAR const struct ft5x06_config_s *config,
bool state);