Ardu? No!Les boutons ≫ Keypad à lecture analogique

Keypad à lecture analogique
Matrice Carrée

Le keypad

Le keypad classique est composé de 12 ou 16 boutons. Il est pré-câblé:

Keyopad 12 et 16 touches

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:

Câblage d'un keyopad 16 touches pour une lecture mono optimale

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

Passage du courant pour 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:

Lecture idéale pour un pavé 16 touches

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:

Câblage réel d'un keyopad 16 touches

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:

Câblage d'un keyopad 12 touches pour une lecture mono optimale

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:

Câblage réel d'un keyopad 12 touches

Mais j'aurais pu faire aussi:

Câblage réel d'un keyopad 12 touches, variante1

Ou:

Câblage réel d'un keyopad 12 touches, variante 2

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é.