Tuto Step motor pas à pas routine interruption frequence

Les posts sur les moteurs pas à pas sont nombreux sur le forum mais peu de tutos
Driving Stepper Motor with Interrupt

**• En effet, il existe 3 types de drivers pour moteur pas à pas : **

De plus, il y a un souvent un paradoxe sur la commande du moteur pas à pas qui est un moteur relativement lent mais dans une table traçante, on aimerait qu’il soit le plus rapide possible mais sans perte de pas.
par contre, le moteur pas à pas permet de faire une vitesse tres lente et tres precise (mode demi pas ou micro pas) sans reducteur à engrenages.

Sur ce post, nous allons se tenir à la programmation pour contrôler le moteur en générant une fréquence avec un timer en routine d’interruption avec différents objectifs :

  • En commandant sa vitesse par 2 boutons poussoirs,
  • Puis demander un position en limitant à une certaine vitesse,
  • Pour vérifier, la programmation, en utilisant un potentiomètre multi tour qui sera mis sur l’arbre moteur pour vérifier la position effectuée
  • Faire un profil trapézoïdal de vitesse avec certaine accélération pour effectuer un positionnement désiré
  • ….

Il est possible de simuler la programmation du moteur pas à pas avec ISIS, mais on n’a pas la vitesse du moteur.
ISIS permet de visualiser les entrées et les sorties
Nous allons utiliser un Arduino nano, mais pourquoi pas un ESP32….pour faire un tuto
**

**

Un debut de programmation pour commander le moteur en frequence

//#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>

#define led13   13       // 13

unsigned int temps=0;        //      Frequence=1/ (T2*0.1ms*2)   
unsigned int T2=10;          // si T2=10  Frequence=1/(10*0.1ms*2)=500Hz     
                           // si T2=100  Frequence=1/(100*0.1ms*2)=50Hz 

float Vbatt;
bool Start=0;
bool direct=0;


void callback()  {    //0.1ms
  temps++;
  
 if (Start==1 && temps>=T2 &&  digitalRead(7)==1)  {digitalWrite(7,0);temps=0;}
 if (Start==1 && temps>=T2 &&  digitalRead(7)==0)  {digitalWrite(7,1);temps=0;}
 
 
}  //fin callback



void setup() {
pinMode (2, INPUT_PULLUP);   //incrementation
pinMode (3, INPUT_PULLUP);    //decrementation
pinMode (4, INPUT_PULLUP);    //start
pinMode (5, INPUT_PULLUP);    //sens horaire et antihoraire   Direction pin

pinMode(led13, OUTPUT); 
pinMode(12, OUTPUT);  
pinMode(7, OUTPUT);      //clock

//Serial.begin(115200);
Serial.begin(57600);
//Serial.begin(19200);


 Timer1.initialize(100);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
 Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt

         //      Frequence=1/ (T2*0.1ms*2)   
           // si T2=10  Frequence=1/(10*0.1ms*2)=500Hz     
           // si T2=100  Frequence=1/(100*0.1ms*2)=50Hz          
}


void loop() { 
  if ( digitalRead(13)== 1 ) {digitalWrite(13,0);}  else {digitalWrite(13,1);}      //duree du programme 2ms en 57600 bauds


if (digitalRead(5)==0 && direct==1 )  {digitalWrite(12,0);direct=0;delay(300);}    //sens horaire   
if (digitalRead(5)==0 && direct==0 )  {digitalWrite(12,1);direct=1;delay(300);}    //sens anti horaire

if (digitalRead(2)==0)  {T2--;delay(100);}     //incrementation vitesse
if (digitalRead(3)==0)  {T2++;delay(100);}     // vitesse
if (T2>1000)    {T2=1000;}      //Fmini=0.5Hz
if (T2<=1)     {T2=1;}          //Fmaxi=5000Hz

if ( digitalRead(4)==0 && Start==0 )   {Start=1;delay(200);} 
if ( digitalRead(4)==0 && Start==1 )   {Start=0;delay(200);}    //stop
Serial.print(temps);Serial.print(";");              
Serial.print(T2);Serial.println(";");                         
  
} // fin loop  

Donc, 3 groupes d’étudiants vont mettre leurs programmations pour faire des tutos pour repondre aux differents objectifs
en image le driver DRI0043 qui est commandable en frequence


en image le driver EM4422S qui est commandable en frequence et en liaison serie

en effet, Il est facile à commander un moteur pas à pas avec une fonctionnement start/stop ou on/off
Connaitre, cette zone de vitesse de fonctionnement sans perte de pas n’est pas si facile car elle dépend du couple de charge qui est peu connu.
Pour connaitre cette zone de vitesse start/stop, il faut faire des changements de direction pour savoir si l’on revient toujours à la position initiale et ne pas avoir de perte de pas. Donc, faire de nombreux essais (apprentissage de la vitesse max)
Donc, il faut un programme qui fasse de la vitesse variable.
Pour atteindre des vitesses au-delà de cette zone de Start stop, il faut un programme qui fasse un profil de mouvement trapézoïdal avec des accélérations constantes ou il faut encore faire de nombreux essais pour vérifier qu’il n’y a pas de perte de pas.
Souvent, les fabricants de moteur et de driver expliquent la relation vitesse et perte de pas


mais il y a aussi des courbes en S de la vitesse pour minimiser les accelerations

3 Likes

Merci, pour vos éloges

Mais, on a que 20 heures de temps de travail sur cette commande de moteur pas à pas….

On va faire ce que l’on peut…..

Notre moteur est un Nema de dimension 575656mm avec un couple de 1.2N.m pour un courant de 3A avec un pas de 1.8°


Ces caractéristiques sont les suivantes

Dont la courbe du couple en fonction de la vitesse est la suivante,
On peut observer que le couple diminue en fonction de la vitesse à cause de la tension interne contre électromotrice proportionnelle à la vitesse qui fait diminuer le courant du moteur donc son couple.

La puissance mécanique développée par le moteur étant le couple fois la vitesse de la rotation mais en rad/s va diminuer lorsque la vitesse est supérieure à 150RPM


La relation de la vitesse en tr/min par rapport à la fréquence en mode pas correspond à l’équation la suivante

N=60 *1.8° *F(Hz)/360°

Exemple pour 1000Hz, la vitesse sera 300rpm

Pour ne pas avoir de perte de pas avec un démarrage Start, le couple d’accélération du moteur demandé correspond à l’équation suivante avec une inertie de 0.028kg.m^2


Avec la courbe du couple du constructeur, on peut observer que la vitesse max sera autour de 500tr/mn, donc fréquence max de 1650Hz mais en pratique le moteur ne peut aller à plus à 300tr/mn

Le rendement (puissance utile/puissance absorbée) des moteurs pas à pas est faible inférieur à 50%, c’est pour cela que le moteur existe seulement en faible puissance.
Sur le DRI0043, le courant indiqué est pour les 2
Pour un réglage de courant avec les switches pour une tension d’alimentation de 24V, à fréquence 0Hz, température ambiante 20°C, voici les valeurs


En mode pas, sous une tension 24V, avec réglage du courant de limitation à 3.5A du driver, voici les mesures de puissance et de courant

On retrouve bien la puissance maximale du moteur aux alentours de 300tr/min puis elle décline fortement avec l’augmentation de la vitesse

le courant alimentation dans les basses vitesses correspond à l'equation suivante

a cause des pertes dans le moteur pas à pas, celui-ci chauffe de façon relativement importante ainsi que le driver
Exemple pour une temperature ambiante de 18°C

A la place de faire varier la période ce qui peut être long, à cause de T2 qui peut varier de 5 à 5000 fois 0.1s le temps de delays d’anti rebond du bouton poussoir.

Autant faire varier la vitesse et que le programme calcul la période.

Mais il y aura une troncature effectuée sur la vitesse. En effet, si la précision dans les basses vitesses est très bonne, elle est faible autour de 300RPM

Evidement avec un moteur pas à pas, l’objectif est de faire de la position.

Donc voici le programme qui compte la position et fait 45 et -45° en permanence

Dans ISIS, il est possible d’avoir des courants énormes sans détruire les composants avec un moteur faible résistance et sans erreur indiquée.

Avec une constante de temps faible électrique 1 ohms et résistance 0.1mH, la simulation de la position fonctionne bien à une vitesse de 100tr/min, mais pas au-delà de 150tr/min cela ne fonctionne plus.

Est-ce que s’est dû au pas de calcul de la simulation ? ou au paramétrage électrique ?

Le paramétrage de l’inertie et de la constance de couple moteur et celui de la charge ne pas être défini dans ISIS.

Pourtant, le paramétrage mécanique peut être fait dans ISIS pour les moteur DC.

Mais, l’objectif de la simulation est de valider le programme.

#include <SoftwareSerial.h>
#include <TimerOne.h>

#define led13   13       // 13

unsigned int temps=0;
unsigned int temps1=0;        
unsigned int T2;             // 
                           
unsigned int RPM=100;   //10 equivaut à 1 tour/minute
unsigned int RPM1; 

int positionref;   //postionreference
int positionmax=50;      // nbr de pas      1 tour=360°/1.8°=200pas
int positionmini=-50;    //50 pas equivaut à 90°   et 25 pas  à 45°


bool Start=0;
bool directio=0;     //0 equi sens anti horraire

bool envoie=1;




void callback()  {    //routine interruption 0.1ms
  temps++;    //temps pour vitesse du moteur
  temps1++;   //
  
 
 if (Start==1 && temps>=T2 &&  digitalRead(7)==0)  {digitalWrite(7,1);temps=0;envoie=1;             //impulsion pour faire 1 pas  durant 0.2ms
                                                   if (directio==1) {positionref=positionref+1;} else {positionref=positionref-1;}  
                                                  }

 if (temps>=2 &&  digitalRead(7)==1)  {digitalWrite(7,0); }       //remise à zero de l'impulsion                                                   
                                                                                                
                                                  
if (positionref>=positionmax) {digitalWrite(12,0);directio=0;}   //inversion du sens de rotation
if (positionref<=positionmini) {digitalWrite(12,1);directio=1;}
  
}  //fin callback



void setup() {
pinMode (2, INPUT_PULLUP);   //incrementation
pinMode (3, INPUT_PULLUP);    //decrementation
pinMode (4, INPUT_PULLUP);    //start
pinMode (5, INPUT_PULLUP);    //sens horaire et antihoraire   Direction pin

pinMode(led13, OUTPUT); 
pinMode(12, OUTPUT);  
pinMode(7, OUTPUT);      //clock

//Serial.begin(115200);
Serial.begin(57600);
//Serial.begin(19200);


 Timer1.initialize(100);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
 Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt

        
}


