[RISOLTO]Pilotare motore stepper sostituendo delay con millis

Buongiorno a tutti,
nonostante abbia letto un bel po' di materiale sulla funzione millis() (in questo forum e in altri siti, ancora non riesco a capirne bene il funzionamento. Non ridete di me, ma c'è un concetto fondamentale che forse mi sfugge. Dubito di aver capito anche il semplice esempio di blinkwithoutdelay.

Vorrei sostituire a questo codice la funzione delay con millis, il codice pilota un motore stepper.

int motorPin1 = 1;
int motorPin2 = 2;
int motorPin3 = 3;
int motorPin4 = 4;
int delayTime = 2;
int arresto = 5000;
 
void setup() {
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(motorPin3, OUTPUT);
  pinMode(motorPin4, OUTPUT);
}
 
void loop() {
 for (int i = 0; i < 512; i++){ //un giro 512 step
  digitalWrite(motorPin1, HIGH);
  digitalWrite(motorPin2, HIGH);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, LOW);
  delay(delayTime);
  digitalWrite(motorPin1, LOW);
  digitalWrite(motorPin2, HIGH);
  digitalWrite(motorPin3, HIGH);
  digitalWrite(motorPin4, LOW);
  delay(delayTime);
  digitalWrite(motorPin1, LOW);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, HIGH);
  digitalWrite(motorPin4, HIGH);
  delay(delayTime);
  digitalWrite(motorPin1, HIGH);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, HIGH);
  delay(delayTime);}
  delay (arresto);
 }

Ho fatto vari tentativi per sostituire il delayTime, ma tra le varie spiegazioni c'è qualcosa che interpreto male o proprio non capisco. Questa una mia prova

int motorPin1 = 1;
int motorPin2 = 2;
int motorPin3 = 3;
int motorPin4 = 4;
int pausa = 2;
unsigned long step1Millis = 0;
unsigned long step2Millis = 0;
unsigned long step3Millis = 0;
unsigned long step4Millis = 0;


void setup() {
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(motorPin3, OUTPUT);
  pinMode(motorPin4, OUTPUT);
  }
  
 
void loop() {
  unsigned long istante = millis();             //dichiaro che tipo di variabile è "istante"
  step1Millis = millis();
  if ((istante - step1Millis) > pausa) {         
  step1Millis = istante;
  digitalWrite(motorPin1, HIGH);                
  digitalWrite(motorPin2, HIGH);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, LOW);
  }
  
  if ((istante - step2Millis) > pausa) {         
  step2Millis = istante;
  digitalWrite(motorPin1, LOW);
  digitalWrite(motorPin2, HIGH);
  digitalWrite(motorPin3, HIGH);
  digitalWrite(motorPin4, LOW); 
  }
   
  if ((istante - step3Millis) > pausa) {
  step3Millis = istante;
  digitalWrite(motorPin1, LOW);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, HIGH);
  digitalWrite(motorPin4, HIGH); 
  }
  
 if ((istante - step4Millis) > pausa) {
  step4Millis = istante;
  digitalWrite(motorPin1, HIGH);
  digitalWrite(motorPin2, LOW);
  digitalWrite(motorPin3, LOW);
  digitalWrite(motorPin4, HIGH); 
  }
}

Con questo secondo codice, il motore non si muove..

Grazie a chi si interesserà.

Ciao
Pier

Ciao Pier, io strutturerei il programma in maniera leggermente diversa (per me è più "lineare"):
definirei una variabile short "posizione" che conta da 0 a 3 ( le 4 posizioni del motore)
Con un switch... case della variabile "posizione" chiami le 4 funzioni che settano le uscite ( o se non vuoi le funzioni puoi mettere i digitalWrite nel case), successivamente metti il codice che incrementa posizione dopo il tempo che decidi, ad esempio:

if ((millis()- tempPausa) > pausa) {         
tempPausa= millis();
posizione++;
if(posizione==4) posizione=0;
}

dove tempPausa è global unsigned long temporanea e pausa global unsigned long contenente la pausa che vuoi.

Considerala una traccia, spero ti possa essere utile.

Stefano

