Aller au contenu


Information tutoriel

  • Ajouté le: févr. 14 2017 12:09
  • Date Updated: févr. 25 2017 10:26
  • Lectures: 1671
 


* * * * *
0 Notes

MeccanoSoft II – librairie poussoir

Un poussoir? Trop facile à programmer!
Oui, mais il peut faire beaucoup plus de choses que vous croyez, pas toujours évidentes à programmer, et souvent très utiles. Venez découvrir nos nouvelles pièces meccano.

Posté par jdn on févr. 14 2017 12:09

Introduction

Ici, on appelle poussoir un élément mécanique qui établit un contact électrique. Il prend toutes sortes de formes et dimensions. Ce qui le caractérise et le rend un peu plus intéressant pour la programmation, c'est qu'il a des rebonds de contact. Quand le contact se ferme, ce n'est pas franc. Les pièces métalliques rebondissent et si on a un bon oscilloscope, on voit une guirlande de mauvais contacts. On peut mouiller avec du mercure - ce n'est plus permis; on peut filtrer le signal avec de l'électronique (RC et schmitt-trigger) c'est cher et encombrant. Quelques instructions feront l'affaire.
Le processeur ne sait que lire un état logique, donc une tension. Le poussoir est associé à une résistance pour former un diviseur de tension; c'est bien connu (figure 1).
Pousma.gif
Fig 1 Schémas électriques pour lire l'état d'un poussoir

On voit que la résistance peut être en pull-up (tirage vers le haut) ou pull-down. Les microcontrôleurs ont souvent la possibilité de connecter une pull-up interne. On peut se passer de la résistance, mais il faut astucer avec la capacité parasite de la pin: on écrit 5V, s'il n'y a pas court-circuit, la tension reste à 5V le temps de décharge du condensateur - à réserver pour sauver un mauvais design.

Avec une pull-up, la figure 2 suivante montre à quoi ressemblent les rebonds. Leur durée maximale dépend de la construction (inerties, matière); on fera un programme de test. Etant donné la lenteur de l'action sur un poussoir, 20 ou 50 ms donne une grande marge de sécurité.
Rebonds.jpg
Fig 2 Rebonds observés sur un encodeur mécanique

Copier l'état d'un poussoir sur une LED

C'est souvent le premier exemple des tutoriaux Arduino. Faisons l'effort de bien définir le no des pins, la polarité, le set-up. C'est essentiel pour aller vest la complexité, et si c'est bien déclaré, pas besoin d'un schéma explicatif.

Le poussoir est branché entre la masse (Gnd) et la pin8, avec une résistance pull-up vers le +5V.
Utilisons la Led connectée à la pin 13 sur toutes les cartes Arduino. Le programme suivant copie l'état du poussoir sur la Led.
//Copy.ino
#define Pous8 8
#define Pous8On !digitalRead(Pous8)
#define Pous8Off digitalRead(Pous8)

#define LedOn digitalWrite(Led,HIGH);
#define LedOff digitalWrite(Led,LOW);

void setup () {
pinMode (Pous8, INPUT);
pinMode(Led, OUTPUT);
}
void loop () {

  if(Pous8On) { LedOn; }
  else { LedOff; }
}

On peut économiser la résistance en programmant une pull-up interne. Le truc du processeur AVR est le suivant: si la pin est en entrée, le registre utilisé pour mémoriser la valeur de sortie peut être utilisé pour autre chose. Si ce registre est à "un" une pull-up interne est connectée.
Dans le set-up, il faut dire
pinMode (Pous8, INPUT);
digitalWrite(Pous8,HIGH);

Arduino a une fonction pour compactifier sur une ligne
pinMode (Pous8, INPUT_PULLUP);

Encore un truc. Mon poussoir a 2 pins au pas de 5mm. Il peut être inséré dans les pins 8 et 10. Je programme la pin 10 comme un Gnd (LOW) et je n'ai pas de fils à câbler!
Pous810.jpg
Ce qui est génial, c'est que ces changements de définitions ne modifient pas la boucle programme.
Je peux faire un .h avec ces définitions, et me concentrer sur les algorithmes (voir MeccanoSoft I).

Totaliser les pressions

Pour suivre une séquence d'actions, il faut attendre que l'on presse, puis relâche, en utilisant l'instruction while.

//Compte.ino
#include "PousLed.h"
void setup () { SetupPousLed(); }

byte compte=0;
void loop () {
  while (PousOff) {LedOff; delay(20);}
  while {PousOn) {LedOn; delay(20);}
  compte++;
}


Pour éviter que ce programme compte les rebonds, on a rajouté un délai de 20ms. C'est la solution pour filtrer les rebonds: échantillonner avec une période supérieure à leur durée.