void loop() { 
  if ( digitalRead(13)== 1 ) {digitalWrite(13,0);}  else {digitalWrite(13,1);}      //duree du programme 2ms en 57600 bauds

if ( digitalRead(4)==0 && Start==0 )   {Start=1;delay(200);}    //start
if ( digitalRead(4)==0 && Start==1 )   {Start=0;delay(200);}    //stop

if (digitalRead(5)==0 && directio==1 )  {digitalWrite(12,0);directio=0;delay(300);}    //sens horaire   direction
if (digitalRead(5)==0 && directio==0 )  {digitalWrite(12,1);directio=1;delay(300);}    //sens anti horaire

if (digitalRead(2)==0)  {RPM=RPM+10;delay(100);}     //incrementation vitesse désirée
if (digitalRead(3)==0)  {RPM=RPM-10;delay(100);}     // vitesse

if (RPM==0) {Start==0;RPM=100}   //ne pas mettre une vitesse nulle pour le calcul de T2

if (RPM>=3000)    {RPM=3000;}      //equivalent à 300 tr/min pour ne pas utiliser de flaot 
if (RPM<=1)   {RPM=1;}      //equivalent à 0.1 tr/min 
T2=(15000/RPM)  ;     //1500=60*1.8/360*0.1ms*2
RPM1=1500/T2;       //vrai valeur de vitesse avec la troncature

if (envoie==1) {
Serial.print(RPM1); Serial.print(","); Serial.print(positionref); Serial.print(","); Serial.print(directio);Serial.print(",");Serial.print(T2);
Serial.println(";"); envoie=0;             
                }     
  
} // fin loop  

En général, le moteur doit faire des mouvements de vitesse séquentiellement donc en fonction du temps.

Ce sera notre prochaine étape de programmation

De plus ayant mis, un potentiomètre multi tour sur l’arbre moteur qui permet de vérifier le positionnement simplement sans mettre de fin de course. Ce positionnement analogique sera lu par le microcontrôleur.

3 Likes

Bonjour iutgeiisoissons

Je ne comprends pas l'utilité d'un potentiomètre multi-tours pour contrôler la position d'MPAP, ça diminue le nombre de tours possibles du moteur (généralement 10 tours), c'est compliqué à mettre en œuvre, ce n'est pas précis. La position du MPAP est connue vu que c'est nous que l'on fait le programme et pour ce qui est du fin de course, c'est beaucoup simple à implémenter.
Bref, contrôler du digital avec de l'analogique?

Ou alors je n'ai rien compris :woozy_face: :wink:

Cordialement
jpbbricole

Cela permet de relever la position absolue avec une certaine précision.

@iutgeiisoissons
une petite coquille dans cette phrase

J'écrirai une "certaine" précision.
Il est plus utile d'avoir un contact de position 0 et surtout plus simple à mettre en œuvre.
Les moteurs en boucle fermée utilisent des codeurs incrémentaux.

Il ne faudrait pas oublier que c’est une l’étude réalisée sous la direction de pédagogues.

Leur but n’est pas de faire une réalisation adaptée à un usage réel, mais de faire réfléchir les élèves et de leur faire bien comprendre comment un moteur p a p fonctionne.

Dans cette situation il est très classique d’ajouter des composants non essentiels au fonctionnement ”normal”, mais qui permettent d’obtenir des compléments d’information.

La contribution est dans tuto et cours, elle n’est pas dans Réalisations finies.

Un codeur incrémental ne donne qu'une position relative. Comment veux-tu enregistrer et vérifier en temps réel la position en sortie de ton asservissement. Les codeurs optiques absolus coûtent 2 bras.
C'est un "devoir" en IUT. D'un coté tu as un cours théorique sur les asservissements, de l'autre tu appliques et tu vérifies que ça colle (ou pas) avec la théorie. Ils font un asservissement, et vérifient son comportement pour cela il faut connaitre en permanence, la consigne et l'état en sortie.
Lorsque tu bricoles un asservissement sur ton coin d'établi tu ne cherches pas à mesurer le temps de réaction, le dépassement, l'amortissement, la réponse à un échelon, etc. Lorsqu'on étudie un asservissement, aussi bien à l'école que dans l'industrie, ce sont des mesures nécessaires.

effectivement, l’objectif du moteur pas à pas est d’être utilisé sans contre réaction….

Je me suis mal exprimé dans le potentiomètre multi tour, car celui utilisé est surnommé « rotatif » ou « sans fin » …quand il a fait un tour, il revient à la valeur 0 ohm. C’est mieux car un défaut de programmation détruirait le multi tour….

Les potentiomètres ont été récupéré car ils ne sont pas donnés.

Le potentiomètre est juste là pour vérifier qu’il n’y a pas de perte de pas, ni de résonnance mécanique…d’observer le fonctionnement en micro pas….sur oscilloscope ou par le micro via une entrée analogique

Mais 1.8°, équivaut à une variation 2.8 numérique via la valeur en analogique pour Atmel 328 car la précision du convertisseur analogique est seulement de 1024.

donc 68tjs et fdufnews ont tous 2 raisons, c’est juste pour faire de la vérification, de l’optimisation….

Ne rien entrainer mécaniquement est très idiot, mais là où on programme, ce n’est pas la salle de réalisation……

Remarque :

On a déjà réalisé une implante 3D et une machine à graver avec ces mêmes moteurs….mais commander par un automate Siemens et Tia portal…..

2 Likes

Les moteurs pas à pas ont souvent des résonnances mécaniques à cause de leur fort couple entrainant des pertes de pas pour une petite plage de vitesse. Ces résonnances peuvent intervenir lors des décélérations ou des accélérations.
Donc, la commande doit sauter cette plage de vitesse.
https://www.portescap.com/fr-FR/ressources/motor-specifications-and-literature/livres-blancs/prévention-de-la-résonance-avec-des-moteurs-pas-à-pas
En mode micro pas, il y a une souvent une atténuation de ce phénomène de résonnance.

Dans notre cas, il a fallu passer la plage 30 tr/min à 74tr/mini
Mais il faut connaitre la condition, si c’est le bouton poussoir « augmentation de vitesse » ou « diminution de vitesse » qui est actionné

if  (RPM>150 &&  RPM<370 && augmentation==1)  {RPM=370;augmentation=0;}   //oscillation évite cette plage de vitesse
if  (RPM>150 &&  RPM<370 && diminution==1)  {RPM=150;diminution=0;}

Avec un moteur pas à pas, il faut faire un profil de position alors qu’un moteur DC, il faut faire un profil de vitesse.

Le potentiomètre permet de visualiser le déplacement.
Lorsque le moteur a fait un tour la tension analogique repasse de 1024 à 0 décimal.
Pour visualiser les changements des vitesses, on envoie la valeur de la tension analogique sur la laissions série ainsi que l’incrémentation du temps.
Un profil de position est demandé est de 3.25 tour en 3.5 secondes

//profil automatique de position 
if (Start==1 && (positionref<=100))    {RPM=150;}             //100*1.8°=180°     30 tr/min   0.5tr/s   donc 1seconde
if (Start==1 && positionref>100  && positionref<=400 )    {RPM=450;}   //90 tr/min    1.5tr/s            donc 1s
if (Start==1 && positionref>400  && positionref<=600)    {RPM=600;}   //120 tr/min    2tr/s              donc 0.5s
if (Start==1 && positionref>600  && positionref<=650)    {RPM=75;}    //0.25tr/s                            donc 1s 
if (Start==1  && positionref>650)                {Start=0;}

pour être tracer des courbes comme oscilloscope, Il suffit de copier les données du moniteur série et crer un fichier à l’extension .csv :smile:

Pour de créer un fichier .csv pour montrer comment agit le moteur

Faire un ctrl+A pour valider toutes les données de moniteur serie

Faire un ctrl+C pour les copier

Faire un ctrl+V pour les coller dans un traitement texte (bloc note), mais enregistrer avec une extension .CSV

Ouvrir avec tableur le fichier .csv (Excel, google sheet….)

Faire un ctrl+A pour sélectionner toute les données dans le tableur puis menu insertion, graphique, le mieux est nuage de point (X, Y)

Sur le graphique sélectionner les données…pour modifier le nom des courbes….

Le moniteur série du compilateur version infeirue à 2.x est indépendant du compilateur, donc il est possible de faire une sélection

Par contre le moniteur du compilateur Arduino version 2.x n’est plus indépendant et les raccourcis de sélection n’est pas possible.

La solution est de télécharger un moniteur série au PC

On peut observer que les temps sont bien respectés en fonction de la position demandée.
la base de temps est de 0.1ms venant de la routine d'interruption, donc 10000 correspond à 1seconde

On peut observer qu’il y a quelques parasites sur la valeur analogique de la position par le potentiomètre mais qui est rattrapé après…donc sans conséquence sur la globalité de la position effectuée par le moteur.

Pour aller d’une position à une autre et changer de sens, il suffit de modifier la direction comme le code suivant

if (positionref>=positionmax) {digitalWrite(12,0);directio=0;}   //inversion du sens de rotation automatique
if (positionref<=positionmini) {digitalWrite(12,1);directio=1;}

Voici le code global qui permet de faire le profil de position en fonction de la vitesse est le suivant

#include <SoftwareSerial.h>
#include <TimerOne.h>

#define led13   13       // 13

unsigned int temps=0;
unsigned long temps1=0;    
   
unsigned int T2=150;             // 
unsigned int valeur;       //analogique
                           
unsigned int RPM=100;   //100 equivaut à 20 tour/minute
unsigned int RPM1; 

int positionref;   //postionreference
int positionmax=50;      // nbr de pas      1 tour=360°/1.8°=200pas
int positionmini=-50;    //50 pas equivaut à 90°   et 25 pas  à 45°


bool Start=0;
bool directio=0;     //0 equi sens anti horraire

bool envoie=1;

bool augmentation=0;
bool diminution=0;

bool Position=0;


void callback()  {    //routine interruption 0.1ms
  temps++;    //temps pour vitesse du moteur
  temps1++;   //permet de tracer comme un oscillocope  dans un fichier .csv avec excel via le moniteur serie
  
 
 if (Start==1 && temps>=T2 &&  digitalRead(7)==0)  {digitalWrite(7,1);temps=0;envoie=1;             //impulsion pour faire 1 pas  durant 0.2ms
                                                   if (directio==1) {positionref=positionref+1;} else {positionref=positionref-1;}  
                                                  }

 if (temps>=2 &&  digitalRead(7)==1)  {digitalWrite(7,0); }       //remise à zero de l'impulsion                                                   


//profil automatique de position 
if (Start==1 && (positionref<=100))    {RPM=150;}             //100*1.8°=180°     30 tr/min   0.5tr/s   donc 1seconde pour faire la position
if (Start==1 && positionref>100  && positionref<=400 )    {RPM=450;}   //90 tr/min    1.5tr/s            donc 1s
if (Start==1 && positionref>400  && positionref<=600)    {RPM=600;}   //120 tr/min    2tr/s              donc 0.5s
if (Start==1 && positionref>600  && positionref<=650)    {RPM=75;}    //0.25tr/s                       donc 2s 
if (Start==1  && positionref>650)                {Start=0;}

 

// aller et retour  d'une posiotn max à un position mini                                                                                                                                          
//if (positionref>=positionmax) {digitalWrite(12,0);directio=0;}   //inversion du sens de rotation automatique
//if (positionref<=positionmini) {digitalWrite(12,1);directio=1;}

T2=(15000/RPM)  ;     //1500=60*1.8/360*0.1ms*2
RPM1=3000/T2;       //vrai valeur de vitesse avec la troncature
  
}  //fin callback



