static int tc_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) { FAR struct inode *inode; FAR struct tc_dev_s *priv; int ret; int i; ivdbg("setup: %d\n", (int)setup); DEBUGASSERT(filep && fds); inode = filep->f_inode; DEBUGASSERT(inode && inode->i_private); priv = (FAR struct tc_dev_s *)inode->i_private; /* Are we setting up the poll? Or tearing it down? */ ret = sem_wait(&priv->devsem); if (ret < 0) { /* This should only happen if the wait was canceled by an signal */ DEBUGASSERT(errno == EINTR); return -EINTR; } if (setup) { /* Ignore waits that do not include POLLIN */ if ((fds->events & POLLIN) == 0) { idbg("Missing POLLIN: revents: %08x\n", fds->revents); ret = -EDEADLK; goto errout; } /* This is a request to set up the poll. Find an available * slot for the poll structure reference */ for (i = 0; i < CONFIG_TOUCHSCREEN_NPOLLWAITERS; i++) { /* Find an available slot */ if (!priv->fds[i]) { /* Bind the poll structure and this slot */ priv->fds[i] = fds; fds->priv = &priv->fds[i]; break; } } if (i >= CONFIG_TOUCHSCREEN_NPOLLWAITERS) { idbg("No availabled slot found: %d\n", i); fds->priv = NULL; ret = -EBUSY; goto errout; } /* Should we immediately notify on any of the requested events? */ if (priv->penchange) { tc_notify(priv); } } else if (fds->priv) { /* This is a request to tear down the poll. */ struct pollfd **slot = (struct pollfd **)fds->priv; DEBUGASSERT(slot != NULL); /* Remove all memory of the poll setup */ *slot = NULL; fds->priv = NULL; } errout: sem_post(&priv->devsem); return ret; }
static void tc_worker(FAR void *arg) { FAR struct tc_dev_s *priv = (FAR struct tc_dev_s *)arg; uint32_t delay; uint16_t value; uint16_t newx; int16_t xdiff; int16_t ydiff; int ret; ASSERT(priv != NULL); /* Perform the next action based on the state of the conversions */ switch (priv->state) { /* The touchscreen is IDLE and we are ready to begin the next sample */ case TC_READY: { /* Start Y- sampling */ tc_yminus_sample(); /* Allow time for the Y- pend down sampling */ priv->state = TC_YMPENDOWN; delay = TC_SAMPLE_TICKS; } break; /* The Y- sampling time has elapsed and the Y- value should be ready * for conversion */ case TC_YMPENDOWN: { /* Convert the Y- sample value */ value = tc_adc_convert(); /* A converted value at the minimum would mean that there is no touch * and that the sampling period is complete. */ if (!tc_valid_sample(value)) { priv->state = TC_PENUP; } else { /* Allow time for touch inputs to stabilize */ priv->state = TC_DEBOUNCE; delay = TC_DEBOUNCE_TICKS; } } break; /* The debounce time period has elapsed and we are ready to re-sample * the touchscreen. */ case TC_RESAMPLE: case TC_DEBOUNCE: { /* (Re-)start Y- sampling */ tc_yminus_sample(); /* Allow time for the Y- sampling */ priv->state = TC_YMSAMPLE; delay = TC_SAMPLE_TICKS; } break; /* The Y- sampling period has elapsed and we are ready to perform the * conversion. */ case TC_YMSAMPLE: { /* Convert and save the Y- sample value */ value = tc_adc_convert(); /* A converted value at the minimum would mean that there is no touch * and that the sampling period is complete. At converted value at * the maximum value is probably bad too. */ if (!tc_valid_sample(value)) { priv->state = TC_PENUP; } else { /* Save the Y- sample and start Y+ sampling */ priv->value = value; tc_yplus_sample(); /* Allow time for the Y+ sampling */ priv->state = TC_YPSAMPLE; delay = TC_SAMPLE_TICKS; } } break; /* The Y+ sampling period has elapsed and we are ready to perform the * conversion. */ case TC_YPSAMPLE: /* Allowing time for the Y+ sampling */ { /* Read the Y+ axis position */ value = tc_adc_convert(); /* A converted value at the minimum would mean that we lost the contact * before all of the conversions were completed. At converted value at * the maximum value is probably bad too. */ if (!tc_valid_sample(value)) { #ifdef CONFIG_TOUCHSCREEN_RESAMPLE priv->state = TC_RESAMPLE; delay = TC_RESAMPLE_TICKS; #else priv->state = TC_PENUP; #endif } else { value = MAX_ADC - value; priv->newy = (value + priv->value) >> 1; ivdbg("Y-=%d Y+=%d[%d] Y=%d\n", priv->value, value, MAX_ADC - value, priv->newy); /* Start X+ sampling */ tc_xplus_sample(); /* Allow time for the X+ sampling */ priv->state = TC_XPSAMPLE; delay = TC_SAMPLE_TICKS; } } break; /* The X+ sampling period has elapsed and we are ready to perform the * conversion. */ case TC_XPSAMPLE: { /* Convert the X+ sample value */ value = tc_adc_convert(); /* A converted value at the minimum would mean that we lost the contact * before all of the conversions were completed. At converted value at * the maximum value is probably bad too. */ if (!tc_valid_sample(value)) { #ifdef CONFIG_TOUCHSCREEN_RESAMPLE priv->state = TC_RESAMPLE; delay = TC_RESAMPLE_TICKS; #else priv->state = TC_PENUP; #endif } else { /* Save the X+ sample value */ priv->value = value; /* Start X- sampling */ tc_xminus_sample(); /* Allow time for the X- pend down sampling */ priv->state = TC_XMSAMPLE; delay = TC_SAMPLE_TICKS; } } break; /* The X+ sampling period has elapsed and we are ready to perform the * conversion. */ case TC_XMSAMPLE: /* Allowing time for the X- sampling */ { /* Read the converted X- axis position */ value = tc_adc_convert(); /* A converted value at the minimum would mean that we lost the contact * before all of the conversions were completed. At converted value at * the maximum value is probably bad too. */ if (!tc_valid_sample(value)) { #ifdef CONFIG_TOUCHSCREEN_RESAMPLE priv->state = TC_RESAMPLE; delay = TC_RESAMPLE_TICKS; #else priv->state = TC_PENUP; #endif } else { /* Calculate the X- axis position */ value = MAX_ADC - value; newx = (value + priv->value) >> 1; ivdbg("X+=%d X-=%d[%d] X=%d\n", priv->value, value, MAX_ADC - value, newx); /* Samples are available */ priv->state = TC_PENDOWN; } } break; } /* Check for terminal conditions.. */ /* Check if the sampling resulted in a pen up decision. If so, we need to * handle the change from pen down to pen up. */ if (priv->state == TC_PENUP) { /* Ignore if the pen was already down (CONTACT_NONE == pen up and already * reported. CONTACT_UP == pen up, but not reported) */ if (priv->sample.contact != CONTACT_NONE) { /* The pen is up. We know from the above 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; /* Indicate the availability of new sample data for this ID */ priv->sample.id = priv->id; priv->penchange = true; /* Notify any waiters that nes touchscreen data is available */ tc_notify(priv); } /* Set up for the next poll */ priv->sample.valid = false; priv->state = TC_READY; delay = TC_PENUP_POLL_TICKS; } /* Check if the sampling resulted in a pen down decision. */ else if (priv->state == TC_PENDOWN) { /* It is a pen down event. If the last loss-of-contact event has not been * processed yet, then we have to ignore the pen down event (or else it will * look like a drag event) */ if (priv->sample.contact != CONTACT_UP) { /* 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. */ xdiff = (int16_t)priv->sample.x - (int16_t)newx; if (xdiff < 0) { xdiff = -xdiff; } ydiff = (int16_t)priv->sample.y - (int16_t)priv->newy; if (ydiff < 0) { ydiff = -ydiff; } if (xdiff >= CONFIG_TOUCHSCREEN_THRESHX || ydiff >= CONFIG_TOUCHSCREEN_THRESHY) { /* There is some change above the threshold... Report the change. */ priv->sample.x = newx; priv->sample.y = priv->newy; priv->sample.valid = true; /* If this is the first (acknowledged) penddown 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; } /* Indicate the availability of new sample data for this ID */ priv->sample.id = priv->id; priv->penchange = true; /* Notify any waiters that nes touchscreen data is available */ tc_notify(priv); } } /* Set up for the next poll */ priv->state = TC_READY; delay = TC_PENDOWN_POLL_TICKS; } /* Set up the next sample event */ ret = work_queue(HPWORK, &priv->work, tc_worker, priv, delay); ASSERT(ret == 0); }
static void tc_worker(FAR void *arg) { FAR struct tc_dev_s *priv = (FAR struct tc_dev_s *)arg; uint32_t delay = TC_PENUP_POLL_TICKS; uint16_t value; uint16_t newx = 0; int16_t xdiff; int16_t ydiff; int ret; DEBUGASSERT(priv != NULL); /* Perform the next action based on the state of the conversions */ switch (priv->state) { /* The touchscreen is IDLE and we are ready to begin the next sample */ case TC_READY: { /* Select DRIVE for Y sampling */ /* Configure XL, XR with drive voltages and disable YU drive. Note that * this is configuring the DRIVEA and DRIVEB outputs to enable the on-board * transistor drive logic to energize the touch panel. */ *((uint32_t *) LCD_TP_PORT_SETRESET) = LCD_SAMPY_BITS; /* Allow time for the Y DRIVE to settle */ priv->resamplecount = 0; priv->sampcount = 0; priv->value = 0; priv->state = TC_READY_SETTLE; delay = TC_SETTLE_TICKS; } break; case TC_READY_SETTLE: { /* Start Y sampling */ tc_y_sample(); /* Allow time for the Y pend down sampling */ priv->state = TC_YPENDOWN; delay = TC_SAMPLE_TICKS; } break; /* The Y sampling time has elapsed and the Y value should be ready * for conversion */ case TC_YPENDOWN: { /* Convert the Y sample value */ value = tc_adc_read_sample(); /* A converted value at the minimum would mean that there is no touch * and that the sampling period is complete. */ if (!tc_valid_sample(value)) { priv->state = TC_PENUP; } else { /* Allow time for touch inputs to stabilize */ priv->state = TC_DEBOUNCE; delay = TC_DEBOUNCE_TICKS; } } break; /* The debounce time period has elapsed and we are ready to re-sample * the touchscreen. */ case TC_RESAMPLE: { /* Select DRIVE for Y sampling */ /* Configure XL, XR with drive voltages and disable YU drive. Note that * this is configuring the DRIVEA and DRIVEB outputs to enable the on-board * transistor drive logic to energize the touch panel. */ *((uint32_t *) LCD_TP_PORT_SETRESET) = LCD_SAMPY_BITS; /* Allow time for the Y DRIVE to settle */ priv->state = TC_DEBOUNCE; delay = TC_SETTLE_TICKS; } break; case TC_DEBOUNCE: { /* (Re-)start Y sampling */ tc_y_sample(); /* Allow time for the Y sampling */ priv->state = TC_YSAMPLE; delay = TC_SAMPLE_TICKS; } break; /* The Y sampling period has elapsed and we are ready to perform the * conversion. */ case TC_YSAMPLE: /* Allowing time for the Y sampling */ { /* Read the Y axis position */ value = tc_adc_read_sample(); /* A converted value at the minimum would mean that we lost the contact * before all of the conversions were completed. At converted value at * the maximum value is probably bad too. */ if (!tc_valid_sample(value)) { #ifdef CONFIG_TOUCHSCREEN_RESAMPLE priv->state = TC_RESAMPLE; delay = TC_RESAMPLE_TICKS; #else priv->state = TC_PENUP; #endif } else { value = MAX_ADC - value; priv->value += value; if (++priv->sampcount < CONFIG_TOUCHSCREEN_AVG_SAMPLES) { priv->state = TC_READY_SETTLE; delay = 1; break; } priv->newy = value / CONFIG_TOUCHSCREEN_AVG_SAMPLES; priv->value = 0; priv->sampcount = 0; iinfo("Y=%d\n", priv->newy); /* Configure YU and YD with drive voltages and disable XR drive. Note that * this is configuring the DRIVEA and DRIVEB outputs to enable the on-board * transistor drive logic to energize the touch panel. */ *((uint32_t *) LCD_TP_PORT_SETRESET) = LCD_SAMPX_BITS; /* Allow time for the X sampling */ priv->state = TC_XSETTLE; delay = TC_SETTLE_TICKS; } } break; case TC_XRESAMPLE: /* Perform X resampling */ { if (priv->resamplecount-- == 0) { priv->state = TC_PENUP; break; } } case TC_XSETTLE: /* Allowing time X to settle after changing DRIVE */ { /* The X Drive settling time has elaspsed and it's time to start * the conversion */ /* Start X sampling */ tc_x_sample(); /* Allow time for the X sampling */ priv->state = TC_XSAMPLE; delay = TC_SAMPLE_TICKS; } break; case TC_XSAMPLE: /* Allowing time for the X sampling */ { /* Read the converted X axis position */ value = tc_adc_read_sample(); /* A converted value at the minimum would mean that we lost the contact * before all of the conversions were completed. At converted value at * the maximum value is probably bad too. */ if (!tc_valid_sample(value)) { #ifdef CONFIG_TOUCHSCREEN_RESAMPLE priv->state = TC_XRESAMPLE; if (priv->resamplecount == 0) priv->resamplecount = 1; delay = TC_RESAMPLE_TICKS; #else priv->state = TC_PENUP; #endif } else { /* Calculate the X axis position */ //value = MAX_ADC - value; priv->value += value; if (++priv->sampcount < CONFIG_TOUCHSCREEN_AVG_SAMPLES) { priv->state = TC_XSETTLE; delay = 1; break; } newx = value / CONFIG_TOUCHSCREEN_AVG_SAMPLES; iinfo("X=%d\n", newx); /* Samples are available */ priv->state = TC_PENDOWN; } } break; } /* Check for terminal conditions.. */ /* Check if the sampling resulted in a pen up decision. If so, we need to * handle the change from pen down to pen up. */ if (priv->state == TC_PENUP) { /* Ignore if the pen was already down (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) { /* The pen is up. We know from the above 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; /* Indicate the availability of new sample data for this ID */ priv->sample.id = priv->id; priv->penchange = true; /* Notify any waiters that new touchscreen data is available */ iinfo("1:X=%d, Y=%d\n", priv->sample.x, priv->sample.y); tc_notify(priv); } /* Set up for the next poll */ priv->sample.valid = false; priv->state = TC_READY; delay = TC_PENUP_POLL_TICKS; } /* Check if the sampling resulted in a pen down decision. */ else if (priv->state == TC_PENDOWN) { /* It is a pen down event. If the last loss-of-contact event has not been * processed yet, then we have to ignore the pen down event (or else it will * look like a drag event) */ if (priv->sample.contact != CONTACT_UP) { /* 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. */ xdiff = (int16_t)priv->sample.x - (int16_t)newx; if (xdiff < 0) { xdiff = -xdiff; } ydiff = (int16_t)priv->sample.y - (int16_t)priv->newy; if (ydiff < 0) { ydiff = -ydiff; } if (xdiff >= CONFIG_TOUCHSCREEN_THRESHX || ydiff >= CONFIG_TOUCHSCREEN_THRESHY) { /* There is some change above the threshold... Report the change. */ #ifdef CONFIG_LCD_LANDSCAPE priv->sample.x = MAX_ADC - priv->newy; priv->sample.y = newx; #else priv->sample.x = newx; priv->sample.y = priv->newy; #endif priv->sample.valid = true; /* If this is the first (acknowledged) penddown 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; } /* Indicate the availability of new sample data for this ID */ priv->sample.id = priv->id; priv->penchange = true; /* Notify any waiters that nes touchscreen data is available */ iinfo("2:X=%d, Y=%d\n", priv->sample.x, priv->sample.y); tc_notify(priv); } } /* Set up for the next poll */ priv->state = TC_READY; delay = TC_PENDOWN_POLL_TICKS; } /* Set up the next sample event */ ret = work_queue(HPWORK, &priv->work, tc_worker, priv, delay); DEBUGASSERT(ret == 0); }