Controllare motori passo a passo con Arduino

Ho deciso di scrivere questo articolo perché, trovandomi nella necessità di sviluppare un progetto che utilizza motori passo a passo, ho riscontrato che le guide disponibili in rete sono spesso lacunose o di difficile comprensione.

Come funziona un motore passo a passo?

Introdurrò in linea generale il principio di funzionamento, per gli aspetti matematico-fisici rimando alla mia tesi di laurea; è importante avere ben chiaro il funzionamento, in modo da capire come è meglio programmare Arduino. Wikipedia scrive: i motori passo-passo sono motori che, a differenza di tutti gli altri, hanno come scopo quello di mantenere fermo l’albero in una posizione di equilibrio: se alimentati si limitano infatti a bloccarsi in una ben precisa posizione angolare. Solo indirettamente è possibile ottenerne la rotazione: occorre inviare al motore una serie di impulsi di corrente, secondo un’opportuna sequenza, in modo tale da far spostare, per scatti successivi, la posizione di equilibrio. È così possibile far ruotare l’albero nella posizione e alla velocità voluta semplicemente contando gli impulsi ed impostando la loro frequenza, visto che le posizioni di equilibrio dell’albero sono determinate meccanicamente con estrema precisione.

L’elettromagnete in alto (1) viene eccitato, attraendo il dente più vicino di un attrezzo a forma di rotore metallico. Con il dente allineato all’elettromagnete 1, inizierà la rotazione verso l’elettromagnete 2.

Fase 1 motore passo

Fase 2 motore

 

 

 

 

 

 

L’elettromagnete in alto (1) viene spento e viene eccitato l’elettromagnete a destra (2), tirando il dente successivo leggermente verso destra. Questo processo produce una rotazione di 3.6° in questo esempio.

Fase 3 motore

fase 4 motore

 

 

 

 

 

 

L’elettromagnete a sinistra (4) viene eccitato, si ha un’altra rotazione di 3.6°. Quando l’elettromagnete in alto (1) sarà di nuovo eccitato, i denti del rocchetto dovranno ruotare nella posizione successiva; poiché vi sono 25 denti, occorrono 100 passi per realizzare una rotazione completa.

Unipolare o Bipolare?

Negli shop online trovate la possibilità di acquistare motori bipolari (generalmente a 4 fili) e motori unipolari (a 8 fili). La differenza consiste che mentre nel bipolare i magneti 1-3 e i magneti 2-4 condividono la stessa linea di alimentazione, negli unipolari le linee di alimentazione sono distinte per ogni magnete. I più diffusi sono i bipolari, perché richiedono dei sistemi di controllo più semplici, ma se avete a disposizione un unipolare, basta accoppiare opportunamente i fili di alimentazione ed otterrete un bipolare. La guida prenderà come esempio i motori bipolari.

Il driver

Per far muovere il motore è necessario dotarsi di un driver, un circuito che fornisce le tensioni e le correnti necessarie; ce ne sono diversi, ma conviene prendere quelli più evoluti, che hanno una logica integrata e sopratutto la possibilità del controllo in micropasso. Per chi volesse approfondire a livello di integrato, rimando al  Blog di Mauro Alfieri.

Il driver che utilizzerò io è PiBot Stepper Motor Driver Rev2.2; i collegamenti e le istruzioni per questo driver sono praticamente identiche per la maggior parte dei dispositivi recuperabili in commercio.

Analizziamo i collegamenti:

Prima di tutto serve individuare le fasi del motore, ed anche qui rimando al Blog di Mauro Alfieri. Una volta individuate le fasi le colleghiamo al driver secondo lo schema elettrico (ATTENZIONE, i colori dei cavi dipendo spesso dalla fantasia del produttore, quindi controllate sempre le fasi come da guida):