Mais comment lire le compteur?
Le plus efficace, mettre des Leds, un afficheur 8 bits avec une interface parallèle sur le port C.
Le port est initialisé en sortie, et une instruction de 200 microsecondes PORTC=compte; - affiche la valeur en 8bits. Pour afficher le nombre de rebonds, c'est la solution la plus simple.
AfPortC.jpg
Fig 3 La carte Diduino a un connecteur pratique pour afficher une variable 8 bits
Le terminal permet d'afficher le compteur avec l'instruction Serial.println (compte); Elle dure 1ms, mais cela ne gêne pas. D'autres solutions d'affichage sont possibles,

Plus élégant: compter les transitions

C'est souvent utile de savoir qu'un signal a changé d'état entre 2 lectures consécutives. Il suffit à chaque lecture de faire une copie, après avoir comparé l'ancienne et la nouvelle valeur. La variable byte antePous sera la copie de PousOn. C'est en fait une variable booléenne qui vaut 0 ou différent de zéro. Si on veut compter la transition positive (quand on pèse), antePous est encore à zéro et (ET logique) PousOn à un.
PousCompte.jpg
Fig 4 Détection de transitions

L'intérêt de cette solution qui utilise un if non bloquant, est que l'on teste l'état sans s'arrêter et on peut faire d'autres choses pendant 20ms. C'est beaucoup plus intéressant que de rester bloqué dans un while en attendant que la condition change.

Le poussoir en interrupteur

On presse une fois, cela s'allume, on represse, cela s'éteint. La Led change d'état à chaque transition du signal. En fait, on compte par 2, c'est une variante du programme précédent. Pour programmer simple, on ajoute dans PousLed.h la définition de LedToggle
#define LedToggle digitalWrite (Led, !digitalRead(Led) )
Le programme devient
//Bascule.ino
#include "PousLed.h"
void setup () { SetupPousLed(); }

void loop () {
  if (!antePous && PousOn) {
    LedToggle;
  }
  antePous = PousOn;
  delay(20);
}


Compter et agir

Je presse 3 fois rapidement. J'aimerais que mon programme en tienne compte.
Le problème est dans le "rapidement". Il faut mesurer les actions et les espaces, tout en supprimant les rebonds.

La solution est d'utiliser un compteur supplémentaire qui compte le temps. Une pression dure 100 ms. Le temps entre 2 pressions rapides est de 200 à 1 secondes. Plus d'une seconde, la salve est terminée. On pourrait jouer sur la durée de maintien: une longue pression annule à vous de rajouter cette fonction !
Pous4.gif
Fig 5  Diagramme des temps pour compter une salve d'actions sur le poussoir
On va utiliser une machine à état qui sépare bien la succession des événements.
Humm, c'est quoi? Voir http://www.didel.com/coursera/LC4.pdf
Faites des aller-retours entre les instructions proposées et la figure 5 pour bien comprendre.
Dans l'état 1, on attend une pression, qui fait passer dans l'état 2, où le compteur est bloqué à zéro. Quand on relâche, on compte toutes les 20 ms. Si on represse rapidement, on retourne dans l'état 2 après avoir incrémenté le compteur d'actions. Si le compteur de temps dépasse 50x20ms = 1s, on quitte la fonction.

// Fonction xx=GetPous() rend le nombre de pressions
byte GetPous () {
  byte compte, cTemps,etat=1; // variables locales
  while (cTemps < 1000/20) {
    delay (20);
    switch (etat) {
     case 1:
      if (PousOff) { break; } // on attend
      etat=2; compte=0; cTemps=0; break; // else
     case 2:
      if (PousOff) { compte ++; cTemps=0; etat=3; }
      break; // else
     case 3:

      cTemps++;
      if (PousOn) { etat=2; }
      break;
    } // end switch
  } // end while
return compte;
} // end function


On voit que la condition dans le while fait que l'on quitte la machine d'état quand le temps de relâchement a été dépassé.
Si on appelle la fonction et que l'on ne presse jamais, c'est facile d'ajouter un timeout dans l'état 1 avec un nouveau compteur. Le problème est alors de gérer cette erreur.

Pour tester cette fonction, ajoutons une fonction qui clignote n fois
void Clignote (byte nfois) {
  for (byte i=0, i<nfois,i++) {
    LedToggle; delay (200);
  }
}


Les fonctions xx=GetPous() et Clignote (nfois) peuvent être mises dans un fichier CntPous.h. On aura certainement d'autres occasions d'utiliser ces deux pièces de meccano.

Le programme de test est alors
//ComptePous.ino
#include "PousLed.h"
#include "CntPous.h"
void setup () { SetupPousLed(); }
byte nPous;
void loop () {
  nPous=GetPous();
  Clignote (nPous);
}

A noter que le clignotement pourrait être incorporé à GetPous(); avoir une quittance est toujours très utile.

Applications
Quand on a à disposition une fonction comme GetPous(), c'est facile de lui trouver des applications: choisir un programme de démonstration, choisir parmi quelques valeurs pour un paramètre, enclencher différents appareils connectés. Avec le robot Xbot, les moustaches sont utilisées à l'enclenchement pour choisir le programme de démonstration.