Qualche
tempo fa comprai per qualche euro uno
schermo "touch" da 5 pollici che misi nello scrigno delle cose "magari
trovo un problema a questa soluzione", intravedendo un Arduino
all'orizzonte, sul suo destriero bianco (una breadboard).
Dopo avere navigato
quà e là, ho appurato che il mio touch screen a
tecnologia resistiva (ce se sono altre più complesse)
è composto da una piastra superiore trasparente, ricoperta
nella parte inferiore di materiale resistivo, ai cui lati destro e
sinistro sono applicati due elettrodi. Un'altra piastra realizzata allo
stesso modo, ma ruotata di 90° chiude la parte inferiore,
mentre in mezzo si trovano dei microdistanziatori che tengono le
piastre separate elettricamente.
Nel momento in cui si tocca la faccia superiore, la pressione la fa
flettere quel tanto che basta da mettere in contatto le due facce dal
lato
resistivo, creando in tal modo un doppio partitore di tensione.
La misura si effettua in due passaggi, applicando dapprima una
differenza di potenziale nota ai capi Y (ovvero Top e Bottom), e
misurando la tensione su un contatto del lato X (diciamo Right). A
questo punto si ripete l'operazione invertendo i contatti: si applica
la stessa differenza di potenziale ai capi X (Left e Right), e si va a
misurare la
tensione sul lato Top. Il risultato è ovviamente una coppia
di tensioni proporzionali alle coordinate orizzontali e verticali del
punto toccato.
Esistono una quantità di integrati (cosiddetti driver
dedicati) che fanno questa
operazione in modo rapidissimo, svincolando il resto dei circuiti da
questa misura speciale, ma è proprio quello che voglio fare,
piegando Arduino ai miei voleri.
Per la realizzazione ho deciso di usare le uscite digitali 2,3,4,5 e
gli ingressi 3 e 4, adottando il seguente schema (perdonate
l'inglesismo, mi sento internescional):
Wiring
Logic
Touchpad
Arduino
Phase
1:
X axis reading
Phase
2:
Y axis reading
Left
Dig 2
mode OUTPUT
value: LOW
mode INPUT
(high impedance)
Top
An 3
Read X axis value
(last operation)*
(not used)
Dig 5
mode INPUT
(high impedance)
mode OUTPUT
value: HIGH
Right
An 4
(not used)
Read Y axis value
(last operation)*
Dig 4
mode OUTPUT
value: HIGH
mode INPUT
(High impedance)
Bottom
Dig 3
mode INPUT
(high impedance)
mode OUTPUT
value: LOW
Per poter effettuare lo scambio dei lati di lettura, ho dovuto
mettere alcuni pin DIGITAL in contatto con un paio di pin INPUT,
(DIGITAL 5 e ANALOGIC 3, DIGITAL 4 e ANALOGIC 4) e per evitare
influenze durante la lettura, ho impostato le uscite digitali in
modo "INPUT" per poterle lasciare flottanti dal punto di vista
elettrico.
*Nota relativa alla logica: la lettura del singolo asse va
effettuata per ultima (An3 nella fase 1 e An4 nella fase 2), dopo avere
impostato prima tutte le uscite ai valori indicati in ogni fase,
altrimenti la misura non avrà senso.
Qui sotto potete vedere l'esempio pratico e il relativo
codice:
nulla di eccezionale, il tocco sul pannello viene interpretato
dall'Arduino usando la logica sopra esposta, direi che con una manciata
di righe di programma il risultato è onorevole, forse troppo
sobrio.
/* * TOUCH.PDE - Arduino sketch * Read from a touch panel the touch position * return it on serial port * * Hardware used: * Resistive touch panel, 4 wire * Arduino Diecimila * * April, 5th 2009 * by Marco Nicolato * */
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // Touch panel wiring // Connect to Arduino these wires (used to drive power) #define Lo 2 // LEFT to digital output 2 #define Bo 3 // BOTTOM to digital output 3 #define Ro 4 // RIGHT to digital output 4 #define To 5 // TOP to Digital output 5
// Connect to Arduino these wires (used to read the touch position) #define Ti 3 // TOP also to analog input 3 #define Ri 4 // RIGHT also to analog input 4
#define LED 13 // LED event // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // current touched int touchX = 0; int touchY = 0; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// return TRUE if touched, and coords in touchX and touchY boolean touched() { boolean touch = false;
// Horizontal routine - set L to gnd and R to Vcc // set L = ground pinMode(Lo, OUTPUT); digitalWrite(Lo, LOW); // set R = Vcc pinMode(Ro, OUTPUT); digitalWrite(Ro, HIGH); // T e B high impedance (input mode) pinMode(To, INPUT); pinMode(Bo, INPUT); // wait a bit, then read from Top delay(10); touchX = analogRead(Ti);
// Vertical routine - set T to gnd and B to Vcc // Set B = Vcc pinMode(Bo, OUTPUT); digitalWrite(Bo, LOW); // set T = gnd pinMode(To, OUTPUT); digitalWrite(To, HIGH); // R e L high impedance (input mode) pinMode(Ro, INPUT); pinMode(Lo, INPUT); // wait a bit, then read from Right delay(10); touchY = analogRead(Ri);
// Only if coords are below 1000 if(touchX < 1000 and touchY < 1000) touch = true;
return touch; }
TOUCH ME MORE... !
Si sa che l'appetito vien mangiando, per cui ho fatto
l'ingordo e mi
sono domandato: ma con una matrice di led "sotto" e il touchpad
"sopra", perchè non accendere il LED nella posizione sotto
il
touch ? Per quanto riguarda la matrice, avevo già realizzato
con
Arduino un driver per questo tipo di componente, quindi non manca che
mescolare
accuratamente le due sciocchezzuole.
Per poter usare la matrice led, tramite l'integrato 7221 della
Maxim, occorre includere la libreria creata da Eberhard Fahle, che si
trova all'indirizzo http://www.wayoda.org/arduino/ledcontrol/index.html e usare il seguente cablaggio tra Arduino e l'integrato 7221:
Maxim 7221
Arduino
Data (1)
Dig 8
Latch (12)
Dig 9
Clock (13)
Dig 10
Secondo la tradizione, quando si lavora con questi
dispositivi occorre eseguire una calibratura iniziale per
determinare dove è posizionata la matrice di led rispetto al
touch panel. Nel video infatti si accendono in sequenza i led dei due angoli
estremi, e la lettura dei relativi punti fornisce la zona
dove accettare i successivi touch, per poterli associare al led
sottostante con una semplice proporzione.
/* * TOUCHDISP.PDE * Read from a touch panel the touch position * and show it on a 8x8 matrix display. * Every led can be swapped ON or OFF, * if doubletouched, the led goes to FLASH * * Hardware used: * Resistive touch panel, 4 wire * MAX 7219/7221 * LED matrix 8x8 * Arduino Diecimila * * March, 28th 2009 * by Marco Nicolato * */
// Use library LedControl - Copyright (c) 2007 Eberhard Fahle // Download it from: // http://www.wayoda.org/arduino/ledcontrol/index.html #include "LedControl.h"
// Connect to Arduino these wires (used to drive power) #define Lo 2 // LEFT to digital output 2 #define Bo 3 // BOTTOM to digital output 3 #define Ro 4 // RIGHT to digital output 4 #define To 5 // TOP to Digital output 5
// Connect to Arduino these wires (used to read the touch position) #define Ti 3 // TOP also to analog input 3 #define Ri 4 // RIGHT also to analog input 4
#define LED 13 // Touch LED // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // MAX7219/7221 wiring // Pin 8 is connected to the DATA IN-pin of the first MAX7221 // Pin 9 is connected to the LOAD(/CS)-pin of the first MAX7221 // Pin 10 is connected to the CLK-pin of the first MAX7221 #define DATA 8 #define LATCH 9 #define CLOCK 10
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv // Working area coords (Top-Left and Bottom-Right) int tlX; int tlY; int brX; int brY;
// Absolute width and height working area int wX; int wY;
// Used to store analog coords int touchX = 0; // current int touchY = 0;
// Current matrix LED coords int ledX; int ledY;
// Local matrix int matrix[8][8]; #define ON 1 #define OFF 0 #define FLASH 2 unsigned long flashTime; boolean flashing = false; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// reset all matrix for(int c = 0; c <= 8; c++) for(int r = 0; r <= 8; r++) matrix[c][r] = OFF;
// Initialize - ask for TOP-LEFT... dsp.setLed(0,7,7,true); // top left led = on while(not touched()); // wait until touched tlX = touchX; // store top-left coords tlY = touchY; dsp.setLed(0,7,7,false); // top left led = off while(touched()); // wait for untouch
// ...now ask for BOTTOM-RIGHT dsp.setLed(0,0,0,true); // bottom-righ = on while(not touched()); // wait until touched brX = touchX; // store bottom-right coords brY = touchY; dsp.setLed(0,0,0,false); // bottom-right led = off while(touched()); // wait for untouched
// working area width and height wX = brX - tlX; wY = tlY - brY; }
void loop() { if (touched() & (touchX > tlX and touchX < brX and touchY < tlY and touchY > brY)) // only when touched inside active area { // store first LED coords int firstledX = ledX; int firstledY = ledY; int v = (matrix[ledX][ledY]);
// swap value: on->off or off->on boolean led = (v == OFF); if (led) matrix[ledX][ledY]=ON; else matrix[ledX][ledY]=OFF;
// wait for untouched while(touched());
// wait for a second touch ... unsigned long wait; wait = millis() + 200; while (millis() < wait) {if (touched() & (touchX > tlX and touchX < brX and touchY < tlY and touchY > brY)) if (ledX == firstledX and ledY == firstledY) matrix[ledX][ledY] = FLASH; // yes, flash it } }
// display current led matrix display();
}
// return 1 if touched, and coords in touchX and touchY boolean touched() { boolean touch = false;
// Horizontal routine - set L to gnd and R to Vcc // set L = ground pinMode(Lo, OUTPUT); digitalWrite(Lo, LOW); // set R = Vcc pinMode(Ro, OUTPUT); digitalWrite(Ro, HIGH); // T e B hight impedance (input mode) pinMode(To, INPUT); pinMode(Bo, INPUT); // wait a bit, then read from Top delay(10); touchX = analogRead(Ti);
// Vertical routine - set T to gnd and B to Vcc // Set B = Vcc pinMode(Bo, OUTPUT); digitalWrite(Bo, LOW); // set T = gnd pinMode(To, OUTPUT); digitalWrite(To, HIGH); // R e L hight impedance (input mode) pinMode(Ro, INPUT); pinMode(Lo, INPUT); // wait a bit, then read from Right delay(10); touchY = analogRead(Ri);
// Only if coords are below 1000 if(touchX < 1000 and touchY < 1000) { // calculate the led to show ledX = 8 * (touchX - tlX) / wX; ledY = 8 * (touchY - brY) / wY;
// Touch LED on for a bit digitalWrite(LED, HIGH); delay(20); digitalWrite(LED, LOW);
touch = true; } return touch; }
// show all matrix display, flashing leds with special FLASH value void display() { boolean led; // every 300ms swap status of flashing leds if ((flashTime + 300) < millis()) {flashing = (not flashing); // swap ON and OFF flashTime = millis(); }
for(int r = 0; r <= 7; r++) { for(int c = 0; c <= 7; c++) { int v = matrix[r][c]; led = (v == ON); if (v == FLASH) led = flashing;
dsp.setLed(0, c, 7-r, led); } } }
VEDO DOPPIO
Per dare un'aspetto un pò più dinamico
ho fatto in modo che il doppio touch facesse lampeggiare il led, e
direi che a questo punto, seppure semplice e grezzo, il
progettino è funzionale e completo. A cosa possa servire,
poi me lo direte voi che leggete, io ci ho ricavato qualche ora di
divertimento e sano hacking grazie alla flessibilità di Arduino
(e alla pazienza della moglie).
CONCLUDENDO...
Non che debba fare pubblicità ad Arduino, ma poter
fare
interagire il computer e il mondo fisico con questa schedina
milleusi è una cosa che fino a qualche anno fa era riservata ai
guru
dell'elettronica, mentre ora pure io (che so a malapena come si accende
un led, o quasi) riesco a fare.
Leggero' con piacere i vostri commenti, se vorrete scrivermi
al
seguente indirizzo: mnicolato@hotmail.com