input-and-outputAll’ingresso di input 1A (o 1B) colleghiamo l’alimentazione per il motore; ricordatevi che più è alta la tensione di alimentazione (ovviamente entro i limiti segnati dal produttore) e più performante sarà il vostro motore. Ora passiamo ai collegamenti lato Arduino (in basso a destra):

  1. en: significa enable. Quando a questo ingresso c’è una tensione alta (3,3 – 5 V) il motore è alimentato. Questo non significa che si muove, ma che sta tenendo la posizione; viene generata una “coppia di tenuta” e se noi proviamo a muovere con le mani l’asse del motore, noteremo che opporrà resistenza. Se togliamo l’alimentazione il motore gira a vuoto; ovviamente nelle fasi di rotazione, su enable ci deve essere la tensione di attivazione.
  2. dir: significa direzione. Quando c’è una tensione il motore gira in un senso, quando non c’è gira nel senso opposto.
  3. clk: significa clock. Qui c’è il cuore per mettere in rotazione il motore. Infatti ogni qualvolta arriva uno stato logico alto, il motore avanzerà di un passo; a seguire ci deve essere uno stato logico basso. Quindi con la sequenza alto-basso facciamo avanzare il motore; è facile dedurre che maggiore è la frequenza con cui avviene questa sequenza e maggiore sarà la velocità di rotazione.
  4. gnd: massa

Colleghiamo ad Arduino

Il terminale di Enable se vogliamo possiamo collegarlo direttamente ai 5V di Arduino; ovviamente così facendo il motore sarà sempre in tensione. Nel caso invece vogliamo spegnere ed accendere il motore, lo colleghiamo ad un uscita digitale; dir e clk li colleghiamo ad un uscita digitale a nostro piacimento.

Programmiamo Arduino (il brutto codice):

ho scritto brutto codice, perché è quello che più frequentemente di trova nel web. Non è per nulla ottimizzato per controllare un motore passo-passo, ma funziona ed è didatticamente valido:

int DIR = 2;
int CLK = 3;
int EN = 4;
 
void setup() {
	pinMode(DIR, OUTPUT);
	pinMode(CLK, OUTPUT);
	pinMode(EN, OUTPUT);
}
 
void loop()
{
	digitalWrite(EN,HIGH);
	digitalWrite(DIR,HIGH);
        delay(50);
	for ( int i=0; i<200; i++) 
	{ 
	  stepGo(); delay(10); 
	}
	digitalWrite(DIR,LOW);
	for ( int i=0; i<200; i++) 
	{ 
	  stepGo(); delay(10); 
	}
	digitalWrite(EN,LOW);	
}
 
void stepGo() {
        digitalWrite(CLK, HIGH);
        delayMicroseconds(100);
        digitalWrite(CLK, LOW);
        delayMicroseconds(100);
}

Come sempre abbiamo dichiarato i pin di uscita ed indicizzati, analizziamo il voip loop()

digitalWrite(EN,HIGH);
digitalWrite(DIR,HIGH);

con la prima linea alimentiamo il motore, mentre con la seconda definiamo il senso di rotazione.

for ( int i=0; i<200; i++) 
 { 
  stepGo(); delay(10); 
 }

qui inizia un semplice ciclo che richiama la funzione stelGo() che ora vediamo:

void stepGo() {
 digitalWrite(CLK, HIGH);
 delayMicroseconds(100);
 digitalWrite(CLK, LOW);
 delayMicroseconds(100);
}

qui viene generata la sequenza alto-basso che fa avanzare di un passo alla volta il motore. Il delayMicroseconds() tra uno stato logico alto e basso è necessario per dare fisicamente al motore il tempo di avanzare, se non ci fosse, il motore inizierebbe a fischiare con aggiunta di rumori inquietanti. Ovviamente variando l’intervallo variamo la velocità di rotazione; più il delay è basso e più veloci si va.