void setup() {
pinMode (2, INPUT_PULLUP);   //incrementation
pinMode (3, INPUT_PULLUP);    //decrementation
pinMode (4, INPUT_PULLUP);    //start
pinMode (5, INPUT_PULLUP);    //sens horaire et antihoraire   Direction pin

pinMode(led13, OUTPUT); 
pinMode(12, OUTPUT);  
pinMode(7, OUTPUT);      //clock

//Serial.begin(115200);
Serial.begin(57600);
//Serial.begin(19200);


 Timer1.initialize(100);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
 Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt

} // fin initialisation
   




void loop() { 

  if ( digitalRead(13)== 1 ) {digitalWrite(13,0);}  else {digitalWrite(13,1);}      //duree du programme 2ms en 57600 bauds

if ( digitalRead(4)==0 && Start==0 )   {Start=1;delay(200);temps1=0;positionref=0;directio=1;}    //start
if ( digitalRead(4)==0 && Start==1 )   {Start=0;delay(200);}    //stop

if (digitalRead(5)==0 && directio==1 )  {digitalWrite(12,0);directio=0;delay(300);}    //sens horaire   direction
if (digitalRead(5)==0 && directio==0 )  {digitalWrite(12,1);directio=1;delay(300);}    //sens anti horaire

if (digitalRead(2)==0)  {RPM=RPM+10;delay(100);augmentation=1;}     //incrementation vitesse désirée
if (digitalRead(3)==0)  {RPM=RPM-10;delay(100);diminution=1;}     // vitesse



if (RPM<=0) {RPM=10;}   //ne pas mettre une vitesse nulle pour le calcul de T2
if (RPM<=10)   {RPM=10;}      //equivalent à 0.1 tr/min 
if (RPM>=1300)    {RPM=1300;}      //3000 equivanent à 300 tr/min pour ne pas utiliser de flaot 

if  (RPM>150 &&  RPM<370 && augmentation==1)  {RPM=370;augmentation=0;}   //oscillation evite cette plage de vitesse
if  (RPM>150 &&  RPM<370 && diminution==1)  {RPM=150;diminution=0;}






if (envoie==1) {
valeur = analogRead(0);
// Serial.print(T2);Serial.print(";"); Serial.print(positionref); Serial.print(";"); Serial.print(directio);Serial.print(";");Serial.print(RPM1);
Serial.print(temps1);Serial.print(";");Serial.print(valeur);Serial.print(";");Serial.print(RPM1);Serial.print(";");Serial.print(positionref);       //fichier .csv
Serial.println(";"); envoie=0;             
                }     
  
} // fin loop  

3 Likes

Sur notre système d’expérimentation de moteur pas à pas, il n’y a pas de fin de course ou de « sensor de proximity » pour aller chercher une position de référence ou de départ….
Par contre, il est possible de mettre la position du potentiomètre à 0 décimal, au démarrage dont voici le code

if (reference==1)  {      //mettre le potentiometre à 0
valeur = analogRead(0);
if (valeur>1 && valeur<=1024) {Start=1;} else {Start=0;reference=0;}
                  }

Avec le profil de position et le potentiomètre, il est possible de connaitre la vitesse max admissible.
Dans notre cas, on peut voir que la vitesse max possible est de 600tr/min mais pas au dela, le moteur est bloqué.

//profil automatique de position 
if (Start==1 && (positionref<=100))    {RPM=150;}             //100*1.8°=180°     30 tr/min   0.5tr/s   donc 1seconde pour faire 1/2 tour
if (Start==1 && positionref>100  && positionref<=400 )    {RPM=600;}   //120 tr/min    2tr/s            donc 0.75s  pour faire 3/2 tour
if (Start==1 && positionref>400  && positionref<=600)    {RPM=1200;}   //240  tr/min   4tr/s            donc 0.25s pour faire 1 tour
if (Start==1 && positionref>600  && positionref<=800)    {RPM=1800;}   //360tr/min     6tr/s            donc 0.16s pour faire 1 tour
if (Start==1 && positionref>800  && positionref<=1000)    {RPM=2400;}   //480tr/min    8tr/s            donc 0.125s pour faire 1 tour
if (Start==1 && positionref>1000  && positionref<=1200)    {RPM=3000;}  //600tr/min   10tr/s            donc 0.1s pour faire 1 tour
if (Start==1 && positionref>1200  && positionref<=1400)    {RPM=600;}
if (Start==1  && positionref>1400)                {Start=0;}

La durée des instructions pour la routine d’interruption est de 0.04ms donc inferieur au temps de cette routine qui est de 0.1ms
Le temps de la boucle loop est de 0.2ms mais la communication serie demande 1.6ms lors d’un envoie.

4 Likes

Présentation des Boutons Poussoirs en Pont Diviseur sur Arduino et programme arduino

Dans le cadre de mon SAE sur le contrôle d'un moteur pas à pas, j'ai intégré un module LCD Keypad Shield pour simplifier l'interface utilisateur. Ce shield inclut un écran LCD 16x2 et plusieurs boutons (Right, Up, Down, Left, Select), tous connectés sur une seule entrée analogique (A0) via un pont diviseur de tension.

Fonctionnement des Boutons en Pont Diviseur

L'avantage d'utiliser un pont diviseur est de réduire l'utilisation de broches numériques sur l'Arduino. Au lieu de connecter chaque bouton sur une entrée distincte, ils sont tous reliés à une seule entrée analogique (A0). Grâce à une chaîne de résistances, chaque bouton génère une tension unique lorsqu'il est pressé, permettant ainsi à l'Arduino de détecter quel bouton est activé.

Principe de Fonctionnement :

  • Les boutons sont configurés en série avec des résistances de valeurs différentes.
  • Lorsque vous appuyez sur un bouton, il crée un circuit fermé avec une résistance spécifique, générant ainsi une tension différente sur A0.
  • En lisant la valeur analogique via analogRead(A0), l’Arduino peut déterminer quel bouton est pressé en fonction de la plage de valeurs obtenue.

Voici les valeurs typiques obtenues pour chaque bouton, selon la documentation :

  • No Press : 5V (aucun bouton pressé)
  • Right : environ 0.71V
  • Up : environ 1.61V
  • Down : environ 2.47V
  • Left : environ 3.62V
  • Select : environ 4.5V

Utilisation dans Mon Projet

Dans mon projet, chaque bouton a une fonction spécifique pour contrôler le moteur :

  • Up : Augmente la vitesse du moteur.
  • Down : Diminue la vitesse.
  • Left : Change la direction du moteur en sens antihoraire.
  • Right : Change la direction en sens horaire.
  • Select : Démarre ou arrête le moteur.

La lecture des valeurs analogiques sur A0 permet de gérer ces fonctions avec un code simple qui interprète la tension mesurée pour déclencher l'action appropriée.

Conclusion

L’utilisation d’un pont diviseur de tension avec les boutons du LCD Keypad Shield est un moyen efficace de maximiser l’utilisation des broches de l’Arduino, surtout dans des projets nécessitant plusieurs entrées pour le contrôle de fonctionnalités avancées, comme la commande de moteurs pas à pas.

  • Anti-rebond des boutons (delay(300);) : Après chaque appui sur les boutons UP et DOWN, on utilise delay(300); pour éviter les multiples lectures dues au rebond mécanique du bouton. Cela garantit qu'un seul appui est pris en compte, même si le bouton "rebondit" pendant quelques millisecondes.
  • Contrôle de la vitesse du moteur (delayMicroseconds()) : Dans la fonction stepMotor(), delayMicroseconds() ajuste le temps entre les impulsions envoyées au moteur. Plus motorSpeed est élevé, plus les impulsions sont rapprochées, ce qui augmente la vitesse du moteur. Cette pause détermine la fréquence des impulsions et donc la vitesse de rotation du moteur.

En résumé, delay(300); stabilise les lectures des boutons, et delayMicroseconds() contrôle la vitesse du moteur en ajustant la fréquence des impulsions.

Programme arduino :

#include <LiquidCrystal.h>
#include <TimerOne.h>

// Initialisation de l'écran LCD
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// Définition des broches pour le driver du moteur
const int dirPin = 12; // DIR- pour la direction
const int pulPin = 7;  // PUL- pour les impulsions

// Broche pour la lecture des boutons
const int buttonPin = A0;

int motorSpeed = 500;  // Vitesse initiale (en Hz)
const int maxSpeed = 1024; // Vitesse maximale
const int minSpeed = 100;  // Vitesse minimale
bool motorDirection = true; // true = sens horaire, false = sens antihoraire
bool motorRunning = false;  // Etat du moteur (marche/arrêt)

// Variables pour la gestion des impulsions
volatile bool pulseState = false; // État de la broche d'impulsion
int normalSpeed = 500;  // Stocke la vitesse normale du moteur

void togglePulse() {
  if (motorRunning) {
    // Générer les impulsions pour le moteur
    pulseState = !pulseState;
    digitalWrite(pulPin, pulseState);
  } else {
    digitalWrite(pulPin, LOW); // Arrête les impulsions si le moteur est désactivé
  }
}

// Fonction pour lire l'état des boutons et ajuster direction, vitesse et marche/arrêt
void readButtons() {
  int buttonValue = analogRead(buttonPin);
  
  if (buttonValue < 100) {
    motorDirection = true; // RIGHT: sens horaire
    digitalWrite(dirPin, HIGH);
    lcd.setCursor(0, 1);
    lcd.print("Direction: Droite ");
  } else if (buttonValue < 300) {
    motorSpeed = min(motorSpeed + 50, maxSpeed); // UP: augmente la vitesse
    normalSpeed = motorSpeed; // Met à jour la vitesse normale
    lcd.setCursor(0, 1);
    lcd.print("Vitesse: ");
    lcd.print(motorSpeed);
    lcd.print("   ");
    updateTimer(); // Mettre à jour le timer avec la nouvelle vitesse
  } else if (buttonValue < 500) {
    motorDirection = false; // LEFT: sens antihoraire
    digitalWrite(dirPin, LOW);
    lcd.setCursor(0, 1);
    lcd.print("Direction: Gauche ");
  } else if (buttonValue < 700) {
    motorSpeed = minSpeed; // DOWN: réduit fortement la vitesse
    lcd.setCursor(0, 1);
    lcd.print("Ralenti: ");
    lcd.print(motorSpeed);
    lcd.print("   ");
    updateTimer(); // Mettre à jour le timer pour la vitesse réduite
  } else if (motorSpeed == minSpeed && buttonValue >= 700) {
    // Rétablir la vitesse normale lorsque le bouton DOWN est relâché
    motorSpeed = normalSpeed;
    lcd.setCursor(0, 1);
    lcd.print("Vitesse: ");
    lcd.print(motorSpeed);
    lcd.print("   ");
    updateTimer(); // Mettre à jour le timer avec la vitesse normale
  } else if (buttonValue < 900) {
    motorRunning = !motorRunning; // SELECT: active/désactive le moteur
    lcd.setCursor(0, 1);
    if (motorRunning) {
      lcd.print("Moteur: ON ");
    } else {
      lcd.print("Moteur: OFF ");
    }
    delay(400); // Anti-rebond
  }
}

