Ardu? No!MTobjectsLes ensembles de boutonsMTanalogButtons ≫ Apprentissage pour MTanalogButtons

Apprentissage pour MTanalogButtons
Gestion par événements pour Uno, Nano, Mega

Quand on utilise MTanalogButtons, il faut donner une table des valeurs des seuils qui permettent de différencier les combinaisons. Le programme qui suit permet de donner quasi automatiquement cette table en donnant un programme de test. Elle informe aussi sur la qualité des lectures analogiques.

Configuration

Il faut bien sûr adapter ce programme à votre montage, et changer éventuellement la broche analogique utilisée, la tension de référence du convertisseur et la vitesse de la console. Cela se fait dans les premières lignes du programme.

Programme d'apprentissage

// Ce programme utilise l'apprentissage pour générer un programme de démonstration
// de MTanalogButtons
//
// Il faut changer quelques constantes pour le configurer.
//
// Il faut donner la broche pour la lecture analogique
#define PIN A0 // Changer A5 en A0..A7 suivant le cas
//
// Il faut choisir la référence de tension à utiliser pour la conversion
// DEFAULT: pour Vcc
// INTERNAL: 1,1V pour Nano/Uno
// INTERNAL1V1: 1,1V pour Mega
// INTERNAL2V56: 2,56V pour Mega
// EXTERNAL: pour utiliser AREF
#define REFERENCE DEFAULT
//
// Il faut donner le taux de transfert de la console
#define BAUDS 115200 // 115200 conseillée, mais on peut mettre 9600
// Ne pas oublier de régler la console à 115200 bauds



// *************************** Mémorisation des valeurs du CAN pour tous les boutons
byte lecture[128]; // Contiendra les valeurs possibles du CAN pour tous les boutons   

inline void ecrire(word valeur) // Enregistre que l'on a vu la valeur passée en argument (0 à 1023)
{
  lecture[valeur/8] = bitSet(lecture[valeur/8], valeur%8);
}

byte lire(word valeur) // true si le CAN a eu cette valeur
{
  return bitRead(lecture[valeur/8], valeur%8); 
}


// *************************** Variables globales
boolean repos; // true pour 1023, false pour 0 quand aucun bouton n'est appuyé
word valeurLue, // Résultat de la conversion puis n° de la touche
     oldValeurLue, oldOldValeurLue, // Mémorisation pour permettre trois lectures consécutives identiques
     tempsDepart; // Pour patienter tant qu'on appuie sur une touche; word suffit car on ne dépassera jamais 3000
int indexDebut = 0, indexFin = 0; // Pour explorer la table et lire les valeurs limites
byte nbBoutons = 0;
boolean pasPremiereVirgule = false;



