teemuatlut / TMCStepper

MIT License
514 stars 202 forks source link

Using multiple TMC5160 BOBs with Teensy 4.0 over SPI #238

Open lukemm1979 opened 2 years ago

lukemm1979 commented 2 years ago

Hi,

I want to control 5 TMC5160 BOBs over SPI using a Teensy 4.0. I've managed to get the library (Release_v1) working with one 5160 using the code below (a modified version of some example code I found posted here). However if I change CS pin (in hardware and software) to anything other than pin 10 it doesn't work. I thought maybe I could get round this using the software SPI library, as follows:

// SW_SPIClass swSPI(TMC_MOSI, TMC_MISO, TMC_SCK); // TMC5160Stepper driver(swSPI, csPin, r_sense);

But this doesn't work at all. Any pointers would be greatly appreciated.

Full code (working using hardware SPI with chip select on pin 10 only):

#include <Arduino.h>
#include <TMCStepper.h>

const int enPin = 22;
// const int TMC_CS = 27;
const int TMC_MOSI = 11;
const int TMC_MISO = 12;
const int TMC_SCK = 13;

// /* H bridge */
// const int DC_BRAKE = 25;
// const int DC_PWM = 33;
// const int DC_DIR = 26;  // 1 - toward load cell

constexpr uint16_t csPin = 10;
constexpr float r_sense = 0.075;
//SW_SPIClass swSPI(TMC_MOSI, TMC_MISO, TMC_SCK);
//TMC5160Stepper driver(swSPI, csPin, r_sense);
// This works (only CS=10 though):
 TMC5160Stepper driver(SPI, csPin, r_sense);

// TMC5160Stepper driver(csPin, r_sense, TMC_MOSI, TMC_MISO, TMC_SCK); //use software SPI
// TMC5160Stepper driver(CS_PIN, R_SENSE, SW_MOSI, SW_MISO, SW_SCK);

void setup() {
    delay(250);
    Serial.begin(115200);
    while(!Serial);
    Serial.println("\nTMCStepper TMC internal motion controller example.\nCompiled " __DATE__ " " __TIME__);
    Serial.println("Start... ");

    // pinMode(TMC_ENABLE, OUTPUT);
    // digitalWrite(TMC_ENABLE, LOW);
    pinMode(csPin, OUTPUT);
    pinMode(enPin, OUTPUT);
    digitalWrite(csPin, HIGH);
    digitalWrite(enPin, LOW);

    // SPI.begin(TMC_SCK, TMC_MISO, TMC_MOSI);
     SPI.begin();
    //swSPI.begin();
    driver.begin();

    TMC5160Stepper::IOIN_t ioin{ driver.IOIN() };

    if (ioin.version == 0xFF || ioin.version == 0) {
        Serial.println("Driver communication error");
        while(true);
    }

    Serial.print("Driver firmware version: ");
    Serial.println(ioin.version);

    if (ioin.sd_mode) {
        Serial.println("Driver is hardware configured for Step & Dir mode");
        while(true);
    }
    if (ioin.drv_enn) {
        Serial.println("Driver is not hardware enabled");
        while(true);
    }

    driver.toff(3);
    driver.rms_current(800);
    // driver.microsteps(256);
    driver.en_pwm_mode(true);

    // driver.a1(9000);
    // driver.v1(50000);
    // driver.AMAX(6000);
    // driver.VMAX(250000);
    // driver.DMAX(1400);
    // driver.d1(1500);
    // driver.vstop(10);
    // driver.RAMPMODE(0);

    // driver.XTARGET(-51200);

    // Original
    driver.a1(12000);
    driver.v1(50000);
    driver.AMAX(6000);
    driver.VMAX(200000);
    driver.DMAX(6000);
    driver.d1(12000);
    driver.vstop(10);
    driver.RAMPMODE(0);

    driver.XTARGET(250000);
}

void loop() {
    static long target = 250000;
    static unsigned long timeLast = 0;

    // delay(1000);

    if(millis() - timeLast > 50) {
        timeLast = millis();

        auto xactual = driver.XACTUAL();
        // auto xtarget = driver.XTARGET();

        char buffer[256];
        sprintf(buffer, "ioin=%#-10x xactual=%7d, target=%d\n",
            driver.IOIN(),
            xactual, target
            );
        Serial.print(buffer);

        if (xactual == target) {
            target *= -1;
            driver.XTARGET(target);
            // Serial.
        }
        // driver.XTARGET(driver.XACTUAL()+300000);
    }
}
cbaugher commented 2 years ago