// Met à jour la fréquence du Timer pour correspondre à la vitesse
void updateTimer() {
  Timer1.detachInterrupt(); // Arrêter l'interruption
  Timer1.initialize(1000000 / (motorSpeed * 2)); // Fréquence basée sur la vitesse
  Timer1.attachInterrupt(togglePulse); // Relancer l'interruption
}

void setup() {
  // Initialisation du LCD
  lcd.begin(16, 2);
  lcd.print("ISIS simulator");
  
  // Configuration des broches
  pinMode(dirPin, OUTPUT);
  pinMode(pulPin, OUTPUT);
  digitalWrite(pulPin, LOW);

  // Initialisation de TimerOne pour les impulsions
  Timer1.initialize(1000000 / (motorSpeed * 2)); // Fréquence initiale
  Timer1.attachInterrupt(togglePulse); // Attach la fonction togglePulse
}

void loop() {
  readButtons(); // Lecture des boutons pour ajuster la direction, vitesse et état du moteur
}

Voici le schéma du projet :

1 Like

Bonjour,

Puisque dans le cadre de votre SAE vous utilisez ce forum pour présenter l'évolution de votre travail, recevez en retour des commentaires conséquent au mode de fonctionnement d'un forum.

Sur votre schéma pont diviseur, l'ordre câblé est
Rien, Right, Up, Down, Left, Select.
Avec valeur analogique décroissante dans cet ordre

Dans votre programme, vous ne semblez pas respecter cet ordre, vous avez fait dans l'ordre
R, U, L, D, S
Il me semble.

Vous décrivez aussi un délais "anti rebond" de 300ms sur les BP Up Down.

Mais ce délais est de 400, et il ne me semble pas l'avoir vu positionné dans le programme au bon endroit.

Ne pensez vous pas, jeunes technicien.ne.s supérieur en GEII que vous auriez pu éventuellement mettre un anti rebond "hard", c à d un RC avec un temps de charge et décharge suffisament long, mais pas de troo, pour supprimer les rebonds sur l'entrée analogique (testé et verifié à l'oscillo)?

Ce sont justes des remarques.

Bonne continuation.

1 Like

Bonjour jef59,
C'est noté, merci pour votre commentaire !!!!

Les étudiants souvent modifient leurs posts pour être de plus en plus explicite et minimiser les coquilles…

En effet, les étudiants balbutient au niveau du schéma électrique, des essais du moteur, de sa commande par le micro, de la vérification de ce que fait le moteur, de la vérification que la routine d’interruption ne prenne pas trop de temps….

Il faut y aller étape par étape….

De même pour moi, quand je vois que les étudiants ont du mal à comprendre, je réédite mes postes….

Dans le programme précèdent d'ISIS simulator, il change le temps de la routine d’interruption pour changer la fréquence….

Ce n’est pas bête, mais le temps de la routine d’interruption n’est plus fixe pour que le micro fasse oscilloscope pour verifier le mouvement du moteur.
mais il y a l'instruction "millis ()"....
sinon utiliser le timer2 pour une deuxieme routine interruption.

La précision de la fréquence donc de la vitesse est bien plus précise que dans le programme ou la routine d’interruption fixe à 0.1ms

Dans le programme "isis simulator" Il y a un conflit de sortie car la broche 7 est utilisé par l’afficheur LCD. donc cela ne peut pas fonctionner....

Ce programme suivant fonctionne mieux en utilisant la sortie 11 pour commander le driver.
avec les boutons poussoirs sur l'entre analogique, il n'y a pas besoin de filtre analogique RC pour minimiser les rebonds du BP

#include <LiquidCrystal.h>
#include <TimerOne.h>

// Initialisation de l'écran LCD
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
//https://forum.arduino.cc/t/petits-exemples-lcd-horloge-communication-motor-capteur-iut-soissons/647014/78

// Définition des broches pour le driver du moteur
int const  dirPin = 12; // DIR- pour la direction

// Broche pour la lecture des boutons


int motorSpeed = 1000;  // frequence initiale (en Hz)    
bool motorDirection  ; // true = sens horaire, false = sens antihoraire
bool motorRunning = 0;  // Etat du moteur (marche/arrêt)
bool envoie;

// Variables pour la gestion des impulsions
bool pulseState = 0;     // État de la broche d'impulsion

int Position = 0;
float Inctemps=0;
float temps=0;
int valeur=0;



void togglePulse() {  //interruption time  à 2 frequence driver
Inctemps=2/motorSpeed;
temps=temps+Inctemps;

if (motorRunning==1) {
    // Générer les impulsions pour le driver du moteur
    pulseState = !pulseState; digitalWrite(11,pulseState);    
    
  if  (motorDirection==1) {Position=Position+1;}  //anti horraire
  if (Position>32000) {Position=32000;}
  if  (motorDirection==0) {Position=Position-1;}   //horraire
  if (Position<-32000) {Position=-32000;} 
  valeur=analogRead(A1);   //lecture potentiometre  
  envoie=1;                  
                      }
         

}   //fin






void setup() {
  
  lcd.begin(16, 2);    // Initialisation du LCD
 https://probots.co.in/1602-lcd-keypad-shield-blue-backlight-for-arduino.html
   
  // Configuration des broches
  pinMode(12, OUTPUT);     //direction
//  pin10 utilisé par le back light
   pinMode(11, OUTPUT);    // frequence driver
Serial.begin(57600);

  // Initialisation de TimerOne pour les impulsions    compteur=10^6/frequence      
//  Timer1.initialize(500000/motorSpeed);   // Fréquence initiale=2000hz  250 =>routine interruption 0.25ms=>      60tr/mn       
  Timer1.initialize(1000);               // Fréquence initiale=500hz => 1000=>routine interruption 1 ms  =>   
  Timer1.attachInterrupt(togglePulse); // Attach la fonction togglePulse
}

void loop() {
  // Lecture des boutons pour ajuster la direction, vitesse et état du moteur
  https://www.botnroll.com/en/alphanumeric/292-lcd-shield-for-arduino.html
  int buttonValue = analogRead(A0);
     
    lcd.setCursor(12, 0);
    lcd.print("     ");   
    lcd.setCursor(12, 0);
    lcd.print(buttonValue);  
 

  int buttonValue1 = analogRead(A0);  //antirebond et vibration switch
  
  if (buttonValue==0  && buttonValue1==0) {
    motorDirection = 0; // RIGHT: sens horaire
    digitalWrite(dirPin, 0);
    lcd.setCursor(0, 1);
    lcd.print("Dr");delay(100);    } 
  
  if (buttonValue > 100 && buttonValue < 150) {       //0.65V=>133DEc
    motorSpeed = (motorSpeed + 50); // UP: augmente la vitesse
    if (motorSpeed > 1000) {   motorSpeed =1000;  }
    Timer1.initialize(500000/motorSpeed);
    
    lcd.setCursor(0, 0);
    lcd.print("V:    ");
    lcd.setCursor(2, 0);
    lcd.print(motorSpeed);
    delay(100);  } 


  
  if (buttonValue > 450 && buttonValue < 500) {      //2.35V=>481 DEC
    motorDirection = 1; // LEFT: sens anti-horraire
    digitalWrite(dirPin, 1);
    lcd.setCursor(0, 1);
    lcd.print("Ga");     
    delay(100);} 
  
   if (buttonValue > 290 && buttonValue < 320) {    //1.51=>310 DEC
    motorSpeed=(motorSpeed - 50);     // DOWN: réduit fortement la vitesse  
    if (motorSpeed<1) {motorSpeed=1;  }
    Timer1.initialize(500000/motorSpeed); 
    lcd.setCursor(0, 0);
    lcd.print("V:    ");
     lcd.setCursor(2, 0);
     lcd.print(motorSpeed);
    delay(100);    } 
   
  
   if (buttonValue > 700 &&  buttonValue < 750) {    //721
    motorRunning = !motorRunning; // SELECT: active/désactive le moteur
    if (motorRunning==0)   { Position=0;}
    lcd.setCursor(0, 0);
    if (motorRunning==1) {
      lcd.print("ON");
      lcd.print(motorSpeed);
    } else {
      lcd.print("OFF     ");

    }} 

  
  

if (envoie==1)   {
    lcd.setCursor(3, 2);
    lcd.print("      ");    
    lcd.setCursor(3, 2);
    lcd.print(Position);
    Serial.print(temps);Serial.print(";");Serial.print(motorSpeed);
Serial.println(";"); 
//
envoie=0;
 }

  
}//fin loop

maintenant, il faudrait verifier la position demandée par le programme et la vitesse

3 Likes

le stepper motor n'est pas le meme que precedent
sa reference est le 17hs19 2004s1 datasheet avec un courant driver de 2A
il n’a pas de plage de frequence avec des pertes de pas.

D’ailleurs En mode pas complet, pour le profil automatique de position avec 200position=0.5tour

if (motorRunning==1 && (Position<=100))    {motorSpeed=100;}    // 0.25tr/s   donc 0.5seconde pour faire 0.25 tour
if (motorRunning==1 && Position>100  && Position<=300 )   {motorSpeed=200;}   // 1tr/s   donc 0.5s  pour faire 0.5 tour
if (motorRunning==1 && Position>300  && Position<=600)    {motorSpeed=300;}   //  1.5tr/s  donc 0.5s pour faire0.75 tour
if (motorRunning==1 && Position>600  && Position<=1200)    {motorSpeed=600;}  // 2.5tr/s donc 0.5s pour faire 1.25 tour
if (motorRunning==1 && Position>1200  && Position<=2000)    {motorSpeed=800;}  // 3tr/s   donc 0.5s pour faire 1.5 tour
if (motorRunning==1 && Position>2000  && Position<=2900)    {motorSpeed=900;}   //3.75tr/s   donc 0.5s pour faire 1.87 tour
if (motorRunning==1 && Position>2900  && Position<=3900)    {motorSpeed=1000;}   //5tr/s  donc  0.5s  pour faire 2.5 tour
if (motorRunning==1 && Position>3900)       {motorRunning=0;}

L’instruction millis() permet de compter le temps et d’utiliser le micro en oscilloscope.

Voici le nouveau code

#include <LiquidCrystal.h>
#include <TimerOne.h>

// Initialisation de l'écran LCD
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
//https://forum.arduino.cc/t/petits-exemples-lcd-horloge-communication-motor-capteur-iut-soissons/647014/78

// Définition des broches pour le driver du moteur
int const  dirPin = 12; // DIR- pour la direction

// Broche pour la lecture des boutons


int motorSpeed = 100;  // frequence initiale (en Hz)    
bool motorDirection  ; // true = sens horaire, false = sens antihoraire
bool motorRunning = 0;  // Etat du moteur (marche/arrêt)
bool envoie;

// Variables pour la gestion des impulsions
bool pulseState = 0;     // État de la broche d'impulsion

int Position = 0;

