Stepper nema 34 con driver DM542

Ciao a tutti, sto cercando di controllare un grosso stepper con arduino attraverso un driver industriale generando sul pul del driver la quadra richiesta per muovere lo stepper e controllandone il verso con il pin dir.

Funziona tutto, riesco anche a controllare la velocità modificando a runtime il delay fra un impulso e l'altro con un potenziometro, ma mi rendo conto che il sw da sviluppare per gestirlo in questo modo è troppo complesso se intendo implementare rotazioni di archi precisi (che in realtà è quello che mi serve).

Sto provando a utilizzare le librerie Stepper e AccelStepper, ma probabilmente c'è qualche differenza fra il driver che uso io e quelli previsti dalle librerie perchè ottengo risultati assolutamente non deterministici in fatto di accelerazione, ampiezza di movimento e velocità.

Sto cercando di fare qualcosa di impossibile o sto solo facendo qualche grossolano errore di programmazione o di cablaggio?

Nello schema che ho implementato pul e dir del driver sono collegati a due porte digitali di arduino a cui ho collegato anche un potenziometro la cui lettura influenza il delay fra un impulso e l'altro, i cablaggi che ho trovato a corredo delle librerie invece mostrano driver che hanno più linee in ingresso, mi viene da pensare che quindi il sw sviluppato per quei driver non sia compatibile con il mio, ho ragione? Io ho ulilizzato

Stepper stepper(STEPS, 6, 7);

per inizializzare la libreria, i due pin sono rispettivamente dir e pul del driver e STEPS gli step impostati sul driver, a questo punto mi aspettavo di avere il controllo con

  stepper.setSpeed(600);
  stepper.step(1600);

ma in realtà non c'è nessuna corrispondenza fra gli step impostati e la rotazione, e spesso il motore stalla, non so dove sto sbagliando...

Grazie a tutti per il vostro tempo.

gp

Ciao. Io di motori capisco poco, ma credo che senza modello del motore e del driver sia difficile aiutarti anche per chi ne capisce...

Dovresti mettere il codice completo perché le librerie in questione prevedono l'utilizzo con diversi tipi di driver e così è difficile capire cosa stai facendo.

Ciao fratt, il motore e il driver sono specificati nel titolo. :wink:

Ciao cotestatnt, in realtà il codice è banale:

#include <Stepper.h>

#define STEPS 800

Stepper stepper(STEPS, 6, 7); // 6=DIR, 7= PUL

void setup() {
  stepper.setSpeed(300);
}

void loop() {
  stepper.step(1600);
  delay(300);
  stepper.step(-1600);
  delay(300);
}

driver settato a 800 impulsi/giro, mi aspetterei 2 giri in un verso e due nell'altro, ne fa circa mezzo, la velocità è molto bassa, ma appena l'aumento stalla o si muove a scatti.

Se lo piloto così:

int driverPUL = 7;    // PUL
int driverDIR = 6;    // DIR
int spd = A0;     // Potentiometer

// Variables

int pd = 500;       // Pulse Delay period

void setup() {

  pinMode (driverPUL, OUTPUT);
  pinMode (driverDIR, OUTPUT);

}

void loop() {
  
    pd = map((analogRead(spd)),0,1023,2000,50);

    digitalWrite(driverDIR,LOW);
    digitalWrite(driverPUL,HIGH);
    delayMicroseconds(pd);
    digitalWrite(driverPUL,LOW);
    delayMicroseconds(pd);
 
}

funziona bene, moto regolare, ottima coppia, velocità massima elevata, ma chiaramente diventa complicato da gestire se voglio movimenti precisi.

Non conosco nel dettaglio come lavora questa libreria, ma credo che non gestisca l'accelerazione.
In passato per realizzare una movimentazione semplice (e non bloccante) ho usato il Timer1 (ATMega328) per la generazione del segnale di stepping.

Non metto lo sketch completo perché era pensato per un driver TMC connesso via UART e potrebbe solo creare confusione, ma era una cosa tipo cosi:

volatile bool doStepCW = false;
volatile bool doStepCCW = false;
bool motorDir = false;

// Set Timer1 interrupt frequency
void setMotorSpeed(uint16_t stepPerSecond) {
  #define PRESCALER 8
  noInterrupts();
  // Clear registers
  TCCR1A = 0; TCCR1B = 0; TCNT1 = 0;
  OCR1A =  (uint16_t) ((16000000UL / (PRESCALER * stepPerSecond)) - 1);
  TCCR1B |= (1 << WGM12);   // CTC
  TCCR1B |= (1 << CS11);    // Prescaler 8
  TIMSK1 |= (1 << OCIE1A);  // Output Compare Match A Interrupt Enable
  interrupts();
}

// Timer 1 Interrupt Service Routine. If motor must run, simply toggle the state of STEP_PIN
ISR(TIMER1_COMPA_vect) {
  if (doStepCW || doStepCCW)
    digitalWrite(STEP_PIN, digitalRead(STEP_PIN) ^ 1);
}

