LOCODUINO

Forum de discussion
Dépôt GIT Locoduino
Flux RSS

mercredi 26 juillet 2017

28 visiteurs en ce moment

Le microcontrôleur ATtiny45 (7)

Automatisme de passage à niveau

. Par : Christian Bézanger

Certaines applications pour un réseau de trains miniatures ne requièrent pas toute la puissance d’un module Arduino/Genuino Uno et on peut trouver dommage de monopoliser un tel module pour si peu. La solution est de les confier à un microcontrôleur moins puissant, donc moins coûteux. Cette série d’articles vous présente le microcontrôleur ATtiny45 du constructeur Atmel et ses possibilités dans le domaine du modélisme ferroviaire.
L’article d’aujourd’hui vous propose de construire un automatisme de passage à niveau gérant l’ouverture ou la fermeture des barrières et le clignotement de ses feux. Ce sera l’occasion d’apprendre à relier un servomoteur à un µC ATtiny45.

La bibliothèque Servo

L’IDE d’Arduino est livré avec des bibliothèques préinstallées bien pratiques pour gérer certains périphériques ; c’est le cas de la bibliothèque Servo qui permet de relier des servomoteurs à des cartes Arduino. Cette bibliothèque est décrite dans l’article Bibliothèque Servo ou bien encore à cette page du site Arduino : https://www.arduino.cc/en/Reference/Servo .

Cette bibliothèque utilise un timer de 16 bits (par exemple le timer1 d’une carte UNO) pour générer le signal de contrôle du servomoteur, comme ceci est rappelé dans l’article Les Timers (I). Pour cette raison, la PWM   n’est plus utilisable sur les broches 9 et 10 d’une carte UNO puisque cette PWM   a besoin du timer1 pour être générée.

Le microcontrôleur ATtiny45 ne dispose pas de timer de 16 bits (il a deux timers de 8 bits). La bibliothèque Servo ne peut donc pas être utilisée avec ce type de microcontrôleur. Pour s’en convaincre, on peut ouvrir le programme Sweep donné en exemple dans l’IDE (Fichier > Exemples > Servo > Sweep) et changer à la ligne 18 myservo.attach(9) ; en myservo.attach(3); puisque l’ATtiny n’a pas autant de broches. On exécute ensuite une compilation par l’option ‘Vérifier’ après avoir pris soin de définir comme type de carte l’ATtiny45 avec le menu ‘Outils’. On obtient un message d’erreur indiquant que la compilation est impossible pour cette carte, ce qui est normal puisque le microcontrôleur n’a pas de timer 16 bits et que la bibliothèque Servo n’a pas été écrite pour un µC ATtiny.

Une solution pour pallier ce problème serait de faire appel à une autre bibliothèque équivalente à Servo mais écrite pour les µC ATtiny, mais ce serait aussi une solution de facilité qui n’a guère sa place dans une série d’articles consacrés à la micro-puce ATtiny45. Nous allons donc générer nous-mêmes le signal de contrôle d’un servomoteur sur une sortie numérique de l’ATtiny45.

Signal de contrôle d’un servomoteur

En général, un servomoteur tourne entre 0 et 180° (il existe des servomoteurs capables de faire un tour complet) et c’est ce genre de servomoteur que nous allons commander pour ouvrir ou fermer les barrières de notre passage à niveau (PN). La figure 1 rappelle comment doit se présenter le signal de contrôle du servomoteur : il s’agit d’un signal périodique qui se renouvelle indéfiniment et présente une impulsion dont la largeur code la position du servomoteur. Si cette impulsion dure 1 ms (milliseconde), le servo est à la position 0°, si elle dure 1,5 ms il est à la position 90°, si elle dure 2 ms il est à la position 180°.

Figure 1


