Sommateur de courants à lecture poly
Schéma de base
Voici le schéma de principe:
Dans ce principe, pour 5 boutons, on a 5 générateurs de courant, et la résistance R est dimensionnée de façon à avoir une tension à ses bornes égale à la tension de référence du CAN de l'Arduino quand elle est traversée par le courant 32I.
L'intérêt de ce montage est de permettre la lecture de l'état de tous les boutons. La lecture est poly. Si par exemple la tension aux bornes de R vaut 22RI, on en déduit que les boutons SW4 (laisse passer 16I), SW2 (laisse passer 4I) et SW1 (lisse passer 2I) sont enfoncés.
J'ai numéroté les boutons en fonction du poids SWn laisse passer un courant de 2nI. Lors de la lecture, on peut obtenir un mot binaire, chaque bit correspondant à un bouton, le bit de poids 2n correspondra au bouton SWn.
Ce serait trop compliqué d'utiliser de vrais générateurs de courant, nous allons utiliser de simples résistances. Le schéma devient alors:
Pour que cela fonctionne au mieux, il faudrait que la tension aux bornes des résistances soit fixe, ce qui donnerait un courant constant. Mais la tension aux bornes de la résistance R va varier, faisant varier les autres tensions. Pour en minimiser l'effet, on pourrait alimenter les résistances avec une tension élevée, mais dans la pratique c'est compliqué car il faudrait une deuxième alimentation, genre 12V, et une erreur pourrait s'avérer fatale. De plus cela compliquerait le schéma et cela deviendrait moins intéressant. Par contre, on peut diminuer la tension maximale de R en utilisant la référence de 1,1V au lieu des 5V par défaut.
Il y a quand même un problème de tension qui n'est pas fixe aux bornes des résistances et cela va fausser les mesures et décaler les lectures. Je pense que l'erreur par défaut est d'environ 35 points sur les 1024 si tous les composants étaient parfaits. Si on utilise 4 boutons, l'écart théorique minimum de lecture entre boutons est de 1024/16 (le convertisseur à 1024 points et avec 4 boutons il y a 16 possibilités) soit de 64. Cela devrait passer. Pour 5 boutons l'écart entre lecture est de 1024/32 soit 32, cela ne peut théoriquement pas fonctionner sans correction (on en reparlera plus bas).
Avec 4 boutons
Je choisis arbitrairement une résistance R de 1kΩ, et si le bouton SW4 est seul appuyé, il y aura 1,1V/2 aux bornes de R qui sera donc parcouru par un courant voisin de 1mA. Il y aura au maximum 2mA si on appuie sur tous les boutons.
Le courant I est tel que l'on a VREF=1,1V (tension de référence du CAN) si R est traversé par 16I. I vaut donc
VREF/16R. Les résistances R0 à R3 ont à leur bornes en moyenne VCC-VREF/2, on va se baser sur ce nombre.
R0 vaudra donc 16R(VCC-VREF/2)/VREF = 16.1000Ω.(5V-0,55V)/1,1V = 64000Ω = 64kΩ.
R1 laisse passer deux fois plus de courant, sa valeur est moitié soit 32kΩ
R2 laisse passer quatre fois plus de courant, sa valeur est 4 fois moins soit 16kΩ
R3 laisse passer huit fois plus de courant, sa valeur est 8 fois plus faible soit 8kΩ
R a été choisie à 1kΩ
Pour minimiser l'erreur et arriver à une erreur maximale des 35 points, il faut en fait choisir des résistances R0 à R3 légèrement plus petites, ou une résistance R légèrement plus grande. Le schéma est alors:
Bien entendu, on peut multiplier ou diviser toutes les valeurs des résistances par le même nombre. Si on a des résistances en pagaille de 33kΩ venant du même lot, on peut l'utiliser pour R1, R0 sera faite avec deux de ces résistances en série, R2 avec deux résistances en parallèle et R3 avec 4 résistances en parallèle. Pour R il faut donc prendre une résistance théorique de 33x1057/32 soit1090Ω. En mettant une 1kΩ cela va donner une lecture un peu plus faible et le programme peut en tenir compte (au lieu de 1024, on mettra 939).
Le programme en dessous fonctionne. Éventuellement pour 4 boutons on pourrait économiser la correction.
Pour plus de sécurité, on peut corriger la lecture, comme on le fait pour 5 boutons
Avec 5 boutons
On a vu que l'écart de lecture dû à la variation de la tension sur les résistance était de 35 points environ. Si on veut pouvoir mettre 5 boutons, il faut absolument corriger la lecture pour diminuer cet écart.
Dans un premier temps, recalculons R pour que 32I fasse 1,1V. Cela correspond à une résistance "en haut" de 4kΩ. La tension de 1,1V vaut donc (pont diviseur avec R et 4kΩ) 1,1V=5V.R/(4kΩ+R) soit R=1,13kΩ. En utilisant la correction de Martin:
valeurLue = analogRead(A0); valeurLue += 0.00024 * (valeurLue - 522.0)*(valeurLue - 522.0) - 64.1;
Cela ramène l'erreur de lecture à environ 2 points, et si tous les composants était parfaits, on pourrait même mettre 9 boutons. Avec 5 boutons cela passe, et cela devrait aussi passer pour 6 boutons avec des résistances correctes (pas les miennes!).
Pas de mystère pour le schéma:
Si on prend une valeur plus faible pour R par exemple 1,1kΩ, au lieu d'aller de 0 à 1024, on n'ira que de 0 à 997 (1024*1.1/1.13) et cela peut facilement se corriger dans le soft, par exemple en faisant la correction:
valeurLue = analogRead(A0) * 1.13 / 1.1; valeurLue += 0.00024 * (valeurLue - 522.0)*(valeurLue - 522.0) - 64.1;
A partir de ces valeurs, on peut toutes les multiplier ou les diviser par un même coefficient, cela ne change ni le programme ni le résultat.
Exemple de programme de lecture
// Sommateur de courant à lecture poly // VCC VCC VCC VCC VCC // │ │ │ │ │ // 128kΩ 64kΩ 32kΩ 16kΩ 8kΩ // │ │ │ │ │ // SW0 SW1 SW2 SW3 SW4 // │ │ │ │ │ // ├─────┴─────┴─────┴─────┴─── vers A0 // │ // 1,1kΩ // │ // GND const byte N = 5; // Nombre de boutons; A ajuster const byte BOUTONS = A0; // Broche de lecture; A ajuster void setup() { Serial.begin(115200); // Régler aussi la console à 115200 bauds! analogReference(INTERNAL); // Pour passer à 1,1V } int valeurLue, // Résultat de la conversion brute puis corrigée oldValeurLue, // Mémorisation pour permettre deux lectures consécutives identiques touche, // Mot de 5 bits contenant les touches (SW4.SW3.SW2.SW1.SW0) oldTouche; // Mot précédent pour ne pas l'afficher plusieurs fois void loop() { do // Lecture brute { oldValeurLue = valeurLue; // Sauvegarde de la mesure précédente valeurLue = analogRead(BOUTONS); // Nouvelle lecture } while (valeurLue!=oldValeurLue); // On recommence jusqu'à avoir deux lectures identiques // Correction de la valeur valeurLue *= 1.13 / 1.1; valeurLue += 0.00024 * (valeurLue - 522.0)*(valeurLue - 522.0) - 64.1; //Extraction touche=((valeurLue+(512>>N)) >> (10-N)); // Cela donne un mot SW4.SW3.SW2.SW1.SW0 // Affichage if (touche!=oldTouche) // Si changement { if (touche) // Il y a au moins une touche appuyée { // Affichage type " SW4 SW3 SW2 SW1 SW0" for (int8_t bouton=N-1; bouton>=0; bouton--) { Serial.print(" SW"); Serial.print(bouton); Serial.print(" "); } Serial.println(); // Affichage des boutons for (int8_t bouton=N-1; bouton>=0; bouton--) // Pour chaque bouton if (touche & (1<<bouton)) Serial.print(" appuyé "); else Serial.print(" -- "); // Non appuyé Serial.print(" écart="); Serial.print(valeurLue-touche*(1<<(10-N))); Serial.print("/"); Serial.print(512/(1<<N)); Serial.println(")\n"); // Retour chariot et saut d'une ligne en plus delay(100); // Anti-rebond } oldTouche=touche; // Mémorisation pour ne pas afficher plusieurs fois delay(1000); // Pas trop souvent } }
Le programme affiche le ou les boutons appuyés et l'écart entre la valeur lue et corrigée et la valeur théorique attendue:
SW4 SW3 SW2 SW1 SW0 -- -- -- -- appuyé écart=0/16) -- -- -- appuyé -- écart=-7/16) -- -- -- appuyé appuyé écart=-4/16) -- -- appuyé -- -- écart=-8/16) -- -- appuyé -- appuyé écart=-6/16) -- -- appuyé appuyé -- écart=-12/16) -- -- appuyé appuyé appuyé écart=-9/16) -- appuyé -- -- -- écart=-7/16) -- appuyé -- -- appuyé écart=-2/16) -- appuyé -- appuyé -- écart=-9/16) -- appuyé -- appuyé appuyé écart=-5/16) -- appuyé appuyé -- -- écart=-8/16) -- appuyé appuyé -- appuyé écart=-4/16) -- appuyé appuyé appuyé -- écart=-10/16) -- appuyé appuyé appuyé appuyé écart=-7/16) appuyé -- -- -- -- écart=-1/16) appuyé -- -- -- appuyé écart=2/16) appuyé -- -- appuyé -- écart=-5/16) appuyé -- -- appuyé appuyé écart=-1/16) appuyé -- appuyé -- -- écart=-2/16) appuyé -- appuyé -- appuyé écart=2/16) appuyé -- appuyé appuyé -- écart=-5/16) appuyé -- appuyé appuyé appuyé écart=-1/16) appuyé appuyé -- -- -- écart=2/16) appuyé appuyé -- -- appuyé écart=5/16) appuyé appuyé -- appuyé -- écart=-3/16) appuyé appuyé -- appuyé appuyé écart=3/16) appuyé appuyé appuyé -- -- écart=0/16) appuyé appuyé appuyé -- appuyé écart=3/16) appuyé appuyé appuyé appuyé -- écart=-4/16) appuyé appuyé appuyé appuyé appuyé écart=-1/16)
Avec mes résistances un peu triées, les écarts vont jusqu'à 12/16, il est donc impossible avec ces composants d'avoir un bit de plus (6 boutons). Mais en utilisant des résistances métalliques à 1%, on devrait sans doute pouvoir utiliser 6 boutons.
Le programme ci-dessus est bon quelque soit le nombre de boutons (changer la valeur de N). Il ne fonctionne que si les erreurs ne sont pas trop importantes. Si avec des composants il fonctionne, on peut retirer un ou des boutons, cela fonctionnera aussi
Bilan
- lecture simple, mais avec correction indispensable pour plus de 4 boutons
- lecture poly
- les boutons ont une borne commune
- utilisation d'interruption possible
MAIS
- résistances non identiques ne compensant pas les erreurs
- une résistance par bouton en plus
- je ne connais pas de bibliothèques pour ce montage
Intéressant, mais peu utilisé. On peut peut être aller jusqu'à 6 boutons.