teemuatlut / TMCStepper

MIT License
501 stars 196 forks source link

Stepper motor stops running after several seconds with TMC5160 and Arduino Uno #208

Open gedeon93 opened 3 years ago

gedeon93 commented 3 years ago

I'm attempting to use BigTreeTech's TMC5160 breakout board via SPI for controlling a 2.0A NEMA bi-polar stepper, in the interest of integrating the Trinamic StallGuard capability into my project. The example configuration using TMCStepper works fine initially when testing, however; the motor randomly stops responding a few seconds (varies) of time after starting.

Here are the logs of my most recent attempt:

Start... (0, sg_result, cs2rms, OCR1A) 0 1023 996 256 0 1023 996 256 0 1023 996 256 0 1023 996 256 0 1023 996 256 0 1023 996 256 0 1023 996 256 0 1023 996 256 0 1023 996 256 0 1023 996 236 0 1023 996 236 0 1023 996 236 0 1023 996 236 0 1023 996 236 0 1023 996 216 0 1023 996 216 0 1023 996 216 0 1023 996 216 0 1023 996 216 0 1023 996 216 0 1023 996 196 0 1023 996 196 0 1023 996 196 0 1023 996 196 0 1023 996 196 0 1023 996 196 0 1023 996 176 0 1023 996 176 0 1023 996 176 0 1023 996 176 0 1023 996 156 0 1023 996 156 0 1023 996 156 0 1023 996 156 0 1023 996 156 0 1023 996 156 0 1023 996 156 0 1023 996 176 0 1023 996 176 0 1023 996 176 0 1023 996 176 0 1023 996 176 0 1023 996 176 0 1023 996 176 0 1023 996 196 0 1023 996 196 0 1023 996 196 0 1023 996 196 0 0 1060 196 (motor stops, sg_result stays 0) 0 0 1999 196 0 0 1999 196 0 0 1999 196 0 0 1999 196 0 0 1999 196 0 0 1999 196 0 0 1999 196 0 0 1999 196 0 0 1999 196 0 0 1999 196 (repeats indefinitely) .....

Attached is a picture of the wiring that I have currently implemented. It's hard to notice in the picture but the CLK pin is connected to GND. I've tried with leaving it unconnected and didn't notice any difference in behavior. Disregard the randomly placed spare red wire. tmc5160_wiring

And lastly is the code I have been using. I am tweaking the driver values here and there with not much luck.

#include <TMCStepper.h>

#define MAX_SPEED      40 // In timer value
#define MIN_SPEED      1000

#define STALL_VALUE      50 // [-64..63]

#define EN_PIN           7 // Enable
#define DIR_PIN          5 // Direction
#define STEP_PIN         6 // Step
#define CS_PIN           10 // Chip select
#define SW_MOSI          11 // Software Master Out Slave In (MOSI)
#define SW_MISO          12 // Software Master In Slave Out (MISO)
#define SW_SCK           13 // Software Slave Clock (SCK)

#define R_SENSE 0.11f // Match to your driver
                      // SilentStepStick series use 0.11
                      // UltiMachine Einsy and Archim2 boards use 0.2
                      // Panucatt BSD2660 uses 0.1
                      // Watterott TMC5160 uses 0.075

// Select your stepper driver type
//TMC2130Stepper driver(CS_PIN, R_SENSE);                           // Hardware SPI
//TMC2130Stepper driver(CS_PIN, R_SENSE, SW_MOSI, SW_MISO, SW_SCK); // Software SPI
TMC5160Stepper driver(CS_PIN, R_SENSE);
//TMC5160Stepper driver(CS_PIN, R_SENSE, SW_MOSI, SW_MISO, SW_SCK);

using namespace TMC2130_n;
//using namespace TMC5130_n;

// Using direct register manipulation can reach faster stepping times
#define STEP_PORT     PORTF // Match with STEP_PIN
#define STEP_BIT_POS      0 // Match with STEP_PIN

ISR(TIMER1_COMPA_vect){
  //STEP_PORT ^= 1 << STEP_BIT_POS;
  digitalWrite(STEP_PIN, !digitalRead(STEP_PIN));
}

