Cannot get reasonable speed - NMEA17 stepper motor TMC2209 driver AccelStepper library

I’m new to stepper motors (and electronics in general), so forgive me if this is obvious (or if I need to change how I post this).

My setup:
Stepperoline NMEA 17 stepper motor (https://www.omc-stepperonline.com/nema-17-bipolar-59ncm-84oz-in-2a-42x48mm-4-wires-w-1m-cable-connector-17hs19-2004s1)
TMC2209 stepper driver (BIGTREETECH-TMC2209-V1.2/TMC2209-V1.2-manual.pdf at master · bigtreetech/BIGTREETECH-TMC2209-V1.2 · GitHub)
I’m driving with a 15V power supply
HiLetgo ESP32 microcontroller (ESP-WROOM-32-Development-Microcontroller-Integrated/dp/B0718T232Z)
Powering ESP32 with USB power from laptop

I am not using any microsteps (turned off via UART control in code below). The below script moves the motor while left or right pedals are depressed and speed is controlled with a rotary encoder (and speed is displayed on an OLED screen). I have also tried moving a set number of steps or moving to a given step position, and all forms of rotation behave as described in the next paragraph (slow).

Speed control seems to work as expected up to about 80-130 steps/second, except that everything takes 2x longer than I expect to take place (at 100 steps/second I get ½ rotation in 2 seconds when I would expect 1 second). If I go over a speed of 80-130 steps/sec, the motor starts to jitter or skips steps. If I go over about 250 steps/second, it just vibrates and cannot move at all. Can anyone suggest what I might be doing wrong that prevents me from getting smooth motion over about 100 steps/second?

//define pin variables
  const int cClockPed = 32;
    const int ClockPed = 33;
    const int dirPin = 27; 
    const int stepPin = 14;
    const int dEncdrSpdDT=15;
    const int dEncdrSpdCLK=2;

  //general variables
  volatile int SpeedSetting = 100;    //initial speed setting, will modify with rotary encoder
  long int OLEDtimer = 0;

  #include <Wire.h> //for OLED
  #include <Adafruit_GFX.h> //for OLED
  #include <Adafruit_SH110X.h> //for OLED  
  #include <digitalWriteFast.h> //for digitalReadFast() function  
  #include <TMCStepper.h>  //allows control of TMC2209 driver via UART
  #include <AccelStepper.h>
    //initialize AccelStepper  
    AccelStepper myStepper(1, stepPin, dirPin); //set up myStepper as stepper motor on AccelStepper (motor type 1)

//set up OLED
  #define SCREEN_WIDTH 132 // OLED display width,  in pixels
  #define SCREEN_HEIGHT 64 // OLED display height, in pixels
  Adafruit_SH1106G oled = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

//UART control of TMC2209 stepper motor driver
  #define SERIAL_PORT Serial2 // HardwareSerial port of ESP32
  #define STALL_VALUE     255 // [0..255]
  #define DRIVER_ADDRESS 0b00 // TMC2209 Driver address
  #define R_SENSE       0.11f // 0.11 for MKS TMC2209
TMC2209Stepper driver(&SERIAL_PORT, R_SENSE, DRIVER_ADDRESS);  //initialize UART control

//void to use rotary encoder to set speed
  void IRAM_ATTR rotateSpeed(){    
    if(digitalReadFast(dEncdrSpdDT)==HIGH){//uses grounded rotary so high DT =clockwise
          SpeedSetting = SpeedSetting + 10; //increment 10 steps/sec for each rotary dent     
      }else{   
        SpeedSetting = SpeedSetting - 10;}     
  }

void setup() {

  //set up TMC2209 driver
    SERIAL_PORT.begin(115200);//for UART to TMC2209
    delay(200);                   //give time for serial port to initialize from previous line
    driver.begin();                // Initialize driver                           
    driver.toff(3);                 // Enables driver in software (number has to do with chopper configuration...5 is default, lower if you cannot get good top speed)
    driver.microsteps(0);            // Set microsteps for driver
    driver.rms_current(1414);        //set max RMS current (2A peak) for new Steperoline motor
    driver.intpol(true);               // Interpolate to 256 steps, smooth stepping even with 0 microsteps.
    myStepper.setMaxSpeed(1000);
    myStepper.setAcceleration(40);
  
  //initialize OLED
    oled.begin(0x3c, true); // Address 0x3C default
    oled.setTextSize(2);          // text size
    oled.setTextColor(SH110X_WHITE);     // text color

  //define pins as outputs/inputs
    pinMode(stepPin, OUTPUT);
    pinMode(dirPin, OUTPUT);
    pinMode(cClockPed, INPUT_PULLUP);
    pinMode(ClockPed, INPUT_PULLUP);    
    pinMode(dEncdrSpdCLK, INPUT_PULLUP);
    pinMode(dEncdrSpdDT, INPUT_PULLUP);
   attachInterrupt(digitalPinToInterrupt(dEncdrSpdCLK), rotateSpeed, RISING);  //hardware interrupt for rotary encoder uses rotateSpeed void below
  
}

void loop() {  
    //if left pedal pressed  
        while(digitalRead(cClockPed)==LOW){  //with input_pullup LOW = pressed
        myStepper.setSpeed(-SpeedSetting);   
              myStepper.runSpeed();
              myStepper.run();
            }    

    //if right pedal pressed  
        while(digitalRead(ClockPed)==LOW){  //with input_pullup LOW = pressed 
          myStepper.setSpeed(SpeedSetting);  
          myStepper.runSpeed();       
          myStepper.run();
        }

//update OLED (don't do every clock cycle as is time consuming)
    if(millis() > OLEDtimer){ //only update 1x / second
      updateOLED();
      OLEDtimer = millis() + 1000;
    }
}
    

void updateOLED(){
  oled.clearDisplay(); // clear display
  oled.setCursor(0, 1);  // position to display
    oled.print("Speed=");
    oled.println(SpeedSetting); 
    oled.display();  
}

NMEA17? What is that? NEMAxx tells about the mechanical face of mounting steppers, nothing more.

Why? Accelstepper has speed controlle by setspeed.

I reach 4000 steps per second using Accelstepper.

What's the acceleration set to and what's the axle load?

The .runSpeed() function tries to run at at constant speed, without acceleration. If you want acceleration, you need to set your target destination .moveTo() and then just repeatedly call .run()

Look at the examples that come with the AccelStepper library.

I have found this reference very useful for understanding the AccelStepper library.
https://hackaday.io/project/183279-accelstepper-the-missing-manual/details

As said, do not call both runSpeed() and run() together.

Your interrupt is for a RISING signal but the digitlRead() value in the ISR is tested for both a high and low. Do you ever see a low value to reduce the speed?

attachInterrupt(digitalPinToInterrupt(dEncdrSpdCLK), rotateSpeed, RISING);
void IRAM_ATTR rotateSpeed(){    
    if(digitalReadFast(dEncdrSpdDT)==HIGH){//uses grounded rotary so high DT =clockwise
          SpeedSetting = SpeedSetting + 10; //increment 10 steps/sec for each rotary dent     
      }else{   
        SpeedSetting = SpeedSetting - 10;}     
  }

Sorry ..that was pretty vague. My code changes the speed using a rotary encoder position.

There is no load on the motor at present...just the bare motor shaft turning.

Faraday, thanks for the link...I'll look that over. I suspect I'm doing something wrong, but I also have had issues using move to position and moveRelative with the SpeedyStepper library (but did not try with AccelStepper. My ratatSpeed code is working fine...the OLED displays the speed resulting from this, so I know what the motor is being told to do...the motor still won't cooperate over around 100 steps/sec.

@dshoup
I have been playing with stepper motor for a very long time. I have never faced issue with vibration and missing steps.
Using Nema 17 with a good driver like DM556 helps get very good control.
The Accel stepper library is restricted to 15000 steps/sec. So if you Microstep you won't be able to achieve very high speed.
Controlling 1 stepper at a time it is easy to achieve 1000-1500 rpm at 400 Microstep, and also have smooth motion.

I decided it was time to simplify this to a very basic set of code…below is what I’m using. I chose moveto() and run() as the functions as I’m pretty sure uses acceleration (and I seem to observe this at least a bit, but it does not accelerate very much before it stops moving well). The below is given belowcode. I am currently using 2 microsteps (i.e., my 200 step/rotation motor now has 400/rotation). I ask it to move 400*3 steps to get 3 full rotations. Max speed is set to 400 steps/second, which should be 1 revolution per second. I have acceleration at 80. I tried to upload a video, but apparently I’ve not been a member long enough to use file uploads so I’ll try to describe. The motor does ok when it is starting/stopping at slow speed, but pretty quickly (about ¼ rotation) becomes jerky and it takes 9 seconds to make the 3 rotations (and actually only gets about 1.5-1.75 rotations in…presumably because of missed steps). Any thoughts?

#include <AccelStepper.h>
#include <TMCStepper.h>  

// pin assignments
const int stepPin = 14;
const int dirPin = 27;
int StepsToTake = 400*3;

//set up myStepper as stepper motor on AccelStepper (motor type 1)
  AccelStepper stepper(1, stepPin, dirPin); 
  
//UART control of TMC2209 driver
    #define SERIAL_PORT Serial2 // HardwareSerial port
    #define STALL_VALUE     255 // [0..255]
    #define DRIVER_ADDRESS 0b00 // TMC2209 Driver address MS1 and MS2
    #define R_SENSE       0.11f // 0.11 for MKS TMC2209
  TMC2209Stepper driver(&SERIAL_PORT, R_SENSE, DRIVER_ADDRESS);

void setup() 
{
SERIAL_PORT.begin(115200);//for UART to TMC2209
  delay(200);
  driver.begin();                // Initialize driver                           
  driver.toff(5);                 // Enables driver in software
  driver.microsteps(2);            // Set microsteps to 1/microSteps
  driver.rms_current(1400);        //set max current
  //driver.intpol(true);               // Interpolate to 256 steps, smooth stepping even with 0 microsteps.

  stepper.setMaxSpeed(400);
  stepper.setAcceleration(80);

}


void loop() 
{
  StepsToTake = -StepsToTake;

  stepper.moveTo(StepsToTake);  
    while (stepper.distanceToGo() != 0){  
      stepper.run(); 
    }
  delay(500);
}

I’ll add that I have tried 2 different brands/sizes of NMEA 17 motors, so I don’t think it has anything to do with my motor. I also know the TMC2209 is pretty widely used and have not seen any complaints of speed problems, so I’m wondering if it is a wiring issue. My motor documentation defines the following wire definitions:
Color Coil
Black A+
Green A-
Red B+
Blue B-

My driver has the following motor outputs labeled 1B, 1A, 2A, 2B in order along one side…I’ve paired these up as follows with the motor wire colors…does this look correct?

TMC2209 pin Wire
1B Black
1A Green
2A Blue
2B Red

That certainly is the standard A and B color pairs Red/Blue Green/Black. You can verify with a multimeter the continuity and resistance of the windings.

I'm not certain that there is an issue with your code, except that for the 3 revolutions you want to use .move(StepsToTake) instead of .moveTo(StepsToTake). I also think you need a .setSpeed() in the setup as well.

Are you using an unloaded motor?

I only have a TMC2208 to test with, and it is set up without using Serial, but just with step/dir legacy mode. With and unloaded motor I can easily use high accelerations, and I can see the timing you would expect from the code (e.g. 10 cycles back and forth in close to 35 seconds).

The behavior of your stepper seems peculiar. Can you set up the TMC2209 in step/dir legacy mode with the current set from the pot?

#include <AccelStepper.h>

#define dirPin 5   //2
#define stepPin 6  // 3

AccelStepper stepper(AccelStepper::DRIVER, stepPin, dirPin);
//#define stepsPerRevolution 400  //half step set by MS1 pin HIGH.

int StepsToTake = 400 * 3;

void setup()
{
  pinMode(stepPin, OUTPUT);
  pinMode(dirPin, OUTPUT);

  stepper.setMaxSpeed(400);  //50rpm  50x400/60
  stepper.setSpeed(400);
  stepper.setAcceleration(2000);
}

void loop()
{
  StepsToTake = -StepsToTake;
  stepper.move(StepsToTake);
  while (stepper.distanceToGo() != 0)
  {
    stepper.run();
  }
  delay(500);
}

I started with the stepper driver in legacy mode. speeds might be slightly different, but still never got as much as 1 revolution/second (200 steps) speed. I have also used 2 of these TMC 2209 units (smoked one due to an accident...electronics are fun...) and have used 2 different stepper motors, so it seems unlikely to just be a bad device and probably is something I'm doing wrong. My power supply is an adapter that uses AC 110 and turns it into a cigarette lighter adapter (should be 12v, but I actually measure it around 15v). I believe it is rated to 3 amps or so, but am not home to check this right now. Is there something I should measure there to see if it is a limiting factor (i.e., is there a way to measure amperage to ensure it has the power the driver needs)?

The motor spec you linked shows 2A per phase so the power supply may not be up to the job but I'm not clear that being slightly underpowered would give the very poor results you see. I think the 2209 is rated up to 2A per coil.

My testing is with an unloaded motor spec'd at 1amp/phase with a 12v/1.5A supply. I think my current limit is less than 1 amp. With half step, I have no problem seeing the 400 steps/second. I am using a 5v mcu (Nano Every) with a 2208 in legacy step/dir mode.

What version of the 2209 do you have. I see that v1.2 has different microstep settings (using the MS pins) from the 2208 and v1.3 of the 2209. I'm unclear about UART mode.

I'm limited in my ability to help you based on the differences in hardware. I don't know if the ESP 32 is an issue for pulse lengths. My suggestion is to try work out your stepper speed issue in legacy mode with an Arduino Uno.

Thanks Cattledog...it actually is pretty helpful just to hear I'm not crazy in thinking a small stepper like the TMC units can achieve more than 200 steps/second. I checked my power supply now that I am home and it is spec'ed for 5.8 amps output @ 12V (but I know i'm getting about 15V)...I assume that should be plenty given I'm limiting to about 1.4 amps for RMS (2 amp peak *0.707). I'm currently running the motor with no load, I assume my problem will only get worse if I add a load. I'm getting ready to step up to 24v (this is going to be run by 2 deep-cycle batteries in the end) and test with a load, so I guess we'll find out if it is the power supply...just need to wait on a buck converter so I can step the 24v going to the ESP32 and the TMC2209 down to 5v first (I'll run the motors at full 24V). If anyone else has any suggestions, I'd love to hear them. Thanks to all who have tried to help so far!

Thanks to all who offered suggestions. I have figured out my issue (well mostly...). My TMC 2209 was not producing the amperage it was supposed to. I'm still not sure why (hence the "mostly" figured out part). I first noticed this when I tried to go back to non-UART control and I was setting the current with the potentiometer...I could not get more than 0.7 volts (i.e., 0.7 amps RMS, which would be about 1 amp peak). I ordered a new TMC 2209 as well as a Steperoline DM542T motor driver. The new TMC worked more or less as it should (it can turn the motor at reasonable speeds), but it still lacks the power I get on the DM542T, which is ironic because on the 542, the closest dip switch setting for my 2 amp peak motor is 1.35 amp RMS (1.91 amps peak)...so theoretically it should push less power than the TMC set for 1.41 amps RMS (2 amp peak). I also find the 542 is actually quieter (though both are great at being quiet compared to a TB6600 I tried...wow is that noisy!). I'm going with the 542 given it produces much better torque (and motor and driver stay totally cool in my application)...just wish it were as cheap as the TMC chip! Again, thanks to everyone for helping!

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