Ottimizzazione delle tastiere analogiche

Nel dominio dei microcontrollori, la necessità di interfacciare una tastiera a matrice porta normalmente all’adozione di una circuiteria che dedica un piedino digitale della MCU ad ogni Linea e Colonna del dispositivo, come nell’esempio seguente.

A volte, però, capita di non disporre di tutti i pin digitali necessari per gestire la tastiera. Vengono in aiuto, in questi casi, delle soluzioni alternative. L’uso di ingressi analogici del microcontrollore permette infatti di spostare il problema della discriminazione del tasto premuto ad una analisi del livello di tensione prodotto da un partitore resistivo attivato dalla pressione del tasto stesso. Si trovano sul web numerosi esempi di Keypad analogiche aventi fino a 20 tasti. Le soluzioni sono molteplici, ma tutte basate sullo stesso principio. Esistono in particolare soluzioni ibride, dove viene utilizzato un ingresso analogico del micro per ogni linea della tastiera di input, come nell’esempio seguente riferito ad una singola linea di tasti.
 

 

Si arriva finalmente alla più efficiente configurazione che offre su una singola linea di uscita il potenziale elettrico stabilito dalla chiusura di un partitore differente a seconda del tasto che viene premuto, come nel seguente esempio (fonte http://www.technoblogy.com/show?28WK).

   

Due parametri vanno considerati, nell’analisi del circuito: La minima distanza digitale nelle letture a fronte della pressione dei pulsanti, e l’impedenza offerta al piedino di lettura analogica del microcontrollore.

Nel caso 4R x 5C di cui sopra, la minima distanza è di 24 unità digitali su 1024, ponendoci nel caso di un ADC a 10 bit. Tale distanza la osserviamo nella pressione dei pulsanti S3 rispetto a S4, oppure S11 rispetto a S16. La tabella riportata di seguito mostra i valori digitali per ogni pulsante, e la differenza rispetto al valore più vicino ottenuto premendo un diverso pulsante.


Per quanto riguarda l’impedenza, prendendo in considerazione il parallelo tra i due rami resistivi volti alla alimentazione ed a massa, si va da un minimo di zero per S20 fino ad un massimo, premendo S1, di 1/(1/27 + 1/(4.7+4.7+8.2+10+33+3.3+2.7))=19K circa.

Questo pone qualche problema se il livello di tensione definito dal partitore attivato viene letto da un microcontrollore come p.es. il PIC18F2620 il cui datasheet raccomanda “The maximum recommended impedance for analog sources is 2.5 k”.  Prendiamo in considerazione proprio questo dispositivo, studiando il modello dei pin analogici descritto nella documentazione tecnica citata.

Dato il valore Rs della impedenza della rete resistiva sorgente, possiamo avvalerci delle formule riportate nel datasheet, per calcolare il tempo minimo di acquisizione necessario:

 

TACQ = TAMP + TC + TCOFF …dove TAMP è fisso a 0.2 us, e TCOFF vale zero per temperatura <25 gradi

Per Rs ideale = 2.5 Kohm otteniamo:

TC(2.5Kohm) = -(CHOLD)(RIC + RSS + RS) ln(1/2047) = -(25 pF) (1 kohm + 2 kohm + 2.5 kohm) ln(0.0004883) = 1.05 us

Ed in definitiva: TACQ = 0.2 us + 1 us = 2.4 us

Ma con Rs = 19Kohm (al di fuori del valore raccomandato), il tempo di acquisizione va aumentato a:

TC(19 Kohm)= -25e-12 * (3 + 19) * (-7,62) = 7.2 us e quindi TACQ = 7.4 us

 

Utilizzando la Adc_Read di MikroC per la lettura del livello di tensione, il tempo dedicato alla acquisizione è di 12 TAD, che calcolato per un clock di 4 MHz fornisce:

T = 12 * ADCS / FOSC = 6…192 us

Ecco come i bit ADCS del registro ADCON2 permettono di impostare il tempo di acquisizione:

Per avere contezza della deviazione rispetto al valore definito dal partitore, ho eseguito delle misure dirette, mostrando su un Display da 4 cifre a 7 segmenti il valore recuperato da Adc_Read, senza cambiare le impostazioni standard del registro ADCON2. L’accuratezza rilevata è di +/-1 unità digitale fino a 100 Kohm di impedenza, che devia rapidamente andando oltre tale valore. Considerando reti resistive che non superino i 20 Kohm di impedenza massima ci assicuriamo buone prestazioni nella fase di lettura, evitando imprecisioni da parte dell’unità ADC.

Per migliorare la discriminazione del tasto premuto nel codice, l’ideale sarebbe quello di massimizzare la “Minima differenza tra i valori digitali”, in modo da ridurre il rischio di ambiguità nell’interpretazione del livello di tensione durante una “veloce” lettura della tastiera. La domanda che a questo punto sorge spontanea è: “Con quale metodo calcolo le resistenze ottimali per le Linee e le Colonne?”.

Per rispondere, partiamo da uno schema modificato rispetto al precedente, ma equivalente. Mi viene più facile, in questa disposizione, impostare i termini del problema. Peraltro, la tastiera in esame fa parte di una applicazione più estesa, che prevede la realizzazione di una calcolatrice scientifica single-chip, che proporrò in seguito.

Ho tentato diversi approcci per giungere ad una soluzione. Dopo aver tentato dapprima analiticamente senza grande successo, visto che il numero di incognite superava quello delle equazioni, ho adottato infine una ricerca mista, che parte dal calcolo delle resistenze a fronte di tensioni generate casualmente per la prima linea e prima colonna di pulsanti, per calcolare infine le tensioni nella parte centrale della matrice. Durante la ricerca, il programma che ho scritto mette via via da parte la “Miglior rete” e prosegue ad oltranza, fino ad interruzione utente o al raggiungimento di un obiettivo. Le formule usate sono le seguenti:


Relazione tra Vij e le resistenze di linea RLi e di colonna RCj:

[1]          Vij = 1-RL1/(Σ(RL,1,i)+ Σ(RC,1,j))

 

Imponendo un valore per RL1 ed alle tensioni di partitore ottenute dai pulsanti disposti sulla prima linea e prima colonna, si possono ricavare in sequenza i valori delle resistenze RL ed RC:

RC1=RL1*(1/(1-V11)-1)-S : S = S+RC1

RC2=RL1*(1/(1-V12)-1)-S : S = S+RC2

RC3=RL1*(1/(1-V13)-1)-S : S = S+RC3

RC4=RL1*(1/(1-V14)-1)-S : S = S+RC4

RC5=RL1*(1/(1-V15)-1)-S : S = S+RC5

RL2=RL1*(1/(1-V21)-(RC1/RL1)-1)-S : S = S+RL2

RL3=RL1*(1/(1-V31)-(RC1/RL1)-1)-S : S = S+RL3

RL4=RL1*(1/(1-V41)-(RC1/RL1)-1)-S : S = S+RL4

Poi con la [1] si ricavano le altre tensioni e si calcola infine MinDiff come valore più piccolo tra le differenze ottenute da ogni coppia di tensioni Vij-Vi’j’.

Il ciclo parte dalla generazione casuale delle tensioni Vij entro i limiti previsti, e di RL1. Dopo la generazione conseguente delle RL e RC, si scartano tutti i casi dove la Rs ottenuta è superiore ad un limite impostato, e di mette da parte la Miglior Rete ottenuta, ovvero quella che fornisce la maggior MinDiff.

Il programma “Matrix.exe” resta in esecuzione per diversi giorni. Questa è la sua scarna ma funzionale interfaccia grafica. L’utente lo può interrompere in qualunque momento, accontentandosi del risultato migliore ottenuto dall’avvio fino all’arresto:

Il programma prevede, in alternativa all’avanzamento per generazione casuale di Tensioni, di scegliere altri due metodi: generazione casuale dei valori delle resistenze, e generazione incrementale, fattibile solo per piccole tastiere, pena l’esecuzione per mesi o anni su un computer normale.


Ho eseguito varie sessioni fino a matrici di 10x10 pulsanti. I risultati ottenuti per la 4R x 5C mostrata sopra sono analoghi a quelli ottenuti dagli autori del sito di origine. In linea generale, il programma Matrix eseguito per almeno due giorni con diverse dimensioni della matrice di tasti indica un andamento del tipo seguente:

 

Considerando i possibili errori di lettura, mitigabili eseguendo letture di conferma, le dimensioni della matrice di tasti non dovrebbero superare i 64 contatti. Quindi 8 righe per 8 colonne. Al superamento di 72 tasti si rischia di scendere sotto 4 unità digitali, sempre con un ADC a 10 bit, aumentando il rischio di errore di interpretazione da parte del microcontrollore.

Il programma Matrix genera anche un disegno Bitmap dello schema, ed un file di testo con codice C utilizzabile per la stesura del programma da sviluppare per la nostra applicazione. Di seguito un esempio per una tastiera da 42 tasti disposti in 6 righe per 7 colonne.


Il codice indica, a titolo di esempio, una singola operazione di lettura tramite la funzione ADC_Read, ma sarebbe preferibile implementare delle ulteriori letture di controllo, prima di convalidare il valore acquisito.

Per i valori delle resistenze è stata scelta la serie E24, ma è possibile eseguire il programma indicando la serie E12. In questo caso però la MinDiff raggiunta è inferiore a 10.

Una ulteriore considerazione riguarda la precisione ed accuratezza dei valori delle resistenze commerciali. Scegliamo una serie, per esempio la E24. La sua “precisione” teorica è del 5%. Questo significa unicamente che se si desidera un valore X qualsiasi, esso discosterà dal valore più prossimo di quella serie non oltre il 5%. La “accuratezza” invece, ovvero lo scarto tra il valore di targa e quello misurato, è in generale migliore. Sempre per le E24, il costruttore si mantiene di solito al di sotto del 1%. Per limitare imprecisioni nella lettura dovute a valori non accurati delle resistenze impiegate, si consiglia tuttavia di misurarle con uno strumento di qualità in un pool di campioni, e scegliere quella più vicina al valore riportato nello schema.

Un secondo file di testo generato dal programma Matrix riporta anche i livelli di tensione teorici letti a fronte della pressione di ogni pulsante della matrice. Questi valori, una volta realizzata la tastiera, potrebbero venir confrontati con le letture reali, per eseguire micro-aggiustamenti negli estremi di discriminazione del codice suggerito.

Invito i lettori interessati ad utilizzare il programma, scaricabile QUI e lasciarlo eseguire per un tempo lungo, al fine di massimizzare la MinDiff. Qualora si riscontrassero anomalie, siete pregati di documentarle e comunicarmele per consentirmi la correzione. Riceverete direttamente la versione aggiornata.