I have 10x TMC5160s running on SPI right now (mostly reliably). I do have an issue with getting the BOBs initialized where I have to initialize them twice for some reason. The really goofy part is that they work perfectly when the Teensy is rebooted after a reprogram. But a regular cold startup needs the redundant initialization. Still trying to track this one down.

BUT, there is a known issue with timing on the Teensy 4.0/4.1, and possibly others. The chip select line is switched too fast and communication fails. A fix for this is to put the following code at the bottom of your main program:

void TMC2130Stepper::switchCSpin(bool state) {
  delayNanoseconds(100);
  digitalWrite(_pinCS, state);
  delayNanoseconds(100);
}

This overrides the chip select switching function to slow it down a bit. It may or may not work for your issue but it's worth a try.

C|

lukemm1979 commented 2 years ago

Hi @cbaugher

Thanks for the CS tip. In fact I solved my initial issue - some of the silkscreen labels on my (commercially produced) PCB were incorrect.

However I am now having some strange behaviour with the driver which I think may be SPI related. I tried implementing the switchCSpin() function you provided, but in the Release_V1 version of the library which I'm using, that function doesn't seem to be used. I'm only using that version of the library because I was under the impression it was the only one that supported trajectory generation on the TMC5160, and all the demo code I found for the 5160 used it. If you have working code which uses the master branch, I'd be grateful if you could share it.

Thanks,

Luke

cbaugher commented 2 years ago

Hi Luke,

I'm using version 0.7.3, which is the one available from the Arduino library manager. I don't know which code branch that is, but I don't think the usage differences are all that much.

All the code for running the motors is contained in various other functions besides the main loop, but my setup code looks like this:

` // Setup SPI channel SPI.begin();

// Make sure chip select pins for motor drivers are set high first pinMode(MCS1_PIN, OUTPUT); digitalWrite(MCS1_PIN, HIGH); pinMode(MCS2_PIN, OUTPUT); digitalWrite(MCS2_PIN, HIGH); pinMode(MCS3_PIN, OUTPUT); digitalWrite(MCS3_PIN, HIGH); pinMode(MCS4_PIN, OUTPUT); digitalWrite(MCS4_PIN, HIGH); pinMode(MCS5_PIN, OUTPUT); digitalWrite(MCS5_PIN, HIGH); pinMode(MCS6_PIN, OUTPUT); digitalWrite(MCS6_PIN, HIGH); pinMode(MCS7_PIN, OUTPUT); digitalWrite(MCS7_PIN, HIGH); pinMode(MCS8_PIN, OUTPUT); digitalWrite(MCS8_PIN, HIGH); pinMode(MCS9_PIN, OUTPUT); digitalWrite(MCS9_PIN, HIGH); pinMode(MCS10_PIN, OUTPUT); digitalWrite(MCS10_PIN, HIGH);

for(i=0; i<10; i++) { delay(200); driver[i].begin(); ver = driver[i].version(); if(ver>0) { Serial.print("Found Driver version: "); Serial.print(ver); Serial.print(" on channel: "); Serial.println(i+1);

  driver[i].toff(0);                              // "0" Disables driver in software
  driver[i].rms_current(MOTOR_DEFAULT_CURRENT, MOTOR_IHOLD);   // Set motor RMS current
  driver[i].microsteps(MOTOR_DEFAULT_MICROSTEPS); // Set default microsteps
   driver[i].en_pwm_mode(true);                     // Toggle stealthChop on TMC2130/2160/5130/5160
   driver[i].pwm_autoscale(true);
   driver[i].TPOWERDOWN(DRIVER_TPOWERDOWN);
   driver[i].TPWMTHRS(DRIVER_TPWMTHRS);
   driver[i].TCOOLTHRS(DRIVER_TCOOLTHRS);
   driver[i].semin(DRIVER_SEMIN);
   driver[i].sgt(DRIVER_SGT);
   driver[i].pwm_ampl(DRIVER_PWM_AMPL);
   driver[i].pwm_grad(DRIVER_PWM_GRAD);
   driver[i].pwm_freq(DRIVER_PWM_FREQ);
  driver[i].VSTART(0);                            // Set starting velocity
  driver[i].v1(0);                                // Set second velocity point
  driver[i].AMAX(MOTOR_DEFAULT_AMAX);             // Set acceleration
  driver[i].VMAX(0);                              // Set maximum velocity
  driver[i].DMAX(MOTOR_DEFAULT_AMAX);             // Set maximum deceleration
  driver[i].VSTOP(10);                            // Set maximum velocity before stopping
  driver[i].RAMPMODE(1);                          // Set internal pulse generator ramping mode
  //driver[i].sfilt(1);                             // Set filter
} else {
  Serial.print("Couldn't find driver channel: ");
  Serial.println(i+1); 
}

} `

