Keypad à lecture analogique
Matrice Carrée
Le keypad
Le keypad classique est composé de 12 ou 16 boutons. Il est pré-câblé:

Si on a beaucoup de boutons (en lecture mono), le clavier matriciel peut être aussi une bonne solution car cela diminue fortement le nombre de résistances.
Un keypad pré-câblé ne pourra jamais faire une lecture poly: si on appuie sur les boutons 1, 2 et 4, on ne pourra pas savoir si le bouton 5 est appuyé ou non, car cela ne rajoute pas de connexions. Dans la suite, je ne vais donc m'intéresser qu'à la lecture mono, bien qu'une lecture stéréo serait possible.
Le keypad 16 touches
Voici un schéma que je préconise pour un keypad 16 touches:

Et voici par où passe le courant si on appuie par exemple sur la touche 9:

On a ainsi un pont diviseur composé d'une résistance de 57,82R d'une part et une résistance "keypad" d'autre part. Cette dernière vaut:
infinie si aucune touche n'est appuyée
0 si on appuie sur la touche 1
R si on appuie sur la touche 2
2R si on appuie sur la touche 3
3R si on appuie sur la touche A
4R si on appuie sur la touche 4
...
15R si on appuie sur la touche #
16R si on appuie sur la touche D
La tension de référence du CNA sera de 1,1V. Quand aucune touche n'est appuyée, On a du 5V en entrée de l'Arduino, et le CNA retournera 1023 (U>1,1V). Voici ce que l'on voudrait lire en sortie du CNA:

On va donc dimensionner la résistance "en haut" de façon à avoir 960 en sortie du CNA pour la touche D. Cela donne une résistance de 57,82R. Mais comme on ne peut avoir cette valeur, on prendra une résistance voisine et on corrigera le résultat. En toute rigueur, on pourrait admettre une valeur de 990, mais l'explication est plus compliquée (même si cela fonctionnerait mieux). Pour le choix de cette résistance, il faut que l'on obtienne la plus grande valeur possible mais inférieure à 1023 qui est la valeur pour aucun bouton appuyé. Dans le programme qui suit, comme j'obtiens 1000, et je corrigerais cette valeur pour obtenir 960 lors de l'appui sur "D".
Évidemment la courbe de lecture ne sera pas une droite, mais nous appliquerons la correction de Martin pour la linéariser.
Pour retrouver le bouton appuyé, on divise la valeur par 64 en arrondissant, ou on ajoute 32 et on fait une division entière par 64.
Comme rien n'est parfait, si on appuie sur la touche "4" par exemple, on aurait aimé avoir 296, mais on aura un peu plus ou un peu moins. Tant que l'on ne s'écarte pas de plus de 32, cela fonctionne. J'appellerai cet écart l'erreur acceptable. Le programme qui suit affiche cette erreur, et comme on constate qu'elle ne dépasse jamais 2, tout est parfait. Cela signifie que l'on peut mettre 10 fois plus de boutons et qu'on saura les différencier. On peut donc sur ce principe utiliser une matrice 6x6 sans risque, 10x10 étant peut être la limite. Mes essais ont été faits avec une Uno (très mauvais câblage du CNA et avec des résistances carbone à 10%).
Pour avoir des résistances de R et 4R, j'ai câblé ainsi:

