uint16_t stmpe811_tempread(STMPE811_HANDLE handle) { FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle; uint32_t temp = 0; uint8_t temp1; uint8_t temp2; /* Acquire data enable */ stmpe811_putreg8(priv, STMPE811_TEMP_CTRL, (TEMP_CTRL_ACQ | TEMP_CTRL_ENABLE)); /* Read the temperature */ temp1 = stmpe811_getreg8(priv, STMPE811_SYS_CTRL2); temp2 = stmpe811_getreg8(priv, STMPE811_SYS_CTRL2+1); /* Scale the temperature (where Vio is assumed to be .33) */ temp = ((uint32_t)(temp1 & 3) << 8) | temp2; temp = (uint32_t)((33 * temp * 100) / 751); temp = (uint32_t)((temp + 5) / 10); return (uint16_t)temp; }
static void stmpe811_gpioinit(FAR struct stmpe811_dev_s *priv) { uint8_t regval; if ((priv->flags & STMPE811_FLAGS_GPIO_INITIALIZED) == 0) { /* Enable Clocking for GPIO */ regval = stmpe811_getreg8(priv, STMPE811_SYS_CTRL2); regval &= ~SYS_CTRL2_GPIO_OFF; stmpe811_putreg8(priv, STMPE811_SYS_CTRL2, regval); /* Disable all GPIO interrupts */ stmpe811_putreg8(priv, STMPE811_GPIO_EN, 0); /* Enable global GPIO interrupts */ #ifndef CONFIG_STMPE811_GPIOINT_DISABLE regval = stmpe811_getreg8(priv, STMPE811_INT_EN); regval |= INT_GPIO; stmpe811_putreg8(priv, STMPE811_INT_EN, regval); #endif priv->flags |= STMPE811_FLAGS_GPIO_INITIALIZED; } }
static void stmpe811_worker(FAR void *arg) { FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)arg; uint8_t regval; DEBUGASSERT(priv && priv->config); /* Get the global interrupt status */ regval = stmpe811_getreg8(priv, STMPE811_INT_STA); /* Check for a touchscreen interrupt */ #ifndef CONFIG_STMPE811_TSC_DISABLE if ((regval & (INT_TOUCH_DET|INT_FIFO_TH|INT_FIFO_OFLOW)) != 0) { /* Dispatch the touchscreen interrupt if it was brought into the link */ #ifdef CONFIG_HAVE_WEAKFUNCTIONS if (stmpe811_tscworker) #endif { stmpe811_tscworker(priv, regval); } stmpe811_putreg8(priv, STMPE811_INT_STA, (INT_TOUCH_DET|INT_FIFO_TH|INT_FIFO_OFLOW)); regval &= ~(INT_TOUCH_DET|INT_FIFO_TH|INT_FIFO_OFLOW); } #endif #if !defined(CONFIG_STMPE811_GPIO_DISABLE) && !defined(CONFIG_STMPE811_GPIOINT_DISABLE) if ((regval & INT_GPIO) != 0) { /* Dispatch the GPIO interrupt if it was brought into the link */ #ifdef CONFIG_HAVE_WEAKFUNCTIONS if (stmpe811_gpioworker) #endif { stmpe811_gpioworker(priv); } stmpe811_putreg8(priv, STMPE811_INT_STA, INT_GPIO); regval &= ~INT_GPIO; } #endif /* Clear any other residual, unhandled pending interrupt */ if (regval != 0) { stmpe811_putreg8(priv, STMPE811_INT_STA, regval); } /* Re-enable the STMPE811 GPIO interrupt */ priv->config->enable(priv->config, true); }
static void stmpe811_timeoutworker(FAR void *arg) { FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)arg; DEBUGASSERT(priv); /* Treat the timeout just like an interrupt occurred */ stmpe811_tscworker(priv, stmpe811_getreg8(priv, STMPE811_INT_STA)); }
static int stmpe811_checkid(FAR struct stmpe811_dev_s *priv) { uint16_t devid = 0; /* Read device ID */ devid = stmpe811_getreg8(priv, STMPE811_CHIP_ID); devid = (uint32_t)(devid << 8); devid |= (uint32_t)stmpe811_getreg8(priv, STMPE811_CHIP_ID+1); ivdbg("devid: %04x\n", devid); if (devid != (uint16_t)CHIP_ID) { /* ID is not Correct */ return -ENODEV; } return OK; }
int stmpe811_gpioattach(STMPE811_HANDLE handle, uint8_t pinconfig, stmpe811_handler_t handler) { FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle; int pin = (pinconfig & STMPE811_GPIO_PIN_MASK) >> STMPE811_GPIO_PIN_SHIFT; uint8_t regval; int ret; DEBUGASSERT(handle && (unsigned)pin < STMPE811_GPIO_NPINS); /* Get exclusive access to the device structure */ ret = sem_wait(&priv->exclsem); if (ret < 0) { int errval = errno; idbg("sem_wait failed: %d\n", errval); return -errval; } /* Make sure that the GPIO interrupt system has been gpioinitialized */ stmpe811_gpioinit(priv); /* Set/clear the handler */ priv->handlers[pin] = handler; /* If an handler has provided, then we are enabling interrupts */ regval = stmpe811_getreg8(priv, STMPE811_GPIO_EN); if (handler) { /* Enable interrupts for this GPIO */ regval |= GPIO_PIN(pin); } else { /* Disable interrupts for this GPIO */ regval &= ~GPIO_PIN(pin); } stmpe811_putreg8(priv, STMPE811_GPIO_EN, regval); sem_post(&priv->exclsem); return OK; }
void stmpe811_gpioworker(FAR struct stmpe811_dev_s *priv) { uint8_t regval; uint8_t pinmask; int pin; /* Get the set of pending GPIO interrupts */ regval = stmpe811_getreg8(priv, STMPE811_GPIO_INTSTA); /* Look at each pin */ for (pin = 0; pin < STMPE811_GPIO_NPINS; pin++) { pinmask = GPIO_INT(pin); if ((regval & pinmask) != 0) { /* Check if we have a handler for this interrupt (there should * be one) */ if (priv->handlers[pin]) { /* Interrupt is pending... dispatch the interrupt to the * callback */ priv->handlers[pin](pin); } else { illdbg("No handler for PIN%d, GPIO_INTSTA: %02x\n", pin, regval); } /* Clear the pending GPIO interrupt by writing a '1' to the * pin position in the status register. */ stmpe811_putreg8(priv, STMPE811_GPIO_INTSTA, pinmask); } } }
int stmpe811_tempinitialize(STMPE811_HANDLE handle) { FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle; uint8_t regval; /* Enable clocking for ADC and the temperature sensor */ regval = stmpe811_getreg8(priv, STMPE811_SYS_CTRL2); regval &= ~(SYS_CTRL2_TS_OFF | SYS_CTRL2_ADC_OFF); stmpe811_putreg8(priv, STMPE811_SYS_CTRL2, regval); /* Enable the temperature sensor */ stmpe811_putreg8(priv, STMPE811_TEMP_CTRL, TEMP_CTRL_ENABLE); /* Aquire data enable */ stmpe811_putreg8(priv, STMPE811_TEMP_CTRL, (TEMP_CTRL_ACQ | TEMP_CTRL_ENABLE)); return OK; }
int stmpe811_gpioread(STMPE811_HANDLE handle, uint8_t pinconfig, bool *value) { FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle; int pin = (pinconfig & STMPE811_GPIO_PIN_MASK) >> STMPE811_GPIO_PIN_SHIFT; uint8_t regval; int ret; DEBUGASSERT(handle && (unsigned)pin < STMPE811_GPIO_NPINS); /* Get exclusive access to the device structure */ ret = sem_wait(&priv->exclsem); if (ret < 0) { int errval = errno; idbg("sem_wait failed: %d\n", errval); return -errval; } regval = stmpe811_getreg8(priv, STMPE811_GPIO_MPSTA); *value = ((regval & GPIO_PIN(pin)) != 0); sem_post(&priv->exclsem); return OK; }
int stmpe811_gpioconfig(STMPE811_HANDLE handle, uint8_t pinconfig) { FAR struct stmpe811_dev_s *priv = (FAR struct stmpe811_dev_s *)handle; int pin = (pinconfig & STMPE811_GPIO_PIN_MASK) >> STMPE811_GPIO_PIN_SHIFT; uint8_t pinmask = (1 << pin); uint8_t regval; int ret; DEBUGASSERT(handle && (unsigned)pin < STMPE811_GPIO_NPINS); /* Get exclusive access to the device structure */ ret = sem_wait(&priv->exclsem); if (ret < 0) { int errval = errno; idbg("sem_wait failed: %d\n", errval); return -errval; } /* Make sure that the pin is not already in use */ if ((priv->inuse & pinmask) != 0) { idbg("PIN%d is already in-use\n", pin); sem_post(&priv->exclsem); return -EBUSY; } /* Make sure that the GPIO block has been initialized */ stmpe811_gpioinit(priv); /* Set the alternate function bit for the pin, making it a GPIO */ regval = stmpe811_getreg8(priv, STMPE811_GPIO_AF); regval |= pinmask; stmpe811_putreg8(priv, STMPE811_GPIO_AF, regval); /* Is the pin an input or an output? */ if ((pinconfig & STMPE811_GPIO_DIR) == STMPE811_GPIO_OUTPUT) { /* The pin is an output */ regval = stmpe811_getreg8(priv, STMPE811_GPIO_DIR); regval &= ~pinmask; stmpe811_putreg8(priv, STMPE811_GPIO_DIR, regval); /* Set its initial output value */ stmpe811_gpiowrite(handle, pinconfig, (pinconfig & STMPE811_GPIO_VALUE) != STMPE811_GPIO_ZERO); } else { /* It is an input */ regval = stmpe811_getreg8(priv, STMPE811_GPIO_DIR); regval |= pinmask; stmpe811_putreg8(priv, STMPE811_GPIO_DIR, regval); /* Set up the falling edge detection */ regval = stmpe811_getreg8(priv, STMPE811_GPIO_FE); if ((pinconfig & STMPE811_GPIO_FALLING) != 0) { regval |= pinmask; } else { regval &= pinmask; } stmpe811_putreg8(priv, STMPE811_GPIO_FE, regval); /* Set up the rising edge detection */ regval = stmpe811_getreg8(priv, STMPE811_GPIO_RE); if ((pinconfig & STMPE811_GPIO_FALLING) != 0) { regval |= pinmask; } else { regval &= pinmask; } stmpe811_putreg8(priv, STMPE811_GPIO_RE, regval); /* Disable interrupts for now */ regval = stmpe811_getreg8(priv, STMPE811_GPIO_EN); regval &= ~pinmask; stmpe811_putreg8(priv, STMPE811_GPIO_EN, regval); } /* Mark the pin as 'in use' */ priv->inuse |= pinmask; sem_post(&priv->exclsem); return OK; }
STMPE811_HANDLE stmpe811_instantiate(FAR struct i2c_dev_s *dev, FAR struct stmpe811_config_s *config) #endif { FAR struct stmpe811_dev_s *priv; uint8_t regval; int ret; /* Allocate the device state structure */ #ifdef CONFIG_STMPE811_MULTIPLE priv = (FAR struct stmpe811_dev_s *)kmm_zalloc(sizeof(struct stmpe811_dev_s)); if (!priv) { return NULL; } /* And save the device structure in the list of STMPE811 so that we can find it later */ priv->flink = g_stmpe811list; g_stmpe811list = priv; #else /* Use the one-and-only STMPE811 driver instance */ priv = &g_stmpe811; #endif /* Initialize the device state structure */ sem_init(&priv->exclsem, 0, 1); priv->config = config; #ifdef CONFIG_STMPE811_SPI priv->spi = dev; #else priv->i2c = dev; /* Set the I2C address and frequency. REVISIT: This logic would be * insufficient if we share the I2C bus with any other devices that also * modify the address and frequency. */ I2C_SETADDRESS(dev, config->address, 7); I2C_SETFREQUENCY(dev, config->frequency); #endif /* Read and verify the STMPE811 chip ID */ ret = stmpe811_checkid(priv); if (ret < 0) { #ifdef CONFIG_STMPE811_MULTIPLE g_stmpe811list = priv->flink; kmm_free(priv); #endif return NULL; } /* Generate STMPE811 Software reset */ stmpe811_reset(priv); /* Configure the interrupt output pin to generate interrupts on high or low level. */ regval = stmpe811_getreg8(priv, STMPE811_INT_CTRL); #ifdef CONFIG_STMPE811_ACTIVELOW regval &= ~INT_CTRL_INT_POLARITY; /* Pin polarity: Active low / falling edge */ #else regval |= INT_CTRL_INT_POLARITY; /* Pin polarity: Active high / rising edge */ #endif #ifdef CONFIG_STMPE811_EDGE regval |= INT_CTRL_INT_TYPE; /* Edge interrupt */ #else regval &= ~INT_CTRL_INT_TYPE; /* Level interrupt */ #endif stmpe811_putreg8(priv, STMPE811_INT_CTRL, regval); /* Attach the STMPE811 interrupt handler. */ config->attach(config, stmpe811_interrupt); /* Clear any pending interrupts */ stmpe811_putreg8(priv, STMPE811_INT_STA, INT_ALL); config->clear(config); config->enable(config, true); /* Enable global interrupts */ regval = stmpe811_getreg8(priv, STMPE811_INT_CTRL); regval |= INT_CTRL_GLOBAL_INT; stmpe811_putreg8(priv, STMPE811_INT_CTRL, regval); /* Return our private data structure as an opaque handle */ return (STMPE811_HANDLE)priv; }
void stmpe811_tscworker(FAR struct stmpe811_dev_s *priv, uint8_t intsta) { uint16_t xdiff; /* X difference used in thresholding */ uint16_t ydiff; /* Y difference used in thresholding */ uint16_t x; /* X position */ uint16_t y; /* Y position */ bool pendown; /* true: pen is down */ ASSERT(priv != NULL); /* Cancel the missing pen up timer */ (void)wd_cancel(priv->wdog); /* Check for pen up or down from the TSC_STA ibit n the STMPE811_TSC_CTRL register. */ pendown = (stmpe811_getreg8(priv, STMPE811_TSC_CTRL) & TSC_CTRL_TSC_STA) != 0; /* Handle the change from pen down to pen up */ if (!pendown) { /* The pen is up.. reset thresholding variables. FIFOs will read zero if * there is no data available (hence the choice of (0,0)) */ priv->threshx = 0; priv->threshy = 0; /* Ignore the interrupt if the pen was already up (CONTACT_NONE == pen up and * already reported; CONTACT_UP == pen up, but not reported) */ if (priv->sample.contact == CONTACT_NONE || priv->sample.contact == CONTACT_UP) { goto ignored; } /* A pen-down to up transition has been detected. CONTACT_UP indicates the * initial loss of contact. The state will be changed to CONTACT_NONE * after the loss of contact is sampled. */ priv->sample.contact = CONTACT_UP; } /* The pen is down... check for data in the FIFO */ else if ((intsta & (INT_FIFO_TH | INT_FIFO_OFLOW)) != 0) { /* Read the next x and y positions from the FIFO. */ #ifdef CONFIG_STMPE811_SWAPXY x = stmpe811_getreg16(priv, STMPE811_TSC_DATAX); y = stmpe811_getreg16(priv, STMPE811_TSC_DATAY); #else x = stmpe811_getreg16(priv, STMPE811_TSC_DATAY); y = stmpe811_getreg16(priv, STMPE811_TSC_DATAX); #endif /* If we have not yet processed the last pen up event, then we * cannot handle this pen down event. We will have to discard it. That * should be okay because there will be another FIFO event right behind * this one. Other kinds of data overruns are not harmful. * * Hmm.. a better design might be to disable FIFO interrupts when we * detect pen up. Then re-enable them when CONTACT_UP is reported. * That would save processing interrupts just to discard the data. */ if (priv->sample.contact == CONTACT_UP) { /* We have not closed the loop on the last touch ... don't report * anything. */ goto ignored; } /* Perform a thresholding operation so that the results will be more stable. * If the difference from the last sample is small, then ignore the event. * REVISIT: Should a large change in pressure also generate a event? */ xdiff = x > priv->threshx ? (x - priv->threshx) : (priv->threshx - x); ydiff = y > priv->threshy ? (y - priv->threshy) : (priv->threshy - y); if (xdiff < CONFIG_STMPE811_THRESHX && ydiff < CONFIG_STMPE811_THRESHY) { /* Little or no change in either direction ... don't report anything. */ goto ignored; } /* When we see a big difference, snap to the new x/y thresholds */ priv->threshx = x; priv->threshy = y; /* Update the x/y position in the sample data */ priv->sample.x = priv->threshx; priv->sample.y = priv->threshy; /* Update the Z pressure index */ priv->sample.z = stmpe811_getreg8(priv, STMPE811_TSC_DATAZ); priv->sample.valid = true; /* If this is the first (acknowledged) pen 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; } } /* Pen down, but no data in FIFO */ else { /* Ignore the interrupt... wait until there is data in the FIFO */ goto ignored; } /* We get here if (1) we just went from a pen down to a pen up state OR (2) * We just get a measurement from the FIFO in a pen down state. Indicate * the availability of new sample data for this ID. */ priv->sample.id = priv->id; priv->penchange = true; /* Notify any waiters that new STMPE811 data is available */ stmpe811_notify(priv); /* If we think that the pen is still down, the start/re-start the pen up * timer. */ ignored: if (priv->sample.contact == CONTACT_DOWN || priv->sample.contact == CONTACT_MOVE) { (void)wd_start(priv->wdog, STMPE811_PENUP_TICKS, stmpe811_timeout, 1, (uint32_t)((uintptr_t)priv)); } /* Reset and clear all data in the FIFO */ stmpe811_putreg8(priv, STMPE811_FIFO_STA, FIFO_STA_FIFO_RESET); stmpe811_putreg8(priv, STMPE811_FIFO_STA, 0); }
static inline void stmpe811_tscinitialize(FAR struct stmpe811_dev_s *priv) { uint8_t regval; iinfo("Initializing touchscreen controller\n"); /* Enable TSC and ADC functions */ regval = stmpe811_getreg8(priv, STMPE811_SYS_CTRL2); regval &= ~(SYS_CTRL2_TSC_OFF | SYS_CTRL2_ADC_OFF); stmpe811_putreg8(priv, STMPE811_SYS_CTRL2, regval); /* Enable the TSC global interrupts */ regval = stmpe811_getreg8(priv, STMPE811_INT_EN); regval |= (uint32_t)(INT_TOUCH_DET | INT_FIFO_TH | INT_FIFO_OFLOW); stmpe811_putreg8(priv, STMPE811_INT_EN, regval); /* Select Sample Time, bit number and ADC Reference */ stmpe811_putreg8(priv, STMPE811_ADC_CTRL1, priv->config->ctrl1); /* Wait for 20 ms */ up_mdelay(20); /* Select the ADC clock speed */ stmpe811_putreg8(priv, STMPE811_ADC_CTRL2, priv->config->ctrl2); /* Select TSC pins in non-GPIO mode (AF=0) */ regval = stmpe811_getreg8(priv, STMPE811_GPIO_AF); regval &= ~(uint8_t)TSC_PIN_SET; stmpe811_putreg8(priv, STMPE811_GPIO_AF, regval); /* Select 2 nF filter capacitor */ stmpe811_putreg8(priv, STMPE811_TSC_CFG, (TSC_CFG_AVE_CTRL_4SAMPLES | TSC_CFG_TOUCH_DELAY_500US | TSC_CFG_SETTLING_500US)); /* Select single point reading */ stmpe811_putreg8(priv, STMPE811_FIFO_TH, 1); /* Reset and clear the FIFO. */ stmpe811_putreg8(priv, STMPE811_FIFO_STA, FIFO_STA_FIFO_RESET); stmpe811_putreg8(priv, STMPE811_FIFO_STA, 0); /* set the data format for Z value: 7 fractional part and 1 whole part */ stmpe811_putreg8(priv, STMPE811_TSC_FRACTIONZ, 0x01); /* Set the driving capability of the device for TSC pins: 50mA */ stmpe811_putreg8(priv, STMPE811_TSC_IDRIVE, TSC_IDRIVE_50MA); /* Enable the TSC. Use no tracking index, touch-screen controller * operation mode (XYZ). */ stmpe811_putreg8(priv, STMPE811_TSC_CTRL, TSC_CTRL_EN); /* Clear all the status pending bits */ stmpe811_putreg8(priv, STMPE811_INT_STA, INT_ALL); }