C|

artmekab commented 1 year ago

Hi! Trying this code to give a lot of same the parameters to many motors suggested above but I'm not sure how to it.

Get this Error:

C:\TMC5160_stepstick\Software_SPI\Software_SPI.ino: In function 'void setup()': C:\TMC5160_stepstick\Software_SPI\Software_SPI.ino:39:5: error: 'driver' was not declared in this scope driver[i].begin(); ^~ C:\TMC5160_stepstick\Software_SPI\Software_SPI.ino:39:5: note: suggested alternative: 'driver4' driver[i].begin(); ^~ driver4

`#include

define EN_PIN 20 // Enable // GND nu

define CS_PIN1 17 // Chip select

define CS_PIN2 0 // Chip select

define CS_PIN3 1 // Chip select

define CS_PIN4 2 // Chip select

define SW_MOSI 19 // Software Master Out Slave In (MOSI)

define SW_MISO 16 // Software Master In Slave Out (MISO)

define SW_SCK 18 // Software Slave Clock (SCK)

define R_SENSE 0.075f

int i = 0; TMC5160Stepper driver1 = TMC5160Stepper(CS_PIN1, R_SENSE, SW_MOSI, SW_MISO, SW_SCK); TMC5160Stepper driver2 = TMC5160Stepper(CS_PIN2, R_SENSE, SW_MOSI, SW_MISO, SW_SCK); TMC5160Stepper driver3 = TMC5160Stepper(CS_PIN3, R_SENSE, SW_MOSI, SW_MISO, SW_SCK); TMC5160Stepper driver4 = TMC5160Stepper(CS_PIN4, R_SENSE, SW_MOSI, SW_MISO, SW_SCK);

void setup() {

int ver = 0; SPI.begin(); pinMode(CS_PIN1, OUTPUT); digitalWrite(CS_PIN1, HIGH); pinMode(CS_PIN2, OUTPUT); digitalWrite(CS_PIN2, HIGH); pinMode(CS_PIN3, OUTPUT); digitalWrite(CS_PIN3, HIGH); pinMode(CS_PIN4, OUTPUT); digitalWrite(CS_PIN4, HIGH); //Set HIGH first..?

for (i = 0; i < 4; i++) { delay(200); driver[i].begin(); ver = driver[i].version(); if (ver > 0) { Serial.print("Found Driver version: "); Serial.print(ver); Serial.print(" on channel: "); Serial.println(i + 1);

  SPI.begin();
  driver[i].begin();  // Initiate pins and registeries

  driver[i].ihold(16);
  driver[i].irun(31);
  driver[i].iholddelay(5);
  driver[i].TPOWERDOWN(10);
  driver[i].GLOBAL_SCALER(80);
  driver[i].microsteps(32);
  driver[i].en_pwm_mode(1);
  driver[i].pwm_autoscale(1);
  driver[i].pwm_autograd(1);
  driver[i].toff(5);
  driver[i].tbl(2);
  driver[i].hstrt(4);
  driver[i].hend(0);

  driver[i].RAMPMODE(0);  // Set internal pulse generator ramping mode
  driver[i].VSTART(0);    // Set starting velocity
  driver[i].v1(0);        // 0 = disable A1 and D1 phase, use AMAX DMAX only
  driver[i].AMAX(1000);   // Set acceleration
  driver[i].VMAX(6000);   // Set maximum velocity
  driver[i].DMAX(1000);   // Set maximum deceleration
  driver[i].VSTOP(10);    // Set maximum velocity before stopping
} else {
  Serial.print("Couldn't find driver channel: ");
  Serial.println(i + 1);
}

} pinMode(EN_PIN, OUTPUT); digitalWrite(EN_PIN, LOW); // Enable driver in hardware

delay(1000); }

void loop() { if (driver1.XACTUAL() == 0) { //Go back and fourth driver1.XTARGET(50000); }

if (driver1.XACTUAL() == 50000) { driver1.XTARGET(0); } }`

cbaugher commented 1 year ago

Hi @artmekab ,

The problem is you've defined your drivers as individual objects and not as a single array of objects. You need to replace this chunk here:

TMC5160Stepper driver1 = TMC5160Stepper(CS_PIN1, R_SENSE, SW_MOSI, SW_MISO, SW_SCK); TMC5160Stepper driver2 = TMC5160Stepper(CS_PIN2, R_SENSE, SW_MOSI, SW_MISO, SW_SCK); TMC5160Stepper driver3 = TMC5160Stepper(CS_PIN3, R_SENSE, SW_MOSI, SW_MISO, SW_SCK); TMC5160Stepper driver4 = TMC5160Stepper(CS_PIN4, R_SENSE, SW_MOSI, SW_MISO, SW_SCK);

With this:

TMC5160Stepper driver[4]; // This creates an array of 4 driver objects for(i=0;i<4;i++) { // This loop instantiates the driver objects TMC5160Stepper driver[i] = TMC5160Stepper(CS_PIN1, R_SENSE, SW_MOSI, SW_MISO, SW_SCK); }

That should get you going.

C|

artmekab commented 1 year ago

Hi! @cbaugher Thanks for your help! Did not work but I learned some about arrays I think. Also they would all get the same CS_PIN1 right?

This one compiled and worked: TMC5160Stepper driver[4] = { TMC5160Stepper(CS_PIN1, R_SENSE, SW_MOSI, SW_MISO, SW_SCK), TMC5160Stepper(CS_PIN2, R_SENSE, SW_MOSI, SW_MISO, SW_SCK), TMC5160Stepper(CS_PIN3, R_SENSE, SW_MOSI, SW_MISO, SW_SCK), TMC5160Stepper(CS_PIN4, R_SENSE, SW_MOSI, SW_MISO, SW_SCK) }; Not a beauty..

I would have preferred your code though.

I tried the code you provided and got lots of errors:

`#include

define EN_PIN 20 // Enable // GND nu

define CS_PIN1 17 // Chip select

define CS_PIN2 0 // Chip select

define CS_PIN3 1 // Chip select

define CS_PIN4 2 // Chip select

define SW_MOSI 19 // Software Master Out Slave In (MOSI)

define SW_MISO 16 // Software Master In Slave Out (MISO)

define SW_SCK 18 // Software Slave Clock (SCK)

define R_SENSE 0.075f

int ver = 0; int i = 0;

TMC5160Stepper driver[4]; // This creates an array of 4 driver objects for(i=0;i<4;i++) { // This loop instantiates the driver objects TMC5160Stepper driver[i] = TMC5160Stepper(CS_PIN1, R_SENSE, SW_MOSI, SW_MISO, SW_SCK); }

void setup() {

SPI.begin(); pinMode(CS_PIN1, OUTPUT); digitalWrite(CS_PIN1, HIGH); pinMode(CS_PIN2, OUTPUT); digitalWrite(CS_PIN2, HIGH); pinMode(CS_PIN3, OUTPUT); digitalWrite(CS_PIN3, HIGH); pinMode(CS_PIN4, OUTPUT); digitalWrite(CS_PIN4, HIGH); //Set HIGH first..?

for (i = 0; i < 4; i++) { delay(200); driver[i].begin(); ver = driver[i].version(); if (ver > 0) { Serial.print("Found Driver version: "); Serial.print(ver); Serial.print(" on channel: "); Serial.println(i + 1);

  SPI.begin();
  driver[i].begin();  // Initiate pins and registeries

  driver[i].ihold(16);
  -------- //removed some settings to keep the code shorter here on the forum
  driver[i].VSTOP(10);    // Set maximum velocity before stopping
} else {
  Serial.print("Couldn't find driver channel: ");
  Serial.println(i + 1);
}

} pinMode(EN_PIN, OUTPUT); digitalWrite(EN_PIN, LOW); // Enable driver in hardware

delay(1000); }

void loop() { if (driver[0].XACTUAL() == 0) { driver[0].XTARGET(50000); }

if (driver[0].XACTUAL() == 50000) { driver[0].XTARGET(0); } }`

