This advanced tutorial shows how to partner a cheap, touch-enabled TFT LCD display screen with the Digistump Oak, and displays the raw touch values to screen. The display itself is an ILI9341-based screen similar to the non-touch 2.2″ version sold by Digistump; it and similar versions are readily available from eBay and other sources. Touch-sensing is handled via the XPT2046 controller. Both run over SPI, to minimize the number of pins required.
Please note that, as is, this code will only run on ILI9341- and XPT2046-based displays.
Third-party libraries are required to compile the sample sketch: Adafruit’s graphic libraries, and Paul Stoffregen’s XPT2046_Touchscreen library. We’ll also show you how to modify the SPI.transfer16 function in your SPI.cpp library to correct a bug in this function, if required.
This sketch returns raw touch data from the screen only. Part two (soon to follow) will return the pixel coordinates of your screen. It requires that a more complex calibration sketch be run first, requires a revised version of the XPT2046_Touchscreen library, and a somewhat more complicated version of the sketch shown here to return the pixel coordinates.
|Oak with soldered headers||1|
|ILI9341- and XPT2046-based TFT LCD display||1|
|330-ohm resistor||1||Colour code: red-red-brown|
A touch-enabled display should really be considered and treated as two devices. The more familiar one is the display, and the Adafruit graphics library is familiar and effective. The version included in the Arduino/Oak IDE should work as is. Check out their documentation at https://learn.adafruit.com/adafruit-gfx-graphics-library/overview.
The touch screen is physically separate from and lies atop the LCD screen, and uses a discrete interface and coordinate system. This is important: your screen might display at 320 x 240 pixels, but the touch screen returns a totally different set of coordinates. The XPT2046 controller defaults to a resolution of 4096 by 4096. And these values do not map neatly to pixel coordinates: the two screens often do not line up exactly, and may be rotated relative to each other.
For this first, simpler-to-implement example, we report only the raw data returned from the touch screen. You could use these raw values for your project, or alternately, in Part 2 we describe how to translate these raw values to pixel positions by running a calibration sketch to determine the parameters required to make this conversion, and provide a function for doing so.
Both devices communicate with the Oak via the SPI, or Serial Peripheral Interface bus. This is a versatile, simple and fast interface that can handle multiple devices simultaneously. The first device will require 4 pins from the Oak (the ‘master’) to control: MOSI, MISO, CLK (pins 7-9), and a fourth to select the device (or slave) you’re targetting. Subsequent devices also connect directly to the first three pins, but for each new device a new/unique pin is required to select it. A Wikipedia example with three devices:
In this example, we’re connecting the Oak’s MOSI to the T_DIN and SDI pins on the touch screen. MOSI stands for Master Out, Slave In, i.e. this pin transfer data out to the slave devices. On the target device, DIN stands for Data In, and SDI for Serial Data In. You may also encounter SDA (Serial Data in), SI (Serial In), and other variants.
MISO, or Master In, Digital Out, is the master’s input pin for data coming from the slave devices, and connects to the T_DO pin, or Data Out for the touch device. The LCD display does not directly send data back to the Oak and its SDO pin does not need to be connected. For completeness’ sake, SDO stands for Serial Data Out; you may also see DO, SO, DOUT, and others.
In order to select a slave device to send information to or receive information from, we do need to turn devices on and off. If we don’t do so when initializing, nothing works. Good thing it’s easy:
pinMode(TFT_CS, OUTPUT); //set TFT_CS low to communicate w screen
pinMode(T_CS, OUTPUT); //set T_CS high to disable touch input
i.e. we set Chip Select for the TFT display to low to enable communication with our screen, and Chip Select for touch to high, to disable input. Later, when we check for touch activity, this automatically disables screen communication and enables touch reading, it reads any active touch activity, then disables the touch screen again and re-enables the display screen.
The LCD display does require a few other pins: Oak’s pin 1 to the display’s D/C or Data Command pin, and Oak’s pin 10 to the display’s RESET.
You may have noticed that we’ve not connected the display device’s pin 1 back to the Oak. This, the T_IRQ or Touch Interrupt Request pin, could be connected to the Oak if you wanted to handle data while the Oak is in the process of executing other code. This is rarely necessary. Interrupt requests require Interrupt Service Routines or ISRs, which interrupt the flow of normal processing, and do require some additional code. They might be triggered multiple times while between loops through the main code. Since we would not be using the results of the interrupt until our next loop through the main code, this is just wasteful.
(One situation where you might use the T_IRQ interrupt is if you were using a screen touch to wake your Oak from sleep. Out of scope for this example.)
The Adafruit graphics library uses character arrays, i.e. an array of characters terminated by a zero character, to write text to screen, using function print(). First, though, text position, size and color are specified, +/- a background color, i.e.
tft.print("This is text");
This is a string, but not a string object. They should not be confused. String objects use more memory and string functions are slower, but add a great deal of functionality, which goes beyond scope. Character arrays are very simple, and more limited.
In the context of this example, character arrays are ideal. The code is simple and short, numbers convert easily to char arrays using the itoa() function (integer to alphabet), we zero char arrays by setting its first member to zero (i.e. char_string = 0), and add text using strcat() to concatenate or append a new char array onto an existing one.
Next: the circuit