Arduino como controlador de motor paso a paso

Usar Arduino para controlar motor paso a paso

Hay un par de maneras de realizar este proyecto con Arduino, una es usar un encoder rotativo para mover el motor paso a paso. En este tutorial cambiaremos el encoder rotativo por un potenciómetro ordinario y lo usaremos para controlar la posición o la velocidad del motor paso a paso. Usaremos un código que se puede usar aunque se realice de la otra manera y haremos algunas alteraciones para lograr el comportamiento deseado. El objetivo de este artículo es no utilizar librerías ni shields.

Esta entrada de blog contendrá principalmente dos ejemplos de código con alguna explicación. Debes ser consciente de cómo funcionan los potenciómetros antes de leer este post. Ya escribimos una entrada en el blog sobre este tema, donde también analizamos el filtrado de firmware, que puede ser útil en este tipo de aplicaciones.

Posición

Veamos cómo podemos usar el potenciómetro para controlar la posición del motor paso a paso.

Aquí tenemos dos parámetros importantes:

  • DRIVER_PULSE_PERIOD_US que establece la velocidad de movimiento.
  • STEPS_PER_ANALOG_VALUE que fija esencialmente el rango de movimiento.

Este código no realiza ninguna calibración, sino que asume que tanto el motor como el potenciómetro están centrados en el arranque del sistema. El código también asume un ADC de 10 bits (1024 valores analógicos).

#define DRIVER_STEP_PIN   10
#define DRIVER_DIR_PIN    9
#define DRIVER_EN_PIN     8
#define POT_PIN           A0
#define DRIVER_PULSE_PERIOD_US  1600
#define STEPS_PER_ANALOG_VALUE  10
enum Driver_pulse_state_enum {PULSE_IDLE, PULSE_HIGH, PULSE_LOW};
unsigned long time_now = 0;
uint16_t driver_pulse_hold_time_us = DRIVER_PULSE_PERIOD_US/2;
uint8_t driver_pulse_state = PULSE_IDLE;
uint16_t target_pos = 512*STEPS_PER_ANALOG_VALUE;
uint16_t actual_pos = 512*STEPS_PER_ANALOG_VALUE;
int pos_error = 0;
void setup() {
pinMode(DRIVER_STEP_PIN, OUTPUT);
pinMode(DRIVER_DIR_PIN, OUTPUT);
pinMode(DRIVER_EN_PIN, OUTPUT);
digitalWrite(DRIVER_EN_PIN, HIGH);
}
void loop() {
target_pos = analogRead(POT_PIN)*STEPS_PER_ANALOG_VALUE;
pos_error = target_pos - actual_pos;
if((pos_error) && (driver_pulse_state == PULSE_IDLE)){
write_pulse_high();
}
if((micros() - time_now > driver_pulse_hold_time_us) && (driver_pulse_state == PULSE_LOW)){
write_pulse_high();
}
if((micros() - time_now > driver_pulse_hold_time_us) && (driver_pulse_state == PULSE_HIGH)){
write_pulse_low();
}
}
void write_pulse_high(void){
driver_pulse_state = PULSE_HIGH;
if(pos_error > 0){
digitalWrite(DRIVER_DIR_PIN, HIGH);
actual_pos += 1;
}
else if(pos_error < 0){
digitalWrite(DRIVER_DIR_PIN, LOW);
actual_pos -= 1;
}
digitalWrite(DRIVER_STEP_PIN, HIGH);
time_now = micros();
}
void write_pulse_low(void){
digitalWrite(DRIVER_STEP_PIN, LOW);
time_now = micros();
if(pos_error){
driver_pulse_state = PULSE_LOW;
}
else{
driver_pulse_state = PULSE_IDLE;
}
}
Este es esencialmente un controlador P donde encontramos una diferencia (pos_error) entre la posición real (actual_pos) y la posición objetivo (target_pos) y la usamos para controlar el motor.

Velocidad

Este enfoque es un poco diferente. Aquí es posible hacer funcionar el motor una cantidad infinita en ambos sentidos. El potenciómetro controla la velocidad de marcha del motor y en qué dirección. Cuando el pote está centrado, el motor no se mueve.

Los tres parámetros importantes de este código son:

  • DEADZONE que establece el rango del potenciómetro central donde el motor está en marcha lenta.
  • MIN_DRIVER_PULSE_PERIOD_US que establece la velocidad máxima.
  • MAX_DRIVER_PULSE_PERIOD_US que establece la velocidad mínima.
#define DRIVER_STEP_PIN   10
#define DRIVER_DIR_PIN    9
#define DRIVER_EN_PIN     8
#define POT_PIN           A0
#define DEADZONE 50
#define MIN_DRIVER_PULSE_PERIOD_US  1000 //max speed
#define MAX_DRIVER_PULSE_PERIOD_US  3000 //min speed
enum Driver_pulse_state_enum {PULSE_IDLE, PULSE_HIGH, PULSE_LOW};
unsigned long time_now = 0;
uint16_t driver_pulse_hold_time_us = MIN_DRIVER_PULSE_PERIOD_US/2;
uint8_t driver_pulse_state = PULSE_IDLE;
int normalized_analog_value = 0;
uint8_t idle_flag = 1;
void setup() {
    pinMode(DRIVER_STEP_PIN, OUTPUT);
    pinMode(DRIVER_DIR_PIN, OUTPUT);
    pinMode(DRIVER_EN_PIN, OUTPUT);
    digitalWrite(DRIVER_EN_PIN, HIGH);
}
void loop() {
    normalized_analog_value = analogRead(POT_PIN) - 512;
    if(abs(normalized_analog_value)-DEADZONE < 0){
        idle_flag = 1;
    }
    else{
        idle_flag = 0;
    }
    driver_pulse_hold_time_us = map(abs(normalized_analog_value), DEADZONE, 512, MAX_DRIVER_PULSE_PERIOD_US, MIN_DRIVER_PULSE_PERIOD_US)/2;
    if(!idle_flag && driver_pulse_state == PULSE_IDLE){
        write_pulse_high();
    }
    if((micros() - time_now > driver_pulse_hold_time_us) && (driver_pulse_state == PULSE_LOW)){
        write_pulse_high();
    }
    if((micros() - time_now > driver_pulse_hold_time_us) && (driver_pulse_state == PULSE_HIGH)){
        write_pulse_low();
    }
}
void write_pulse_high(void){
    driver_pulse_state = PULSE_HIGH;
    if(normalized_analog_value > 0){
        digitalWrite(DRIVER_DIR_PIN, HIGH);
    }
    else if(normalized_analog_value < 0){
        digitalWrite(DRIVER_DIR_PIN, LOW);
    }
    digitalWrite(DRIVER_STEP_PIN, HIGH);
    time_now = micros();
}
void write_pulse_low(void){
    digitalWrite(DRIVER_STEP_PIN, LOW);
    time_now = micros();
    if(!idle_flag){
        driver_pulse_state = PULSE_LOW;
    }
    else{
        driver_pulse_state = PULSE_IDLE;
    }
}

Conclusiones

El filtrado de paso bajo de la señal analógica es casi crucial en este tipo de configuración, especialmente para el código de control de posición. Esto puede hacerse en hardware o en firmware (por ejemplo, utilizando un filtro de media móvil exponencial). Todavía no tenemos ninguna forma de aceleración en nuestro control de movimiento, por lo que los cambios de velocidad demasiado altos provocarán la pérdida de pasos.

El propósito de estos ejemplos de código es servir de inspiración para otros que están haciendo un proyecto que involucra el control de motores paso a paso. Hay muchas maneras de hacer esto, y probablemente sería mejor usar librerías de control de paso a paso y/o shields o módulos para un movimiento y control más seguro, más robusto y suave.

Pin It on Pinterest

Shares