Perché brutto codice? Per la presenza del delay(); tenete presente che in modalità full-step (passo intero, in fondo all’articolo spiego il micropasso), nella maggior parte degli attuatori in commercio ci vogliono 200 passi per fare un giro completo. Il delay() ha l’effetto di bloccare qualsiasi altra funzione della scheda arduino, in pratica rimane in “sospeso” finché non è trascorsa la pausa. Capite bene che se oltre al motori avete collegati sensori, pannelli LCD o altro, vi ritrovate con la scheda ferma anche per diversi secondi durante l’avanzamento del motore il che è una cosa da evitare.

Un codice buono:

int DIR = 2;
int CLK = 3;
int EN = 4;

boolean motorStatus = false;
unsigned long onDuration = 100;
unsigned long offDuration = 100;
unsigned long NextToggle = micros() + offDuration;
 
void setup() {
	pinMode(DIR, OUTPUT);
	pinMode(CLK, OUTPUT);
	pinMode(EN, OUTPUT);
} 
void loop()
{
	digitalWrite(EN,HIGH);
	digitalWrite(DIR,HIGH);
    delay(50);
	for ( int i=0; i<200; i++) 
	{ 
          if (micros() > NextToggle) 
          {
		stepGo(CLK);
          }
	}
	digitalWrite(DIR,LOW);
	
        for ( int i=0; i<200; i++) 
        {
	  if (micros() > NextToggle) 
          {
		stepGo(CLK);
          }
        }
	digitalWrite(EN,LOW);	
}
 
void stepGo(int CLK){
motorStatus = !motorStatus;
digitalWrite(CLK,motorStatus);
if (motorStatus)
{
NextToggle = micros() + onDuration;
}
else
{
NextToggle = micros() + offDuration;
}
}

Rispetto al precedente ho aggiunto qualche variabile:

boolean motorStatus = false;
unsigned long onDuration = 100;
unsigned long offDuration = 100;
unsigned long NextToggle = micros() + offDuration;

micros() restituisce il valore in microsecondi del tempo trascorso dall’accensione della scheda Arduino.

All’interno dei cicli for è comparso:

if (micros() > NextToggle)

mentre la funzione è cambiata in:

void stepGo(int CLK){
motorStatus = !motorStatus;
digitalWrite(CLK,motorStatus);
if (motorStatus)
{
NextToggle = micros() + onDuration;
}
else
{
NextToggle = micros() + offDuration;
}
}

Il principio di funzionamento è abbastanza semplice. Viene salvato nella variabile NextToggle il tempo trascorso dall’accensione della scheda più un valore definito. Ad ogni ciclo di loop il valore restituito da micros() ovviamente aumenta, finché non diventa maggiore di NextToggle, attivando la chiamata alla funzione.

