The touchscreen keyboard
I have then written the GetNum function that
allows you to print a prompt message and to type an integer number.
To test this function I wrote a simple analog
data logger program that required two parameters, the first is the sampling
period and the second the number of samples. In this example the number of
channels to be scanned is set to three, but the program can be modified to
request a third parameter with the number of channels.
As hardware a 2.8" TFT touch shield for
Arduino with resistive touch screen can be used. On the market there are many types, you only
need to use the libraries suitable for the controllers that are mounted on the
shield.
To test the prototype I used a 3.2" touch
screen interfaced to a Teensy LC board. In my case, I had to do a lot more soldering
work, but I like it and it is creative too. For more details of my display
refer to the post "Adapt a 3.2" TFT display for Raspberry to Arduino
", in my blog ArduPicLab.
The
touch keyboard graphics
In addition to the numeric keys, I added the
"C" key to delete the set number and the "ok" key to accept
it. The upper space is occupied by the printing of the number to be set and the
prompt message that tells the operator what is required by the program. The
numbers on the keys are written in tft.setTextSize (3) while the prompt message
is written in tft.setTextSize (2) and has a maximum of 19 characters in length.
The message is printed in yellow on a black background while the number to be
set is printed in black on a cyan background tft.setTextSize (4). The previous figure
shows the appearance of an example that requires sampling time in [ms] and 250
was keyed.
The touchscreen calibration
This operation is necessary to know the
coordinates of the vertices of the keyboard for which it is necessary to write
a simple program that prints the coordinates of the contact points on the
display. . To
do this I made a program that traces the keyboard on the screen and prints the
x, y coordinates on the top. With the stylet I pressed on the grid of the
keyboard and I wrote the coordinates on a sheet and then I averaged the values
by row and by column. The
results are shown in the following figure.
These numbers are indispensable for identifying
keys with the DetectButtons () function. It proceeds by column, starting from
the left. For each column, examine the rows starting from the lowest. Using
other displays the numbers could be very different from those I obtained, so it
is important to calibrate the touch screen.
The GetNum function
This function has the following syntax:
unsigned
int GetNum (String prompt)
The prompt string is passed from the calling
program. The routine turns black the screen, prints the prompt, draws the keys
and waits in loop for the required number to be keyed. By pressing the
"ok" button the data is returned to the program.
This function accepts unsigned integers from 0
to 65535, but with some minor changes it can work with larger numbers. Things
get complicated with numbers with decimals: one more key is needed.
The hardware
As I said, if you buy the shield for Arduino
you do not have to do any work because everything is ready, but in my case I
had to work a lot.
The graphics resolution of this display is
good, QVGA means ¼ of VGA, ie half the pixels on each side, so 240x320 points with
64K or 256K colors. Many shields have the ILI9340 chip as a graphic controller
and the XPT2046 as a touch screen controller. The interface used is SPI. The
shields for Arduino also have a SD card connector on the back, very convenient
for loading images or saving data.
But the card for Raspberry I used did not have
the micro SD connector and I had to add it.
The electrical scheme of my prototype is that
of the following figure. If I had used a shield for Arduino, things would have
been much simpler, I should not have used the solder.
I expected to have three analog channels, so I
used a 5-pin connector also to power any sensors.
The button key0 I have dedicated to duplicate
the "program" button that is no longer accessible because below the
display.
Unlike Arduino Uno, Teensy has a 12-bit
converter. The following figure shows the board of my prototype.
The datalogger program
I used the Adafruit libraries. As an example of
using the GetNum function, I wrote a program to acquire three analog signals,
converted with 12-bit ADC (10 bits for Arduino Uno ADC), and transfer them to
the micro SD card. The program asks for the sampling period in [ms], checking
the number, then asking for the number of samples.
Then it makes the acquisition, also printing
the data on the screen for long times. The file name is always the same, and
new acquisitions are added in the queue.
Of course you can improve by asking for other
parameters, such as the number of channels or the file number.
Code
/***************************************************
TeensyTFTacq and uSD test program
based on Adaftuit TFT library
Giovanni Carrera, rev 15/06/2018
/****************************************************/
#include "SPI.h"
#include <SD.h>
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9340.h"
#include <XPT2046_Touchscreen.h>
#if defined(__SAM3X8E__)
#undef
__FlashStringHelper::F(string_literal)
#define
F(string_literal) string_literal
#endif
// These are the pins used for the TeensyTFT32
#define _sclk 13
#define _miso 12
#define _mosi 11
#define _cs 10
#define _dc 7
#define _rst 8
// for TeensyTFT32 touchscreen
#define
CS_PIN 4
#define
TIRQ_PIN 3
//
for TeensyTFT32 uSD card
const
int chipSelect = 9;
unsigned
long startMillis, ns;
unsigned
long deltat = 200;// period in ms
unsigned
int value,NSamples;
boolean
SDok = false;
int
X,Y;
long
Number;
boolean
ok = false;
File
logFile;
XPT2046_Touchscreen
ts(CS_PIN, TIRQ_PIN);// interrupt enabled polling
//Adafruit_ILI9340
tft = Adafruit_ILI9340(_cs, _dc, _mosi, _sclk, _rst, _miso);
//
Use hardware SPI
Adafruit_ILI9340
tft = Adafruit_ILI9340(_cs, _dc, _rst);
void
setup() {
analogReadResolution(12);
tft.begin();// initialize ILI9340
ts.begin();// initialize XPT2046_Touchscreen
tft.fillScreen(ILI9340_BLACK);
tft.setCursor(0, 100);
tft.setTextColor(ILI9340_GREEN);
tft.setTextSize(2);
tft.println("TeensyTFT32 logger");
tft.println("Initializing SD");
// check if the SD is present and functioning
if (!SD.begin(chipSelect)) {
tft.setTextColor(ILI9340_RED);
tft.println("Bad or absent SD");
// does not do anything else
return;
}
tft.println("Card initialized");
delay(3000);
ns= 0;
value = GetNum("Sample Period
[ms]=");
if (ok){
if (value==0) value = 1;// minimum value
1ms
deltat= value;
ok= false;
}
value = GetNum("Number of
samples=");
if (ok){
if (value==0) value = 1;// minimum value 1
NSamples= value;
ok= false;
}
String dataString = "Sample Period =
"+String(deltat)+" [ms]";
logFile = SD.open("datalog.txt",
FILE_WRITE);
logFile.print(dataString);
tft.fillScreen(ILI9340_BLACK);
tft.setRotation(1);// 90°
tft.setCursor(0, 100);
tft.setTextColor(ILI9340_GREEN);
tft.setTextSize(2);
tft.println(dataString);
dataString = "Number of samples =
"+String(NSamples);
logFile.println(", " + dataString);
tft.println(dataString);
SDok = true;
}
void
loop(void) {
startMillis = millis();
while( millis() - startMillis < deltat);//
loop for deltat msec
String dataString = String(ns)+ ",
";
int val;
for (int i=0; i <= 2; i++){
val = analogRead(i);
val = map(val,0,4095,0,3299);// convert
values in mV
dataString += String(val);
if (i < 2) dataString += ", ";
}
if (deltat >= 500){ // print only for long
periods
tft.fillRect(0, 0, 479,70, ILI9340_BLACK);//
clear previous prints
tft.setCursor(0, 0);
tft.setTextColor(ILI9340_YELLOW);
tft.setTextSize(2);
tft.println(dataString);
}
++ns;
if (SDok){
logFile.println(dataString);// save
nsample,A0,A1,A2
if (ns== NSamples){
logFile.close();
SDok= false;// stop acquisition
tft.setCursor(20, 200);
tft.setTextColor(ILI9340_BLUE);
tft.setTextSize(2);
tft.println("End of
acquisition");
}
}
}
//
Functions used
unsigned
int GetNum(String prompt){
//
print a numeric keyboard and a prompt message
//
and get a numeric unsigned integer
tft.fillScreen(ILI9340_BLACK);
Number=0;
draw_BoxNButtons();// draw the virtual
keyboard
tft.setCursor(10, 20);
tft.setTextSize(2);
tft.setTextColor(ILI9340_YELLOW);
tft.println(prompt); //print prompt
do { // repeat until ok is pressed
if (ts.touched()) {
TS_Point p = ts.getPoint();
X= p.y; Y= p.x;// invert the axes
DetectButtons();
}
}while (!ok);
return Number;
}
void
draw_BoxNButtons() {
String symbol[3][4] = {
{ "6", "7",
"8", "9" },
{ "2", "3",
"4", "5" },
{ "C", "0",
"1", "ok" }
};
//Draw the Result Box
tft.fillRect(0, 60, 240, 80,ILI9340_CYAN);
//Draw keys
tft.fillRect
(0,260,60,60,ILI9340_RED);
tft.fillRect
(0,140,240,120,ILI9340_BLUE);
tft.fillRect
(60,260,120,60,ILI9340_BLUE);
tft.fillRect
(180,260,60,60,ILI9340_GREEN);
//Draw Horizontal Lines
for (int h=140; h<=320; h+=60)
tft.drawFastHLine(0, h, 240,ILI9340_WHITE);
//Draw Vertical Lines
for (int v=0; v<=240; v+=60)
tft.drawFastVLine(v, 140,
1800,ILI9340_WHITE);
//Display keypad lables
for (int j=0;j<3;j++) {
for (int i=0;i<4;i++) {
tft.setCursor(22 + (60*i), 160 + (60*j));
tft.setTextSize(3);
tft.setTextColor(ILI9340_WHITE);
if (j==2 & i==3)
tft.setTextColor(ILI9340_BLACK);// ok is more visible
tft.println(symbol[j][i]);
}
}
}
void
DetectButtons(){
int TSx[] = {319,1174,2097,3031,3838 };//
Touch Screen x intervals
int TSy[] = {1764,2445,3124,3752};// Touch
Screen y intervals
if (X<TSx[1] && X>TSx[0]){
//detecting Buttons on Column 1
if (Y>TSy[2] && Y<TSy[3])
Number= 0;//If Cancel button is pressed
if (Y>TSy[1] && Y<TSy[2])
Number= (Number*10)+2;// button 2 is pressed
if (Y>TSy[0] && Y<TSy[1])
Number= (Number*10)+6;// button 6 is pressed
}
if (X<TSx[2] && X>TSx[1]){
//detecting buttons on Column 2
if (Y>TSy[2] && Y<TSy[3])
Number= (Number*10)+0;// button 0 is pressed
if (Y>TSy[1] && Y<TSy[2]) Number=
(Number*10)+3;// button 3 is pressed
if (Y>TSy[0] && Y<TSy[1])
Number= (Number*10)+7;// button 7 is pressed
}
if (X<TSx[3] && X>TSx[2]){
//detecting Buttons on Column 3
if (Y>TSy[2] && Y<TSy[3])
Number= (Number*10)+1;// button 1 is pressed
if (Y>TSy[1] && Y<TSy[2])
Number= (Number*10)+4;// button 4 is pressed
if (Y>TSy[0] && Y<TSy[1])
Number= (Number*10)+8;// button 8 is pressed
}
if (X<TSx[4] && X>TSx[3]){
//Detecting Buttons on Column 4
if (Y>TSy[2] && Y<TSy[3]) ok
= true;// button ok is pressed
if (Y>TSy[1] && Y<TSy[2])
Number= (Number*10)+5;// button 5 is pressed
if (Y>TSy[0] && Y<TSy[1])
Number= (Number*10)+9;// button 9 is pressed
}
tft.fillRect(0, 60, 240, 80,
ILI9340_CYAN); //clear result box
tft.setCursor(10, 80);
tft.setTextSize(4);
tft.setTextColor(ILI9340_BLACK);
tft.println(Number); //update new value
delay(100);
}
References
3) “Arduino Touch Screen Calculator
using TFT LCD”, B.Aswinth Raj, https://circuitdigest.com/microcontroller-projects/arduino-touch-screen-calculator-tft-lcd-project-code