Resistive Touchscreen directly (no touch controller) #993
Replies: 13 comments
-
Posted at 2014-10-04 by @allObjects But there is also some good news: You may wonder whether the edge matters where the sensing sheet is connected. It does not (really). Why: the resistance of the coating from the touch point to the edge is (practically) negligible compared to the ADC pin's input impedance (inner resistance) and therefore practically no power (amperes) flows compared to the power that flows in the powered sheet's resistive coating, which is the voltage divider with the division point at the touch point. In other words, it is as if the pin would connect right there where the touch point is. It's all about Ohm's Law. And last but not least: your soft, fat finger creates anyway a bit a fuzzy touch area with 'a lot of (electrical) noise'. But because it is fat - compared to the location sensitivity of the touch screen (or pad), there have anyway to be multiple measuring and algorithm that find kind of the center of gravity of the touch point. But to that later. There are chips out there - touch controllers - that handle all the cases and corner cases, create an interrupt, and deliver (more then less) clean numbers through a simple connection interface (see http://www.espruino.com/ADS7843 - ADS7843 TOUCHSCREEN). But I want to explore how Espruino can do that, I connect Espruino directly to the edges of the sheets. The mentioned color TFT LCD is (or has) a (transparent) resistive touch screen (on top of the display) and the carrier/breakout board makes the 4 edges available (sequence as on the boards bottom connector from left to right, pins counted from 1...):
These are the delivered values:
After sending to the board, the code immediately starts running. With a frequency of 20Hz - twenty times per second / in 50[ms] intervals - it invokes the self explanatory logXY() function. Since the code is initialized with var enabled = false, the main part of the function body is skipped. At the end of the method, the method sets a timeout - of 50[ms] - for self-invocation. After enabling with command toggle(); in command pane, the code initializes some control values and various minimum and maximum variables, and prints a header in the console pane for the values to be logged (lines 27..36). Furthermore, the code starts to count in cnt variable - from 10000 upwards for aligned column oriented output - the cycles - 20 per second - as defined by the setTimeout (line 65) and invokes the reading of x and y (voltage) values. Since the reading needs some setup and settling time, the statistics / calculation and logging is passed as a callback to the readXY() function. More about that readXY() function a bit later. The callback code will detect when a touch happens by the fact that any of the x or y values are beyond a certain threshold: 1000 (line 40). The threshold's value was determined by some code tinkering and is touch screen dependent. The initial value ranging from 0.0000 to 1 is multiplied by 10000 and rounded to an integer (line 38) to trigger the set threshold, and to ease the - optical - tracking of value changes and detecting value and value change patterns. When the threshold is overstepped, the current cnt counter is checked against the past cnt2 counter and if there is a difference of more than 4 (missed cycles - 200[ms], line 41), the touch (tap) is considered a new one. past cnt2 is updated to current cnt as long as the touch keeps going (line 44). Lines 45 thru 52 do statistics to keep track of the minimum and maximum values. Finally, line 53 prints the following values:
As you can see, the difference of the values from both edges are very small compared to the read values (with a few exceptions). This proofs, that is sufficient to read just from one edge of the sensing sheet. Codes touch2.js (as shown above) and touch3.js (reading from one edge of the sensing sheet only) are attached as files at the very bottom of this post. The reading algorithm with its settings of the pins and stabilization times is in lines 68 thru 87 - implemented as readXY() function. @gordon, could you please comment on optimal pin setting and reading including timing and provide advice? = From what I understood from other implementations is that it is crucial for clean measuring that no output conflicts are there and powering of the to-power sheet has to stabilize, as well as the sensing ADC pin has to be ready and eventual capacitive energy has been discharged before measurements are taken. Most applications take multiple measurements and increase the accuracy of the (virtual) touch point coordinates. If the same effects can be achieved with less statements the better. Because when integrated into any other apps, every spared (cpu) cycle will be welcome to not just detect a touch or swipe, but more so to process it and its related biz or tech process. So much for now. Next steps will be to get some basic algorithm for increasing accuracy of mapping of read value to actual coordinate on screen. For that, some material is there done as part of designing dedicated touch controllers (see http://www.ti.com/lit/an/sbaa036/sbaa036.pdf, http://www.ti.com/lit/an/slyt277/slyt277.pdf, and most helpful http://www.ti.com/lit/an/sbaa155a/sbaa155a.pdf - Reducing Analog Input Noise in Touch Screen Systems). After that a module will be sketched that includes also some basic calibration support - Do you remember when you had to tap with your stylus X-es in the corners and center of the screen? Codes attached as files touch2X.js: Attachments: |
Beta Was this translation helpful? Give feedback.
-
Posted at 2014-10-06 by @gfwilliams Looks great - it could make a really handy module... About pins, what you've got looks good. Personally, I'd change the code to use the multiple digitalRead/Write functionality - and just for safety I'd make sure you switch the pins to inputs before you set the other pins to outputs... I'm not sure if using digitalRead (instead of analogRead) will work for setting state, but it's worth a try. Also it's worth trying without the setTimeouts - as the code takes a while to execute anyway, you'll probably find you don't need them.
or with multiple reads:
One neat addition would be to detect taps without polling, and then to only poll when you know a finger is pressed. I'm not 100% sure if this works, but what about:
That'd effectively stop using CPU cycles when a finger wasn't pressed. For other applications where you want long battery life it'd be handy too. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2014-10-08 by @allObjects
I had intended that - ...crucial... that no output conflicts are there... - but obviously missed to code it properly... :( Blind or continuous polling is not - practically never - sustainable, especially when not the only thing that has to go on - not just in regard of (battery) power consumption, but more so for wasting cycles. Was going for touch begin and end detection in a later state when having figured out the calculation stuff. For now, I just have the counters. I'll definitively try the provided code... When I thought about the edge-event drivenness, I was not sure if I would get always a clean edge-trigger because of the voltage divider circuitry of the powered plane edges preset to supply and ground voltage. Your code I read the solution: you set both C2 AND C3 to high, C1 to a float (free, 'un-pulled input), and C0 to (pulled-down) input with (one time only) edge trigger (interrupt set to be fire only once). I see no reason why this should not work for a touch begin-detection. After a begin has been detected, the pin settings for polling and polling will take place until end-detection. End-detection is required - because the trigger has to be setup again (you may notice that in my code has only begin-detection). When timeout can be dropped, then there is no need for callback pattern either. Callback was only introduced to make delayed reading possible. Some testing could show if the code execution creates enough delay for the outputs and inputs to stabilize / settle. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2014-10-17 by @allObjects ...have a hard time to detect when to listen again. Btw, in the listen(), setWatch() has to be on C1 and not on C2, because latter has just been set to high and therefore will never rise... ;). The issue I have is after 'untouch' to read values different from when touched. With suggested readXY() I get values within the touched range. Interestingly, I did not get such values with my initial code (touch2.js). After 'fixing' the listen(), I used a hybrid of suggested listen() and my initially working readXY() code, but with the same result: for untouched I get values within the range of touched. So, I have to do a bit backing out.... |
Beta Was this translation helpful? Give feedback.
-
Posted at 2014-10-18 by @allObjects After backing out and rebuilding up to including the onDown touch event and investigating how to detect an onUp untouch event, I ended up with a code containing the core elements shown below as listen() and read xy() methods (code uploaded as touch4.js). The read xy() method is not just for reading the coordinates onDown, but later also for possible dragging while touching (onTrack), and - last but not least - for 'untouch' (onUp). Since with every event time is provided in addition to the x-y coordinates, touch durations can be used to determine short, long, very long, and dobule touches or taps. The code incorporates the suggestions from @gordon. The essentials in a nutshell:
This is the output for touching the four corners ( 240 (x) px horizontal x 320 (y) px vertical screen):
The above data shows the trouble in the untouched stage: untouched values are in the range of touch min-max values. Elaborating on different items - even bringing in the reading of the xy from the old touch2.js code with the 1[ms] timeouts, which worked before for detecting the untouched state - did not help. Just having dropped the continuous polling and the timeouts for a faster and cycle/time-leaner execution, I did not want to go back and increase the timeouts to may-be have success. The analysis of the data - untouched values are influenced by the previously touched area - and the difference between touch2.js and touch4.js code - unintended output-clashes - made me think that there is not enough time to 'discharge' the previously powered plane sufficiently to be ready for reading. Adding very brief input with pull-down on the reading plane pin AFTER powering the powered plane provided the solution. Somehow, the lingering capacities between the planes in combination with the very sensitive, input-/sink-frugal ADCs lead to the 'misreadings'. Below the xy() method with the added lines to sink-away lingering capacities, and the new, related output data.
The new data show now untouched values that are comfortably outside of the range of the touched values.
The pin-set sequence can now also be optimized towards the added lines. Looking at in what state the pins are left by the reading of xy(), I reduced listen() to just the pulling hight the second edge of the powered plane and call the xy() before the listen() at connect. But for some reason, this made the watch() to trip on a regular basis without a touch happening (and triggered xy() showed also an 'untouch'...). So I left the listen and optimized only the reading of xy(). Playing around while optimizing, I also noticed that keeping the pull-down had no adverse effect, so I eliminated its removal. (The complete code for the touch initialization and touch onDown event detection is uploaded as touch5.js file.)
Next steps are now to bring back all the complete onDown, onTrack - track while touching and possibly dragging - and optional on onUp support.Attachments: |
Beta Was this translation helpful? Give feedback.
-
Posted at 2014-10-18 by @allObjects Yeah!... got my first module running... in the sandbox... anyway... thanks to the conversation Writing modular code using "require". I knew about and used the Web available modules from repository only, so far... Now I can easily QA the modules locally before thinking about publishing for use from the Web. I created a folder, registered it in Web IDE Settings - Project - Sandbox, and placed the TOUCH.js module file in the sandbox's modules folder. The TOUCH module is now at a stage where I need to combine the TOUCH SCREEN with the LCD for calibration purposes to finish the implementations of the onTouch, onTrack (while touching) and onUp functions. The TOUCH module goes into calibration mode on a very long touch - 5 [s] - with 'no' movement/dragging (<10%) anywhere on the screen. Calibration mode will show spots on the LCD to touch / tap. The spots' x-y coordinates are then formula-mapped to the related, read x-y coordinate values (0.000..0.999) for the calculation of the x-y pixel coordinates of the 240 horizontal x 320 vertical (2.8" TFT 262K Color) LCD. The TOUCH module so far:
The TOUCH module usage example (sampling/polling interval on tracking/while touching is 100 [ms] / 10Hz / 10 times per second - defined in .C.trkIv constant):
The examples output: The first number on line - cnt - counts the touches, second number counts the track events. Lines with 5 dots (.....) indicated removed ('redundandent/boring' ) output to keep post conciser.
Notes:
#require #module #localmodule |
Beta Was this translation helpful? Give feedback.
-
Posted at 2014-10-20 by @gfwilliams This looks great - I'm glad you got the setWatch-based touch detection working so well... It'd be really neat on battery powered devices. Now we just need a touchscreen LCD that doesn't draw loads of power! Actually I wonder how well those 320x240 LCDs cope without a backlight - I've never tried them. By the way, on the other touchscreen modules I've done before like this I've tended to just have a single callback, and to call it with no arguments when a finger is lifted off. It's probably not better, but it might be best to keep the APIs the same. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2014-10-20 by @allObjects
For a decent touch interface - touch duration (at one point), stay or drag and drag-drop - it is not good enough to have only an onUp... but my API can do all. I picked down as the primary because it enables all options, even just an onUp. And beyond that, whit the down at a particular location, the onTrack and onUp can be modified to that particular location in order to simplify and speed-up the subsequently needed detection processing... Looking at the ADS7843 implementation, the question arise: how much or little - no insult here - a module should do... Initially I had only one callback that received a touch object with the defining parameters inside, such as x and y, time, and state - down, track, up. I dropped it - at least for now - for several reasons, mainly for speed and directness in resource constraint environment, such as Espruino, and because JavaScript applications at first sight use just global functions... hence JavaScript being at first sight 'just' a functional language. I intentionally flipped the 'chicken and egg'... (That JavaScript is at first sight a functional language is last but not least noticeably too by the needed hassle of var _this = this; all over the place when going light-weight object-oriented... alleviated though by some frameworks which introduce an optional parameter following the callback function to provide that 'deferred' resolved this context object. But that becomes cumbersome when having multiple callbacks). I'm about to publish a calibration module - it is already working. I needed the code to do initial settings in the TOUCH module for transforming the analog value into pixel coordinates. In the calibration module I just use the onUp - with the very same TOUCH module. Notice the context/callback stack that is implemented in the [0v02] TOUCH module! This is the current [0v02] state of the TOUCH nodule:
So much for calibration for now: TOUCH enters the calibration - or callout - when touching for longer than a specified quite long time at the same location (within +-10% in regard to the onDown). For calibration, the callout darks the screen for a bit for UI feedback, and comes back with five markers to touch - one after the other - to gather data and then perform the calibration / adjustment. The calibration has also a timeout to return when accidentally entered or/and not completed. With more experience, calibration may be needed only once to get a particular type and instance of touch screen going. After that it may not be needed anymore, because the once set parameters stay accurate enough over time. If so, I'n thinking of enabling additional optional parms to provide these values on connect (which leads me to think for some JS extensions, such as the very convenient mix(), mixin(), saveMixin()... I may start other conversations for those matters). I have also some thoughts about naming the things... to not restrict the mind when thinking how applying modules. For example, the (optional) calibration intercept/interrupt related things are called something calib..., but it can be used for just anything a user likes to do. Any callback (or callout) could be set, even with coexistence of different ones at different times/states. For a final settling some feedback from the community could help. The current forum does not have a voting implemented/enabled, which could be used for a RFC process... ;-) - Btw, I did not get a single response for the RFC for my Software Buttons - Many buttons from just one hardware button at http://forum.espruino.com/comments/4904059/. Having now some +-experience with the 'cheap' resistive touch screen technology, I'm thinking about capacitive touch screen technology... I do not like the spongy feeling and the noise rate. But first things first: bring the began project(s) to the finish line. Attached file: TOUCH.js [0v02]Attachments: |
Beta Was this translation helpful? Give feedback.
-
Posted at 2014-10-20 by DrAzzy
Pretty poorly, like any other unbacklit color LCD - it'll look like a laptop screen with a burned out backlight (if you owned a pre-recession Acer laptop, you're lucky if you haven't seen this). If it's assembled, you can barely make out large shapes if you hold it up to light at the right angle. If you can pull the backing layers off without ruining it, you could probably read it with difficulty if you hold it up to the light. If screen power usage is a big issue (can't you just turn off the backlight except when it's in use?), you could put a touch overlay on top of a B&W LCD or even one of those exorbitantly expensive e-Paper modules. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2014-10-21 by @gfwilliams Thanks... I just remember that the N95 had a screen that worked passably well without a backlight (it had a clock that was displayed on it). It was a similar size so I wondered if it might be a similar display. |
Beta Was this translation helpful? Give feedback.
-
Posted at 2014-10-22 by @allObjects Here the first cut of LCD and TOUCHR(esistive) screen combined with calibration module connected.
The code is still pretty fat and needs to go for some diet. On the other hand it shows that not for no reason touch controller chips are available to offload an application controller from that tedious work... Some fat is intended to be burned - saved - when going for a simple UI w/ buttons and sliders. Below some output, including the re-calibration.
As you may notice, most touches / taps with a resistive and thus spongy feel are longer than the set track interval of 100 [ms] - 10 cycles or reads/second when while touching... and two for safely detecting - and 'debouncing' an 'untouch'. Therefore you see onTrack: output lines - such as 14, 15, and 19, even though I intended only a (short) touch / tap. With output line 8 a very long tap starts... After the defined esc(ape)Time of 10 [sec] the connected recalibrate callback will kick in - at line 31 - after 91 track (way) points (of 100 theoretical possible ones). This is a great testimony Espruino's amazing performance. Lines 34..42 show calibration values. First - lines 34..38 - show the read raw ADC values of top-left, top-right, bottom-right, and bottom-left corners (@ 10 px from the border) and center. (In the attached shot I commented the removal of the markers to tap to show all locations at once. The marker size is 20x20 px). Surprising to me was the quite accurate reproducibility of the values between calibration. The last two numbers in line 39 show the ADC value difference for 1 px in x and y axis, and the last number on line 40 and 41 show the offset difference. Touch detection accuracy is a fraction of the display accuracy of +-1 pixel. The shape of the touching object and the pressure add quite some variation - especially a finger tip - which is reflected in unexpected high value of 12 px for sameThd (...Threshold) to detect still same touch location. For a pointy pen 3 px worked (line 29 in the code). With the experience made, I do not (anymore) believe that calibration has to be available at runtme. It is good enough to verify with a particular new Touch LCD device - and if the device is within the tolerance, no value adjustments are needed. If there are - because of different types - then the connect (and constructor) allows to pass a C(onfig) object with the parameters that need to be different from what the module code has coded. More about the code and some application samples in a different post. Attached touchrCalib.js file (with touch and calibration 'module').Attachments: |
Beta Was this translation helpful? Give feedback.
-
Posted at 2014-11-01 by @allObjects Below module code I intend to use for my resistive touch screen. It is a much lighter version of what you have seen from me so far. It is inspired by @gordon 's ADS7843 touch controller module AND is - of course - 100% callback backward-compatible. Planned name for the module is TOUCHR - R for resistive. The module includes the defaults configuration for any ILI9341 controlled 320x240 262K Color TFT LCD with resistive touch screen (used in 65K color mode). Any of the default LCD and touch screen parameters can be overwritten on connect() to use it with other displays and touch screen, and to manage variations between same touch screens. Power consumption is optimized and can be lowered even more for touch tracking by setting the scan interval i at connect time or afterwards, even dynamically in callback depending tracking needs identified on particular touch.
The sample test code
shows the console output below on touching top-left, top-right, bottom-right, and bottom-left corner and the center of the LCD area. On touching, it shows the calculated x and y coordinates and the raw rx and ry values read by the ADC pins. On up (just once after touching) just the raw values read by the DC pin are shown. The touch thresholds (xt, yt) for detecting an un-touch are between the lowest read touch value and the up (un-touch) value and are different for x and y axis. Touches are detected by a hardware event caught by setWatch().
Because Espruino IS the (software) controller - like the ADS7843 chip in the ADS7843 module - the TOUCHR module returns a (singleton) control object at connect() to provide additional access - control and data. The control object returns itself also in listen() and xy() methods, and is passed to callback as third (3rd) argument. On connect() or connect().listen(true), it is useful to hold on to the control module in a global variable named, for example, touchr:
Some displays have touch screens mounted that are larger than the active display area. These areas are called porches. Some touch screens show a blank porch and some have printed icons on it, such as a letter, a clock, a calendar, etc., to mark function buttons. The porch can be used as input but. The touch screen at hand has a bottom porch of about 30px. To use the porch, the controllers Y and yc touch screen parms have to be adjusted:
Adjustment can be done anytime after connecting. Remember though to consider the extra y range in the callback as well and do not use it for mapping to pixel position of the display:
Below code is the classic drawing 'application':
Notice the shortened tracking interval time passed on on connect (line 42). Colors of attached screen shot are 'a bit' off... angle and exposure and ambient light matter messed with capturing.
A calibration module for TOUCHR module will be presented in a separate post. It will help you to set or adjust the touch screen parms xt, yt, xc, yc, xd, and yd for your (same or different LCD w/) resistive touch screen (...c stands for expected value at the center, ...d for the difference between pixel. In a nutshell, the calibration module uses the read raw values of touches at known (displayed px) x / y locations to calculate the touch screen parms. The above provided simple test code can be used to manually gather the raw values and do the calculations. Set pixels at defined x / y coordinates equally close to the corners (about 10 px away from the borders) and one in the center, touch them, and perform the calculations. Simple average and linear formulae are accurate enough:
Attachments: |
Beta Was this translation helpful? Give feedback.
-
Posted at 2018-07-21 by @allObjects Edit of Post #1 on 2018-07-21: Found attached pic on https://www.epectec.com/articles/the-technology-within-touch-panels.html:Attachments: |
Beta Was this translation helpful? Give feedback.
-
Posted at 2014-10-04 by @allObjects
An other multi part series
This conversation covers some leg work to be done for next steps in * DIY Marine GPS using enhanced GPS Module, u-blox NEO-6M GPS receiver, and ILI9341 Module controlled 2.8" Color TFT LCD* project (http://forum.espruino.com/conversations/255759) and Exploring 2.8" Color TFT Touch LCD by PacMan Game attempt project (http://forum.espruino.com/conversations/127039).
In both projects I intend to use the touch capability of the display.
The touch screen is a resistive touch screen. A good description you can find at https://www.sparkfun.com/datasheets/LCD/HOW%20DOES%20IT%20WORK.pdf.
Basically, a touch screen consists of two stacked 'sheets' with a small gap in between. When touching the (flexible) top sheet, it will touch the (rigid) bottom sheet in the area of the touch point.
When touching the (flexible) top sheet, it will touch the (rigid) bottom sheet in the are of the touch point.
Because the sheets' facing sides are coated with a conductive material with significant resistance versus a plain wire, and one sheet is put under current with one edge all along the edge to ground (0 Volts) and the opposite edge to supply (3.3 Volts), the other sheet senses at the touch point something in between 0 and 3.3 Volts.
The sensing sheet is connected to a pin (for example, C2) with an analog-to-digital converter (ADC), which provides through analogRead(C3); a voltage-related floating point value from roughly 0.000 to 1. Of course, the edges of the sheet under power are also connected to pins of which one is set to Ground (0 Volts) with C0.reset();, and the other one is set to supply (3.3 Volts) using C1.set(); in order to set that sheet under power.
Assuming a screen of 240 horizontal (x) by 320 vertical (y) resolution and below axis configuration with origin {x:0,y:0} in the top left corner of the screen, and the top sheet's left edge to ground (0 V) at x=0 and right edge to supply (3.3 V) at x=239, a touch point sensing (2.2 V) is about 2 thirds in at about x=160.
But this is only the x-axis value... - what about the y-axis value?
Ingeniously, the the bottom sheet has 90 degrees turned edge connectors as well - at the top and the bottom - of which only the bottom is used for the sensing.
But: now roles of the sheets are flipped:
Et voila: we get the value y for the y-axis, because the a touch at the top is (0 V) and corresponds to y=0, and a touch at the bottom corresponds to (3.3 V) and corresponds to y=240; and a touch point a quarter way down will provide (0.825 V = 3.3 / 4 V) corresponding to a value of y=80.
I said basically... because looking into the produced values, it is not that a clear cut - at least not for all values.
There is also much more to the building if a touch screen - for example: In order that the sheets to not accidentally touch by sagging or vibration over a too wide area, very small insulating spacers spread in the gap. But because the top is flexible, touching happens between the - insulating, non-conductive - spacers (S):
Edit 2018-07-21: Found attached pic on https://www.epectec.com/articles/the-technology-within-touch-panels.html:
Attachments:
Beta Was this translation helpful? Give feedback.
All reactions