Pour positionner un servomoteur à une certaine position entre 0 et 180°, il faut donc générer une impulsion comprise entre 1 et 2 ms, selon une certaine périodicité comprise entre 10 et 20 ms et pour que le servomoteur garde la position voulue, il faut envoyer ce signal en permanence ce qui peut se faire à l’intérieur d’une boucle de programmation. Certains servomoteurs ne respectent pas tout à fait ces spécificités et ont besoin, pour explorer la plage 0 à 180°, d’impulsions plus courtes que 1 ms (soit 1000 µS) ou plus longues que 2 ms (soit 2000 µS) ; c’est pourquoi la bibliothèque Servo permet des impulsions de 544 µs à 2400 µs comme c’est expliqué dans l’article Bibliothèque Servo.

Le problème est donc de générer des impulsions sur une sortie numérique du microcontrôleur de telle sorte que ces impulsions durent entre 1000 et 2000 µs et se répètent avec une période comprise entre 10000 et 20000 µs. On peut être tenté de faire appel à la fonction delayMicroseconds() pour calibrer la durée de l’impulsion (signal à l’état HIGH) et la répétitivité des impulsions (signal à l’état LOW durant le reste du temps), mais cette solution est contraignante puisque la fonction delayMicroseconds() est bloquante tout comme la fonction delay(). Pour solutionner ce problème, nous allons nous inspirer du programme donné en exemple ‘BlinkWithoutDelay’ en utilisant la fonction micros() au lieu de millis() pour une meilleure précision. Et c’est la fonction digitalWrite qui fait passer le signal de l’état LOW à l’état HIGH ou réciproquement. On mémorise dans la variable ‘previousMicros’ le moment où on met le signal à l’état HIGH (flèche rouge sur la figure 1) et au fur et à mesure, on regarde si on a atteint la durée d’impulsion. À ce moment, on fait basculer le signal à l’état LOW jusqu’à la fin de la période souhaitée, donc pour une durée égale à (période – durée de l’impulsion). Tout cela se trouve à l’intérieur d’une boucle while pour que le signal se répète.

  1. // constantes :
  2. const int bpPin = 4; // branchement des ILS
  3. const int ServoPin = 3; // servomoteur
  4. const long period = 15000; // periode (en microsecondes; comprise entre 10 et 20 ms)
  5. const long pulseMin = 1000; // logiquement >= 1000
  6.  
  7. // variables :
  8. unsigned long previousMicros = 0; // memorise l instant du front montant
  9. long pulse = 1000; // Impulsion en microsecondes du signal
  10.  
  11. while(digitalRead(bpPin)==HIGH){
  12. // ILS non declenche
  13. pulse = pulseMin; // pour positionner barrières ouvertes
  14.  
  15. unsigned long currentMicros = micros();
  16. if(currentMicros - previousMicros >= pulse) {
  17. digitalWrite(ServoPin, LOW);
  18. }
  19.  
  20. if (currentMicros - previousMicros >= (period - pulse)) {
  21. digitalWrite(ServoPin, HIGH);
  22. previousMicros = currentMicros;
  23. }
  24. }

Automatisme de passage à niveau

Le servomoteur devra être réglé pour aller d’une position 0° (barrières ouvertes) à 90° (barrières fermées). En absence de train, les barrières sont ouvertes en permanence ; le signal est généré avec la durée d’impulsion minimum pour que sa position soit 0° à l’intérieur d’une boucle while dont on ne devrait sortir qu’à l’approche d’un train. En fait, on en sort au bout de deux secondes (avec break) pour limiter à cette valeur (’duree_signal’) la durée du signal généré, ceci pour éviter un léger frétillement du servomoteur. À ce moment-là, on entre à nouveau dans une boucle while prévue pour attendre l’approche d’un train. Celui-ci est repéré par un ILS (Interrupteur à Lames Souples) situé en amont du PN, suffisamment loin pour avoir le temps de fermer les barrières avant l’arrivée du train. Lorsque l’ILS se déclenche, on sort de la boucle pour générer un signal de positionnement avec une durée d’impulsion maximum correspondant à une position de 90°, et ceci de façon continue grâce à une troisième boucle while dont on ne devrait sortir que lorsqu’un deuxième ILS, situé suffisamment loin en aval du PN pour que tout le train soit passé, est déclenché par le train. Une fois de plus, on en sort avant pour limiter le signal généré à une durée de deux secondes. Cette troisième boucle, qui correspond aux barrières fermées, peut aussi commander le clignotement des feux de PN en s’inspirant du programme ‘BlinkWithoutDelay’ (avec la fonction millis()) ; quand on en sort au bout de deux secondes, il faut continuer à faire clignoter les DEL   jusqu’à recevoir le déclenchement du deuxième ILS indiquant que le train est passé.