int long temps=0;
int long temps1=0;
int valeur=0;



void togglePulse() {  //interruption time  à 2 frequence driver


if (motorRunning==1) {
    // Générer les impulsions pour le driver du moteur
    pulseState = !pulseState; digitalWrite(11,pulseState);    
    
  if  (motorDirection==1) {Position=Position+1;}  //anti horraire
  if (Position>32000) {Position=32000;}
  if  (motorDirection==0) {Position=Position-1;}   //horraire
  if (Position<-32000) {Position=-32000;} 
  valeur=analogRead(A1);   //lecture potentiometre  
  envoie=1;                  
                      }
         

//profil automatique de position   200position=0.5tour 
if (motorRunning==1 && (Position<=100))    {motorSpeed=100;}    // 0.25tr/s   donc 0.5seconde pour faire 0.25 tour
if (motorRunning==1 && Position>100  && Position<=300 )   {motorSpeed=200;}   // 1tr/s   donc 0.5s  pour faire 0.5 tour
if (motorRunning==1 && Position>300  && Position<=600)    {motorSpeed=300;}   //  1.5tr/s  donc 0.5s pour faire0.75 tour
if (motorRunning==1 && Position>600  && Position<=1200)    {motorSpeed=600;}  // 2.5tr/s donc 0.5s pour faire 1.25 tour
if (motorRunning==1 && Position>1200  && Position<=2000)    {motorSpeed=800;}  // 3tr/s   donc 0.5s pour faire 1.5 tour
if (motorRunning==1 && Position>2000  && Position<=2900)    {motorSpeed=900;}   //3.75tr/s   donc 0.5s pour faire 1.87 tour
if (motorRunning==1 && Position>2900  && Position<=3900)    {motorSpeed=1000;}   //5tr/s  donc  0.5s  pour faire 2.5 tour
if (motorRunning==1 && Position>3900)       {motorRunning=0;}
 Timer1.initialize(500000/motorSpeed);


// changement de direction automatique                                                                                                                                      
//if (position>=positionmax) {motorDirection=true}   //inversion du sens de rotation automatique
//if (positionref<=positionmini) {motorDirection==false}


}   //fin






void setup() {
  
  lcd.begin(16, 2);    // Initialisation du LCD
 https://probots.co.in/1602-lcd-keypad-shield-blue-backlight-for-arduino.html
   
  // Configuration des broches
  pinMode(12, OUTPUT);     //direction
//  pin10 utilisé par le back light
   pinMode(11, OUTPUT);    // frequence driver
Serial.begin(57600);

  // Initialisation de TimerOne pour les impulsions    compteur=10^6/frequence      
    
   Timer1.initialize(500000/motorSpeed);
  Timer1.attachInterrupt(togglePulse); // Attach la fonction togglePulse
    lcd.setCursor(0, 0);
    lcd.print("OFF"); 

motorDirection = 1;
    motorDirection = 1; // LEFT: sens anti-horraire
    digitalWrite(dirPin, 1);
    lcd.setCursor(0, 1);
    lcd.print("Ga");  
  
}

void loop() {
  // Lecture des boutons pour ajuster la direction, vitesse et état du moteur
  https://www.botnroll.com/en/alphanumeric/292-lcd-shield-for-arduino.html
  int buttonValue = analogRead(A0);
     
/*    lcd.setCursor(12, 0);
    lcd.print("     ");   
    lcd.setCursor(12, 0);
    lcd.print(buttonValue);    */
    lcd.setCursor(0, 0);
    lcd.print("V:     ");
    lcd.setCursor(2, 0);
    lcd.print(motorSpeed); 

  int buttonValue1 = analogRead(A0);  //antirebond et vibration switch
  
  if (buttonValue==0  && buttonValue1==0) {
    motorDirection = 0; // RIGHT: sens horaire
    digitalWrite(dirPin, 0);
    lcd.setCursor(0, 1);
    lcd.print("Dr");delay(100);    } 
  
  if (buttonValue > 100 && buttonValue < 150) {       //0.65V=>133DEc
    motorSpeed = (motorSpeed + 50); // UP: augmente la vitesse
    if (motorSpeed > 1000) {   motorSpeed =1000;  }         //1000Hz=>300RPM
    Timer1.initialize(500000/motorSpeed);
    
    lcd.setCursor(0, 0);
    lcd.print("V:    ");
    lcd.setCursor(3, 0);
    lcd.print(motorSpeed);
    delay(100);  } 


  
  if (buttonValue > 450 && buttonValue < 500) {      //2.35V=>481 DEC
    motorDirection = 1; // LEFT: sens anti-horraire
    digitalWrite(dirPin, 1);
    lcd.setCursor(0, 1);
    lcd.print("Ga");     
    delay(100);} 
  
   if (buttonValue > 290 && buttonValue < 320) {    //1.51=>310 DEC
    motorSpeed=(motorSpeed - 50);     // DOWN: réduit fortement la vitesse  
    if (motorSpeed<1) {motorSpeed=1;  }
    Timer1.initialize(500000/motorSpeed); 
    lcd.setCursor(0, 0);
    lcd.print("V :    ");
     lcd.setCursor(3, 0);
     lcd.print(motorSpeed);
    delay(100);    } 
   
  
   if (buttonValue > 700 &&  buttonValue < 750) {    //721
    motorRunning = !motorRunning; // SELECT: active/désactive le moteur
    lcd.setCursor(0, 0);  
    if (motorRunning==1) {
      lcd.print("ON");
      lcd.print(motorSpeed);
      Position=0;temps1=millis();
    } else {
      lcd.print("OFF     ");
      Position=0;
    }
    delay(400); } 

  
  

if (envoie==1)   {
  temps=millis()-temps1;
    lcd.setCursor(3, 2);
    lcd.print("      ");    
    lcd.setCursor(3, 2);
    lcd.print(Position);
    Serial.print(temps);Serial.print(";");Serial.print(motorSpeed);Serial.print(";");Serial.print(Position);Serial.print(";");Serial.print(valeur);
Serial.println(";"); 
//
envoie=0;
 }

  
}//fin loop

Le programme d’interruption dure 0.150ms donc une fréquence max de 1000hz avec une période max 1ms permet au à la boucle loop de ce faire
La boucle loop dure environ 6 ms à 10ms en fonction des nombres à envoyer sur la liaison série.
Donc, il n’y a pas toutes les informations du déplacement à oscilloscopes mais ce n’est pas grave pour vérifier le mouvement total et savoir s’il y a perte de pas ou pas…

il reste à faire un profil trapezoidal de vitesse

2 Likes

Si on veut réaliser 2000 pas donc 5 tours avec un profil trapézoïdal et non en démarrage Start stop.
Donc que la vitesse augmente progressivement avec la position pour avoir une accélération progressive et ainsi qu’une décélération.
L’incrémentation de la vitesse se fera par récurrence en fonction la boucle de routine d’interruption qui se fait « delta temps ». Donc, la variation de la fréquence est codée
Moteur speed (n)=incrementation*delta temps+moteur speed (n-1)
Si l’incrémentation est positive cela Corre spot à l’accélération est positive.
Si l’incrémentation est négative alors cela correspond à la décélération.

Si « delta temps » est constant et si l’incrémentation est constante alors la variation de moteurspeed sera linéaire

Mais dans le programme précèdent, le temps de la routine d’interruption varie avec la fréquence du moteur. Par conséquent avec une incrémentation constante, l’évolution de la vitesse ne sera pas proportionnelle mais exponentielle.

Pour vérifier s’il y a perte de pas et pour ne pas avoir de confusion dans les termes

  • La « position » sera la mesure par le potentiomètre ramené à l’échelle du pas.
  • le « nbrpas » sera la position demandée au moteur pas à pas
    Pour compter le nombre de tour effectué par le potentiomètre, plusieurs solutions sont possible :
  • la détection de la variation du potentiomètre quand il passe de 1024 à 0 est detecté et filtré

D’ailleurs voici la courbe avec une incrémentation constante de 0.1 de la vitesse pour atteindre les 2000 pas en 50 secondes
La position du potentiometre peut avoir un leger decalage de la valeur de 400 qui correspond à 1 tour car la detection de chaque tour est filtré. le filtre est present car la valeur du potentiometre reste à 0 pendant 4° ainsi que la valeur à 1023

if (nbrpas<=500)    {motorSpeed=motorSpeed+incrementation;}    // 
if (nbrpas>500  && nbrpas<=1500 )   {motorSpeed=motorSpeed+0;}   // 
if (nbrpas>1500  && nbrpas<=2000)   {motorSpeed=motorSpeed-incrementation;}   //  
if (nbrpas>2000)       {motorRunning=0;}       //5 tours

La position indiquée par le potentiometre correspond à chaque fois au nombre de pas demandé mais la vitesse n’atteint que 50pas/s

on peut tracer l'erreur entre la position indiquée du potentiometre et le nombre de pas demandée. on peut remuarquer que cette erreur s'annule d'elle meme.
le potentiometre n'est pas le meilleur capteur.

Pour une incrémentation de 2, le temps pour atteindre les 2000 pas en 4.5seconde avec une vitesse de 1000pas/s
Il y a une erreur de 9 pas à 30 pas entre la position du potentiometre et le nombre de pas comme on peut l’observer sur la figure suivante

Voici le code complet avec, si l’on appuie sur up, le potentiomètre se mettra à 0.

#include <LiquidCrystal.h>
#include <TimerOne.h>

// Initialisation de l'écran LCD
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
//https://forum.arduino.cc/t/petits-exemples-lcd-horloge-communication-motor-capteur-iut-soissons/647014/78

// Définition des broches pour le driver du moteur
int const  dirPin = 12; // DIR- pour la direction

// Broche pour la lecture des boutons


float motorSpeed = 1;  // frequence initiale (en Hz)  
float incrementation=2 ; 
bool motorDirection  ; // true = sens horaire, false = sens antihoraire
bool motorRunning = 0;  // Etat du moteur (marche/arrêt)
bool envoie;
bool reference;

// Variables pour la gestion des impulsions
bool pulseState = 0;     // État de la broche d'impulsion

int nbrpas = 0;      //nombre de pas effectué compté
int nbrtour=0;       //nombre de tour par le potentiometre
bool RAZ=0;
int long Position=0;      //deplacement du potentiometre


int long temps=0;
int long temps1=0;
int valeur=0;
int valeur1=0;
int valeur2=0;
int valeur3=0;

int ecart2=0;
int ecart1=0;
int ecart=0;
int ecartmoyen=0;

int derivee=0;