void setup() {
  SPI.begin();
  Serial.begin(250000);         // Init serial port and set baudrate
  while(!Serial);               // Wait for serial port to connect
  Serial.println("\nStart...");

  pinMode(EN_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  pinMode(CS_PIN, OUTPUT);
  pinMode(DIR_PIN, OUTPUT);
  pinMode(MISO, INPUT_PULLUP);
  digitalWrite(EN_PIN, LOW);

  driver.begin();
  driver.toff(4);
  driver.blank_time(24);
  driver.rms_current(2000); // mA
  driver.microsteps(16);
  driver.en_pwm_mode(true);
  driver.pwm_autoscale(true);
  driver.TCOOLTHRS(0xFFFFF); // 20bit max
  driver.THIGH(0);
  driver.semin(5);
  driver.semax(2);
  driver.sedn(0b01);
  driver.sgt(STALL_VALUE);

  // Set stepper interrupt
  {
    cli();//stop interrupts
    TCCR1A = 0;// set entire TCCR1A register to 0
    TCCR1B = 0;// same for TCCR1B
    TCNT1  = 0;//initialize counter value to 0
    OCR1A = 256;// = (16*10^6) / (1*1024) - 1 (must be <65536)
    // turn on CTC mode
    TCCR1B |= (1 << WGM12);
    // Set CS11 bits for 8 prescaler
    TCCR1B |= (1 << CS11);// | (1 << CS10);
    // enable timer compare interrupt
    TIMSK1 |= (1 << OCIE1A);
    sei();//allow interrupts
  }
}

void loop() {
  static uint32_t last_time=0;
  uint32_t ms = millis();

  while(Serial.available() > 0) {
    int8_t read_byte = Serial.read();
    #ifdef USING_TMC2660
      if (read_byte == '0')      { TIMSK1 &= ~(1 << OCIE1A); driver.toff(0); }
      else if (read_byte == '1') { TIMSK1 |=  (1 << OCIE1A); driver.toff(driver.savedToff()); }
    #else
      if (read_byte == '0')      { TIMSK1 &= ~(1 << OCIE1A); digitalWrite( EN_PIN, HIGH ); }
      else if (read_byte == '1') { TIMSK1 |=  (1 << OCIE1A); digitalWrite( EN_PIN,  LOW ); }
    #endif
    else if (read_byte == '+') { if (OCR1A > MAX_SPEED) OCR1A -= 20; }
    else if (read_byte == '-') { if (OCR1A < MIN_SPEED) OCR1A += 20; }
  }

  if((ms-last_time) > 100) { //run every 0.1s
    last_time = ms;

    DRV_STATUS_t drv_status{0};
    drv_status.sr = driver.DRV_STATUS();

    Serial.print("0 ");
    Serial.print(drv_status.sg_result, DEC);
    Serial.print(" ");
    Serial.print(driver.cs2rms(drv_status.cs_actual), DEC);
    Serial.print(" ");
    Serial.println(OCR1A);
  }
}

I honestly can't say that I know much of what I am doing in tweaking the TMC5160 driver values. I can't tell from the docs what they mean nor how they impact my runtime behavior and results. The TMCStepper docs reference the class variables and function definitions but I can't find any sort of descriptions on how they impact the setup.

I greatly appreciate any advice on this issue.

gedeon93 commented 3 years ago

Please disregard my last paragraph with many premature assumptions on the TMCStepper library. I now see that the inherited class variables in TMC5160 are essentially a one-to-one reflection of those in Trinamic's 5160 datasheet.

I have tested this setup on both an Arduino Mega and Nano and the problem still persists. The logs from DRV_STATUS infinitely read StallGuard=0, RMS=1999mA once the motor stops running. I've found that each event occurs roughly any time between 1 and 15 seconds of a fresh Arduino AND power supply reset. If my 24V / 6.5A power source is left on and the Arduino is reset, then the logs still read StallGuard=0, RMS=1999mA. This is probably because the TMC5160 is still getting powered, which is where I think the problem is.

The motor still successfully operates as I need when using the Arduino boards and a TB6560 driver (what I'm currently trying to upgrade from).

Possible issues (I think):

  1. Arduino Interrupt/SPI protocol has timing/performance issues with 5160
  2. BigTreeTech 5160 board has timing/signal cycle issues with the motor
  3. 5160 driver configuration in my code is wrong

I've ordered a TMC5160-BOB manufactured by Trinamic, so hopefully that can be a simple solution.

teemuatlut commented 3 years ago

I'd suggest trying to rule out different things and trying to break down everything. Does the same happen with the provided example? I'm seeing some changes and for example for TMC5160 stallGuard is in spreadCycle only, whereas you enable stealthChop with driver.en_pwm_mode(true);. Does the motor stop if you remove any communication beyond initial setup? Does the motor stop after 15s if you run the Simple example? How does running from interrupt versus a busy loop differ?

gedeon93 commented 3 years ago

Thank you for the reply.

I have tested several settings with the code below (straight from the Simple.ino example) and still experience the same problem I've been having. The motor also runs for a few seconds less, on average, using the Simple example.

I've tested the Simple example with nothing beyond the setup; an empty loop function. Not quite sure how to verify the run/stop behavior of the motor since the STEP_PIN must switch between LOW and HIGH in order to move the motor. So, I tried putting the contents of the loop function in the setup as a test and still had no luck.

@teemuatlut since stallGuard is only within spreadCycle, then do I need to manually enable spreadCycle? The TMC5160 driver class doesn't appear to inherit en_spreadCycle() so is it right to assume that spreadCycle is automatically (or always) enabled in the TMC5160?

Lastly, would enabling stealthChop with driver.en_pwm_mode(true); conflict with spreadCycle? Might it present any issues if both are set to enabled?


#include <TMCStepper.h>

#define EN_PIN           7 // Enable
#define DIR_PIN          5 // Direction
#define STEP_PIN         6 // Step
#define CS_PIN           53 // Chip select
#define SW_MOSI          50 // Software Master Out Slave In (MOSI)
#define SW_MISO          51 // Software Master In Slave Out (MISO)
#define SW_SCK           52 // Software Slave Clock (SCK)

#define R_SENSE 0.11f

TMC5160Stepper driver(CS_PIN, R_SENSE);
//TMC5160Stepper driver(CS_PIN, R_SENSE, SW_MOSI, SW_MISO, SW_SCK);

void setup() {
  pinMode(EN_PIN, OUTPUT);
  pinMode(STEP_PIN, OUTPUT);
  pinMode(DIR_PIN, OUTPUT);
  //pinMode(CS_PIN, OUTPUT);
  //pinMode(MISO, INPUT_PULLUP);
  digitalWrite(EN_PIN, LOW);      // Enable driver in hardware

                                  // Enable one according to your setup
  SPI.begin();                    // SPI drivers

  driver.begin();                 //  SPI: Init CS pins and possible SW SPI pins
                                  // UART: Init SW UART (if selected) with default 115200 baudrate
  driver.toff(5);                 // Enables driver in software
  driver.rms_current(2000);        // 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
}

bool shaft = false;

void loop() {
  // Run 5000 steps and switch direction in software
  for (uint16_t i = 5000; i>0; i--) {
    digitalWrite(STEP_PIN, HIGH);
    delayMicroseconds(160);
    digitalWrite(STEP_PIN, LOW);
    delayMicroseconds(160);
  }
teemuatlut commented 3 years ago

You can use en_pwm_mode(false) to disable spreadCycle and enable stealthChop. The two modes are mutually exclusive.

The sense resistor typically used with TMC5160 is 0.075Ohms. If this is set incorrectly, it will throw off the current calculation in the library. You should also start with a lower current if you have no cooling. Here's what Watterott has to say about it https://learn.watterott.com/silentstepstick/faq/#how-to-cool-the-silentstepsticks

gedeon93 commented 3 years ago

I never figured out the timing (or random motor stopping) when using BigTreeTech's TMC5160 breakout. I assume it has some internals that do not work with Arduino clock or SPI protocol?

This week my two Trinamic TMC5160-BOBs finally arrived and now this board successfully drives the motor consistently and does not fail after a random short time. However, I have not been able to get stallGuard2 to work using TMCStepper with either of my code samples above.

Questions: 1) Is the Simple.ino example meant for operating the TMC5160 using Step/Dir mode (SD_MODE=1 in TMC5160 datasheet)? I did not configure this board before and immediately de-soldered R1 and soldered R2. 2) Does the TMC5160 need to be configured first in SD_MODE=0 (R2 soldered, not R1) before switching over to SD_MODE=1 for Step/Dir control? I can not find any information at all in the docs that instructs how to configure TMC5160 before using it.

Any help is appreciated. I will continue to document my progress - hopefully someone else may one day find this helpful.

teemuatlut commented 3 years ago

The Simple example is for step/dir interface originally written for 2130 in mind but it works for other ICs as well.

There is no need to program the driver for anything before using the S/D interface. Typically the driver is configured on startup and is then ready to use. Almost all of the settings can be programmed during runtime but some, like the control mode (ramp controller vs step/dir) can only be changed by making a hardware change.

You can use this example code when you're configuring your code between different modes. https://gist.github.com/teemuatlut/8622510d07429c980ba7fbcea28216b1

gedeon93 commented 3 years ago

Thank you very much for this insight. I have tested and confirmed with your script (adjusted to my motor specs) that I am indeed able to configure TMC5160-BOB and read stallGaurd2 using ATMega2560 via Arduino.

However, a different issue presented itself when testing. The motor easily stalls out and stops turning when I apply physical resistance during operation (rms_current set to 2000, which this NEMA 17's rating). For reference, when driving this particular motor on TB6560 it is very difficult to physically stall the motor and prevent it from turning. It appears as if the TMC5160-BOB is reducing supplied load to the motor upon detecting physical resistance. I do not have much experience with very many motor drivers so perhaps there is a simple explanation for this kind of behavior?

teemuatlut commented 3 years ago

It's possible that if you've enabled coolStep then the motor is running at half power if the thresholds are not properly tuned. You can read the current CS value with .cs_actual().

However you should first try to isolate the issue with torque. How does the torque feel when running just the simple example with no stallGuard or coolStep enabled? Remember that the driver is not fully reset until you turn off power to it.

yoc1983 commented 1 year ago

BTT using sense resistor of 75mOHM use 'R_SENSE 0.075f'