Ciao e grazie per il suggerimento, leggo solo ora per motivi di tempo (non ne ho!!).
Appena ho un minuto provo e ti/vi saprò dire cosa ottengo.

Intanto grazie mille!!

Pier

cam9500:

if ((millis()- tempPausa) > pausa) {         

tempPausa= millis();
posizione++;
if(posizione==4) posizione=0;
}




Stefano

Le mie perplessità sono proprio in queste 4 righe. Inizializzo tempPausa a 0. Qundi il programma, almeno alla primissima compilazione assume come valore di tempPausa = 0.
tempPausa assumerà un valore diverso (maggiore tra l'altro) solo nel momento in cui la espressione di verifica di cui sopra di verificherà VERA. Mi viene da pensare che:

SE (orario - tempPausa(che è 0)) > pausa; se VERO salva un nuovo tempPausa; altrimenti nulla.

Mi sembra un cane che si mangia la coda! Finchè la condizione non diventa vera tempPausa non cambierà e finche tempPausa non cambierà, la condizione non potrà essere vera...

dove sbaglio??

Grazie e scusate

Ciao, giusto.... il micro fa sempre operazioni ripetitive e far girare un motore è troppo ripetitivo :slight_smile:
Leggila così:
se metti tempPausa a 0 nell'if, il primo "giro" diventa subito vero poichè millis()-tempPausa è praticamente sempre maggiore di zero quindi tempPausa diventerà uguale a millis(), entra nell'if incrementa la posizione e via.....
I giri successivi finchè millis()-tempPausa sarà minore della pausa l'if sarà falso e nulla cambierà
Solo quando il delta tra millis() e tempPausa sarà superiore di pausa rientrerà nell'if, tempPausa ridiventa uguale a millis(), e farà girare il motore di uno step....

Stefano

Ciao Stefano,
c'ho ragionato su e, immaginando gli eventi dalla primissima condizione in poi... credo di aver capito!!!
ho scritto questo codice:

int motorPin1 = 1;
int motorPin2 = 2;
int motorPin3 = 3;
int motorPin4 = 4;
short pos = 0;
unsigned long pausa = 1;


void setup() {
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(motorPin3, OUTPUT);
  pinMode(motorPin4, OUTPUT);
}

void loop() {
  unsigned long istantePausa = millis();

  switch (pos) {
    case 0:
      digitalWrite(motorPin1, HIGH);
      digitalWrite(motorPin2, HIGH);
      digitalWrite(motorPin3, LOW);
      digitalWrite(motorPin4, LOW);
      break;
    case 1:
      digitalWrite(motorPin1, LOW);
      digitalWrite(motorPin2, HIGH);
      digitalWrite(motorPin3, HIGH);
      digitalWrite(motorPin4, LOW);
      break;
    case 2:
      digitalWrite(motorPin1, LOW);
      digitalWrite(motorPin2, LOW);
      digitalWrite(motorPin3, HIGH);
      digitalWrite(motorPin4, HIGH);
      break;
    case 3:
      digitalWrite(motorPin1, HIGH);
      digitalWrite(motorPin2, LOW);
      digitalWrite(motorPin3, LOW);
      digitalWrite(motorPin4, HIGH);
      break;
  }
  if ((millis() - istantePausa) > pausa) {
    istantePausa = millis();
    pos++;
    if (pos == 4) pos = 0;
  }
}

Il motore gira, però non in maniera fluida e veloce come con il delay.
Cosa può essere?
EDIT: forse l'alimentazione, devo provare con una alimentazione diversa per il motore..

Ti devo un grande grazie per la pazienza e per le spiegazioni. A buon rendere!

Pier

ma prego....figurati!
Digerito il millis() senza alkaselzer 8)....
...e la pulizia del codice che si ottiene facendo cose "apparentemente" più complicate?

Il delay era 2 ms, qui sei sceso ad uno. Purtroppo di stepper non ho grande esperienza......

Stefano

1000 step al secondo... Senza rampa d'accellerazione potrebbero cominciare ad essere un problema... e se non hai un driver sufficientemente potente ti scontri ancora di più con i limiti fisici dello stepper. oltretutto il millis comincia vedo ad avere una certa "granulometria" che potrebbe dare fastidi anche provando a fare un minimo di accellerazione.... forse meglio passare ai micros.