void togglePulse() {  //interruption time  à 2 frequence driver

if (reference==1)  {      //mettre le potentiometre à 0
  pulseState = !pulseState; digitalWrite(11,pulseState); 
  valeur = analogRead(A1);
if (valeur>1 && valeur2<=1024) {reference==1;} else {reference=0;}
                    }



if (motorRunning==1) {
//  digitalWrite(13, 1);
    
    pulseState = !pulseState; digitalWrite(11,pulseState);    // Générer les impulsions pour le driver du moteur
    
  if  (motorDirection==1) {nbrpas=nbrpas+1;}  //anti horraire
  if (nbrpas>32000) {nbrpas=32000;}
  if  (motorDirection==0) {nbrpas=nbrpas-1;}   //horraire
  if (nbrpas<-32000) {nbrpas=-32000;} 

    valeur2=valeur1;   //n-2
    valeur1=valeur;    //n-1
    valeur=analogRead(A1);   //lecture potentiometre
 if (valeur==1023 && valeur1<1015 ) {valeur=valeur1+6;}   //evite les perturbations

  ecart2=ecart1;
  ecart1=ecart;
  ecart=valeur-valeur2;
  ecartmoyen=(ecart+ecart1+ecart2)/3;

  if (valeur==0 && ecartmoyen<-250 && RAZ==1  )  {nbrtour++;RAZ=0;}   // augmentation  anti horraire
  if (valeur>20 && RAZ==0)  {RAZ=1; }   // remise à zero c
//  if (valeur==0 && ecartmoyen>250 && RAZ==1  )  {nbrtour--;RAZ=0;}   // augmentation  anti horraire
//   if (valeur<1010 )  {RAZ=1; }   // augmentation  hanti horraire
   
    Position=nbrtour*1024+valeur ;   //1 tour=1024
    Position=(Position*400)/1024;      //conversion en pas du potentiometre


//profil automatique de position   200pas=0.5tour         1000pas/s
if (nbrpas<=500)    {motorSpeed=motorSpeed+incrementation;}    // 
if (nbrpas>500  && nbrpas<=1500 )   {motorSpeed=motorSpeed+0;}   // 
if (nbrpas>1500  && nbrpas<=2000)   {motorSpeed=motorSpeed-incrementation;}   //  
if (nbrpas>2000)       {motorRunning=0;}       //5 tours
if (motorSpeed<=1) {motorSpeed=1;}       //limite div 0
if (motorSpeed>=1000) {motorSpeed=1000;}    
 Timer1.initialize(500000/motorSpeed);

   

  envoie=1;                  
                  }
         





//  digitalWrite(13, 0);


}   //fin






void setup() {
  
  lcd.begin(16, 2);    // Initialisation du LCD
 https://probots.co.in/1602-lcd-keypad-shield-blue-backlight-for-arduino.html
   
  // Configuration des broches
  pinMode(13, OUTPUT);     //direction
  pinMode(12, OUTPUT);     //direction
//  pin10 utilisé par le back light
   pinMode(11, OUTPUT);    // frequence driver
Serial.begin(57600);

  // Initialisation de TimerOne pour les impulsions    compteur=0.5 10^6/frequence      
    
   Timer1.initialize(500000/motorSpeed);
  Timer1.attachInterrupt(togglePulse); // Attach la fonction togglePulse


motorDirection = 1;
    motorDirection = 1; // LEFT: sens anti-horraire
    digitalWrite(dirPin, 1);
    lcd.setCursor(0, 1);
    lcd.print("Ga");  
  
}