C:\Users\norde\Dropbox\ARDUINO KOD\TMC5160_stepstick\Software_SPI\Software_SPI.ino:21:24: error: no matching function for call to 'TMC5160Stepper::TMC5160Stepper()' TMC5160Stepper driver[4]; // This creates an array of 4 driver objects ^ In file included from C:\Users\norde\Dropbox\ARDUINO KOD\TMC5160_stepstick\Software_SPI\Software_SPI.ino:6:0: c:\Users\norde\OneDrive\Documents\Arduino\libraries\TMCStepper-master\src/TMCStepper.h:726:3: note: candidate: TMC5160Stepper::TMC5160Stepper(uint16_t, float, uint16_t, uint16_t, uint16_t, int8_t) TMC5160Stepper(uint16_t pinCS, float RS, uint16_t pinMOSI, uint16_t pinMISO, uint16_t pinSCK, int8_t link_index = -1); ^~~~~~ c:\Users\norde\OneDrive\Documents\Arduino\libraries\TMCStepper-master\src/TMCStepper.h:726:3: note: candidate expects 6 arguments, 0 provided c:\Users\norde\OneDrive\Documents\Arduino\libraries\TMCStepper-master\src/TMCStepper.h:725:3: note: candidate: TMC5160Stepper::TMC5160Stepper(uint16_t, uint16_t, uint16_t, uint16_t, int8_t) TMC5160Stepper(uint16_t pinCS, uint16_t pinMOSI, uint16_t pinMISO, uint16_t pinSCK, int8_t link_index = -1); ^~~~~~ c:\Users\norde\OneDrive\Documents\Arduino\libraries\TMCStepper-master\src/TMCStepper.h:725:3: note: candidate expects 5 arguments, 0 provided c:\Users\norde\OneDrive\Documents\Arduino\libraries\TMCStepper-master\src/TMCStepper.h:724:3: note: candidate: TMC5160Stepper::TMC5160Stepper(uint16_t, float, int8_t) TMC5160Stepper(uint16_t pinCS, float RS = default_RS, int8_t link_index = -1); ^~~~~~ c:\Users\norde\OneDrive\Documents\Arduino\libraries\TMCStepper-master\src/TMCStepper.h:724:3: note: candidate expects 3 arguments, 0 provided c:\Users\norde\OneDrive\Documents\Arduino\libraries\TMCStepper-master\src/TMCStepper.h:722:7: note: candidate: constexpr TMC5160Stepper::TMC5160Stepper(const TMC5160Stepper&) class TMC5160Stepper : public TMC5130Stepper { ^~~~~~ c:\Users\norde\OneDrive\Documents\Arduino\libraries\TMCStepper-master\src/TMCStepper.h:722:7: note: candidate expects 1 argument, 0 provided c:\Users\norde\OneDrive\Documents\Arduino\libraries\TMCStepper-master\src/TMCStepper.h:722:7: note: candidate: constexpr TMC5160Stepper::TMC5160Stepper(TMC5160Stepper&&) c:\Users\norde\OneDrive\Documents\Arduino\libraries\TMCStepper-master\src/TMCStepper.h:722:7: note: candidate expects 1 argument, 0 provided C:\Users\norde\Dropbox\ARDUINO KOD\TMC5160_stepstick\Software_SPI\Software_SPI.ino:22:1: error: expected unqualified-id before 'for' for(i=0;i<4;i++) { // This loop instantiates the driver objects ^~~ C:\Users\norde\Dropbox\ARDUINO KOD\TMC5160_stepstick\Software_SPI\Software_SPI.ino:22:9: error: 'i' does not name a type for(i=0;i<4;i++) { // This loop instantiates the driver objects ^ C:\Users\norde\Dropbox\ARDUINO KOD\TMC5160_stepstick\Software_SPI\Software_SPI.ino:22:13: error: 'i' does not name a type for(i=0;i<4;i++) { // This loop instantiates the driver objects ^

exit status 1

Compilation error: no matching function for call to 'TMC5160Stepper::TMC5160Stepper()'

cbaugher commented 1 year ago

Hi! @cbaugher Thanks for your help! Did not work but I learned some about arrays I think. Also they would all get the same CS_PIN1 right?

This one compiled and worked: TMC5160Stepper driver[4] = { TMC5160Stepper(CS_PIN1, R_SENSE, SW_MOSI, SW_MISO, SW_SCK), TMC5160Stepper(CS_PIN2, R_SENSE, SW_MOSI, SW_MISO, SW_SCK), TMC5160Stepper(CS_PIN3, R_SENSE, SW_MOSI, SW_MISO, SW_SCK), TMC5160Stepper(CS_PIN4, R_SENSE, SW_MOSI, SW_MISO, SW_SCK) }; Not a beauty..

I would have preferred your code though.

I tried the code you provided and got lots of errors:

@artmekab

Oh yes, that's right. You do have to initialize the objects in the array declaration. I did do it that way in my own code and had forgotten. It doesn't look the best but you can play around with the formatting to make it a bit less clunky.

You will need to use separate chip select lines for each chip, like you've done.

C|