In void stepGo la prima operazione eseguita è un cambio di livello logico rispetto al precedente (ricordate la sequenza alto-basso necessaria per l’avanzamento?), e si ha la scrittura di questo nel pin di CLK (digitalWrite(CLK,motorStatus;). L’if serve solo a poter definire un tempo differente tra il livello logico alto ed il livello logico basso, andando comunque ad implementare il valore di NextToggle. Quindi la funzione si risolve e si torna in loop dove reinizia il ciclo.

Con questo codice riusciamo a definire un tempo per il livello logico alto e per quello basso, senza dover utilizzare il delay().

Il codice migliore:

il controllo dei motori passo è una delle cose più interessanti da fare con Arduino e quindi non è tardato molto prima che qualcuno scrivesse una libreria dedicata, che si chiama Stepper e la troviamo tra gli esempi.

#include <Stepper.h>

int DIR = 2;
int CLK = 3;
int EN = 4;
int steps = 1;
int velocita = 1500;
 
 Stepper stepGo(steps, DIR, CLK);
 
void setup() {
	pinMode(DIR, OUTPUT);
	pinMode(CLK, OUTPUT);
	pinMode(EN, OUTPUT);
} 
void loop()
{
	digitalWrite(EN,HIGH);
	digitalWrite(DIR,HIGH);
        stepGo.setSpeed(velocita);
        delay(50);
	for ( int i=0; i<200; i++) 
	{ 
        stepGo.step(steps);
	}
	digitalWrite(DIR,LOW);
	
        steps=-1;
        for ( int i=0; i<200; i++) 
        {
       stepGo.step(steps);
        }
	digitalWrite(EN,LOW);	
}

questa libreria ha diverse modalità di funzionamento, io analizzo quello concorde agli esempi precedenti:

Stepper stepGo(steps, DIR, CLK);

abbiamo definito la funzione stepGo, che riceve 3 input:

  1. il numero di passi che deve fare ogni volta che viene chiamata;
  2. il pin su cui è collegato il Dir del nostro driver;
  3. il pin su cui è collegato il CLK del nostro driver.

Si imposta la velocità:

 stepGo.setSpeed(velocita);

Quindi ogni qualvolta viene richiamata la funzione, viene fatto avanzare il motore di un numero definito di passi:

stepGo.step(steps);

Per poter far andare il motore in senso inverso basta definire un numero di passi negativo:

steps=-1;

Rispetto ai codici analizzati prima, questa libreria è decisamente più versatile e sopratutto si possono controllare più motori con la stessa funzione, inserendo semplicemente tre parametri.

Questa è una piccola introduzione al controllo dei motori passo a passo; ci sono ancora parecchie cose da apprendere, come l’accelerazione (non si può pretendere che il motore parta fin da subito alla velocità massima) o il controllo in retroazione per il conteggio dei giri.

Il micropasso:

fino ad ora abbiamo ipotizzato di alimentare le fasi una alla volta in sequenza, però se noi alimentassimo contemporaneamente due fasi, l’asse del motore di metterebbe in una posizione intermedia. In questo modo con la sequenza di alimentazione A, A-B, B, abbiamo raddoppiato il numero di passi necessari al motore per fare un giro completo. Questa configurazione si chiama Half-Step

Se inoltre iniziassimo ad alimentare la fase A con frazioni di corrente della fase B, aumenteremo ancora il numero di fasi, da qui controllo in micropasso. Per questo motivo i motori passo a passo stanno avendo un enorme diffusione, perché con un opportuna elettronica si aumenta la precisione di spostamento, mantenendo (più o meno) la coppia erogata, senza riduttori meccanici.Ovviamente per micropassi elevati, servono notevoli capacità di calcolo e frequenze di lavoro.

 

VN:F [1.9.22_1171]
Rating: 9.9/10 (21 votes cast)
VN:F [1.9.22_1171]
Rating: +12 (from 18 votes)
Controllare motori passo a passo con Arduino, 9.9 out of 10 based on 21 ratings

6 pensieri su “Controllare motori passo a passo con Arduino

  1. ottitmo tutorial ma io generalmento uso stepMotor
    MAURO ALFIERI <3

    VA:F [1.9.22_1171]
    Rating: 1.0/5 (1 vote cast)
    VA:F [1.9.22_1171]
    Rating: +1 (from 1 vote)
  2. Ottimo chiaro ci sono riuscito pure io grazie.

    VA:F [1.9.22_1171]
    Rating: 2.0/5 (1 vote cast)
    VA:F [1.9.22_1171]
    Rating: +1 (from 1 vote)
  3. Ciao,
    il post è abbastanza chiaro ma non molto quando parli di come collegare lo stepper driver Pibot ad Arduino..

    Io ho provato ma il motore non si muove. Utilizzo il tuo sketch, ma.. nulla.
    Ruoi darmi indicazioni più precise? magari allegare una foto o uno sketch fritzing?
    Grazie

    VA:F [1.9.22_1171]
    Rating: 2.5/5 (2 votes cast)
    VA:F [1.9.22_1171]
    Rating: +1 (from 1 vote)

Lascia una risposta

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *

Immagine CAPTCHA

*

È possibile utilizzare questi tag ed attributi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>