void loop() {
  // Lecture des boutons pour ajuster la direction, vitesse et état du moteur
  https://www.botnroll.com/en/alphanumeric/292-lcd-shield-for-arduino.html
//if ( digitalRead(13)== 1 ) {digitalWrite(13,0);}  else {digitalWrite(13,1);}
  
  int buttonValue = analogRead(A0);
     
    lcd.setCursor(0, 0);
    lcd.print("V:     ");
    lcd.setCursor(2, 0);
    lcd.print(motorSpeed,1); 

    lcd.setCursor(10, 0);
    lcd.print("T:  ");
    lcd.setCursor(12, 0);
    lcd.print(nbrtour); 

    lcd.setCursor(10, 1);
    lcd.print("    ");
    lcd.setCursor(10, 1);
    lcd.print(ecartmoyen); 


  if (buttonValue > 100 && buttonValue < 150) {       //0.65V=>133DEc   up
     reference=1;        //mettre le potentiometre à 0
     motorSpeed=100;
     Timer1.initialize(500000/motorSpeed);
                  }
    

    
   if (buttonValue > 700 &&  buttonValue < 750) {    //721
    motorRunning = !motorRunning; // SELECT: active/désactive le moteur
    lcd.setCursor(0, 0); 
    nbrpas=0;
    nbrtour=0; 
    if (motorRunning==1) {
      lcd.print("ON");
      lcd.print(motorSpeed);
      motorSpeed=1;
    } else {
      lcd.print("OFF     ");
      
    }
    delay(00); 
    temps1=millis();} 
  

if (envoie==1)   {
  temps=millis()-temps1;
     lcd.setCursor(3, 2);
    lcd.print("      ");    
    lcd.setCursor(3, 2);   
    lcd.print(Position);
    Serial.print(temps);Serial.print(";");Serial.print(motorSpeed);Serial.print(";");Serial.print(Position);Serial.print(";");Serial.print(nbrpas);
Serial.println(";"); 
//
envoie=0;
 }

Évidemment, il serait intéressant de faire varier l’incrémentation de la vitesse pour éviter la perte de pas.
En effet, il faudrait augmenter rapidement la vitesse au démarrage puis l'augmenter faible à vitesse haute

3 Likes

Présentation du schéma de câblage simplifiée via l'application Xrelais.

Bonjour voici un aperçu simplifié du câblage de l'Arduino ,le microstep driver, le moteur pas à pas et l'écran lcd.

Ce schéma permet de comprendre plus simplement le câblage du moteur avec tous les autres éléments externes ce qui peut être une plaie à comprendre si on regarde le montage fini (avec tous les câbles qui trainent on se perd vite.) .

Ce schéma nous permet aussi de savoir sur quelle broche nous avons branché les boutons ce qui nous facilite grandement pour le programme avec la bonne affectation des broches.

Les fils rouges représentent la partie puissance et les fils bleu représentent la partie commande.

Nous avons dû créer toutes les composants via Xsymboles car aucune n'existait.

Ensuite nous avons fait un code permettant d'incrémenter et décrémenter grâce à des boutons branchés directement à la Arduino nano et grâce à l'écran LCD nous affichons la vitesse en tr/min, le sens de rotation et la période.

La communication de l'arduino avec le LCD est une communication en bus I2C, Une communication ne comportant que 2 fils le SCL et le SDA.
Ce mode de communication nous permet d'avoir que 2 câbles à brancher sur le arduino avec le SCL qui reçoit un signal d'horloge et le SDA qui reçoit les données.
Il est bien plus simple à brancher et évite d'avoir trop de câbles partout ce qui peut être très vite dérangeant surtout sur des petits systèmes électroniques.

voici le code en question :

#include <SoftwareSerial.h>
#include <TimerOne.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>




#define led13   13       // 13








LiquidCrystal_I2C lcd(0x27, 20, 4);




unsigned int temps=0;
unsigned int temps1=0;        
unsigned int T2;             //








unsigned int RPM=100;   //10 equivaut à 1 tour/minute
unsigned int RPM1;
 int F;
int valeur;




int positionref;   //postionreference
int positionmax=200;      // nbr de pas      1 tour=360°/1.8°=200pas
int positionmini=-200;    //50 pas equivaut à 90°   et 25 pas  à 45°








bool Start=0;
bool directio=0;     //0 equi sens anti horraire




bool envoie=1;
















void callback()  {    //routine interruption 0.1ms
  temps++;    //temps pour vitesse du moteur
  temps1++;   //
 
 
 if (Start==1 && temps>=T2 &&  digitalRead(7)==0)  {digitalWrite(7,1);temps=0;envoie=1;             //impulsion pour faire 1 pas  durant 0.2ms
                                                   if (directio==1) {positionref=positionref+1;} else {positionref=positionref-1;}  
                                                  }




 if (temps>=2 &&  digitalRead(7)==1)  {digitalWrite(7,0); }       //remise à zero de l'impulsion                                                  
                                                                                               
                                                 
+//if (positionref>=positionmax) {digitalWrite(12,0);directio=0;}   //inversion du sens de rotation
//if (positionref<=positionmini) {digitalWrite(12,1);directio=1;}
 
}  //fin callback












void setup() {
pinMode (2, INPUT_PULLUP);   //incrementation
pinMode (3, INPUT_PULLUP);    //decrementation
pinMode (4, INPUT_PULLUP);    //start
pinMode (5, INPUT_PULLUP);    //sens horaire et antihoraire   Direction pin




pinMode(led13, OUTPUT);
pinMode(12, OUTPUT);  
pinMode(7, OUTPUT);      //clock




//Serial.begin(115200);
Serial.begin(57600);
//Serial.begin(19200);








 Timer1.initialize(100);           // initialize timer1, and set a 0,1 second period =>  100 000  pour 0.01s  10 000
 Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt




lcd.init();
lcd.backlight();




}








void loop() {
  if ( digitalRead(13)== 1 ) {digitalWrite(13,0);}  else {digitalWrite(13,1);}      //duree du programme 2ms en 57600 bauds




if ( digitalRead(4)==0 && Start==0 )   {Start=1;delay(200);}    //start
if ( digitalRead(4)==0 && Start==1 )   {Start=0;delay(200);}    //stop




if (digitalRead(5)==0 && directio==1 )  {digitalWrite(12,0);/*directio=0*/;delay(300);}    //sens horaire   direction
if (digitalRead(5)==0 && directio==0 )  {digitalWrite(12,1);directio=1;delay(300);}    //sens anti horaire




if (digitalRead(2)==1)  {RPM=RPM+10;delay(100);}     //incrementation vitesse désirée
if (digitalRead(3)==0)  {RPM=RPM-10;delay(100);}     // vitesse




if (RPM==0) {Start==0;RPM=100;}   //ne pas mettre une vitesse nulle pour le calcul de T2




if (RPM>=3000)    {RPM=3000;}      //equivalent à 300 tr/min pour ne pas utiliser de flaot
if (RPM<=1)   {RPM=1;}      //equivalent à 0.1 tr/min
T2=(15000/RPM)  ;     //1500=60*1.8/360*0.1ms*2
RPM1=3000/T2;       //vrai valeur de vitesse avec la troncature
F= (RPM1*360)/(1.8*60);
if(envoie==1)
{valeur=analogRead(A0);
Serial.print(RPM1); Serial.print(","); Serial.print(positionref); Serial.print(","); Serial.print(directio);Serial.print(",");Serial.print(T2);Serial.print(",");Serial.print(valeur);Serial.print(",");Serial.print(F);
Serial.println(";"); envoie=0;




lcd.setCursor(0,0); // positionne le curseur à la colonne 1 et à la ligne 2  
lcd.print("Frequence:");
lcd.setCursor(11,0);  
lcd.print(F);
lcd.setCursor(0,1);
lcd.print("tr/min:");
lcd.setCursor(8,1);
lcd.print(RPM1);
lcd.setCursor(0,2);
lcd.print("sens rota:");
lcd.setCursor(11,2);
lcd.print(directio);
lcd.setCursor(0,3);
lcd.print("periode:");
lcd.setCursor(9,3);
lcd.print(T2);
delay(1000);
}




}             // fin loop

Malheureusement nous n'avons pas de photo en fonctionnement de l'écran LCD.

Voici les courbes que nous avons relevées et dessiner grace a excel :

Comme nous pouvons voir sur le graphique, la vitesse(en tr/min) augmente et la période diminue.
Plus la fréquence augmente, la vitesse augmente et a contrario la période diminue.

Si vous avez des questions ou des améliorations possible sur ce code ou notre manière de l'avoir pensez dites le nous !

4 Likes

Il manque

  • une photo de votre montage réel et une capture d’écran de la simulation.
  • Le tableau de mesure de la variation du courant d’alimentation en mode pas et demi pas…..
  • Quelle est la vitesse max ?

Bref, À la place d’avoir une variation de la vitesse qui augmente par addition.
Une variation suivant avec un multiple du nombre de pas demandé peut être effectuée

if (nbrpas<=500)    {motorSpeed=nbrpas*multiple;}     
if (nbrpas>500  && nbrpas<=1500 )   {motorSpeed=motorSpeed;}    
if (nbrpas>1500  && nbrpas<=2000)   {motorSpeed=(2000-nbrpas)*multiple   ;}  

Évidemment, la variation de la vitesse n’est pas linéaire exemple pour un multiple de 1.
Mais il y a aussi des pertes de pas qui sont entourées en rouge

À force de faire des essais on se rencontre qu’il y a des fréquences qu’il faut quand meme bannir et qui n’ont pas été observé car le programme de test de la vitesse augmentait de 50 par 50.

Avec un multiple de 0.3, il n’y a jamais de perte de pas, mais la fréquence max est de 150Hz donc le temps du déplacement est long

Évidemment, avec des données dans une table, on pourrait augmenter la vitesse petit à petit en fonction du nombre de pas avec une boucle for.
Mais on peut le faire avec quelques si

if (nbrpas<=125)       {motorSpeed=350;}      
if (nbrpas>125 && nbrpas<=250 )   {motorSpeed=400;}   
if (nbrpas>250 && nbrpas<=500 )   {motorSpeed=450;}                                                                 
if (nbrpas>500 && nbrpas<=750 )   {motorSpeed=500;} 
if (nbrpas>750  && nbrpas<=1000 )   {motorSpeed=550;}
if (nbrpas>1000  && nbrpas<=1250 )   {motorSpeed=600;}    
if (nbrpas>1250  && nbrpas<=1500)   {motorSpeed=550;}                                        
if (nbrpas>1500  && nbrpas<=1750)   {motorSpeed=350;} 
if (nbrpas>2000)       {motorRunning=0;}       //5 tours
if (motorSpeed<=10) {motorSpeed=10;}       //limite div 0

On peut observer la courbe de l’augmentation de la vitesse et de la pente du nombre de pas, pour le profil de position précèdent.

Évidemment, il est possible d’augmenter la précision en diminuant la vitesse via des switchs du driver TB6600
Mais aucun étudiant à pris l’initiative, de faire un essai en micro pas avec une division par 4 par 16 ou 32.
exemple Pour une division par 4, une fréquence de 500Hz, et un nombre de pas de 2000, voici le résultat avec un démarrage Start stop

Le temps de la routine d’interruption est de 0.2ms donc inferieur à 1ms correspondant à la fréquence max de la vitesse de noter moteur
La mémoire ram est de 15%
La mémoire rom est de 20%
On est loin de remplir le micro atmega 328

le nouveau code

#include <LiquidCrystal.h>
#include <TimerOne.h>

// Initialisation de l'écran LCD
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
//https://forum.arduino.cc/t/petits-exemples-lcd-horloge-communication-motor-capteur-iut-soissons/647014/78

// Définition des broches pour le driver du moteur
int const  dirPin = 12; // DIR- pour la direction

// Broche pour la lecture des boutons


int motorSpeed = 10;  // frequence initiale (en Hz)  

 
bool motorDirection  ; // true = sens horaire, false = sens antihoraire
bool motorRunning = 0;  // Etat du moteur (marche/arrêt)
bool envoie;
bool reference;

// Variables pour la gestion des impulsions
bool pulseState = 0;     // État de la broche d'impulsion

int nbrpas = 0;      //nombre de pas effectué compté
int nbrtour=0;       //nombre de tour par le potentiometre
bool RAZ=0;
int long Position=0;      //deplacement du potentiometre


int long temps=0;
int long temps1=0;
int valeur=0;
int valeur1=0;
int valeur2=0;
int valeur3=0;

int ecart2=0;
int ecart1=0;
int ecart=0;
int ecartmoyen=0;


void togglePulse() {  //interruption time  à 2 frequence driver
 
if (reference==1)  {      //mettre le potentiometre à 0
  pulseState = !pulseState; digitalWrite(11,pulseState); 
  valeur = analogRead(A1);
if (valeur>1 && valeur2<=1024) {reference==1;} else {reference=0;}
                    }



if (motorRunning==1) {
  digitalWrite(13, 1);
    
    pulseState = !pulseState; digitalWrite(11,pulseState);    // Générer les impulsions pour le driver du moteur
    
  if  (motorDirection==1) {nbrpas=nbrpas+1;}  //anti horraire
  if (nbrpas>32000) {nbrpas=32000;}
  if  (motorDirection==0) {nbrpas=nbrpas-1;}   //horraire
  if (nbrpas<-32000) {nbrpas=-32000;} 

    valeur2=valeur1;   //n-2
    valeur1=valeur;    //n-1
    valeur=analogRead(A1);   //lecture potentiometre
 if (valeur==1023 && valeur1<1015 ) {valeur=valeur1+6;}   //evite les perturbations

  ecart2=ecart1;
  ecart1=ecart;
  ecart=valeur-valeur2;
  ecartmoyen=(ecart+ecart1+ecart2)/3;

  if (valeur==0 && ecartmoyen<-250 && RAZ==1  )  {nbrtour++;RAZ=0;}   // augmentation  anti horraire
  if (valeur>20 && RAZ==0)  {RAZ=1; }   // remise à zero c
//  if (valeur==0 && ecartmoyen>250 && RAZ==1  )  {nbrtour--;RAZ=0;}   // augmentation  anti horraire
//   if (valeur<1010 )  {RAZ=1; }   // augmentation  hanti horraire
   
    Position=nbrtour*1024+valeur ;   //1 tour=1024
    Position=(Position*400)/1024;      //conversion en pas du potentiometre


//profil automatique de position   200pas=0.5tour 
//if (nbrpas<=2000)       {motorSpeed=500;}      

//profil automatique de position   200pas=0.5tour  avec test de differentes vitesses
if (nbrpas<=125)       {motorSpeed=350;}      
if (nbrpas>125 && nbrpas<=250 )   {motorSpeed=400;}   
if (nbrpas>250 && nbrpas<=500 )   {motorSpeed=450;}                                                                 
if (nbrpas>500 && nbrpas<=750 )   {motorSpeed=500;} 
if (nbrpas>750  && nbrpas<=1000 )   {motorSpeed=550;}
if (nbrpas>1000  && nbrpas<=1250 )   {motorSpeed=600;}    
if (nbrpas>1250  && nbrpas<=1500)   {motorSpeed=550;}                                        
if (nbrpas>1500  && nbrpas<=1750)   {motorSpeed=350;} 
if (nbrpas>2000)       {motorRunning=0;}       //5 tours

if (nbrpas>2000)       {motorRunning=0;}       //5 tours
if (motorSpeed<=10) {motorSpeed=10;}       //limite div 0

if (motorSpeed>=1000) {motorSpeed=1000;}    
 Timer1.initialize(500000/motorSpeed);

   

  envoie=1;                  
                  }
         
  digitalWrite(13, 0);

}   //fin






void setup() {
  
  lcd.begin(16, 2);    // Initialisation du LCD
 https://probots.co.in/1602-lcd-keypad-shield-blue-backlight-for-arduino.html
   
  // Configuration des broches
  pinMode(13, OUTPUT);     //direction
  pinMode(12, OUTPUT);     //direction
//  pin10 utilisé par le back light
   pinMode(11, OUTPUT);    // frequence driver
Serial.begin(57600);

  // Initialisation de TimerOne pour les impulsions    compteur=0.5 10^6/frequence      
    
   Timer1.initialize(500000/motorSpeed);
  Timer1.attachInterrupt(togglePulse); // Attach la fonction togglePulse


motorDirection = 1;
    motorDirection = 1; // LEFT: sens anti-horraire
    digitalWrite(dirPin, 1);
    lcd.setCursor(0, 1);
    lcd.print("Ga");  
  
}

void loop() {
  // Lecture des boutons pour ajuster la direction, vitesse et état du moteur
  https://www.botnroll.com/en/alphanumeric/292-lcd-shield-for-arduino.html
//if ( digitalRead(13)== 1 ) {digitalWrite(13,0);}  else {digitalWrite(13,1);}
  
  int buttonValue = analogRead(A0);
     
    lcd.setCursor(0, 0);
    lcd.print("V:     ");
    lcd.setCursor(2, 0);
    lcd.print(motorSpeed,1); 

    lcd.setCursor(10, 0);
    lcd.print("T:  ");
    lcd.setCursor(12, 0);
    lcd.print(nbrtour); 



  if (buttonValue > 100 && buttonValue < 150) {       //0.65V=>133DEc   up
     reference=1;        //mettre le potentiometre à 0
     motorSpeed=100;
     Timer1.initialize(500000/motorSpeed);
                  }
    

    
   if (buttonValue > 700 &&  buttonValue < 750) {    //721
    motorRunning = !motorRunning; // SELECT: active/désactive le moteur
    lcd.setCursor(0, 0); 
    nbrpas=0;
    nbrtour=0; 
    if (motorRunning==1) {
      lcd.print("ON");
      lcd.print(motorSpeed);
      motorSpeed=1;
    } else {
      lcd.print("OFF     ");
      
    }
    delay(00); 
    temps1=millis();} 
  

if (envoie==1)   {
  temps=millis()-temps1;
     lcd.setCursor(3, 2);
    lcd.print("      ");    
    lcd.setCursor(3, 2);   
    lcd.print(Position);
    Serial.print(temps);Serial.print(";");Serial.print(motorSpeed);Serial.print(";");Serial.print(Position);Serial.print(";");Serial.print(nbrpas);
Serial.println(";"); 
//
envoie=0;
 }

  
}//fin loop

3 Likes

Conclusion :

Le choix d’un driver et du moteur pas à pas est très important pour connaitre quelle vitesse maximale il peut atteindre.

Il faut de nombreux essais pour vérifier les limites du mouvement sans perte de pas.

Utiliser un potentiomètre pour vérifier ce que fait le moteur, aide bien même si faire un fichier .csv peut être rébarbatif. Le choix de verifier la position avec un potentiometre « rotatif » ou « sans fin » n’est pas idéal mais permet de faire le job.

D’ailleurs, un moniteur graphique x, y serait intéressant d’être réalisé pour faire fonctionner le micro en mode oscilloscope…donc un monitor qui permettrait de choisir les données des axes, voir des échelles

Il reste quelques questions en suspens et des perspectives dans ce tuto :

Combien d’axe pourrait être gérer par l’atmega 328 ? sachant qu’il va être limité avec le nombre d’entrées et sorties mais aussi limité par le temps d’instruction de programme ?

Test de Library déjà existante sur les moteurs pas à pas ? pour connaitre leurs possibilités

Avec le choix d’un ESP 32, quelle aurait été la fréquence maximale qui aurait pu être effectué ?

Est que la vitesse max que peut faire l’ensemble moteur peut être atteinte avec l’ESP32 ?

Dans les imprimantes 3D, le G code indique étape par étape les déplacement (en millimètre) avec une vitesse (millimètre/min) ainsi que le dépôt de matière

Le temps doit être compté pour savoir si c’est effectué et passer à l’étape suivante….

Donc, un programme pour faire la communication avec un fichier Gcode, lisant les instructions du Gcode puis de faire les déplacements et de gérer tous les axes ainsi que le dépôt….

Optimiser la vitesse une découpeuse dépend de la partie thermique et de l’épaisseur à découper…ou du depot de l’imprimante 3D

Mais il y a aussi tous les mouvements du déplacement sans découpe qui n’a pas besoin de précision et des fins de courses au détectant des positions par des leds infrarouges

Il y a beaucoup de choses réaliser en open source par les fablab….

Exemple une découpeuse à plasma utilisant 4 moteurs NEMA 23HS5628

https://agrilab.unilasalle.fr/projets/projects/tutoriels/questions/221-decoupeuse-plasma-cnc-linuxcnc-qtplasmac-mesa-7i96s


on peut observer les 4 drivers et la carte qui permet de les commander et la carte 7i96s qui donne les fréquences, direction...
http://www.mesanet.com/pdf/parallel/7i96sman.pdf

3 Likes

Gestion des Boutons Poussoirs en Pont Diviseur avec Arduino et Contrôle d’un Moteur Pas à Pas

Introduction

Dans le cadre de la SAE sur la commande d’un moteur pas à pas, l’objectif principal est de concevoir un système simple et efficace pour interagir avec le moteur. Pour cela, un LCD Keypad Shield a été intégré au projet. Ce shield offre une interface utilisateur composée d’un écran LCD 16x2 et de boutons poussoirs (Right, Up, Down, Left, Select). Ces boutons sont connectés à une seule entrée analogique de l’Arduino via un pont diviseur de tension, permettant une gestion optimisée des broches.


Présentation Technique

Principe des Boutons en Pont Diviseur

Les boutons du LCD Keypad Shield sont configurés en série avec des résistances formant un pont diviseur. Lorsqu’un bouton est pressé :

  • Une résistance spécifique est sélectionnée, modifiant la tension en sortie.
  • Cette tension est lue par l’Arduino via l’entrée analogique A0.
  • Chaque bouton est associé à une plage de valeurs analogiques, permettant à l’Arduino d’identifier lequel est activé.
Composants Utilisés :
  1. Moteur pas à pas :
  • Modèle : 17HS19-2004S1.
  • Type : Bipolaire, 1.8°/pas (200 pas/tour).
  • Courant nominal : 2A par phase.
  • Résistance : Environ 1.66 Ω/phase.
  • Couple : 59 Ncm.
  1. Driver DRI0043 :
  • Contrôle via fréquence.
  • Réglage du courant limite pour protéger le moteur.
  1. LCD Keypad Shield :
  • Écran LCD 16x2.
  • Boutons connectés à l’entrée analogique A0 via un pont diviseur.
  1. Arduino :
  • Modèle : Nano ou Pro Mini, avec le processeur ATmega328P.
Principe des Boutons en Pont Diviseur :

Les boutons poussoirs sont reliés à une seule entrée analogique grâce à une chaîne de résistances. Lorsqu’un bouton est pressé, une tension unique est générée sur la broche A0. Cette tension est mesurée par l’Arduino pour identifier quel bouton est pressé.

Avantage :

3. Programme Arduino

Fonctionnement :
  1. Lecture continue des valeurs analogiques sur A0.
  2. Identification des boutons pressés et déclenchement des actions correspondantes :
  • Modification de la vitesse avec les boutons Up et Down.
  • Changement de direction avec les boutons Left et Right.
  • Démarrage/Arrêt avec Select.
  1. Génération des impulsions pour le moteur avec TimerOne pour un contrôle précis de la vitesse.

Code Arduino :

#include <LiquidCrystal.h>
#include <TimerOne.h>

// Initialisation de l'écran LCD
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

// Définition des broches pour le driver du moteur
int const dirPin = 12; // DIR - pour la direction
int motorSpeed = 1000;  // Fréquence initiale (en Hz)
bool motorDirection;    // true = sens horaire, false = sens antihoraire
bool motorRunning = 0;  // État du moteur (marche/arrêt)
bool envoie;

// Variables pour la gestion des impulsions
bool pulseState = 0;     // État de la broche d'impulsion
int Position = 0;
float Inctemps = 0;
float temps = 0;
int valeur = 0;

// Fonction pour générer les impulsions (gérée par interruption Timer1)
void togglePulse() {
    Inctemps = 2 / motorSpeed;
    temps += Inctemps;

    if (motorRunning == 1) {
        // Générer les impulsions pour le driver du moteur
        pulseState = !pulseState;
        digitalWrite(11, pulseState);    
    
        if (motorDirection == 1) { // Sens antihoraire
            Position++;
        }
        if (Position > 32000) { 
            Position = 32000; 
        }
        if (motorDirection == 0) { // Sens horaire
            Position--;
        }
        if (Position < -32000) { 
            Position = -32000; 
        }

        // Lecture du potentiomètre (A1)
        valeur = analogRead(A1);   
        envoie = 1; 
    }
}

void setup() {
    lcd.begin(16, 2); // Initialisation du LCD
    lcd.setCursor(0, 0);
    lcd.print("ISIS Simulator"); // Affichage du nom du projet
    
    pinMode(12, OUTPUT); // Direction du moteur
    pinMode(11, OUTPUT); // Fréquence driver
    Serial.begin(57600); // Initialisation du port série

    // Initialisation du Timer1 pour les impulsions
    Timer1.initialize(1000); // Fréquence initiale = 500 Hz
    Timer1.attachInterrupt(togglePulse); // Attacher la fonction togglePulse
}

void loop() {
    // Lecture des boutons pour ajuster direction, vitesse, et état du moteur
    int buttonValue = analogRead(A0);

    lcd.setCursor(12, 0);
    lcd.print("     ");   
    lcd.setCursor(12, 0);
    lcd.print(buttonValue);  

    // Anti-rebond et détection des boutons
    if (buttonValue == 0) { // RIGHT : Sens horaire
        motorDirection = 0;
        digitalWrite(dirPin, 0);
        lcd.setCursor(0, 1);
        lcd.print("Dr");
        delay(100);
    }

    if (buttonValue > 100 && buttonValue < 150) { // UP : Augmenter la vitesse
        motorSpeed += 50;
        if (motorSpeed > 1000) {
            motorSpeed = 1000;
        }
        Timer1.initialize(500000 / motorSpeed);

        lcd.setCursor(0, 0);
        lcd.print("V:    ");
        lcd.setCursor(2, 0);
        lcd.print(motorSpeed);
        delay(100);
    }

    if (buttonValue > 450 && buttonValue < 500) { // LEFT : Sens antihoraire
        motorDirection = 1;
        digitalWrite(dirPin, 1);
        lcd.setCursor(0, 1);
        lcd.print("Ga");
        delay(100);
    }

    if (buttonValue > 290 && buttonValue < 320) { // DOWN : Réduire la vitesse
        motorSpeed -= 50;
        if (motorSpeed < 1) {
            motorSpeed = 1;
        }
        Timer1.initialize(500000 / motorSpeed);

        lcd.setCursor(0, 0);
        lcd.print("V:    ");
        lcd.setCursor(2, 0);
        lcd.print(motorSpeed);
        delay(100);
    }

    if (buttonValue > 700 && buttonValue < 750) { // SELECT : Activer/Désactiver le moteur
        motorRunning = !motorRunning;
        if (motorRunning == 0) {
            Position = 0;
        }
        lcd.setCursor(0, 0);
        if (motorRunning == 1) {
            lcd.print("ON ");
            lcd.print(motorSpeed);
        } else {
            lcd.print("OFF     ");
        }
    }

    // Affichage et envoi des données
    if (envoie == 1) {
        lcd.setCursor(3, 2);
        lcd.print("      ");
        lcd.setCursor(3, 2);
        lcd.print(Position);

        Serial.print("ISIS Simulator - Temps: ");
        Serial.print(temps);
        Serial.print(" | Vitesse: ");
        Serial.print(motorSpeed);
        Serial.print(" | Position: ");
        Serial.println(Position);

        envoie = 0;
    }
}

Ensuite, voici les courbes :mechanical_arm:

Le projet ISIS Simulator nous a permis de comprendre et d’explorer toutes les étapes nécessaires à la réalisation d’un système de commande avancée d’un moteur pas à pas. En travaillant comme un groupe soudé, nous avons pu relever les défis techniques et développer des compétences clés à chaque étape du processus.

  1. Analyse et planification :
    Nous avons commencé par comprendre les besoins du projet, notamment la commande du moteur pas à pas, la gestion des boutons du LCD Keypad Shield, et l'utilisation d’un pont diviseur de tension. Cette phase a renforcé notre capacité à organiser un travail d'équipe et à répartir les responsabilités efficacement.
  2. Câblage et montage matériel :
    L’assemblage du circuit, avec le moteur, le driver et l’Arduino, nous a appris l’importance d’un câblage précis et bien documenté. Nous avons découvert comment optimiser les ressources matérielles, comme l’utilisation d’une seule broche analogique pour gérer plusieurs boutons.
  3. Simulation avec ISIS Simulator :
    La simulation dans ISIS Simulator a été une étape essentielle pour tester nos idées avant les essais pratiques. Cela nous a permis de détecter et de corriger des erreurs de conception, renforçant ainsi la fiabilité du système.
  4. Programmation Arduino :
    Le développement du programme a été une expérience enrichissante, notamment avec l'utilisation de TimerOne pour gérer les interruptions et garantir un contrôle précis de la vitesse. Nous avons aussi appris à gérer les rebonds des boutons et à envoyer des données sur le port série.
  5. Test et validation :
    Les essais pratiques ont validé nos choix techniques et nous ont montré l'importance de mesurer les performances (temps, vitesse, position). Cela a renforcé notre capacité à analyser et interpréter les résultats.
  6. Travail d’équipe et enseignements :
    Ce SAE nous a appris que la collaboration, l’écoute et l’entraide sont essentielles dans un projet technique. En partageant nos connaissances, nous avons surmonté les difficultés et renforcé notre cohésion.

En conclusion, ce projet a été une expérience complète et formatrice, combinant théorie, pratique, et travail d'équipe. Le nom de notre groupe, ISIS Simulator, symbolise notre approche collaborative et notre capacité à innover ensemble. Nous avons acquis des compétences transférables pour des applications variées, allant de la robotique aux systèmes automatisés.