Cela multiplie le nombre de résistances par deux, mais c'est un peu mieux que certains montages qui demandent une résistance par poussoir. Sinon c'est prendre de résistances 560Ω et 2,2kΩ.
Le fait d'utiliser qu'une seule valeur pour les résistances permet d'autocompenser les erreurs, et c'est pour cela que ce montage permet d'aller jusqu'à une matrice de 6x6, soit 36 boutons, alors que bien des montages n'en permettent qu'une douzaine. Je recommande donc d'utiliser des résistances en série et en parallèle comme sur le schéma.
Le CNA de la Uno étant assez mal câblé, j'ai choisi de lire plusieurs fois la valeur jusqu'à obtenir deux fois consécutivement la même valeur.
// Clavier matriciel 16 touches // // VCC // │ // 27kΩ // │ // ├── Vers A0 // ┌─ 500Ω ─┬─ 500Ω ─┬─ 500Ω ─┤ // ├─ D ┐ ├─ # ┐ ├─ 0 ┐ ├─ * ┐ // │ └───│────┴───│────┴───│────┴────┐ // ├─ C ┐ ├─ 9 ┐ ├─ 8 ┐ ├─ 7 ┐ 2kΩ // │ └───│────┴───│────┴───│────┴────┤ // ├─ B ┐ ├─ 6 ┐ ├─ 5 ┐ ├─ 4 ┐ 2kΩ // │ └───│────┴───│────┴───│────┴────┤ // └─ A ┐ └─ 3 ┐ └─ 2 ┐ └─ 1 ┐ 2kΩ // └────────┴────────┴────────┴────┤ // GND void setup() { Serial.begin(115200); // Régler aussi la console à 115200 bauds! analogReference(INTERNAL); // Vref=1,1V; mettre INTERNAL1V1 pour la Mega } word valeurLue, // Résultat de la conversion brute puis corrigée puis n° de la touche oldValeurLue, // Mémorisation pour permettre deux lectures consécutives identiques oldTouche; // Touche précédente appuyé pour ne pas l'afficher plusieurs fois int erreur; // Permet de visualiser l'erreur de la conversion; on a le droit à 32 maxi char touche[16]= {'1', '2', '3', 'A', '4', '5', '6', 'B', '7', '8', '9', 'C', '*', '0', '#', 'D'}; // Noms gravés void loop() { // Mesure do { oldValeurLue = valeurLue; // Sauvegarde de la mesure précédente valeurLue = analogRead(A0); // Nouvelle lecture } while (valeurLue!=oldValeurLue); // On recommence jusqu'à avoir deux lectures identiques if (valeurLue!=1023) // Si une touche est appuyée { // Serial.print(valeurLue); // Pour lire ce que l'on a en appuyant sur 'D' // Ajustement à 960 valeurLue=valeurLue*960.0/1000; // le 1000 est ce qui est lu en appuyant sur 'D' // Correction de Martin valeurLue+=0.00024*(valeurLue-484.0)*(valeurLue-484.0)-54.6; erreur = valeurLue; // Mémorisation pour calculer l'erreur //Extraction valeurLue=(valeurLue+32)/64; // Cela donne le N° de la touche 0..15 } // Affichage if (valeurLue!=oldTouche) // Si changement { if (valeurLue!=1023) // C'est une nouvelle touche { Serial.print("Bouton "); Serial.print(touche[valeurLue]); // Ce qui est gravé sur la touche Serial.print(" (erreur:"); Serial.print(erreur-int(valeurLue)*64); // Valeur de l'erreur Serial.println("/32)"); // On a le droit d'une erreur de 32 maximum } oldTouche=valeurLue; // Mémorisation pour ne pas afficher plusieurs fois } }
Et voici le résultat si on appuie une fois sur chaque touche:
Bouton 1 (erreur:1/32) Bouton 2 (erreur:-2/32) Bouton 3 (erreur:-3/32) Bouton A (erreur:-2/32) Bouton 4 (erreur:-3/32) Bouton 5 (erreur:-1/32) Bouton 6 (erreur:-2/32) Bouton B (erreur:-1/32) Bouton 7 (erreur:-2/32) Bouton 8 (erreur:0/32) Bouton 9 (erreur:2/32) Bouton C (erreur:-1/32) Bouton * (erreur:2/32) Bouton 0 (erreur:2/32) Bouton # (erreur:1/32) Bouton D (erreur:-2/32)
Ce résultat a été obtenu avec une Uno et des résistances à couche carbone à 5% (les pires conditions) et le résultat montre que l'erreur est faible devant l'écart permit.
Le keypad 12 touches
Voici un schéma que je préconise pour un keypad 12 touches:

Toute la démarche vue ci-dessus est la même. Il y a quelques petites différences dans la correction. D'autre part, l'erreur théorique est sensiblement la même, mais on peut se permettre un écart plus grand, la lecture est donc plus fiable.
Voici le programme de test utilisé:
// Clavier matriciel 12 touches // VCC // │ // 18kΩ // │ // ├── Vers A0 // ┌─ 500Ω ─┬─ 500Ω ─┤ // ├─ # ┐ ├─ 0 ┐ ├─ * ┐ // │ └───│────┴───│────┴────┐ // ├─ 9 ┐ ├─ 8 ┐ ├─ 7 ┐ 1,5kΩ // │ └───│────┴───│────┴────┤ // ├─ 6 ┐ ├─ 5 ┐ ├─ 4 ┐ 1,5kΩ // │ └───│────┴───│────┴────┤ // └─ 3 ┐ └─ 2 ┐ └─ 1 ┐ 1,5kΩ // └────────┴────────┴────┤ // GND void setup() { Serial.begin(115200); // Régler aussi la console à 115200 bauds! analogReference(INTERNAL); // Vref=1,1V; mettre INTERNAL1V1 pour la Mega } int valeurLue, // Résultat de la conversion brute puis corrigée puis n° de la touche oldValeurLue, // Mémorisation pour permettre deux lectures consécutives identiques oldTouche, // Touche précédente appuyé pour ne pas l'afficher plusieurs fois erreur; // Permet de visualiser l'erreur de la conversion; on a le droit à 32 maxi char touche[12]= {'1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '0', '#'}; // Noms gravés void loop() { // Mesure do { oldValeurLue = valeurLue; // Sauvegarde de la mesure précédente valeurLue = analogRead(A5); // Nouvelle lecture } while (valeurLue!=oldValeurLue); // On recommence jusqu'à avoir deux lectures identiques if (valeurLue!=1023) // Si une touche est appuyée { // Serial.print(valeurLue); // Pour lire ce que l'on a en appuyant sur '#' // Ajustement à 938 valeurLue=valeurLue*938.0/943; // le 943 est ce qui est lu en appuyant sur '#' // Correction de Martin valeurLue+=0.00024*(valeurLue-521.0)*(valeurLue-521.0)-63.4; erreur = valeurLue; // Mémorisation pour calculer l'erreur //Extraction valeurLue=(valeurLue+42.6)/85.3; // Cela donne le N° de la touche 0..15 } // Affichage if (valeurLue!=oldTouche) // Si changement { if (valeurLue!=1023) // C'est une nouvelle touche { Serial.print("Bouton "); Serial.print(touche[valeurLue]); // Ce qui est gravé sur la touche Serial.print(" (erreur:"); Serial.print(erreur-int(valeurLue)*85.3); // Valeur de l'erreur Serial.println("/42)"); // On a le droit d'une erreur de 32 maximum } oldTouche=valeurLue; // Mémorisation pour ne pas afficher plusieurs fois } }
Et voici le résultat si on appuie une fois sur chaque touche:
Bouton 1 (erreur:1.00/42) Bouton 2 (erreur:-1.30/42) Bouton 3 (erreur:-3.60/42) Bouton 4 (erreur:-5.90/42) Bouton 5 (erreur:-7.20/42) Bouton 6 (erreur:-1.50/42) Bouton 7 (erreur:-1.80/42) Bouton 8 (erreur:-2.10/42) Bouton 9 (erreur:1.60/42) Bouton * (erreur:-0.70/42) Bouton 0 (erreur:0.00/42) Bouton # (erreur:-4.30/42)
Pour avoir des résistances de R et 3R, j'ai câblé ainsi:

Mais j'aurais pu faire aussi:

Ou:

Choix des résistances
Pour le choix des résistances qui sont "en haut du schéma", ce n'est pas très critique car on peut ajuster par programme leur écart avec la valeur théorique.
Pour le choix des résistances R/4R, si on opte pour une seule résistance il va y avoir une erreur supplémentaire. En choisissant le couple 470Ω/910Ω au lieu de 470Ω/940Ω, cela va augmenter l'erreur et il faudrait modifier la correction de Martin. Mais cette correction dépend des résistances choisies, je ne peux donner un cas général.
Bilan
- lecture simple, mais avec correction indispensable
- permet d'utiliser les claviers matriciels pré-câblés usuels
- un seule broche pour un keypad
- résistances proches compensant en partie les erreurs
- utilisation d'interruption possible
MAIS
- lecture mono
- des résistances en plus
- je ne connais pas de bibliothèques pour ce montage
Très intéressant, mais peu utilisé.