void setup()
{

  // *************************** Initialisations
  Serial.begin(BAUDS); // Régler aussi la console à BAUDS bauds!
  analogReference(REFERENCE); // Permet de choisir la référence de tension
  Serial.println(F("/*\nAppuyez sur toutes les touches, une par une\n"));
  repos = (analogRead(PIN) > 512); // Au repos on a 0 ou 1023

  // *************************** On attend qu'une touche soit appuyée
  do
  {
    oldValeurLue = valeurLue; // Sauvegarde de la mesure précédente du CAN
    valeurLue = analogRead(PIN); // Nouvelle lecture
  } while ((valeurLue!=oldValeurLue) || // Il faut lire deux valeurs identiques (double lecture par sécurité)
     (repos && (valeurLue > 1020)) || (!repos && (valeurLue < 3))); // Et on s'écarte du repos

  // *************************** Recherche des valeurs possibles du CAN
  do // Tant qu'on appui sur des touches
  {
    do // Tant que l'on ne lit pas une bonne valeur du CAN
    {
      oldOldValeurLue = oldValeurLue; // Sauvegarde des mesures précédentes du CAN
      oldValeurLue = valeurLue;
      valeurLue = analogRead(PIN); // Nouvelle lecture
    } while ((valeurLue!=oldValeurLue) || (valeurLue!=oldOldValeurLue)); // On recommence jusqu'à avoir trois lectures identiques
    ecrire(valeurLue); // On trouve une valeur, on l'enregistre

    // Gestion du timer, on sortir de la boucle si on n'appuie plus sur une touche pendant 3s
    if (repos) // Suivant qu'on ait 0 ou 1023 sans appuis
    {
      if (analogRead(PIN) < 1020) tempsDepart = millis(); // RAZ du timer tant qu'on appuie
    }
    else if (analogRead(PIN) > 5) tempsDepart = millis();
  } while ((word)millis() - tempsDepart < 3000);


  // ***************************  Affichage des valeurs lues pour contrôle
  // 1 indique une valeur vue, 0 une valeur que ne donne pas le CAN
  for (word ligne=0; ligne<16; ligne++) // 1023 valeurs possibles affichées avec 16 lignes de 64 valeurs 
  {
    if (ligne == 0) Serial.print("  0"); // N° de la ligne affichée à gauche
    else if (ligne == 1) Serial.print(" 64");
    else Serial.print(ligne*64);
    Serial.print(" "); // Pour aligner la table
    for (word colonne=0; colonne<64; colonne++) Serial.print(lire(ligne*64+colonne)); // Les valeurs
    Serial.println(); // Fin de ligne
  }
  Serial.println();


  // *************************** On compte le nombre de boutons. 
  // C'est le nombre d'intervalle de la table qui contiennent que des 0. Les 1 indiquent un bouton
  while (indexDebut < 1024)
  {
    while (!lire(indexDebut)) indexDebut++; // On se place sur la première valeur 1
    while (lire(indexDebut) && (indexDebut < 1024)) indexDebut++; // On se place sur le premier 0 qui suit
    nbBoutons++;
  }


  // *************************** Affichage un nombre de boutons sur la console
  Serial.print(F("\nJ'ai vu "));
  Serial.print(--nbBoutons);
  Serial.print(F(" boutons\n*/\n\n"));


  // *************************** On fait maintenant l'affichage du programme de test
  // Commentaires du départ, début commun
  Serial.print(F("// Ce programme teste la lecture de l'ensemble de boutons avec MTanalogButtons\n\n"
    "#include <MTobjects.h> // V1.1.1 Voir http://arduino.dansetrad.fr/MTobjects\n\n"
    "const byte PIN = "));
  Serial.print(PIN);  
  Serial.print(F(";\nconst word TABLE_DES_SEUILS[] = {"));

  // Il faut donner la table des valeurs
  // Les valeurs sont par ordre décroissantes si au repos on a CAN=1023
  if (repos)
  {
    indexDebut = 1023;
    while (!lire(indexDebut)) indexDebut--; // On se place sur la dernière valeur
    while (lire(indexDebut) && (indexDebut >= 0)) indexDebut--; // On se place avant la dernière valeur
    while ((indexDebut >= 0) && (indexFin >= 0))
    {
      indexFin = indexDebut; // On cherche un intervalle sans bouton. Cela finit à indexDebut
      while (!lire(indexFin) && (indexFin >= 0)) indexFin--;  // La zone commence à indexFin + 1
      if (indexFin >= 0) // C'est un intervalle sans boutons
      {
        if (pasPremiereVirgule) Serial.print(F(", ")); // La première fois on ne met pas de virgules
        pasPremiereVirgule = true; // Les autres fois on la mettra
        Serial.print((indexDebut + indexFin + 1)/2); // Et on met le milieu de l'intervalle
      }
      indexDebut = indexFin;
      while (lire(indexDebut) && (indexDebut >= 0)) indexDebut--; // On se replace avant le bouton
    }
    Serial.print(F(", 0"));
  }
  // Les valeurs sont par ordre croissant si au repos on a CAN=0
  else
  {
    indexDebut = 0;
    while (!lire(indexDebut)) indexDebut++; // On se place sur la première valeur
    while (lire(indexDebut) && (indexDebut < 1024)) indexDebut++; // On se place après la première valeur
    while ((indexDebut < 1024) && (indexFin < 1024))
    {
      indexFin = indexDebut; // On cherche un intervalle sans bouton. Cela commence à indexDebut
      while (!lire(indexFin) && (indexFin < 1024)) indexFin++;  // La zone finit à indexFin - 1
      if (indexFin < 1024) // C'est un intervalle sans boutons
      {
        if (pasPremiereVirgule) Serial.print(F(", ")); // La première fois on ne met pas de virgules
        pasPremiereVirgule = true; // Les autres fois on la mettra
        Serial.print((indexDebut + indexFin - 1)/2); // Et on met le milieu de l'intervalle
      }
      indexDebut = indexFin;
      while (lire(indexDebut) && (indexDebut < 1024)) indexDebut++; // On se replace après le bouton
    }
    Serial.print(F(", 1024"));
  }

  // Suite du programme
  Serial.print(F("}; // Valeurs des seuils de comparaisons\n\n"

    "void appuis(byte touche)\n"
    "{\n"
    "  Serial.print(\"La touche n°\");\n"
    "  Serial.print(touche);\n"
    "  Serial.print(\" vient d'être appuyée \");\n"
    "}\n"

    "void relachement(void)\n"
    "{\n"
    "  Serial.println(\"et relâchée\");\n"
    "}\n"

    "MTanalogButtons Keypad(PIN, TABLE_DES_SEUILS, appuis, relachement);\n\n"


    "void setup()\n"
    "{\n"
    "  Serial.begin("));
  Serial.print(BAUDS);
  Serial.print(F("); // Régler aussi la console à "));
  Serial.print(BAUDS);
  Serial.print(F(" bauds!\n"));
  if (REFERENCE != DEFAULT)
  {
    Serial.print(F("  analogReference("));
    Serial.print(REFERENCE);
    Serial.print(F(");\n"));
  }
  Serial.print(F("  Serial.println(\"Appuyez sur des touches\");\n"
    "}\n\n"

    "void loop(){}"));
}


