diff --git a/configs/lpcxpresso-lpc54628/README.txt b/configs/lpcxpresso-lpc54628/README.txt index ffc36016411..f88a7553ada 100644 --- a/configs/lpcxpresso-lpc54628/README.txt +++ b/configs/lpcxpresso-lpc54628/README.txt @@ -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. diff --git a/configs/lpcxpresso-lpc54628/fb/defconfig b/configs/lpcxpresso-lpc54628/fb/defconfig index a8506b6f478..a6e853d527e 100644 --- a/configs/lpcxpresso-lpc54628/fb/defconfig +++ b/configs/lpcxpresso-lpc54628/fb/defconfig @@ -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 diff --git a/configs/lpcxpresso-lpc54628/src/lpc54_ft5x06.c b/configs/lpcxpresso-lpc54628/src/lpc54_ft5x06.c index df2cd8c8b7a..162089b190b 100644 --- a/configs/lpcxpresso-lpc54628/src/lpc54_ft5x06.c +++ b/configs/lpcxpresso-lpc54628/src/lpc54_ft5x06.c @@ -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 */ diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index f65b9cbf658..477f66830a6 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -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 diff --git a/drivers/input/ft5x06.c b/drivers/input/ft5x06.c index 5d04a3b7c69..ed7e4b9224d 100644 --- a/drivers/input/ft5x06.c +++ b/drivers/input/ft5x06.c @@ -78,9 +78,8 @@ #include #include #include -#include +#include -#include #include #include @@ -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); diff --git a/include/nuttx/input/ft5x06.h b/include/nuttx/input/ft5x06.h index 0ec8641ce3f..791f13ee105 100644 --- a/include/nuttx/input/ft5x06.h +++ b/include/nuttx/input/ft5x06.h @@ -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);