Using a TMC2209 silent stepper motor driver with an arduino

Thank you for the detailed infos and your post!

In the current test code, which produced the above, I did not send any signal to the controllers at all. I just did what I described. After connecting two of the three motor controllers, the motors were stable and quiet, with all three connected, the back and forth movement started.
I also have a video of the phenomenon, it does not move microsteps, but several centimeters in this case.

I tried connecting the step pins to ground, but the phenomenon did not change.

By the way, I use the fastaccelstepper library in my original complete code. Since the motors need to hold, they need to be enabled after the movement is finished.

Here is my simplyfied code, for my test described above:

#include <TMCStepper.h>
#include <HardwareSerial.h>

// #define SLIDE_MOTOR_STEP_PIN 26
// #define SLIDE_MOTOR_DIRECTION_PIN 27
// #define PAN_MOTOR_STEP_PIN 22
// #define PAN_MOTOR_DIRECTION_PIN 23
// #define TILT_MOTOR_STEP_PIN 19
// #define TILT_MOTOR_DIRECTION_PIN 21

#define SLIDE_MAX_SPEED 700
#define SLIDE_MAX_ACCELERATION 8000
#define PAN_MAX_SPEED 500
#define PAN_MAX_ACCELERATION 8000
#define TILT_MAX_SPEED 250
#define TILT_MAX_ACCELERATION 8000

#define RXD2 16
#define TXD2 17

#define SERIAL_PORT_2 Serial2  // TMC2208/TMC2224 HardwareSerial port - pins 16 & 17
#define DRIVER_S_ADDRESS 0b00  // TMC2209 Driver address according to MS1 and MS2
#define DRIVER_P_ADDRESS 0b01  // TMC2209 Driver address according to MS1 and MS2
#define DRIVER_T_ADDRESS 0b10  // TMC2209 Driver address according to MS1 and MS2
#define R_SENSE 0.11f          // E_SENSE for current calc, SilentStepStick series use 0.11

TMC2209Stepper driver_s(&SERIAL_PORT_2, R_SENSE, DRIVER_S_ADDRESS);  // Create TMC driver
TMC2209Stepper driver_p(&SERIAL_PORT_2, R_SENSE, DRIVER_P_ADDRESS);  // Create TMC driver
TMC2209Stepper driver_t(&SERIAL_PORT_2, R_SENSE, DRIVER_T_ADDRESS);  // Create TMC driver

// RC Channel order and functions //
// CH1 Stepper 1 Axle S Slide (Only negative positions enabled, the ZERO is the limitswitch)
// CH2 Stepper 2 Axle P Pan
// CH3 Stepper 3 Axle T Tilt
// CH4 Speed Control for AutoMOve
// CH5 Set IN/OUT position if CH8 ENABLED, the In/Out block is selected in CH7 (1,2,3)
// CH6 Start / Stop / ReSet AUTO MOve according to CH7 setting (1,2,3), 3 sec long push resets all axles to ZERO
// CH7 Set IN/OUT SET (1,2,3)
// CH8 UP In/Out setting ENABLED, center nothing, LOW Move to In or OUT, selected by CH5 - CH8
// CH9 Reset board

//TMC2209 variables
int current = 1000;
int stall = 255;
int motor_microsteps = 4;
int motor_steps_per_rev = 200;

void setup() {

  // pinMode(SLIDE_MOTOR_DIRECTION_PIN, OUTPUT);
  // pinMode(SLIDE_MOTOR_STEP_PIN, OUTPUT);
  // pinMode(PAN_MOTOR_DIRECTION_PIN, OUTPUT);
  // pinMode(PAN_MOTOR_STEP_PIN, OUTPUT);
  // pinMode(TILT_MOTOR_DIRECTION_PIN, OUTPUT);
  // pinMode(TILT_MOTOR_STEP_PIN, OUTPUT);

  Serial1.begin(115200, SERIAL_8N1, 33, -1);

  // INITIALIZE SERIAL2 UART FOR TMC2209
  SERIAL_PORT_2.begin(115200);  // INITIALIZE UART TMC2209, SERIAL_8N1, 16, 17
  Serial.println(F("UART2 interface for TMC2209 intitialized   @ 115200"));
  Serial.println(F("---------------------------------------------------"));

  //************************************************************************************************************************//
  //TMCSTEPPER SETTINGS
  driver_s.begin();
  driver_s.pdn_disable(true);      // Use PDN/UART pin for communication
  driver_s.toff(3);                // [1..15] enable driver_s in software
  driver_s.blank_time(24);         // [16, 24, 36, 54]
  driver_s.I_scale_analog(false);  // Use internal voltage reference
  driver_s.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."
  driver_s.mstep_reg_select(true);
  driver_s.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  driver_s.seimin(1);                     // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver_s.semin(15);                     // [0... 15] If the StallGuard4 result falls below SEMIN*32, the motor current becomes increased to reduce motor load angle.
  driver_s.semax(15);                     // [0... 15]  If the StallGuard4 result is equal to or above (SEMIN+SEMAX+1)*32, the motor current becomes decreased to save energy.
  driver_s.iholddelay(3);                 // 0 - 15 smooth current drop
  driver_s.intpol(true);                  // interpolate to 256 microsteps
  driver_s.SGTHRS(stall);
  driver_s.pwm_autoscale(true);    // Needed for stealthChop
  driver_s.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver_s.internal_Rsense(false);
  driver_s.TCOOLTHRS(0);
  driver_s.TPWMTHRS(0);
  //************************************************************************************************************************//

  //************************************************************************************************************************//
  //TMCSTEPPER SETTINGS
  driver_p.begin();
  driver_p.pdn_disable(true);      // Use PDN/UART pin for communication
  driver_p.toff(3);                // [1..15] enable driver_p in software
  driver_p.blank_time(24);         // [16, 24, 36, 54]
  driver_p.I_scale_analog(false);  // Use internal voltage reference
  driver_p.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."
  driver_p.mstep_reg_select(true);
  driver_p.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  driver_p.seimin(1);                     // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver_p.semin(15);                     // [0... 15] If the StallGuard4 result falls below SEMIN*32, the motor current becomes increased to reduce motor load angle.
  driver_p.semax(15);                     // [0... 15]  If the StallGuard4 result is equal to or above (SEMIN+SEMAX+1)*32, the motor current becomes decreased to save energy.
  driver_p.iholddelay(3);                 // 0 - 15 smooth current drop
  driver_p.intpol(true);                  // interpolate to 256 microsteps
  driver_p.SGTHRS(stall);
  driver_p.pwm_autoscale(true);    // Needed for stealthChop
  driver_p.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver_p.internal_Rsense(false);
  driver_p.TCOOLTHRS(0);
  driver_p.TPWMTHRS(0);
  //************************************************************************************************************************//

  //************************************************************************************************************************//
  //TMCSTEPPER SETTINGS
  driver_t.begin();
  driver_t.pdn_disable(true);      // Use PDN/UART pin for communication
  driver_t.toff(3);                // [1..15] enable driver_t in software
  driver_t.blank_time(24);         // [16, 24, 36, 54]
  driver_t.I_scale_analog(false);  // Use internal voltage reference
  driver_t.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."
  driver_t.mstep_reg_select(true);
  driver_t.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  driver_t.seimin(1);                     // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver_t.semin(15);                     // [0... 15] If the StallGuard4 result falls below SEMIN*32, the motor current becomes increased to reduce motor load angle.
  driver_t.semax(15);                     // [0... 15]  If the StallGuard4 result is equal to or above (SEMIN+SEMAX+1)*32, the motor current becomes decreased to save energy.
  driver_t.iholddelay(3);                 // 0 - 15 smooth current drop
  driver_t.intpol(true);                  // interpolate to 256 microsteps
  driver_t.SGTHRS(stall);
  driver_t.pwm_autoscale(true);    // Needed for stealthChop
  driver_t.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver_t.internal_Rsense(false);
  driver_t.TCOOLTHRS(0);
  driver_t.TPWMTHRS(0);
  //************************************************************************************************************************//

}

void loop() {
  vTaskDelete(NULL);
}

How do you have them wired? I don't think I saw a wiring diagram for you setup.

I'm no pro, but I did my best.

Why do you have the lines commented out in your sketch to set the step and direction pins? I would pull those low at a minimum rather than leave them floating. By default the pins at boot will be high but floating. So they could easily drift to cause your motor to step.

1 Like

Hi,
Thanks for the schematic, good attempt and you kept everything open and clear to read. :+1:
Layout technique will come with practice.

Just a tip, avoid connecting more that two wire together at one point, unless indicating a definite physical star point connection.

Thanks.. Tom... :smiley: :+1: :coffee: :australia:

I had a request in the Fastaccelstepper library forum to delete everything that is not related to TMC, thus trying to find the root of the error. That's why this code is here now, as bare as possible.

By the way, if you read all the way through, you will see that if I remove the logic current from any of the controllers, or disconnect the motor power supply from any of them, no error occurs.

The back-and-forth random movement is still there when I run the entire code, only then it stops for a few seconds and then starts again. This is my main problem right now.

I also tried the tip of connecting all three step wires to ground, but that didn't change anything either.

Regardless, I'll do a test by removing the "pin" comments from the code...

What voltage is your power supply? Do you have the three TMC2209s wired up in series of parallel?
If it is happening regardless of the code, then it has to be a hardware problem. Right? I've not tried to use three TMC 2209s in UART mode on the same micro controller. What you could try is completely removing the microcontroller. If you see steps then, then it's you're 2209s.

update:
another try:

  1. removed the STEP/DIR/ENABLE wires from the TMCs -> no movement
  2. added the STEP/DIR/ENABLE wire to one TMC -> no movement
  3. added the STEP/DIR/ENABLE wire to two TMCs -> no movement
  4. added three STEP/DIR/ENABLE wire to three TMCs -> random weird moves on all axes

Is there a GPIO on the esp32 that I should not use for this purpose? I really have no idea, even though it took a lot of energy to find the error.

26V for motor power. TMCs are wiredone after the other, so in series.

Tested with pin assigments uncommented, the result is the same. So i think one or more TMC is bad i think, or i'm out of ideas...

last test done with this code:

#include <TMCStepper.h>
#include <HardwareSerial.h>

#define SLIDE_MOTOR_STEP_PIN 26
#define SLIDE_MOTOR_DIRECTION_PIN 27
#define PAN_MOTOR_STEP_PIN 22
#define PAN_MOTOR_DIRECTION_PIN 23
#define TILT_MOTOR_STEP_PIN 19
#define TILT_MOTOR_DIRECTION_PIN 21

#define SLIDE_MAX_SPEED 700
#define SLIDE_MAX_ACCELERATION 8000
#define PAN_MAX_SPEED 500
#define PAN_MAX_ACCELERATION 8000
#define TILT_MAX_SPEED 250
#define TILT_MAX_ACCELERATION 8000

#define RXD2 16
#define TXD2 17

#define SERIAL_PORT_2 Serial2  // TMC2208/TMC2224 HardwareSerial port - pins 16 & 17
#define DRIVER_S_ADDRESS 0b00  // TMC2209 Driver address according to MS1 and MS2
#define DRIVER_P_ADDRESS 0b01  // TMC2209 Driver address according to MS1 and MS2
#define DRIVER_T_ADDRESS 0b10  // TMC2209 Driver address according to MS1 and MS2
#define R_SENSE 0.11f          // E_SENSE for current calc, SilentStepStick series use 0.11

TMC2209Stepper driver_s(&SERIAL_PORT_2, R_SENSE, DRIVER_S_ADDRESS);  // Create TMC driver
TMC2209Stepper driver_p(&SERIAL_PORT_2, R_SENSE, DRIVER_P_ADDRESS);  // Create TMC driver
TMC2209Stepper driver_t(&SERIAL_PORT_2, R_SENSE, DRIVER_T_ADDRESS);  // Create TMC driver

// RC Channel order and functions //
// CH1 Stepper 1 Axle S Slide (Only negative positions enabled, the ZERO is the limitswitch)
// CH2 Stepper 2 Axle P Pan
// CH3 Stepper 3 Axle T Tilt
// CH4 Speed Control for AutoMOve
// CH5 Set IN/OUT position if CH8 ENABLED, the In/Out block is selected in CH7 (1,2,3)
// CH6 Start / Stop / ReSet AUTO MOve according to CH7 setting (1,2,3), 3 sec long push resets all axles to ZERO
// CH7 Set IN/OUT SET (1,2,3)
// CH8 UP In/Out setting ENABLED, center nothing, LOW Move to In or OUT, selected by CH5 - CH8
// CH9 Reset board

//TMC2209 variables
int current = 1000;
int stall = 255;
int motor_microsteps = 4;
int motor_steps_per_rev = 200;

void setup() {

  pinMode(SLIDE_MOTOR_DIRECTION_PIN, OUTPUT);
  pinMode(SLIDE_MOTOR_STEP_PIN, OUTPUT);
  pinMode(PAN_MOTOR_DIRECTION_PIN, OUTPUT);
  pinMode(PAN_MOTOR_STEP_PIN, OUTPUT);
  pinMode(TILT_MOTOR_DIRECTION_PIN, OUTPUT);
  pinMode(TILT_MOTOR_STEP_PIN, OUTPUT);

  Serial1.begin(115200, SERIAL_8N1, 33, -1);

  // INITIALIZE SERIAL2 UART FOR TMC2209
  SERIAL_PORT_2.begin(115200);  // INITIALIZE UART TMC2209, SERIAL_8N1, 16, 17
  Serial.println(F("UART2 interface for TMC2209 intitialized   @ 115200"));
  Serial.println(F("---------------------------------------------------"));

  //************************************************************************************************************************//
  //TMCSTEPPER SETTINGS
  driver_s.begin();
  driver_s.pdn_disable(true);      // Use PDN/UART pin for communication
  driver_s.toff(3);                // [1..15] enable driver_s in software
  driver_s.blank_time(24);         // [16, 24, 36, 54]
  driver_s.I_scale_analog(false);  // Use internal voltage reference
  driver_s.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."
  driver_s.mstep_reg_select(true);
  driver_s.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  driver_s.seimin(1);                     // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver_s.semin(15);                     // [0... 15] If the StallGuard4 result falls below SEMIN*32, the motor current becomes increased to reduce motor load angle.
  driver_s.semax(15);                     // [0... 15]  If the StallGuard4 result is equal to or above (SEMIN+SEMAX+1)*32, the motor current becomes decreased to save energy.
  driver_s.iholddelay(3);                 // 0 - 15 smooth current drop
  driver_s.intpol(true);                  // interpolate to 256 microsteps
  driver_s.SGTHRS(stall);
  driver_s.pwm_autoscale(true);    // Needed for stealthChop
  driver_s.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver_s.internal_Rsense(false);
  driver_s.TCOOLTHRS(0);
  driver_s.TPWMTHRS(0);
  //************************************************************************************************************************//

  //************************************************************************************************************************//
  //TMCSTEPPER SETTINGS
  driver_p.begin();
  driver_p.pdn_disable(true);      // Use PDN/UART pin for communication
  driver_p.toff(3);                // [1..15] enable driver_p in software
  driver_p.blank_time(24);         // [16, 24, 36, 54]
  driver_p.I_scale_analog(false);  // Use internal voltage reference
  driver_p.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."
  driver_p.mstep_reg_select(true);
  driver_p.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  driver_p.seimin(1);                     // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver_p.semin(15);                     // [0... 15] If the StallGuard4 result falls below SEMIN*32, the motor current becomes increased to reduce motor load angle.
  driver_p.semax(15);                     // [0... 15]  If the StallGuard4 result is equal to or above (SEMIN+SEMAX+1)*32, the motor current becomes decreased to save energy.
  driver_p.iholddelay(3);                 // 0 - 15 smooth current drop
  driver_p.intpol(true);                  // interpolate to 256 microsteps
  driver_p.SGTHRS(stall);
  driver_p.pwm_autoscale(true);    // Needed for stealthChop
  driver_p.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver_p.internal_Rsense(false);
  driver_p.TCOOLTHRS(0);
  driver_p.TPWMTHRS(0);
  //************************************************************************************************************************//

  //************************************************************************************************************************//
  //TMCSTEPPER SETTINGS
  driver_t.begin();
  driver_t.pdn_disable(true);      // Use PDN/UART pin for communication
  driver_t.toff(3);                // [1..15] enable driver_t in software
  driver_t.blank_time(24);         // [16, 24, 36, 54]
  driver_t.I_scale_analog(false);  // Use internal voltage reference
  driver_t.rms_current(current);   // motor RMS current (mA) "rms_current will by default set ihold to 50% of irun but you can set your own ratio with additional second argument; rms_current(1000, 0.3)."
  driver_t.mstep_reg_select(true);
  driver_t.microsteps(motor_microsteps);  //note that MicroPlyer will interpolate to 256
  driver_t.seimin(1);                     // minimum current for smart current control 0: 1/2 of IRUN 1: 1/4 of IRUN
  driver_t.semin(15);                     // [0... 15] If the StallGuard4 result falls below SEMIN*32, the motor current becomes increased to reduce motor load angle.
  driver_t.semax(15);                     // [0... 15]  If the StallGuard4 result is equal to or above (SEMIN+SEMAX+1)*32, the motor current becomes decreased to save energy.
  driver_t.iholddelay(3);                 // 0 - 15 smooth current drop
  driver_t.intpol(true);                  // interpolate to 256 microsteps
  driver_t.SGTHRS(stall);
  driver_t.pwm_autoscale(true);    // Needed for stealthChop
  driver_t.en_spreadCycle(false);  // false = StealthChop / true = SpreadCycle, Spreadcycle can have higher RPM but is louder
  driver_t.internal_Rsense(false);
  driver_t.TCOOLTHRS(0);
  driver_t.TPWMTHRS(0);
  //************************************************************************************************************************//

}

void loop() {
  vTaskDelete(NULL);
}

So, since the software is staying the same but the hardware is changing, it is most likely a hardware problem, right?
Are you going to try and pull them low instead of leaving them floating when they are connected?

1 Like

If i reconnect the wires the other end of the wire goes to the esp32 pin outputs that can you see on the diagram and the code. If i think good it's not floating when i set up like in the last code.
Or am i wrong?

You're not driving them high or low, just setting as output.

Hi,
How many motor drivers do you have connected at the moment.

If all three, then STOP.
Have only ONE connected and write some code just to test ONE motor.

I hope this is how you started your code for this project.

Tom.. :smiley: :+1: :coffee: :australia:

Yes, this is how the story began.

Hi,
Can you show us your short test code?
Can you please post a schematic of your simple one driver circuit?

Can you post some images of your one drive project?

Thanks... Tom.. :smiley: :+1: :coffee: :coffee: :coffee:

Added these lines, if I understand correctly, this is how I set the outputs to low.

  digitalWrite(SLIDE_MOTOR_DIRECTION_PIN, LOW);
  digitalWrite(SLIDE_MOTOR_STEP_PIN, LOW);
  digitalWrite(PAN_MOTOR_DIRECTION_PIN, LOW);
  digitalWrite(PAN_MOTOR_STEP_PIN, LOW);
  digitalWrite(TILT_MOTOR_DIRECTION_PIN, LOW);
  digitalWrite(TILT_MOTOR_STEP_PIN, LOW);

Same result.

So, you don't get any movement when you only have two connected? Have you tried using separate software serial connections from your ESP to each TMC2209 instead of chaining all 3 onto one?

Hi,
Its a long read, so I'll ask here.

How are you controlling the 2209s, Serially UART or Step/Dir Pins?

You cannot use both.

Your schematic in post #106 shows connections for BOTH.

If you are using UART, then disconnect the Step/Dir pin connections.
If you are using Step/Dir pins, then disconnect the UART connections.

Tom... :smiley: :+1: :coffee: :coffee: :australia:

If you don't want to use the onboard PWM generator how else would you step the motor except for the STEP and DIR pins? You can set the configuration options using the UART connection and then step the motor using the STEP pins. From the TMC2209 datasheet:

It says right there, there are 3 lines for control. STEP, DIR and UART.

1 Like

6 posts were split to a new topic: Do not hijack again

What? Are you thinking of the diag pin? That's where StallGuard signals. The index pin gets a pulse every time the motor is commanded to take 1 step. It has no idea if it was successful or not.

1 Like