Des essais sont à faire pour déterminer les bonnes durées d’impulsion pour que le servomoteur parcourt les 90° nécessaires à la manœuvre des barrières ; pour ma part, j’ai utilisé un servo Futaba S3001 avec des durées d’impulsion de 1000 µs et 2000 µs pour avoir 90° de débattement (au lieu des 180° prévues par les spécificités des servos pour ces valeurs d’impulsion). Le servo est relié sur la sortie 3 de l’ATtiny45 et c’est donc sur cette sortie qu’il faut générer le signal de contrôle.

Les deux ILS (montés en parallèle) sont reliés à l’entrée 4 de l’ATtiny ; le montage en parallèle permet de faire circuler des trains dans les deux sens. Un premier déclenchement (peu importe par quel ILS) indique qu’un train arrive (peu importe de quelle direction) et un deuxième déclenchement que ce train est passé.

Deux DEL   rouges (une pour chaque sens de circulation routière) sont reliées à la sortie 0 de l’ATtiny45 et clignotent trois fois avant que les barrières se ferment et lorsqu’elles sont fermées. On peut augmenter la durée de clignotement des DEL avant la fermeture des barrières grâce à la variable ‘nbre_cligno_avant’ (ce qui implique des ILS situés encore plus loin du PN). Les DEL s’arrêtent de clignoter dès que les barrières se rouvrent comme dans la réalité. La résistance de 150 Ω limite le courant à une dizaine de mA dans les DEL.

La figure 2 montre le montage à réaliser pour avoir l’ensemble de l’automatisme.

Figure 2


Le programme

Le programme commence par définir les constantes (broches, clignotement des DEL avant fermeture des barrières, demi-période de clignotement des DEL, périodicité du signal de contrôle, largeurs extrêmes des impulsions de commandes) puis les variables (mémorisation des moments, largeur d’impulsion). Vous pouvez bien sûr modifier ces valeurs en fonction de votre réseau.

Le setup positionne les broches du µC ATtiny45 en entrée ou en sortie.

