Completes first cut of TSC2007 driver

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@3831 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2011-07-30 17:41:22 +00:00
parent 8501188ee2
commit 488a8fa9d5
5 changed files with 190 additions and 37 deletions

View File

@ -1955,4 +1955,7 @@
* graphics/nxfonts/nxfonts_*.h: Add serveral more new fonts
* arch/z80/src/ez80/ez80_serial.c: Fix some errors in serial driver
setup for UART1 (submitted by Paul Osmialowski).
* drivers/input/tsc2007.c and include/nuttx/input/*: Add a generic NuttX
touchscreen interface. Add a driver for the TI TSC2007 touchscreen
controller.

View File

@ -67,6 +67,8 @@
#include <nuttx/fs.h>
#include <nuttx/i2c.h>
#include <nuttx/wqueue.h>
#include <nuttx/input/touchscreen.h>
#include <nuttx/input/tsc2007.h>
#include "tsc2007.h"
@ -74,6 +76,13 @@
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
/* Reference counting is partially implemented, but not needed in the
* current design.
*/
#undef CONFIG_TSC2007_REFCNT
/* Driver support ***********************************************************/
/* This format is used to construct the /dev/skel[n] device driver path. It
* defined here so that it will be used consistently in all places.
@ -85,11 +94,22 @@
/****************************************************************************
* Private Types
****************************************************************************/
/* This describes the state of one contact */
enum tsc2007_contact_3
{
CONTACT_NONE = 0, /* No contact */
CONTACT_DOWN, /* First contact */
CONTACT_MOVE, /* Same contact, possibly different position */
CONTACT_UP, /* Contact lost */
};
/* This structure describes the results of one TSC2007 sample */
struct tsc2007_sample_s
{
bool pendown; /* Pen down state */
uint8_t contact; /* Contact state (see enum tsc2007_contact_e) */
uint16_t x; /* Measured X position */
uint16_t y; /* Measured Y position */
uint16_t pressure; /* Calculated pressure */
@ -102,8 +122,11 @@ struct tsc2007_dev_s
#ifdef CONFIG_TSC2007_MULTIPLE
FAR struct tsc2007_dev_s *flink; /* Supports a singly linked list of drivers */
#endif
#ifdef CONFIG_TSC2007_REFCNT
uint8_t crefs; /* Number of times the device has been opened */
#endif
uint8_t nwaiters; /* Number of threads waiting for TSC2007 data */
uint8_t id; /* Current touch point ID */
volatile bool penchange; /* An unreported event is buffered */
sem_t devsem; /* Manages exclusive access to this structure */
sem_t waitsem; /* Used to wait for the availability of data */
@ -111,7 +134,7 @@ struct tsc2007_dev_s
FAR struct tsc2007_config_s *config; /* Board configuration data */
FAR struct i2c_dev_s *i2c; /* Saved I2C driver instance */
struct work_s work; /* Supports the interrupt handling "bottom half" */
struct tsc2007_sample_s sample; /* Last sampled data */
struct tsc2007_sample_s sample; /* Last sampled touch point data */
/* The following is a list if poll structures of threads waiting for
* driver events. The 'struct pollfd' reference for each open is also
@ -251,6 +274,23 @@ static int tsc2007_sample(FAR struct tsc2007_dev_s *priv,
*/
memcpy(sample, &priv->sample, sizeof(struct tsc2007_sample_s ));
/* Now manage state transitions */
if (sample->contact == CONTACT_UP)
{
/* Next.. no contract. Increment the ID so that next contact will be unique */
priv->sample.contact = CONTACT_NONE;
priv->id++;
}
else if (sample->contact == CONTACT_DOWN)
{
/* First report -- next report will be a movement */
priv->sample.contact = CONTACT_MOVE;
}
priv->penchange = false;
ret = OK;
}
@ -455,9 +495,11 @@ static void tsc2007_worker(FAR void *arg)
if (!pendown)
{
/* Ignore the interrupt if the pen was already down */
/* Ignore the interrupt if the pen was already down (CONTACT_NONE == pen up and
* already reported. CONTACT_UP == pen up, but not reported)
*/
if (!priv->sample.pendown)
if (priv->sample.contact == CONTACT_NONE)
{
goto errout;
}
@ -556,8 +598,31 @@ static void tsc2007_worker(FAR void *arg)
/* Note the availability of new measurements */
priv->sample.pendown = pendown;
priv->penchange = true;
if (pendown)
{
/* If this is the first (acknowledged) pend down report, then report
* this as the first contact. If contact == CONTACT_DOWN, it will be
* set to set to CONTACT_MOVE after the contact is first sampled.
*/
if (priv->sample.contact != CONTACT_MOVE)
{
/* First contact */
priv->sample.contact = CONTACT_DOWN;
}
}
else /* if (priv->sample.contact != CONTACT_NONE) */
{
/* The pen is up. NOTE: We know from a previous test, that this is a
* loss of contact condition. This will be changed to CONTACT_NONE
* after the loss of contact is sampled.
*/
priv->sample.contact = CONTACT_UP;
}
priv->penchange = true;
/* Notify any waiters that nes TSC2007 data is available */
@ -626,6 +691,7 @@ static int tsc2007_interrupt(int irq, FAR void *context)
static int tsc2007_open(FAR struct file *filep)
{
#ifdef CONFIG_TSC2007_REFCNT
FAR struct inode *inode;
FAR struct tsc2007_dev_s *priv;
uint8_t tmp;
@ -659,17 +725,9 @@ static int tsc2007_open(FAR struct file *filep)
goto errout_with_sem;
}
/* Check if this is the first time that the driver has been opened. */
if (tmp == 1)
{
irqstate_t flags = irqsave();
/* Perform one time hardware initialization */
#warning "Missing logic"
irqrestore(flags);
}
/* When the reference increments to 1, this is the first open event
* on the driver.. and an opportunity to do any one-time initialization.
*/
/* Save the new open count on success */
@ -678,6 +736,9 @@ static int tsc2007_open(FAR struct file *filep)
errout_with_sem:
sem_post(&priv->devsem);
return ret;
#else
return OK;
#endif
}
/****************************************************************************
@ -686,6 +747,7 @@ errout_with_sem:
static int tsc2007_close(FAR struct file *filep)
{
#ifdef CONFIG_TSC2007_REFCNT
FAR struct inode *inode;
FAR struct tsc2007_dev_s *priv;
int ret;
@ -707,22 +769,18 @@ static int tsc2007_close(FAR struct file *filep)
return -EINTR;
}
/* Decrement the reference count unless it would decrement to zero */
if (priv->crefs > 1)
/* Decrement the reference count unless it would decrement a negative
* value. When the count decrements to zero, there are no further
* open references to the driver.
*/
if (priv->crefs >= 1)
{
priv->crefs--;
sem_post(&priv->devsem);
return OK;
}
/* There are no more references to the port */
priv->crefs = 0;
/* Perform driver teardown */
#warning "Missing logic"
sem_post(&priv->devsem);
#endif
return OK;
}
@ -732,10 +790,11 @@ static int tsc2007_close(FAR struct file *filep)
static ssize_t tsc2007_read(FAR struct file *filep, FAR char *buffer, size_t len)
{
FAR struct inode *inode;
FAR struct tsc2007_dev_s *priv;
struct tsc2007_sample_s sample;
int ret;
FAR struct inode *inode;
FAR struct tsc2007_dev_s *priv;
FAR struct touch_sample_s *report;
struct tsc2007_sample_s sample;
int ret;
DEBUGASSERT(filep);
inode = filep->f_inode;
@ -743,6 +802,19 @@ static ssize_t tsc2007_read(FAR struct file *filep, FAR char *buffer, size_t len
DEBUGASSERT(inode && inode->i_private);
priv = (FAR struct tsc2007_dev_s *)inode->i_private;
/* Verify that the caller has provided a buffer large enough to receive
* the touch data.
*/
if (len < SIZEOF_TOUCH_SAMPLE_S(1))
{
/* We could provide logic to break up a touch report into segments and
* handle smaller reads... but why?
*/
return -ENOSYS;
}
/* Get exclusive access to the driver data structure */
ret = sem_wait(&priv->devsem);
@ -785,7 +857,36 @@ static ssize_t tsc2007_read(FAR struct file *filep, FAR char *buffer, size_t len
* to the caller.
*/
#warning "Missing logic"
report = (FAR struct touch_sample_s *)buffer;
memset(report, 0, SIZEOF_TOUCH_SAMPLE_S(1));
report->npoints = 1;
report->point[0].id = priv->id;
report->point[0].x = sample.x;
report->point[0].y = sample.y;
report->point[0].pressure = sample.pressure;
/* Report the appropriate flags */
if (sample.contact == CONTACT_UP)
{
/* Pen is now up */
report->point[0].flags = TOUCH_UP | TOUCH_ID_VALID | TOUCH_POS_VALID |TOUCH_PRESSURE_VALID;
}
else if (sample.contact == CONTACT_DOWN)
{
/* First contact */
report->point[0].flags = TOUCH_DOWN | TOUCH_ID_VALID | TOUCH_POS_VALID |TOUCH_PRESSURE_VALID;
}
else /* if (sample->contact == CONTACT_MOVE) */
{
/* Movement of the same contact */
report->point[0].flags = TOUCH_MOVE | TOUCH_ID_VALID | TOUCH_POS_VALID |TOUCH_PRESSURE_VALID;
}
ret = SIZEOF_TOUCH_SAMPLE_S(1);
errout:
sem_post(&priv->devsem);
@ -824,7 +925,37 @@ static int tsc2007_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
switch (cmd)
{
/* Add support for ioctl commands here */
case TSIOC_SETCALIB: /* arg: Pointer to int calibration value */
{
FAR int *ptr = (FAR int *)((uintptr_t)arg);
DEBUGASSERT(priv->config != NULL && ptr != NULL);
priv->config->rxplate = *ptr;
}
break;
case TSIOC_GETCALIB: /* arg: Pointer to int calibration value */
{
FAR int *ptr = (FAR int *)((uintptr_t)arg);
DEBUGASSERT(priv->config != NULL && ptr != NULL);
*ptr = priv->config->rxplate;
}
break;
case TSIOC_SETFREQUENCY: /* arg: Pointer to uint32_t frequency value */
{
FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
DEBUGASSERT(priv->config != NULL && ptr != NULL);
priv->config->frequency = I2C_SETFREQUENCY(priv->i2c, *ptr);
}
break;
case TSIOC_GETFREQUENCY: /* arg: Pointer to uint32_t frequency value */
{
FAR uint32_t *ptr = (FAR uint32_t *)((uintptr_t)arg);
DEBUGASSERT(priv->config != NULL && ptr != NULL);
*ptr = priv->config->frequency;
}
break;
default:
ret = -ENOTTY;
@ -996,6 +1127,19 @@ int tsc2007_register(FAR struct i2c_dev_s *dev,
sem_init(&priv->devsem, 0, 1); /* Initialize device structure semaphore */
sem_init(&priv->waitsem, 0, 0); /* Initialize pen event wait semaphore */
/* Set the I2C frequency (saving the actual frequency) */
config->frequency = I2C_SETFREQUENCY(dev, config->frequency);
/* Set the I2C address and address size */
ret = I2C_SETADDRESS(dev, config->address, 7);
if (ret < 0)
{
idbg("I2C_SETADDRESS failed: %d\n", ret);
goto errout_with_priv;
}
/* Make sure that interrupts are disabled */
config->clear(config);

View File

@ -114,7 +114,7 @@
*
****************************************************************************/
#define I2C_SETADDRESS(d,f,b) ((d)->ops->setaddress(d,f,b))
#define I2C_SETADDRESS(d,a,n) ((d)->ops->setaddress(d,a,n))
/****************************************************************************
* Name: I2C_SETOWNADDRESS
@ -140,7 +140,7 @@
*
****************************************************************************/
#define I2C_SETOWNADDRESS(d,f,b) ((d)->ops->setownaddress(d,f,b))
#define I2C_SETOWNADDRESS(d,a,n) ((d)->ops->setownaddress(d,a,n))
/****************************************************************************
* Name: I2C_WRITE

View File

@ -121,7 +121,7 @@ struct tsc2007_config_s
* pendown - Return the state of the pen down GPIO input
*/
int (*attach)(FAR struct tsc2007_config_s *state, xcpt_t isr);
int (*attach)(FAR struct tsc2007_config_s *state, xcpt_t isr);
void (*enable)(FAR struct tsc2007_config_s *state, bool enable);
void (*clear)(FAR struct tsc2007_config_s *state);
bool (*pendown)(FAR struct tsc2007_config_s *state);

View File

@ -59,6 +59,7 @@
#define _MTDIOCBASE (0x8a00) /* MTD ioctl commands */
#define _SIOCBASE (0x8b00) /* Socket ioctl commands */
#define _ARPBASE (0x8c00) /* ARP ioctl commands */
#define _TSBASE (0x8d00) /* Touchscreen ioctl commands */
/* Macros used to manage ioctl commands */
@ -160,6 +161,11 @@
#define _ARPIOCVALID(c) (_IOC_TYPE(c)==_ARPBASE)
#define _ARPIOC(nr) _IOC(_ARPBASE,nr)
/* NuttX ARP touchscrren ioctl definitions (see nuttx/input/touchscreen.h) **/
#define _TSIOCVALID(c) (_IOC_TYPE(c)==_TSBASE)
#define _TSIOC(nr) _IOC(_TSBASE,nr)
/****************************************************************************
* Public Type Definitions
****************************************************************************/