void loop() {
// If low speed button CW or CCW is pressed, set speed (step/second)
  if (minSpeedCW.falling() || minSpeedCCW.falling())
    setMotorSpeed(SPEED_MIN);

  if (midSpeedCW.falling() || midSpeedCCW.falling())
    setMotorSpeed(SPEED_MID);

  if (maxSpeedCW.falling() || maxSpeedCCW.falling())
    setMotorSpeed(SPEED_MAX);

  // Set motor direction according to type of button pushed
  if ((minSpeedCW || midSpeedCW || maxSpeedCW) && motorDir != false) {    // Clockwise
    motorDir = false;
    digitalWrite(DIR_PIN, motorDir);    // Set DIR pin value
  }
  if ((minSpeedCCW || midSpeedCCW || maxSpeedCCW ) && motorDir != true) { // Counterclockwise
    motorDir = true;
    digitalWrite(DIR_PIN, motorDir);    // Set DIR pin value
  }

  // Keep motor running while button remain pushed
  doStepCW = minSpeedCW || midSpeedCW || maxSpeedCW;
  doStepCCW = minSpeedCCW || midSpeedCCW || maxSpeedCCW;
}

minSpeedCW, minSpeedCCW, midSpeedCW sono istanze di una piccola classe fatta apposta per gestire la pressione del pulsante e distinguere i diversi eventi (ovviamente se può essere utile non ho problemi a condividere).

Hai ragione... Mi sto rincogl....

Nel titolo è riportato "Stepper nema 34" e, quello NON identifica il motore ma le sue dimensioni ...

... devi indicare il modello esatto così da poter identificare il motore e le sue caratteristiche.

Guglielmo

Vero, nema 34 indica solo la dimensione, è un 34hs7440d12.7l34j5-25-2 , di seguito le caratteristiche:
image

Domanda: visto che il motore è compatibile con il driver (da specifiche del driver) e visto che la parte elettronica si interfaccia al driver e non al motore, perchè è importante conoscere le caratteristiche elettriche del motore? Non dovrebbe bastare conoscere le caratteristiche del driver?

Grazie.

In linea di massima si, almeno finché resti nel range operativo del motore: ad esempio se cerchi di fare andare uno stepper a 5/10000 RPM, il driver magari ce la fa senza problemi, ma il motore andrà in stallo di sicuro. Comunque non mi pare questo il caso...

@cotestatnt Grazie per il codice che hai condiviso, ma purtroppo credo che tu sia anni luce avanti a me :wink: e ho un po' di problemi a interpretarlo.

Il concetto di utilizzare il timer per generare la quadra per il driver mi è chiaro, quello che mi è meno chiaro è come vari l'ampiezza del segnale per cambiare la velocità, immagino sia tutto qui:

void setMotorSpeed(uint16_t stepPerSecond) {
  #define PRESCALER 8
  noInterrupts();
  // Clear registers
  TCCR1A = 0; TCCR1B = 0; TCNT1 = 0;
  OCR1A =  (uint16_t) ((16000000UL / (PRESCALER * stepPerSecond)) - 1);
  TCCR1B |= (1 << WGM12);   // CTC
  TCCR1B |= (1 << CS11);    // Prescaler 8
  TIMSK1 |= (1 << OCIE1A);  // Output Compare Match A Interrupt Enable
  interrupts();
}

potresti commentarmi ad uso DUMMIES questa procedura?

Poi c'è il loop:

...
 if (minSpeedCW.falling() || minSpeedCCW.falling())
    setMotorSpeed(SPEED_MIN);
...

Mi è più o meno chiaro il fatto che ci siano gli eventi CW e CCW in or per settare le relative velocità, ma cosa sono min, mid e maxSpeedCW e i corrispettivi CCW?

Grazie.

gp

Ciao @gporciello , sarà un post lungo. Armati di pazienza :wink:

Come prima cosa devi avere chiaro come funzionano gli interrupt su un microcontrollore, non ho idea di quali siano le tue conoscenze al riguardo. Detto molto banalmente per ciascun tipo di MCU puoi avere diversi tipi di interrupt: associati ad eventi "esterni" come un segnale di input, associati ad uno dei Timer disponibili, alla fine di una conversione analogica, quando hai un byte nell'UART etc etc.
Quando viene sollevato un interrupt (che è stato precedentemente configurato), il micro mette in pausa quello che sta facendo, esegue la Interrupt Service Routine (ISR) e continua da dove aveva interrotto.
Prova a googlare qualcosa tipo come funzionano gli interrupt nei microcontrollori per chiarirti un po' le idee.

Tornando al pezzo di codice in questione, la funzione setMotorSpeed() va a modificare la frequenza del segnale ad onda quadra generata con la ISR associata all'output compare interrupt.