Dans la fonction loop, la première boucle while (ligne 46) correspond au cas où le PN est ouvert. On en sort au bout de deux secondes, pour attendre l’approche d’un train (ligne 63). Lorsque celui-ci déclenche le premier ILS, on fait clignoter les DEL (ligne 68) puis on entre dans une troisième boucle while (ligne 75) correspondant aux barrières fermées et aux DEL clignotantes. On en sort au bout de deux secondes en continuant le clignotement des DEL (ligne 99) qui s’arrête lorsque le train s’est éloigné pour revenir aux conditions initiales (barrières ouvertes, DEL éteintes).

  1. /* Servo_PN_ATtiny.ino
  2.  * --------------------------------------------------------------------------------
  3.  * Servomoteur avec un ATtiny pour barrieres PN
  4.  * Fabrique sur la sortie ServoPin un signal de servomoteur avec une
  5.  * impulsion reglable de pulseMin a pulseMax (soit 1 a 2 ms) et
  6.  * avec une période de 15000 µS (soit 15 ms) qui est fixe.
  7.  * 1,0 ms ==> servo a -90°
  8.  * 1,5 ms ==> servo centré
  9.  * 2,0 ms ==> servo a +90°
  10.  * En fait, depend du modele de servo
  11.  * --------------------------------------------------------------------------------
  12.  * ILS non declenche ==> barrieres fixes (ouvertes ou fermees)
  13.  * ILS declenche ==> provoque mouvement des barrieres
  14.  * --------------------------------------------------------------------------------
  15.  * Introduit le clignotement des feux rouges de PN sur sortie ledPin
  16.  */
  17.  
  18. // constantes :
  19. const int bpPin = 4; // ILS
  20. const int ServoPin = 3; // Servo
  21. const int ledPin = 0; // DEL de feux rouges
  22. const int nbre_cligno_avant = 3; // clignotement des DEL avant fermeture barrieres
  23. const int duree_cligno = 500; // demi periode de clignotement des DEL en ms
  24. const long period = 15000; // periode (en microsecondes; comprise entre 10 et 20 ms)
  25. const long pulseMin = 1000; // logiquement >= 1000
  26. const long pulseMax = 2000; // logiquement <= 2000
  27. // Ces deux valeurs permettent un debattement de 90° du servo S3001 Futaba
  28.  
  29. // variables :
  30. unsigned long debut_boucle = 0; // memorise le debut du signal Servo
  31. unsigned long duree_signal = 2000; // signal limite en duree pour eviter fretillement
  32. unsigned long previousMicros = 0; // memorise l instant du front montant
  33. unsigned long previousMillis = 0; // memorise changement d etat de la DEL
  34. unsigned long currentMicros;
  35. unsigned long currentMillis;
  36. long pulse = 1000; // Impulsion en microsecondes du signal, de pulseMin a pulseMax
  37.  
  38. void setup() {
  39. pinMode(bpPin, INPUT_PULLUP);
  40. pinMode(ServoPin, OUTPUT);
  41. pinMode(ledPin, OUTPUT);
  42. }
  43.  
  44. void loop() {
  45. debut_boucle = millis();
  46. while(digitalRead(bpPin)==HIGH){
  47. // ILS non declenche
  48. pulse = pulseMin;
  49.  
  50. currentMicros = micros();
  51. if(currentMicros - previousMicros >= pulse) {
  52. digitalWrite(ServoPin, LOW);
  53. }
  54.  
  55. if (currentMicros - previousMicros >= (period - pulse)) {
  56. digitalWrite(ServoPin, HIGH);
  57. previousMicros = currentMicros;
  58. }
  59. if(millis() - debut_boucle >= duree_signal){
  60. break; // on arrete le signal après 2 secondes
  61. }
  62. }
  63. while(digitalRead(bpPin)==HIGH) {
  64. // attendre
  65. }
  66.  
  67. // Ici, ILS declenche : clignotement des DEL
  68. for(int i = 1; i <= nbre_cligno_avant; i++) {
  69. digitalWrite(ledPin, HIGH);
  70. delay(duree_cligno);
  71. digitalWrite(ledPin, LOW);
  72. delay(duree_cligno);
  73. }
  74. debut_boucle = millis();
  75. while(digitalRead(bpPin) == HIGH) {
  76. currentMillis = millis();
  77. pulse = pulseMax;
  78.  
  79. currentMicros = micros();
  80. if(currentMicros - previousMicros >= pulse) {
  81. digitalWrite(ServoPin, LOW);
  82. }
  83.  
  84. if (currentMicros - previousMicros >= (period - pulse)) {
  85. digitalWrite(ServoPin, HIGH);
  86. previousMicros = currentMicros;
  87. }
  88.  
  89. // Clignotement des DEL rouges
  90. if (currentMillis - previousMillis >= duree_cligno) {
  91. digitalWrite(ledPin, !digitalRead(ledPin));
  92. previousMillis = currentMillis;
  93. }
  94. if(millis() - debut_boucle >= duree_signal) {
  95. break; // on arrete le signal apres 2 secondes
  96. }
  97. }
  98.  
  99. while(digitalRead(bpPin)==HIGH) {
  100. // DEL continuent a clignoter
  101. currentMillis = millis();
  102. if(currentMillis - previousMillis >= duree_cligno) {
  103. digitalWrite(ledPin, !digitalRead(ledPin));
  104. previousMillis = currentMillis;
  105. }
  106. }
  107. // Declenchement ILS
  108. delay(500); // anti-rebond
  109. digitalWrite(ledPin, LOW);

    // Extinction des DEL rouges
  110. // ouverture des barrières se fera en debut de fonction loop
  111. }

Télécharger

La procédure de programmation de l’ATtiny45 est expliquée dans l’article Le microcontrôleur ATtiny45 (2)