Se corre male c'è da verificare le sequenze di accensione dei pin (ma penso che fossero corrette). Eventualmente puoi usare delle combinazioni diverse di pin per comandare in modo differente il motore (step/halfstep eccetera) ma forse è l'ora di usare la libreria accellStepper.h... che appunto s'arrangia a fare delle rampe d'accellerazione e frenata.
http://www.airspayce.com/mikem/arduino/AccelStepper/

Che driver usi?

Ciao, il driver è un ULN2003APG.
Riguardando il codice ho trovato una parte che credo di aver sbagliato:

void loop() {
  unsigned long istantePausa = millis();

Eh si, ritorno sul millis... Sbaglio o dovrebbe essere inizializzata a 0 e non a millis??

qsecofr:
1000 step al secondo... Senza rampa d'accellerazione potrebbero cominciare ad essere un problema... e se non hai un driver sufficientemente potente ti scontri ancora di più con i limiti fisici dello stepper. oltretutto il millis comincia vedo ad avere una certa "granulometria" che potrebbe dare fastidi anche provando a fare un minimo di accellerazione.... forse meglio passare ai micros.

Se corre male c'è da verificare le sequenze di accensione dei pin (ma penso che fossero corrette). Eventualmente puoi usare delle combinazioni diverse di pin per comandare in modo differente il motore (step/halfstep eccetera) ma forse è l'ora di usare la libreria accellStepper.h... che appunto s'arrangia a fare delle rampe d'accellerazione e frenata.
AccelStepper: AccelStepper library for Arduino

Che driver usi?

Sarò anche banale ma chiedo lo stesso... Perchè parli di 1000 step al secondo?

Intanto ho pure provato con questo codice ma ottengo solo un "fischio", e non capisco perchè se aumento notevolmente il valore di pausa, ottengo immobilismo totale, mi aspettavo che almeno i led integrati nel driver lampeggiassero ( con una frequenza minore, ma che lampeggiassero..)

int motorPin1 = 1;
int motorPin2 = 2;
int motorPin3 = 3;
int motorPin4 = 4;
short pos = 0;
unsigned long pausa = 1;


void setup() {
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(motorPin3, OUTPUT);
  pinMode(motorPin4, OUTPUT);
}

void loop() {
  for (int i = 0; i < 511; i++) {           //512 = giro copmleto
    motore1();
  }
}

void motore1() {
  unsigned long istantePausa = micros();
  switch (pos) {
    case 0:
      digitalWrite(motorPin1, HIGH);
      digitalWrite(motorPin2, LOW);
      digitalWrite(motorPin3, LOW);
      digitalWrite(motorPin4, LOW);
      break;
    case 1:
      digitalWrite(motorPin1, LOW);
      digitalWrite(motorPin2, HIGH);
      digitalWrite(motorPin3, LOW);
      digitalWrite(motorPin4, LOW);
      break;
    case 2:
      digitalWrite(motorPin1, LOW);
      digitalWrite(motorPin2, LOW);
      digitalWrite(motorPin3, HIGH);
      digitalWrite(motorPin4, LOW);
      break;
    case 3:
      digitalWrite(motorPin1, LOW);
      digitalWrite(motorPin2, LOW);
      digitalWrite(motorPin3, LOW);
      digitalWrite(motorPin4, HIGH);
      break;
  }
  if ((micros() - istantePausa) >= pausa) {
    istantePausa = micros();
    pos++;
    if (pos == 4) pos = 0;
  }
}
void loop() {
  unsigned long istantePausa = millis();
......
  if ((millis() - istantePausa) > pausa) {
    istantePausa = millis();
    pos++;
    if (pos == 4) pos = 0;
  }
}

Vero... guarda senza lo switch!
Non dichiararlo li: metti "unsigned long istantePausa" prima del setup() -> nelle dichiarazioni.
Così in teoria non dovrebbe funzionare..... mi sa che se metti pausa a 10 non si muove neppure....

Ecco appunto.... :slight_smile:

Ciao a tutti, questo il codice, funzionante, che condivido con voi.
Una pausa inferiore a 1 non fa funzionare il motore, che emetterebbe solo un "fischio", rimanendo immobile (come con il codice "delay" infondo..)
Grazie agli utili consigli, ho raggiunto un obiettivo tra i tanti che mi sono posto, grazie!!

int motorPin1 = 1;
int motorPin2 = 2;
int motorPin3 = 3;
int motorPin4 = 4;
short pos = 0;
unsigned long pausa = 2;
unsigned long istantePausa = 0;

void setup() {
  pinMode(motorPin1, OUTPUT);
  pinMode(motorPin2, OUTPUT);
  pinMode(motorPin3, OUTPUT);
  pinMode(motorPin4, OUTPUT);
}

void loop() {
  for (int i = 0; i < 511; i++) {           //512 = giro copmleto
    motore1();
  }
}

void motore1() {

  switch (pos) {
    case 0:
      digitalWrite(motorPin1, HIGH);
      digitalWrite(motorPin2, HIGH);
      digitalWrite(motorPin3, LOW);
      digitalWrite(motorPin4, LOW);
      break;
    case 1:
      digitalWrite(motorPin1, LOW);
      digitalWrite(motorPin2, HIGH);
      digitalWrite(motorPin3, HIGH);
      digitalWrite(motorPin4, LOW);
      break;
    case 2:
      digitalWrite(motorPin1, LOW);
      digitalWrite(motorPin2, LOW);
      digitalWrite(motorPin3, HIGH);
      digitalWrite(motorPin4, HIGH);
      break;
    case 3:
      digitalWrite(motorPin1, HIGH);
      digitalWrite(motorPin2, LOW);
      digitalWrite(motorPin3, LOW);
      digitalWrite(motorPin4, HIGH);
      break;
  }
  if ((millis() - istantePausa) >= pausa) {
    istantePausa = millis();
    pos++;
    if (pos == 4) pos = 0;
  }
}

Okkk bene!
Se vuoi provare (per cultura) in effetti potresti sostituire millis() con micros() e metterlo a 1000 (mille micros= 1 millis)
Probabilmente puoi accelerare ancora un pò riducendo da 1000 a 900 micros.

P.S.: l'uln2003 non è un driver per stepper quindi ha dei compromessi.

Stefano

Fatto anche il tentativo con micros e con pausa = 1700.
Questa è la combinazione più veloce che riesco ad ottenere e a me va più che bene!
Con un valore più basso di 1700 (es. 1600) il motore emette quel famoso fischio... Credo sia un limite fisico" dell'hardware.

Comunque ripeto: soddisfatto con millis() e pausa = 2 - ultra soddisfatto con micros() e pausa = 1700.
Per quello che devo fare è pressochè irrilevante, ma l'esperienza è sempre utile.

grazie ancora!

Pier

Prego, alla prossima :wink:

bravo. se vuoi come ulteriore prova potresti far partire la pausa da supponiamo 3000 e diminuirla di qualche micro ogni step che fai fino a raggiungere pause minori. questo ti permette un accellerazione e forse ti permette di far gurare meglio lo step.
passo successivo comprare un pololu :slight_smile:

Forse vado o.t., casomai ditemi se aprire una nuova discussione (se serve..)
Stamani ho recuperato un bellissimo e praticamente nuovo motore passo passo nema 17.
Nonostante abbia guardato qui:
http://www.mauroalfieri.it/elettronica/motori-passo-passo-unipolari-riconoscerne-le-fasi-seconda-parte.html non capisco come individuare le fasi, fino alla "compilazione" della tabella ci arrivo, ma poi non capisco come determinare gli A+, A-,B+,B-,A,B.

Se serve posto la tabellina compilata.
Infine, in attesa dei nuovi acquisti, riuscirò a pilotare questo motore con il driver che ho (ULN2003)?

riuscirò a pilotare questo motore con il driver che ho (ULN2003)?

Se il motore ha 6 o 8 fili , si
E se si, dipende anche da quanto consuma il motore, hai una sigla ?

Non più di tanto: è un motore da 1 5A, l'uln 2003 ti va a fuoco :frowning: