Slow Stepper Motor

Here's my setup:
Arduino Due with a TB6600 driver and a small NEMA23 motor. I'm running it at 12v but upping it to 24v makes no difference.
The best reliable rpm I'm getting is 300 with a 60 delay.
Microstepping is set at 8 but if I set it to 4 I have to use 120 delay and still end up at 300 rpm.
The programming, to my novice eyes, looks pretty straight forward and should easily give me at least double that speed.
So, my question is: Do I have a programming issue or a hardware problem?

/* Example sketch to control a stepper motor with TB6600 stepper motor driver 
  and Arduino without a library: continuous rotation. 
  More info: https://www.makerguides.com */
// Define stepper motor connections:
const int ena = 13;  //Enable Pin
const int Switch = 53;
int D = 60; //Delay

#define stepPin 11
#define dirPin 12
#define enaPin 13
//#define stepsPerRevolution 1600

void setup() {
  pinMode(Switch, INPUT_PULLUP);
  // Declare pins as output:
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);
  pinMode(enaPin, OUTPUT);

  // Set the spinning direction CW/CCW:
  digitalWrite(dirPin, HIGH);
  digitalWrite(enaPin, LOW);
}

//Main Program.
void loop() {
  digitalWrite(ena, HIGH);  // Set enable high
  if (digitalRead(Switch) == LOW) 
    digitalWrite(ena, LOW);  // Set enable low
    Run();
    
  }


  //Run
  void Run() {
    //digitalWrite(ena, LOW);  // Set enable low
    // These four lines result in 1 step:
    while (digitalRead(Switch) == LOW) {
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(D);  //324 rpm @ 55 @ 1600
    digitalWrite(stepPin, LOW);
    delayMicroseconds(D);}  //343 rpm @ 50 @ 1600
  }

  • First things first.
  if (digitalRead(Switch) == LOW) 
    digitalWrite(ena, LOW);  // Set enable low
    Run();

Verses

  if (digitalRead(Switch) == LOW) 
  {
    digitalWrite(ena, LOW);  // Set enable low
    Run();
  }
  • What do you have the current set to ?

  • Does 1/2 step run faster then 1/4 step ?

  • If you use delayMicroseconds(D); and D set to 200, does the stepper move smoothly ?

1 Like

I've tried it on two different motors. One is 3A and the other is 2A. Same result.

"Microstepping is set at 8 but if I set it to 4 I have to use 120 delay and still end up at 300 rpm." I've not tried 1/2 or full step, that's too coarse.

Yes, but that just makes the motor go slower. I need to go faster. The motor run smoothly all the way from extremely slow to a bit over 300. At that point it just hums and doesn't turn like it can't handle that frequency. It also has very good torque right up to the point where it dies.

Thanks for cleaning up the enable code. :grinning:

  • I don’t have the hardware here to do a similar testing.

  • Let’s do some math:
    300RPM ÷ 60sec/min = 5 rev per second
    1 ÷ 5 = 200ms in one revolution
    200ms ÷ 120us = 1666 steps in 1 revolution.
    1666 ÷ 8 (1/8 stepping) ≈ 200 full steps
    Agree with the above ? :thinking:

  • What is your stepper’s steps per revolution rating ?

What kind of RPM are you expecting?

This is the motor I'll be using: https://www.omc-stepperonline.com/dual-shaft-nema-23-hollow-shaft-stepper-motor-bipolar-0-78-nm-110-5oz-in-2-0a-57x57x45mm-23hs18-2004h

I'd like to get at least 600 rpm. I've easily gotten that with a couple of CNC's running under Mach with Gecko drivers.

I have a hybrid from Stepperonline that will do 3k all by itself.

  • 1.8° steps gives 200 steps per revolution, agrees with that in Post #4

  • So you want to go from 120us to 60us per step (to get to 600 RPM)
    There is a delay from just executing code on the Arduino, not sure what the fastest step rate the motor or the stepper driver can handle.

  • Out of curiosity, what is the minimum delay that still lets the stepper move (. . . 100us, 80us, 40us, 30us . . .) ?

In order to achieve higher speeds, you need to implement an acceleration profile. You can't just hit a stepper with max rate frequency and expect it to perform an instant acceleration.

There are several libraries to drive steppers with acceleration, e.g. AccelStepper.

2 Likes

Actually, I started with AccelStepper and one other library that I can't recall the name of just now. Neither, it seems, could do continuous rotation. They're made to just do positional movement.

But, you nailed it about implementing acceleration. I added a pot and it now does 606 rpm at 24 delay. In the end, this should work out fine since the finished machine will have the speed selected with a rotary encoder.

If you know of a way to get one of the libraries to run continuous, I'd love to hear about it.

I just wanted to stop back and say thanks to everyone and mark this solved.
And, come see my new thread - 'Switch from Due to Zero'. :wink:

To compensate the lazyness of user @01sporty to not post a link

here is the link to this other thread

Oh, sorry about. I assumed AccelStepper would do acceleration for all modes.. I mean it's in the name!

You could try GitHub - bblanchon/ArduinoContinuousStepper: An Arduino library to spin stepper motors in continuous motions. although I haven't tried it myself, not sure if it works on Due.

That library looks promising but the acceleration is very non-linear. I messaged him to see if there is anything that can be done.

So, I thought I'd drop by with an update. I did end up re-writing the code with ContinuousStepper only to find that it didn't want to play well with the rest of my code.
I ended up going back to the libraries and taking a second look at ContinuousStepper_Generic. I had been avoiding that one because I really don't understand it.
Long story short, it worked fine and integrated nicely with the rest of my code. Here's the finished code:


```cpp
#include "ContinuousStepper_Generic.h"
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>

#define _PWM_LOGLEVEL_ 4
#define USING_TIMER true
#define STEP_PIN 11  //For Stepper Driver
#define DIR_PIN 12   //For Stepper Driver
#define ENA_PIN 13   //For Stepper Driver
int ena = 13;

U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C u8g2(U8G2_R0, SCL, SDA, U8X8_PIN_NONE);
//CLK=SCL pin, DT=SDA pin ~ For Display

#define CLK_PIN 2     //For Encoder
#define DATA_PIN 3    //For Encoder
#define SWITCH_PIN 4  //For Encoder
int StopSW = 4;

#define REV_PIN 53  //For Reverse Switch
int RevSW = 53;

SAMDUE_PWM* stepper;

void setSpeed(int speed) {
  if (speed == 0) {
    // Use DC = 0 to stop stepper
    stepper->setPWM(STEP_PIN, 500, 0);
  } else {
    //  Set the frequency of the PWM output and a duty cycle of 50%
    digitalWrite(DIR_PIN, (speed < 0));
    stepper->setPWM(STEP_PIN, abs(speed), 50);
  }
}

void setup() {
  u8g2.begin();

  pinMode(STEP_PIN, OUTPUT);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(ENA_PIN, OUTPUT);

  // Create PWM object and passed just a random frequency of 500
  // The duty cycle is how you turn the motor on and off
  stepper = new SAMDUE_PWM(STEP_PIN, 500, 0);

  pinMode(CLK_PIN, INPUT);
  pinMode(DATA_PIN, INPUT);
  pinMode(SWITCH_PIN, INPUT_PULLUP);

  pinMode(REV_PIN, INPUT_PULLUP);

  digitalWrite(ena, HIGH);  // Set enable high
  PrintReady();
}
static uint16_t state = 0;
static int8_t counter = 0;
char buf[3];
int rpm = 0;
int sps = rpm * 1600 / 60;

//Main Program
void loop() {

  if (digitalRead(StopSW) == LOW) { StopRotation(); }
  if (digitalRead(RevSW) == LOW && counter == 0) { Reverse(); }

  sps = (rpm * 1600 / 60);
  setSpeed(sps);

  Counter();
}
//End Main Program

void Counter() {

  state = (state << 1) | digitalRead(CLK_PIN) | 0xe000;  //Digital Debounce Filter
  if (state == 0xf000) {
    state = 0x0000;
    if (digitalRead(DATA_PIN)) counter++;
    else counter--;

    if (counter > 12) counter = 12;
    if (counter < 0) counter = 0;
    rpm = counter * 50;
    if (rpm != 0) PrintRpm();
    else
      PrintReady();
  }
}

void PrintRpm() {
  digitalWrite(ena, LOW);              // Set enable low
  u8g2.clearBuffer();                  // clear internal memory
  u8g2.setFont(u8g2_font_helvB24_tf);  // choose font
  sprintf(buf, "%d", rpm);
  u8g2.drawStr(35, 28, buf);  // write to the internal memory
  u8g2.sendBuffer();          // transfer internal memory to the display
}

void PrintReady() {
  digitalWrite(ena, HIGH);             // Set enable high
  u8g2.clearBuffer();                  // clear internal memory
  u8g2.setFont(u8g2_font_helvB24_tf);  // choose font
  u8g2.drawStr(3, 28, "READY");        // write to the internal memory
  u8g2.sendBuffer();                   // transfer internal memory to the display
}

void StopRotation() {
  counter = 0;
  PrintReady();
}

void Reverse() {
  while (digitalRead(RevSW) == LOW) {
    u8g2.clearBuffer();                  // clear internal memory
    u8g2.setFont(u8g2_font_helvB18_tf);  // choose font
    u8g2.drawStr(3, 26, "REVERSE");      // write to the internal memory
    u8g2.sendBuffer();                   // transfer internal memory to the display
    digitalWrite(ena, LOW);              // Set enable low
    setSpeed(-1333);                     // 50 RPM
  }
  PrintReady();
}

Thanks again for all the help.

AccelStepper does do non-positional continuous rotation with stepper.runspeed(). However you would need to manage the transition from acceleration mode (stepper.run()) where it does the acceleration calculations into the no-acceleration-calculation constant speed mode (stepper.runspeed()) once it reaches the target speed.

For example.

// AccelStepper_AccelToConstantSpeed
// https://wokwi.com/projects/422896273363206145
// based on:
// https://wokwi.com/projects/419849124942020609
// Code from https://github.com/waspinator/AccelStepper/blob/master/examples/ConstantSpeed/ConstantSpeed.pde
// Other example simulations https://forum.arduino.cc/t/wokwi-simulations-for-arduino-built-in-examples/1304754
// You should not drive a stepper direcly from the controller
// Use a modern H-bridge driver like a TB6612FNG, or an old,
// inefficient L298N, or a modern STEP+DIR driver.

// ConstantSpeed.pde
// -*- mode: C++ -*-
//
// Shows how to run AccelStepper in the simplest,
// fixed speed mode with no accelerations
/// \author  Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2009 Mike McCauley
// $Id: ConstantSpeed.pde,v 1.1 2011/01/05 01:51:01 mikem Exp mikem $

#include <AccelStepper.h>

AccelStepper stepper; // Defaults to AccelStepper::FULL4WIRE (4 pins) on 2, 3, 4, 5

enum State {INIT, ACCEL, CRUISING, STOPPING} state = INIT;

const int StopPin = A0;
const int GoPin = A1;

void setup()
{
  pinMode(StopPin,INPUT_PULLUP);
  pinMode(GoPin,INPUT_PULLUP);
}

void loop()
{
  switch (state) {
    case INIT:
      stepper.setMaxSpeed(4000);
      stepper.setAcceleration(400);
      stepper.move(100000); // needs to be far enough to ramp up
      state = ACCEL;
      break;
    case ACCEL:
      if (stepper.speed() == stepper.maxSpeed()) {
        state = CRUISING;
      }
      if( digitalRead(StopPin) == LOW){
        stepper.stop();
        state = STOPPING;
      }
      stepper.run();
      break;
    case CRUISING:
      stepper.runSpeed();
      if( digitalRead(StopPin) == LOW){
        stepper.stop();
        state = STOPPING;
      }
      break;
    case STOPPING:
      stepper.run();
      if(stepper.speed() == 0 && digitalRead(GoPin)== LOW){
        state = INIT;
      }
  }
}
2 Likes

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.