Translate

Saturday, 16 June 2018

A touch numeric keyboard for Arduino or Teensy


 A system for entering numerical values in our programs on Arduino or Teensy is described. To test this function I wrote also a simple analog data logger program.

The touchscreen keyboard

Very often, for our programs, we need a system to set parameters, usually of a numerical type. A 4x4 keyboard requires some space and then we also need a display. Here is the idea of using a touchscreen display to do both.
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
For the numeric keyboard I used 12 keys, arranged on 3 rows and 4 columns according to the following figure.
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. For this purpose it is advisable to use a suitable plastic stylus. I wrote the coordinates on a sheet and I averaged them 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.

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

4)      “How to use the TFT display 2.2" QVGA with Arduino”, G. Carrera,  http://ardupiclab.blogspot.it/


Wednesday, 21 February 2018

Adapt a 3.2 "TFT display for Raspberry to Arduino

By Giovanni Carrera, 21/02/2018

The aim of this project is to interface a 3.2"TFT display designed for Raspberry with Arduino .

Introduction
Some time ago I bought a 3.2 "TFT touch screen for my Raspberry. This display is connected to the Raspberry GPIO via a 26-pin connector and appropriate drivers are required. It worked well but it was too small and not very suitable for my eyesight, so I subsequently purchased a 7"display with an HDMI interface. I did not want to leave it unused and I thought about using it with Arduino. I also noticed that the same manufacturer Waveshare sold an Arduino shield with the same display, equipped with the same graphics controller ILI9340 and the touch screen controller XPT2046. The respective libraries are also available for this shield.
Studying the wiring diagram of the board I got what were the signals and the pins used. The following table shows the names of the Raspberry signals and those used by the TFT display.
pin
Raspberry name
TFT
pin
Raspberry name
TFT
1
3.3 V
3.3V
2
5V
3

4
5V
5

6
Gnd
7
P7
8

9
Gnd
10

11
P0 - TP IRQ
12
P1 – Key0
13
P2 - RST
14
Gnd
15
P3 - DC
16
P4 – Key1
17
3.3V
18
P5 – Key2
19
P MOSI
20
Gnd
21
P MISO
22
P6
23
P SCK
24
P CE0
25
Gnd
26
P CE1
Since I had already built a system with an ATMega328P chip powered at 3.3 V [3], I did the test with that board (see my post “How to use the TFT display 2.2" QVGA with Arduino”).
I also realized that the display only worked if I used pin 25 or 14 as a mass. I powered the 5V display on pin 2 and the ground on pin 25. I powered my system with its battery and everything worked good. The program was that of the oscilloscope.
Some signals are different from those in the following table because those were used in my project, while pin 8 turned on the backlight led of the other display.
Since I have not found on the datasheets if the chip inputs are 5 volt tolerant, I would recommend using Arduino boards with 3.3V I/O, even if the Arduino Waveshare shield is connected to boards like Arduino Uno without level adapters.
TFT pin
name
Arduino pin
Description
2
5V
5V
5V power input
25
GND
GND
Ground
23
P SCK
D13
SPI clock
21
P MISO
D12
SPI data input
19
P MOSI
D11
SPI data output
24
P CE0
D10
LCD chip select
13
P2 - RST
D8
LCD reset
15
P3 - DC
D7
LCD data/command selection
??
TP_BUSY
D6
Touch panel busy
26
P CE1
D4
Touch panel chip select
11
TP IRQ
D3
Touch panel interrupt
12
Key0

button 0
16
Key1

button 1
18
Key2

button 2
With a new project based on Teensy, I could also test the touch screen and everything worked very well. I used the Adafruit libraries that also allow you to rotate the text.

References

3)      “How to use the TFT display 2.2" QVGA with Arduino”, G. Carrera,  http://ardupiclab.blogspot.it/