teemuatlut / TMCStepper

MIT License
512 stars 201 forks source link

Trying to understand how to use stall guard with TMC 2209 #178

Open surei01 opened 3 years ago

surei01 commented 3 years ago

Hi all, I am trying to set up a small machine using the TMC 2209 driver and want to use the sensorless homing feature. So my idea is to have the stepper run until it reaches the end and have it stop when the driver reports a stall. I am running this on an Arduino Uno using the CNC shield. I got the thing up and running with the driver in UART mode, communication appears to work, I can control the stepper and query the driver. But I do not find any documentation what it actually is reporting and how I can use that to detect the stall.

Below is my code, bits and pieces copied together from various examples. After setting up the driver it is executing a home() function that should stop when the driver reports the stall. What is the correct code piece to make that work? Also it appears that reading the driver.SG_result() function is rather slow, so I might need another approach to have get the motor run at a higher speed during homing (it is rather slow now).

Any help is highly appreciated!

Best regards Reiner

/**
 * Author Teemu Mäntykallio
 * Initializes the library and runs the stepper
 * motor in alternating directions.
 */

#include <MultiStepper.h>
#include <TMCStepper_UTILITY.h>
#include <TMCStepper.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>

LiquidCrystal_I2C lcd(0x27, 20, 4);

#define DIR_PIN_X          5 // Direction
#define STEP_PIN_X         2 // Step
#define SW_RX_X            10 // TMC2208/TMC2224 SoftwareSerial receive pin
#define SW_TX_X            9 // TMC2208/TMC2224 SoftwareSerial transmit pin
#define DRIVER_ADDRESS 0b00 // TMC2209 Driver address according to MS1 and MS2

#define R_SENSE 0.11f // Match to your driver

TMC2209Stepper driver_x(SW_RX_X, SW_TX_X, R_SENSE, DRIVER_ADDRESS);

#define STALL_VALUE     100 // [0..255]

bool shaft = true;

#include <AccelStepper.h>
AccelStepper stepper = AccelStepper(stepper.DRIVER, STEP_PIN_X, DIR_PIN_X);

void home() {

    driver_x.shaft(false);
    lcd.setCursor(0, 1);
    lcd.print("Going home");
    bool run = true;
    while (run) {
        digitalWrite(STEP_PIN_X, HIGH);
        delayMicroseconds(100);
        digitalWrite(STEP_PIN_X, LOW);
        delayMicroseconds(100);
        if (driver_x.SG_RESULT() > 100)
            run = false;
    }
    driver_x.shaft(shaft);
}

void setup() {

    Serial.begin(115200);         // Init serial port and set baudrate
    while (!Serial);               // Wait for serial port to connect
    Serial.println("\nStart...");

    pinMode(STEP_PIN_X, OUTPUT);
    pinMode(DIR_PIN_X, OUTPUT);

                                    // Enable one according to your setup
    driver_x.beginSerial(115200);     // SW UART drivers

    driver_x.begin();                 //  SPI: Init CS pins and possible SW SPI pins
                                    // UART: Init SW UART (if selected) with default 115200 baudrate
    driver_x.toff(5);                 // Enables driver in software
    driver_x.rms_current(400);        // Set motor RMS current
    driver_x.microsteps(16);          // Set microsteps to 1/16th

  //driver.en_spreadCycle(false);   // Toggle spreadCycle on TMC2208/2209/2224
    driver_x.pwm_autoscale(true);     // Needed for stealthChop

    driver_x.shaft(shaft);

    driver_x.TCOOLTHRS(0xFFFFF); // 20bit max
    driver_x.semin(5);
    driver_x.semax(2);
    driver_x.sedn(0b01);
    driver_x.SGTHRS(STALL_VALUE);

    lcd.init();  
    lcd.backlight(); 
    lcd.setCursor(0, 0);
    lcd.print("INIT");

    home();
}

int count = 0;

void loop() {

    lcd.setCursor(10, 3);
    lcd.print(count++);

    static uint32_t last_time = 0;
    uint32_t ms = millis();

    // Run 5000 steps and switch direction in software
    for (uint16_t i = 100; i > 0; i--) {
        digitalWrite(STEP_PIN_X, HIGH);
        delayMicroseconds(100);
        digitalWrite(STEP_PIN_X, LOW);
        delayMicroseconds(100);

    }
    delay(150);
    shaft = !shaft;
    driver_x.shaft(shaft);
    for (uint16_t i = 100; i > 0; i--) {
        digitalWrite(STEP_PIN_X, HIGH);
        delayMicroseconds(100);
        digitalWrite(STEP_PIN_X, LOW);
        delayMicroseconds(100);
    }
    shaft = !shaft;
    driver_x.shaft(shaft);
    delay(1000);

    delay(100);
    Serial.print(driver_x.SG_RESULT(), DEC);
    Serial.print(" ");
    Serial.println(driver_x.cs2rms(driver_x.cs_actual()), DEC);
}
teemuatlut commented 3 years ago

The TMC2209 DIAG pin will inform you whenever the stallGuard reading falls below the configured value and you don't have to read the value with slow SW UART. Your condition seems to be the wrong way. See Chapter 11 on datasheet. You also need to reset the flag after the fact. image

daniel-frenkel commented 3 years ago

Aside from not setting up StallGuard parameters correctly, you need to add an interrupt in order to actually stop the motor once a high signal is detected on the DIAG pin

Please reference my updated book for more info: https://amzn.to/3XCbUEb

WheeSci commented 2 years ago

For anyone trying to do sensorless homing, here is some Arduino example code that's working for me. It uses the TMC2209 driver from BigTreeTech to run a NEMA 17 stepper and is controlled by a Teensy 4.0.

/**

include

define EN_PIN 10 // Enable

//#define DIR_PIN 3 // Direction

define STEP_PIN 2 // Step

define STALL_PIN_X 21 // Teensy pin that diag pin is attached to

define SERIAL_PORT Serial2 // HardwareSerial port for Teensy 4.0

define DRIVER_ADDRESS 0b00 // TMC2209 Driver address according to MS1 and MS2

define R_SENSE 0.11f // Match to your driver

// higher value of STALL_VALUE increases stall sensitivity
// diag pin pulsed HIGH when SG_RESULT falls below 2*STALL_VALUE
// must be in StealthChop Mode for stallguard to work
// Value of TCOOLTHRS must be greater than TSTEP & TPWMTHRS

define STALL_VALUE 50 // [0..255]

int stepTime = 160; bool startup = true; // set false after homing

TMC2209Stepper driver(&SERIAL_PORT, R_SENSE, DRIVER_ADDRESS); bool shaftVal = false; bool stalled_X = false;

void stallInterruptX(){ // flag set when motor stalls stalled_X = true; }

void setup() { pinMode(EN_PIN, OUTPUT); pinMode(STEP_PIN, OUTPUT); // shaft direction controlled through uart: driver.shaft(true or false) pinMode(STALL_PIN_X, INPUT);

SERIAL_PORT.begin(115200); // HW UART drivers

driver.begin(); // SPI: Init CS pins and possible SW SPI pins driver.toff(4); // Enables driver in software, changed from 5 driver.blank_time(24); driver.rms_current(400); // Set motor RMS current driver.microsteps(16); // Set microsteps to 1/16th

//driver.en_pwm_mode(true); // Toggle stealthChop on TMC2130/2160/5130/5160 //driver.en_spreadCycle(false); // Toggle spreadCycle on TMC2208/2209/2224 driver.pwm_autoscale(true); // Needed for stealthChop driver.semin(5);
driver.semax(2);
driver.sedn(0b01);
driver.shaft(shaftVal); // TCOOLTHRS needs to be set for stallgaurd to work // driver.TCOOLTHRS(0xFFFFF); // 20bit max driver.SGTHRS(STALL_VALUE);
attachInterrupt(digitalPinToInterrupt(STALL_PIN_X), stallInterruptX, RISING); digitalWrite(EN_PIN, LOW); // Enable driver in hardware }

void loop() { if(startup){ // home on starting up startup = false;
homeX(); }

if(stalled_X){ int backSteps = 1000; Serial.println("stalled"); stalled_X = false; shaftVal =! shaftVal; motor(backSteps,160); }

if(Serial.available()>0){ char readVal = Serial.read(); if (readVal == 'x'){
int steps = Serial.parseInt(); if(steps < 0){ shaftVal = true; steps *= -1; } else { shaftVal = false; } motor(steps,160); } else if (readVal == 'h'){ homeX(); } } }

void homeX(){ int homeDelay = 160;
int backSteps = 5000; Serial.println("fast homing x"); shaftVal = true; while(!stalled_X){ // fast home x motor(1000,homeDelay); } stalled_X = false; delay(1000); Serial.println("backing off"); shaftVal = false; motor(backSteps,homeDelay);

Serial.println("slow homing x"); shaftVal = true; while(!stalled_X){ // slow home x motor(1000,homeDelay*2); } stalled_X = false; delay(1000); Serial.println("backing off"); shaftVal = false; motor(backSteps, homeDelay); }

void motor(int steps, int stepDelay){ digitalWrite(EN_PIN, LOW); driver.shaft(shaftVal); for(int i = 0; i<steps; i++){ digitalWrite(STEP_PIN,HIGH); delayMicroseconds(stepDelay); digitalWrite(STEP_PIN,LOW); delayMicroseconds(stepDelay); if(stalled_X){ i = steps; } } digitalWrite(EN_PIN,HIGH); }

jnball89 commented 1 year ago

@WheeSci can you provide some type of wiring diagram that you used for you setup. I have read online that the TMC2209 can have a voltage spike when turning on the motor power source and end up killing the Teensy 4.0/ 4.1 due to it only being 3.3v tolerant. I'm pretty new to this so I hope that makes sense. I don't want to kill my Teensy but want to use the TMC2209. Greatly appreciated!!!

andreasjaede commented 1 year ago

@WheeSci Wow, the first working example I've found! Thank you very much for sharing!

jamesgi001 commented 1 year ago

Hello all - I just spent one month trying to figure this out - so frustrating... In the labyrinth of confusing TMC related docs online, including Github, there is little about using the TMC drivers as a standalone controller, as it is all about 3D printers on mother boards like Marlin, Octopus etc. I emailed BigtreeTech as well though got zero response - despite their promise on their website that they are responsive.

The main problem is that the Arduino Library examples are useless and confusing as they miss the most important element of getting these to work which is the use of interrupts - totally missing from the Arduino library examples - e.g. the "Stallguard" example has no interrupts at all which is kind of dumb. It would be much better if they included the code above in the library download??

Thanks to @WheeSci, this code actually works as it includes the interrupts that pick up the change on the diag pin. Without this, nothing happens.

The other thing to be highlighted is that the code above uses a combination of UART and wired communications in that the step pin is still used together with UART (controlling a bunch of parameters such as shaft direction, micro-stepping, current etc.) The step pin controls the stepping and works fine, though there most probably is a way to control the stepping though UART though after spending to much time on this I am sticking with this as it works.

Once sorted, however, this code works really well and the TMC controller it really great piece of technology - way more powerful than the A4988 and a lot quieter. The homing function is excellent with accurate, repeatable results.

Its a pity that TMC/BigtreeTech etc. do not have better docs for standalone operation of their products, and moreover, better examples in the Arduino Library!!

daniel-frenkel commented 1 year ago

Hello all - I just spent one month trying to figure this out - so frustrating...

I agree, I've been working with these drivers for years and it's frustrating how bad the documentation is. I ended up writing a kindle book that talks about them in layman's terms if anyone is interested. It's everything I wish someone shared with me when I started to work with these steppers

https://amzn.to/3XCbUEb

jamesgi001 commented 1 year ago

Thanks @daniel-frenkel I wish I had known about your book one month ago! I will certainly get one.

KushagraK7 commented 1 year ago

For anyone trying to do sensorless homing, here is some Arduino example code that's working for me. It uses the TMC2209 driver from BigTreeTech to run a NEMA 17 stepper and is controlled by a Teensy 4.0.

/**

  • HomeX sketch for sensorless homing stepper
  • TMC2209 with Teensy 4.0
  • Based on Simple sketch by Teemu Mäntykallio
  • homes on power up.
  • send 'x' followed by int to begin stepping.
  • send 'h' to re-home.
  • RX on pin 7, TX on pin 8
  • PDN_UART pin on tmc2209 connected to pin 7 
  • 1 k resistor between teensy pins 7 and 8
  • motor direction controlled by uart:
  •   driver.shaft(true or false);

**/

include

define EN_PIN 10 // Enable //#define DIR_PIN 3 // Direction #define STEP_PIN 2 // Step #define STALL_PIN_X 21 // Teensy pin that diag pin is attached to #define SERIAL_PORT Serial2 // HardwareSerial port for Teensy 4.0 #define DRIVER_ADDRESS 0b00 // TMC2209 Driver address according to MS1 and MS2 #define R_SENSE 0.11f // Match to your driver

// higher value of STALL_VALUE increases stall sensitivity
// diag pin pulsed HIGH when SG_RESULT falls below 2*STALL_VALUE
// must be in StealthChop Mode for stallguard to work
// Value of TCOOLTHRS must be greater than TSTEP & TPWMTHRS

define STALL_VALUE 50 // [0..255] int stepTime = 160; bool startup = true; // set false after homing

TMC2209Stepper driver(&SERIAL_PORT, R_SENSE, DRIVER_ADDRESS); bool shaftVal = false; bool stalled_X = false;

void stallInterruptX(){ // flag set when motor stalls stalled_X = true; }

void setup() { pinMode(EN_PIN, OUTPUT); pinMode(STEP_PIN, OUTPUT); // shaft direction controlled through uart: driver.shaft(true or false) pinMode(STALL_PIN_X, INPUT);

SERIAL_PORT.begin(115200); // HW UART drivers

driver.begin(); // SPI: Init CS pins and possible SW SPI pins driver.toff(4); // Enables driver in software, changed from 5 driver.blank_time(24); driver.rms_current(400); // Set motor RMS current driver.microsteps(16); // Set microsteps to 1/16th

//driver.en_pwm_mode(true); // Toggle stealthChop on TMC2130/2160/5130/5160 //driver.en_spreadCycle(false); // Toggle spreadCycle on TMC2208/2209/2224 driver.pwm_autoscale(true); // Needed for stealthChop driver.semin(5); driver.semax(2); driver.sedn(0b01); driver.shaft(shaftVal); // TCOOLTHRS needs to be set for stallgaurd to work // driver.TCOOLTHRS(0xFFFFF); // 20bit max driver.SGTHRS(STALL_VALUE); attachInterrupt(digitalPinToInterrupt(STALL_PIN_X), stallInterruptX, RISING); digitalWrite(EN_PIN, LOW); // Enable driver in hardware }

void loop() { if(startup){ // home on starting up startup = false; homeX(); }

if(stalled_X){ int backSteps = 1000; Serial.println("stalled"); stalled_X = false; shaftVal =! shaftVal; motor(backSteps,160); }

if(Serial.available()>0){ char readVal = Serial.read(); if (readVal == 'x'){ int steps = Serial.parseInt(); if(steps < 0){ shaftVal = true; steps *= -1; } else { shaftVal = false; } motor(steps,160); } else if (readVal == 'h'){ homeX(); } } }

void homeX(){ int homeDelay = 160; int backSteps = 5000; Serial.println("fast homing x"); shaftVal = true; while(!stalled_X){ // fast home x motor(1000,homeDelay); } stalled_X = false; delay(1000); Serial.println("backing off"); shaftVal = false; motor(backSteps,homeDelay);

Serial.println("slow homing x"); shaftVal = true; while(!stalled_X){ // slow home x motor(1000,homeDelay*2); } stalled_X = false; delay(1000); Serial.println("backing off"); shaftVal = false; motor(backSteps, homeDelay); }

void motor(int steps, int stepDelay){ digitalWrite(EN_PIN, LOW); driver.shaft(shaftVal); for(int i = 0; i<steps; i++){ digitalWrite(STEP_PIN,HIGH); delayMicroseconds(stepDelay); digitalWrite(STEP_PIN,LOW); delayMicroseconds(stepDelay); if(stalled_X){ i = steps; } } digitalWrite(EN_PIN,HIGH); }

Thank you very much for sharing the code. With a few modifications, I was able to get it running with the AccelStepper library. Now the motor homes without and limit switch and it is very satisfying to see it do so.

dominickairmyne commented 8 months ago

For anyone trying to do sensorless homing, here is some Arduino example code that's working for me. It uses the TMC2209 driver from BigTreeTech to run a NEMA 17 stepper and is controlled by a Teensy 4.0. /**

  • HomeX sketch for sensorless homing stepper
  • TMC2209 with Teensy 4.0
  • Based on Simple sketch by Teemu Mäntykallio
  • homes on power up.
  • send 'x' followed by int to begin stepping.
  • send 'h' to re-home.
  • RX on pin 7, TX on pin 8
  • PDN_UART pin on tmc2209 connected to pin 7 
  • 1 k resistor between teensy pins 7 and 8
  • motor direction controlled by uart:
  •   driver.shaft(true or false);

**/

include

define EN_PIN 10 // Enable //#define DIR_PIN 3 // Direction #define STEP_PIN 2 // Step #define STALL_PIN_X 21 // Teensy pin that diag pin is attached to #define SERIAL_PORT Serial2 // HardwareSerial port for Teensy 4.0 #define DRIVER_ADDRESS 0b00 // TMC2209 Driver address according to MS1 and MS2 #define R_SENSE 0.11f // Match to your driver

// higher value of STALL_VALUE increases stall sensitivity
// diag pin pulsed HIGH when SG_RESULT falls below 2*STALL_VALUE
// must be in StealthChop Mode for stallguard to work
// Value of TCOOLTHRS must be greater than TSTEP & TPWMTHRS

define STALL_VALUE 50 // [0..255] int stepTime = 160; bool startup = true; // set false after homing

TMC2209Stepper driver(&SERIAL_PORT, R_SENSE, DRIVER_ADDRESS); bool shaftVal = false; bool stalled_X = false; void stallInterruptX(){ // flag set when motor stalls stalled_X = true; } void setup() { pinMode(EN_PIN, OUTPUT); pinMode(STEP_PIN, OUTPUT); // shaft direction controlled through uart: driver.shaft(true or false) pinMode(STALL_PIN_X, INPUT); SERIAL_PORT.begin(115200); // HW UART drivers driver.begin(); // SPI: Init CS pins and possible SW SPI pins driver.toff(4); // Enables driver in software, changed from 5 driver.blank_time(24); driver.rms_current(400); // Set motor RMS current driver.microsteps(16); // Set microsteps to 1/16th //driver.en_pwm_mode(true); // Toggle stealthChop on TMC2130/2160/5130/5160 //driver.en_spreadCycle(false); // Toggle spreadCycle on TMC2208/2209/2224 driver.pwm_autoscale(true); // Needed for stealthChop driver.semin(5); driver.semax(2); driver.sedn(0b01); driver.shaft(shaftVal); // TCOOLTHRS needs to be set for stallgaurd to work // driver.TCOOLTHRS(0xFFFFF); // 20bit max driver.SGTHRS(STALL_VALUE); attachInterrupt(digitalPinToInterrupt(STALL_PIN_X), stallInterruptX, RISING); digitalWrite(EN_PIN, LOW); // Enable driver in hardware } void loop() { if(startup){ // home on starting up startup = false; homeX(); } if(stalled_X){ int backSteps = 1000; Serial.println("stalled"); stalled_X = false; shaftVal =! shaftVal; motor(backSteps,160); } if(Serial.available()>0){ char readVal = Serial.read(); if (readVal == 'x'){ int steps = Serial.parseInt(); if(steps < 0){ shaftVal = true; steps = -1; } else { shaftVal = false; } motor(steps,160); } else if (readVal == 'h'){ homeX(); } } } void homeX(){ int homeDelay = 160; int backSteps = 5000; Serial.println("fast homing x"); shaftVal = true; while(!stalled_X){ // fast home x motor(1000,homeDelay); } stalled_X = false; delay(1000); Serial.println("backing off"); shaftVal = false; motor(backSteps,homeDelay); Serial.println("slow homing x"); shaftVal = true; while(!stalled_X){ // slow home x motor(1000,homeDelay2); } stalled_X = false; delay(1000); Serial.println("backing off"); shaftVal = false; motor(backSteps, homeDelay); } void motor(int steps, int stepDelay){ digitalWrite(EN_PIN, LOW); driver.shaft(shaftVal); for(int i = 0; i<steps; i++){ digitalWrite(STEP_PIN,HIGH); delayMicroseconds(stepDelay); digitalWrite(STEP_PIN,LOW); delayMicroseconds(stepDelay); if(stalled_X){ i = steps; } } digitalWrite(EN_PIN,HIGH); }

Thank you very much for sharing the code. With a few modifications, I was able to get it running with the AccelStepper library. Now the motor homes without and limit switch and it is very satisfying to see it do so.

Would you be willing to share your code? I am stuck trying to do the exact same thing with accelstepper and TMC2209!