Arduino 10
Clavier analogique
Sommaire
| Clavier analogique à diodes |
Dans le chapitre 7, j'ai décrit un clavier à 10-24 touches sur une entrée analogique.
 | | un clavier simple avec 10 touches |
 | | un câblage un peu plus compliqué si le clavier est matricé (prévu pour être multiplexé au départ) |
La valeur de tension relevée sur le pin 'clavier' va donc nous permettre de savoir quelle touche est appuyée.
Afin d'éviter d'avoir des valeurs erronées à cause des contacts aléatoires ou des rebonds, on va lire plusieurs fois cette entrée, séparé par 10 ms entre chaque lecture.
Puis rechercher par rapport à la table ci-dessous (dans les déclarations) dans quelle fourchette on est pour retrouver la touche. Les valeurs déclarées sont celles relevés pendanta les tets, on comparera avec la fourchette "moyenne" par la ligne :
* while (itouche > ((xtouche[i]+xtouche[i+1])/2) && i < nbtouche); *
// CLAVIER 10 TOUCHES A DIODES 0,2V
int nbtouche = 10;
// caractère touche renvoyée
// <----------------clavier-10 touches------------> 16 touches---> 24 touches--->
char ltouche[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '}; // nbtouche
// valeurs analogiques. A ajuster selon valeurs relevées après montage # 41 pour 0,2 Volts/diode
int xtouche[] = { 0, 45, 90, 136, 181, 226, 270, 313, 354, 396, 437, 1023}; // nbtouche+1
/*
// CLAVIER 24 TOUCHES A DIODES 0,2V
int nbtouche = 24;
// caractère touche renvoyée
// <----------------clavier-10 touches------------> 16 touches---> 24 touches--->
char ltouche[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', ' '}; // nbtouche
// valeurs analogiques. A ajuster selon valeurs relevées après montage # 41 pour 0,2 Volts/diode
int xtouche[] = { 0, 45, 91, 136, 181, 226, 270, 313, 354, 396, 437, 478, 519, 560, 601, 642, 683, 724, 765, 806, 847, 888, 929, 970, 1010, 1023}; // nbtouche+1
*/
Ces fonctions sont assurées par le sous-programme 'lecture_clavier' que voici, avec des commentaires supplémentaires :
void ger_scrute_clavier(){
// transfert des anciennes valeurs lues
itouche2 = itouche1;
itouche1 = itouche/facteur;
// lecture à l'instant t
itouche = analogRead(clavier);
// pour calibrage, affichage des valeurs lues (pour ajuster votre tableau 'xtouche[]'
// if (itouche < 1020 ) Serial.println(analogRead(clavier));
// si la touche est stable (donc pendant 3 appels)
if ((itouche/facteur)==itouche1 && itouche1==itouche2) {
// recherche quelle touche est appuyée en comparant aux valeurs déclarées
i=-1;
do {
i++;
} while (itouche > ((xtouche[i]+xtouche[i+1])/2) && i < nbtouche);
// Ici, i contient le numero de la touche appuyée (avec 'i'='nbtouche' si aucune appuyée)
// si la touche a varié, on mémorise sa valeur dans 'vtouche'
if (ltouche[i] != vtouche ) {
vtouche = ltouche[i];
// touche valide ( c'est à dire pas 'pas de touche')
if (i < nbtouche) {
touche = vtouche;
// AH ! on gère aussi la 'répétition', c'est à dire que si on garde la touche enfoncée,
// ... au bout de 500ms, on va répéter cette touche toutes les 250 ms.
touche_repeat = vtouche;
// attente 1ère répétition
tempo_repeat=500; // duree avant repetition
// pas de touche appuyée = ' '
} else {
touche_repeat = ' ';
}
// si la touche est la même : on la répète
} else {
if (i < nbtouche && tempo_repeat == 0 ) {
touche_repeat = vtouche;
tempo_repeat=250; // duree repetition
}
}
}
return;
}
Ce sous-programme est appelé toutes les 10 millisecondes dans le sous-programme 'ger_top_centieme' qui gère les problèmes de temps.
void ger_top_centieme (){
unsigned long delta;
unsigned long currentMillis = millis();
// Centième de secondes
delta = currentMillis - previousMillis;
if (delta >= 10) {
// save the last time you come here
previousMillis = currentMillis;
// scrutation du clavier toutes les 10ms
ger_scrute_clavier(); // Gestion du clavier ANALOGIQUE <<<<<<<<<<<
SUITE ...
Ensuite, comment utiliser ce clavier ? Dans le programme, n'importe où où l'on attends une touche :
if (touche == '1') {
TRAITEMENT ...
touche = ' '; // pour signifier que la touche est traitée
}
Si on veut utiliser le traitement de répétition (pour augmenter une valeur par exemple) :
if (touche_repeat == '1') {
TRAITEMENT ...
touche_repeat = ' '; // pour signifier que la touche est traitée
}
Vous verrez dans la page 12, l'utilisation dans le menu général ... :
// - sans répétition
if (touche == '3') {
mode--;
grafcet_a = 10;
touche = ' ';
}
// + avec répétition
if (touche_repeat == '4') {
mode++;
grafcet_a = 10;
touche_repeat = ' ';
}
// Ok
if (touche != ' ' and touche != '4') { // 3 est exclue du fait que traitée avant !
grafcet_a = 100*mode;
tempo_a = 0;
// debug lcd.setCursor(0, 1); lcd << "mod=" << mode << " gr=" << grafcet_a; delay(1000);
Serial << modex[mode] << endl;
lcd.clear(); touche_repeat = ' '; touche = ' ';
}
SUITE ...
Il est intéréssant de conditionner l'affichage, en particulier de 'Serial.print' seulement quand on 'debugge'. J'ai défini une constante de compilation 'debug' que l'on positionne par un 'define' :
// DEBUG : 0=pas de debug, valeur selon besoins
#define debug 1
Certaines parties de programme sont alors mises en service ou non par cette syntaxe :
#if debug > 0
Serial << "Serial ok" << endl;
#endif
#if debug > 2
Serial << "DEBUG est plus grand que 2" << endl;
#endif
Voici le listing complet de la version 10 de mon ARDUINO UNO.
Ici, on ne fait qu'afficher la touche appuyée, c'est déjà pas mal !!!
/*
GoodFields 10_clavier_analogique
Clavier analogique à diodes et debug
LCD
Gestion du temps
Compactage des sorties série (intégration d'une bibliothèque : Streaming)
GRAFCET
TEMPORISATION basique
FRONT MONTANT/DESCENDANT
*/
//**********************************************************
// BIBLIOTHEQUES
// incluses dans le package Arduino de base
#include <LiquidCrystal.h> // LCD
// ajoutées
#include <Streaming.h>
#include <Time.h>
// DEBUG : 0=pas de debug, valeur selon besoins
#define debug 1
//**********************************************************
// DECLARATIONS
// LCD initialize the library with the numbers of the interface pins
/* Câblage du LCD
* LCD pin 1 to GND
* LCD pin 2 to VCC 5V
* LCD pin 3 : contraste sur le point milieu d'un potentiomètre de 10K entre le GND et le VCC (réglé très près de la masse)
* LCD pin 4 RS to digital pin 4
* LCD pin 5 RW to GND
* LCD pin 6 Enable pin to digital pin 2
* LCD pin 11 D4 pin to digital pin 6
* LCD pin 12 D5 pin to digital pin 7
* LCD pin 13 D6 pin to digital pin 8
* LCD pin 14 D7 pin to digital pin 9
*/
// LiquidCrystal lcd(RS, Enable, D4, D5, D6, D7)
LiquidCrystal lcd(4, 2, 6, 7, 8, 9);
// entrées-sorties
int led_a = 13;
// temporisations
int tempo_a,tempo_b;
// vitesse de clignotement : maintenant en millisecondes
int tempo_a_reset=1000;
int tempo_b_reset=700;
// gestion du temps
int previousSecond; // secondes de l'horloge
int top_dixieme=false; // top pour traitements au 1/10 seconde dans le P/P
int top_seconde=false; // top pour traitements à la seconde dans le P/P
int top_minute=false; // top pour traitements à la minute dans le P/P
unsigned long previousMillis, previousDecis;
// clavier à 16 touches et diodes sur entrée analogique
int clavier=A0;
int itouche,itouche1=-1,itouche2=-1,vtouche,tempo_repeat;
// touche renvoyée
char touche=' ',touche_repeat=' ';
// CLAVIER 10 TOUCHES A DIODES 0,2V
int nbtouche = 10;
// caractère touche renvoyée
// <----------------clavier-10 touches------------> 16 touches---> 24 touches--->
char ltouche[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' '}; // nbtouche
// valeurs analogiques. A ajuster selon valeurs relevées après montage # 41 pour 0,2 Volts/diode
int xtouche[] = { 0, 45, 90, 136, 181, 226, 270, 313, 354, 396, 437, 1023}; // nbtouche+1
/*
// CLAVIER 24 TOUCHES A DIODES 0,2V
int nbtouche = 24;
// caractère touche renvoyée
// <----------------clavier-10 touches------------> 16 touches---> 24 touches--->
char ltouche[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', ' '}; // nbtouche
// valeurs analogiques. A ajuster selon valeurs relevées après montage # 41 pour 0,2 Volts/diode
int xtouche[] = { 0, 45, 91, 136, 181, 226, 270, 313, 354, 396, 437, 478, 519, 560, 601, 642, 683, 724, 765, 806, 847, 888, 929, 970, 1010, 1023}; // nbtouche+1
*/
// speedomètre = calcul de vitesse ...
int tours_ps,tours;
// GRAFCETS
int grafcet_a,grafcet_b;
//**********************************************************
// INITIALISATIONS
void setup() {
// définition taille du LCD :
lcd.begin(16, 2);
// mise à l'heure
setTime(12,15,00,25,12,13); // 12h15 le 25 déc 2013 setupArduinoTime(2013,25,21,12,15,00));
// initialize the pins
pinMode(led_a, OUTPUT);
pinMode(clavier, INPUT);
digitalWrite(clavier, HIGH); // turn on pullup resistors
// ligne série (pour traces)
Serial.begin(9600); // ou 9600 selon votre port COM
#if debug > 0
Serial << "Serial ok" << endl;
#endif
// AFFICHAGE sur LCD
lcd.setCursor(0, 0); lcd << "Serial OK"; // 1ere ligne
lcd.setCursor(0, 1); lcd << "GoodFields"; // 2eme ligne lcd.print ("toto="); lcd.pprint (toto,2);
delay(1000); // pour profiter du LCD au démarrage
lcd.clear(); // Effacement général LCD
// on arme les grafcets
grafcet_a = 1;
grafcet_b = 1;
}
//**********************************************************
// PROGRAMME PRINCIPAL
void loop() {
// tours par seconde
tours++;
// gestion du temps
ger_top_centieme ();
// opérations à chaque dixième de seconde
if (top_dixieme) {
top_dixieme = false;
#if debug > 0
// premier test
itouche = analogRead(clavier);
itouche1 = (itouche+20)/44;
lcd.setCursor(0, 1); lcd << "t=" << itouche1 << "," << itouche << " "; // 2eme ligne lcd
// lcd.setCursor(0, 1); lcd << "b=" << grafcet_b << " a=" << grafcet_a ; // 2eme ligne lcd
#endif
}
// opérations à chaque seconde
if (top_seconde) {
top_seconde = false;
tours_ps=tours; tours=0;
#if debug > 0
lcd.setCursor(12, 1); lcd << tours_ps ; //<< "t/s "; // 2eme ligne lcd
#endif
// horloge
#if debug > 0
horodatage_serial(); Serial << " top_seconde " << tours_ps << "t/s " << endl;
#endif
lcd.setCursor(0, 0);
if (second()%10 > 7) {
lcd << "XXX " << ((day())?"0":"") << day() << "/" << ((month())?"0":"") << month() << "/" << year();
} else {
lcd << " " << ((hour())?"0":"") << hour() << ":" << ((minute())?"0":"") << minute() << ":" << ((second())?"0":"") << second() << " ";
}
// idéalement, XXX devrait être remplacé par {"Lun","Mar","Mer","Jeu","Ven","Sam","Dim"} (au boulot !)
// heart-beat chaque seconde
// if (lamp_tempo == 0 ) lamp_tempo = 20; // ms
}
//--------------------------------
// GRAFCET A
switch (grafcet_a) {
case 1:
if ( tempo_a == 0 ) { // si la tempo est terminée,
digitalWrite(led_a, HIGH); // on allume la LED
tempo_a = tempo_a_reset; // on arme la tempo
grafcet_a++; // et on passe à l'étape suivante
}
break; // cette instruction vous fait sauter à la fin du "switch"
// sinon, on continue, et cela fait perdre du temps, et il peut arriver que d'autre "case" fonctionnent !
case 2:
if ( tempo_a == 0 ) {
digitalWrite(led_a, LOW); // turn the LED off by making the voltage LOW
tempo_a = tempo_a_reset; // on arme la tempo
grafcet_a=1; // on reboucle
}
break;
// si grafcet_a n'a pas une valeur correcte, on passe ici
default:
grafcet_a = 0; // on arrête
break;
} // fin du "switch"
// FIN GRAFCET A
//--------------------------------
// GRAFCET B
switch (grafcet_b) {
case 1:
if ( tempo_b == 0 ) { // si la tempo est terminée,
horodatage_serial(); Serial << " grafcet_b=" << grafcet_b << " grafcet_a=" << grafcet_a << endl;
tempo_b = tempo_b_reset; // on arme la tempo
grafcet_b++; // et on passe à l'étape suivante
}
break; // cette instruction vous fait sauter à la fin du "switch"
case 2:
if ( tempo_b == 0 ) {
horodatage_serial(); Serial << " grafcet_b=" << grafcet_b << " grafcet_a=" << grafcet_a << endl;
tempo_b = tempo_b_reset; // on arme la tempo
grafcet_b=1; // on reboucle
}
break;
// si grafcet_a n'a pas une valeur correcte, on passe ici
default:
grafcet_b = 0; // on arrête
break;
} // fin du "switch"
// FIN GRAFCET B
//--------------------------------
}
// ************************************************************************************************
// ************************************************************************************************
// SOUS-PROGRAMMES
// ************************************************************************************************
void ger_scrute_clavier(){
// transfert des anciennes valeurs lues
itouche2 = itouche1;
itouche1 = itouche/facteur;
// lecture à l'instant t
itouche = analogRead(clavier);
// pour calibrage, affichage des valeurs lues
// if (itouche < 1020 ) Serial.println(analogRead(clavier));
// si la touche est stable (donc pendant 3 appels)
if ((itouche/facteur)==itouche1 && itouche1==itouche2) {
// recherche quelle touche est appuyée en comparant aux valeurs déclarées
i=-1;
do {
i++;
} while (itouche > ((xtouche[i]+xtouche[i+1])/2) && i < nbtouche);
// Ici, i contient le numero de la touche appuyée (avec 'i'='nbtouche' si aucune appuyée)
// si la touche a varié, on mémorise sa valeur dans 'vtouche'
if (ltouche[i] != vtouche ) {
vtouche = ltouche[i];
// touche valide ( c'est à dire pas 'pas de touche')
if (i < nbtouche) {
touche = vtouche;
// AH ! on gère aussi la 'répétition', c'est à dire que si on garde la touche enfoncée,
// ... au bout de 500ms, on va répéter cette touche toutes les 250 ms.
touche_repeat = vtouche;
// attente 1ère répétition
tempo_repeat=500; // duree avant repetition
// pas de touche appuyée = ' '
} else {
touche_repeat = ' ';
}
// si la touche est la même : on la répète
} else {
if (i < nbtouche && tempo_repeat == 0 ) {
touche_repeat = vtouche;
tempo_repeat=250; // duree repetition
}
}
}
return;
}
// ************************************************************************************************
// Programme au -ieme de seconde
// gestion des temporisations
// NB : le débordement du compteur en (unsigned long) a lieu tous les 50 jours environ ...
void ger_top_centieme (){
unsigned long delta;
unsigned long currentMillis = millis();
// Centième de secondes
delta = currentMillis - previousMillis;
if (delta >= 10) {
// save the last time you come here
previousMillis = currentMillis;
// scrutation du clavier toutes les 10ms
ger_scrute_clavier();
// gestion des TEMPORISATIONS (en ms mais précise à 10ms près)
if (tempo_a >= delta ) { tempo_a -= delta; } else { tempo_a = 0; }
if (tempo_b >= delta ) { tempo_b -= delta; } else { tempo_b = 0; }
if (tempo_repeat >= delta ) { tempo_repeat -= delta; } else { tempo_repeat = 0; }
}
// Dixième de seconde
delta = currentMillis - previousDecis;
if (delta >= 100) {
previousDecis = currentMillis;
top_dixieme = true;
// tops seconde et minute pour traitements dans le main
if (second() != previousSecond) {
previousSecond = second();
top_seconde = true;
if (previousSecond == 0) top_minute=true;
}
}
return;
}
// ************************************************************************************************
// affichage horloge complete sur la sortie serie, sans fin de ligne
void horodatage_serial() {
// digital clock display of the time
// Serial << day() << "/" << month() << "/" << year() << " " << hour() << printDigits(minute()) << printDigits(second()) << endl;
Serial << ((day())?"0":"") << day() << "/" << ((month())?"0":"") << month() << "/" << year() << " " << ((hour())?"0":"") << hour() << ":" << ((minute())?"0":"") << minute() << ":" << ((second())?"0":"") << second();
}
// ************************************************************************************************
// ************************************************************************************************
Photos de la page :
Dernière mise à jour : 11:39:59 18/09/2020