void loop()
{
}

Ce programme se trouve dans la bibliothèque, chapitre Examples/MTanalogButtons, avec le nom 1_fr_Apprentissage.ino.

Analyse du résultat

Quand on arrête d'appuyer sur les touches pendant plus de 3 secondes, le programme affiche sur la console tout d'abord une table et le nombre de boutons. Dans la suite, j'ai utilisé un keypad analogique de 20 touches avec une tension de référence du CAN de 1,1V et avec une Uno. Le montage est le suivant:

Il est calculé pour une référence à 1,1V. J'ai donc change la ligne 16:

#define REFERENCE DEFAULT

par

#define REFERENCE INTERNAL

Le début du résultat est:

  0 1000000000000000000000000000000000000000000000000000000001100000
 64 0000000000000000000000000000000000000000000000000000110000000000
128 0000000000000000000000000000000000000000000000110000000000000000
192 0000000000000000000000000000000000000001100000000000000000000000
256 0000000000000000000000000000000011100000000000000000000000000000
320 0000000000000000000000111000000000000000000000000000000000000000
384 0000000000011100000000000000000000000000000000000000000000000011
448 1000000000000000000000000000000000000000000000011111000000000000
512 0000000000000000000000000000000000011110000000000000000000000000
576 0000000000000000001111000000000000000000000000000000000000000000
640 0111100000000000000000000000000000000000000000011110000000000000
704 0000000000000000000000000001111000000000000000000000000000000000
768 0000000011110000000000000000000000000000000000000011100000000000
832 0000000000000000000000000001111000000000000000000000000000000000
896 0000111100000000000000000000000000000000001111100000000000000000
960 0000000000000000000000000000000000000000000000000000000000000001

La grille indique ce que l'on a vu pour les valeurs en sortie du CAN entre 0 et 1023. Un "1" indique que si en appuyant sur les boutons, on obtient la valeur considérée, un "0" indique que l'on n'obtient pas cette valeur. En regardant le tableau, on voit que l'on peut avoir 0, 57, 58, 116, 117... 1023. "0" correspond à un appui sur le bouton n°0, 57 et 58 correspond à un appui sur le bouton n°1... et 1023 est obtenu si aucun bouton n'est appuyé.

On observera que l'appui sur un bouton donne de 1 à 4 valeurs possibles. Si tout étaii parfait, on aurait des paquets de un seul "1". On peut voir de plus que les paquets de 1 sont très espacés, ce qui indique que l'on pourrait presque mettre 4 fois plus de boutons.

Si pour un bouton, on obtenait 0000101110000, cela signifierait que l'on n'a pas appuyé suffisamment longtemps sur la touche.

On remarquera aussi que les valeurs utilisent quasiment tout l'espace ce qui fait que l'on peut se permettre plus de bruit et d'incertitudes.

Cette représentation permet de se rendre compte de la qualité de la réalisation.

Suit ensuite le nombre de boutons vus:

J'ai vu 20 boutons

J'ai bien un keypad de 20 touches, tout va bien. Si j'avais eu moins de 20 boutons, ce serait sans doute un oubli d'un appui, ou suffisamment de dispersion qui ferait une seule suite de 1 pour deux boutons différents (trop de bruits ou trop e boutons). Si j'avais eu plus de 20 boutons, c'est que j'aurais une suite genre 000000101100000 pour un bouton. Soit je n'aurais pas appuyé assez longtemps, soit le montage est trop perturbé.

Le programme résultat

Suit enfin un programme de test. On peut par exemple obtenir:

// Ce programme teste la lecture de l'ensemble de boutons avec MTanalogButtons

#include <MTobjects.h> // V1.1.1 Voir http://arduino.dansetrad.fr/MTobjects

const byte PIN = 14;
const word TABLE_DES_SEUILS[] = {982, 920, 880, 840, 797, 754, 710, 665, 619, 571, 523, 471, 421, 369, 316, 260, 204, 145, 87, 28, 0}; // Valeurs des seuils de comparaisons

void appuis(byte touche)
{
  Serial.print("La touche n°");
  Serial.print(touche);
  Serial.print(" vient d'être appuyée ");
}
void relachement(void)
{
  Serial.println("et relâchée");
}
MTanalogButtons Keypad(PIN, TABLE_DES_SEUILS, appuis, relachement);

void setup()
{
  Serial.begin(115200); // Régler aussi la console à 115200 bauds!
  analogReference(3);
  Serial.println("Appuyez sur des touches");
}

void loop(){}