Nota : En limitant à deux secondes la durée du signal généré sur la broche 3 pour contrôler le servomoteur, on évite un léger frétillement des barrières en position ouverte ou fermée. Ce laps de temps est réglable par l’intermédiaire de la variable ‘duree_signal’ pour que le mouvement des barrières soit terminé avant de couper le signal de contrôle (cas d’un servomoteur tournant plus lentement). Dans ce montage, il peut être nécessaire de prévoir une alimentation pour le servomoteur séparée de l’alimentation de l’électronique ; cela dépend de la consommation du servomoteur que vous utilisez. Dans ce cas, seules les masses des deux alimentations sont à relier ensemble (voir le cours d’électronique en téléchargement libre dans l’article Démarrer en électronique).

Conclusion

Le montage proposé ici nous a permis de connecter un servomoteur sur un simple ATtiny45 et de développer un automatisme de passage à niveau très réaliste puisque les feux clignotent avant la fermeture des barrières jusqu’à leur réouverture. La vidéo suivante vous montre l’effet obtenu :

On pourrait sans doute reprocher que le mouvement des barrières est un peu rapide ; ceci peut être réglé en provoquant un débattement de 180° du servomoteur et en adjoignant une démultiplication du mouvement par un facteur deux grâce à des engrenages ou de simples roues à gorge. C’est juste un problème mécanique qui peut facilement se résoudre. Il reste deux sorties de disponibles sur l’ATtiny45 dont une pourrait servir à déclencher un système reproduisant la sonnerie du PN ; ceux qui aiment les ambiances sonores ont donc encore du pain sur la planche…

Le passage à niveau décrit dans cet article est conçu pour une ligne à voie unique parcourue dans les deux sens. Les modélistes souhaitant un passage à niveau pour traverser plusieurs voies se reporteront aux articles Un automatisme de Passage à Niveau et Etude d’un passage à niveau universel qui décrivent une solution plus universelle.

On peut aussi remarquer que ce montage, qui positionne un servomoteur avec un débattement de 90°, peut aussi servir à ouvrir les portes d’une remise à l’approche de la locomotive, ou encore les grilles d’une propriété lorsque le maître des lieux arrive avec son cabriolet.

On a déjà fait beaucoup de choses avec ce simple petit microcontrôleur et d’autres restent encore à faire.

Réagissez à « Le microcontrôleur ATtiny45 (7) »

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)

Rubrique « Matériel »

Fonctionnement et pilotage d’une DEL

Qu’est ce qu’une carte Arduino ?

Amplifier le signal de sortie d’un ARDUINO avec un ULN 2803

Résistances, kézako ?

Relais électromagnétique

Les diodes classiques

Détecteurs à ultrasons

La carte Arduino Uno

Bouton poussoir

Les différents types de mouvements d’un servo-moteur

Les encodeurs en quadrature

Les écrans LCD alphanumériques

Des bus de communication pour l’Arduino

Les interrupteurs

Signaux lumineux et Arduino

Les shields de prototypage et de connexion

Commande de moteur à courant continu

Les derniers articles

Le microcontrôleur ATtiny45 (7)


Christian Bézanger

Commande de moteur à courant continu


Christian Bézanger

Le microcontrôleur ATtiny45 (6)


Christian Bézanger

Les shields de prototypage et de connexion


Christian Bézanger

Le microcontrôleur ATtiny45 (5)


Christian Bézanger

Signaux lumineux et Arduino


Christian Bézanger

Le microcontrôleur ATtiny45 (4)


Christian Bézanger

Le microcontrôleur ATtiny45 (3)


Christian Bézanger

Le microcontrôleur ATtiny45 (2)


Christian Bézanger

Le microcontrôleur ATtiny45 (1)


Christian Bézanger

Les articles les plus lus

Commande de moteur à courant continu

Les écrans LCD alphanumériques

La carte Arduino Uno

Amplifier le signal de sortie d’un ARDUINO avec un ULN 2803

Les diodes classiques

Qu’est ce qu’une carte Arduino ?

Signaux lumineux et Arduino

Bouton poussoir

Les shields de prototypage et de connexion

Des bus de communication pour l’Arduino