Come puoi vedere l'unica variabile sulla quale puoi agire a run time è stepPerSecond che va a determinare a quale valore verrà impostato il valore del registro OCR1A che è appunto il registro di "output compare registers" (vedilo come una variabile a 16 bit, solo che è a livello hardware nel micro).
Questo registro viene costantemente comparato con il valore del Timer1 e quando sono uguali viene settato il flag di interrupt.
La formula per determinare il valore la trovi nel datasheet. Per i più pigri (come me), ci sono anche dei calcolatori online.

Le altre istruzioni servono a "configurare" il micro al fine di usare questa modalità e con le frequenze desiderate.
Per altri dettagli sui registri in questione ti rimando al datasheet del microcontrollore ATMega328

Per quanto riguarda le istruzioni nel loop(), come ti ho scritto c'è una classe che gestisce la pressione dei pulsanti (debounce, fronte di salita, fronte di discesa etc etc). Se ti interessa metto anche quella.
Puoi interpretare l'if sostanzialmente in questo modo (solo che esegue le istruzioni solo sul fronte di discesa dei due segnali):

if (digitalRead(midSpeedCW) == LOW || digitalRead(midSpeedCCW) == LOW) {
  etc etc

In pratica il pezzo di codice è preso da un progetto che serviva per gestire da remoto il fuoco di un grande telescopio di un osservatorio astronomico dove erano previsti 6 pulsanti per muovere il meccanismo in modalità FAST, NORMAL, SLOW ovviamente avanti e indietro.

Il vantaggio di questa soluzione per generare l'onda quadra è che hai una grande precisione nel segnale generato e che viene gestito praticamente tutto a livello hardware dal micro senza rallentamenti dovuti al resto del codice.

Lo svantaggio è che il software non è direttamente trasportabile su altre MCU, ma è necessario adeguarlo all'hardware specifico. Inoltre l'escursione in frequenza è limitata dai valori possibili per OCR1A anche se piuttosto ampi in realtà.
Ad esempio con il prescaler fissato ad 8, i valori di frequenza possibili (dell'interrupt) possono variare da circa 30Hz fino a svariati KHz.
In realtà la frequenza del segnale ad onda quadra sarà la metà perché ogni volta che viene sollevato l'interrupt lo stato dell'uscita viene invertito.

Mi rendo conto che un post non può essere assolutamente esaustivo per la materia e spero di non aver confuso ulteriormente le tue idee :crazy_face:
Per qualsiasi altro chiarimento chiedi pure.

Ho dimenticato di aggiungere tra gli aspetti "negativi" anche il fatto che perdi la possibilità di usare i pin associati al Timer1 (D9 e D10) in modalità PWM, ma ovviamente li puoi usare come gli altri gpio come input o output.

Direi che ci risentiamo quando avrò imparato a giocare con gli interrupt, avevo intenzione di affrontarli già da tempo, adesso è il momento.

Nel frattempo mi acconteto di andare a naso con la variazione di velocità...

Grazie.

gp

Scusate se ritorno sull'argomento, ma quindi le librerie Stepper e AccelStepper non si possono usare con i driver del tipo che sto usando io? E in questo caso, esiste un driver compatibile con queste librerie in grado di reggere i 4 A dello stepper che ho bisogno di pilotare?

Certo che si possono usare!

Ottima notizia, ti ringrazio, ma a questo punto come va fatto il cablaggio, visto che rispetto agli schemi che trovo sul sito io ho solo pul e dir come segnali da gestire?

Qui trovi tutte le indicazioni del caso

Se non ricordo male il segnale di enable dovrebbe essere attivo di default e quindi non dovrebbe essere necessario cablarlo, ma se lo prevedi male non fa.

Grazie cotestatnt, ma il mio problema non è il datasheet del driver (a proposito, grazie per avermelo girato, ma lo avevo già), il mio problema è il cablaggio fra arduino e il driver.

Guardando gli esempi di utilizzo della libreria Stepper (Stepper) i cablaggi prevedono che arduino gestisca le singole fasi del motore pilotandole attraverso il driver, mentre i driver come il mio gestiscono loro le fasi del motore richiedendo solo la quadra della frequenza e il verso di rotazione (PUL e DIR), come dici tu il segnale di ENABLE è opportuno ma non necessario, da qui il mio dubbio che le librerie non siano fatte per questo tipo di driver.

Se è previsto che quelle librerie possano gestire anche questo tipo di driver da qualche parte ci dovrà pur essere uno schema di collegamento, ma io non l'ho trovato da nessuna parte...

Allora non avevo capito la tua richiesta...

Di che schema hai bisogno? Sono due pin!

Andando a spulciare i sorgenti della libreria Stepper direi che non è adatta al tuo driver e aggiungo che nell'eseguire la sequenza degli step impostati è pure bloccante...

Se invece vuoi usare AccelStepper devi impostare il tipo di driver corretto AccelStepper::DRIVER

Inoltre ci sono anche altre ottime librerie nel repository come ad esempio questa FastAccelStepper oppure questa StepperDriver