0
\$\begingroup\$

I am attempting to run a Nema 17 stepper motor with the BigTreeTech TMC2240 stepper motor driver. I am connecting to the driver module with an Arduino UNO R3 board, and I'm using Arduino IDE v2.3.0.

Datasheets:


At first I tried to connect the driver in the same fashion as it's predescessor, the TMC2209 which I used before and had no issues with. I want to run this driver as simply as possible, with the STEP and DIR pins like most of the other simillar drivers out there. I can see that the TMC2240 has a simillar pinout to the TMC2209, with the microstep MS pins replaced with the SPI & UART connection pins:

enter image description here enter image description here

The following setup (with the STEP pin connected to pin 6, DIR pin connected to pin 7, EN pin set to LOW and 12V supplied to VM):

enter image description here

With the following code:

const int PIN_STEP = 6;
const int PIN_DIR = 7;
const int PIN_EN = 0;

void setup() 
{
  Serial.begin(9600);

  pinMode(PIN_STEP, OUTPUT);
  pinMode(PIN_DIR, OUTPUT);
  pinMode(PIN_EN, OUTPUT);

  digitalWrite(PIN_EN, LOW);
}

void loop() 
{
  digitalWrite(PIN_DIR, HIGH);
  for (int i = 0; i < 200; i++)
  {
    digitalWrite(PIN_STEP, HIGH);
    delayMicroseconds(500);
    digitalWrite(PIN_STEP, LOW);
    delayMicroseconds(500);
  }
  digitalWrite(PIN_DIR, LOW);
  for (int i = 0; i < 200; i++)
  {
    digitalWrite(PIN_STEP, HIGH);
    delayMicroseconds(500);
    digitalWrite(PIN_STEP, LOW);
    delayMicroseconds(500);
  }

  delay(3000);
}

Resulted in no activity or energiziation of the motor. Since there was virtually no current being drawn by VMot, and since when I used the TMC2209 even when disabled the driver would still draw very little but some current, this setup clearly didn't work.


I then took a good look at the datasheet, to see what exactly I was missing. Since I want to run the driver using the STEP and DIR pins, I went to the section that covers that communication mode:

enter image description here enter image description here

(pages 27, 28)

The only mention of internal registers altering the operation mode using the STEP and DIR pins is the CHOPCONF register. Since the TMC2240 replaced the microstep MS pins with the SPI & UART pins, I had initially assumed that the SPI & UART pins would only be used for 3D printer boards that these drivers are made for. However, while this section doesn't directly state that SPI needs to be used in order to enable the STEP and DIR interface, given that the previous setup didn't work at all made me want to try and use SPI to change the value of that register, and see whether that improves the situation.

I then went to the SPI section in the datasheet, to try and figure out exactly how I could change internal registers of the driver.

enter image description here

(page 22)

The above section on the SPI interface details the structure of the messages sent, with each message constisting of 5 bytes; the first byte being the register address, and the rest being the data. It supports both reading & writing of most registers.

In every reply message recieved from the driver, one of the bytes apparently contains the SPI_STATUS flags, which are defined as follows:

enter image description here

(page 23)

The driver is also apparently configured to communicate via SPI by default, with UART being disabled as per the UART_EN pin and a solder joint on the back of the board:

enter image description here

(page 15)

The document as includes some example SPI commands:

enter image description here

(page 23)


I then setup my driver board with my Arduino as follows, with all the SPI pins connected:

enter image description here enter image description here

I wanted to then try and send some sample read commands to the driver, to try and read some of the registers.

All of the registers and their purposes are defined at the bottom of the document, and are apparently separated into a few broad categories, defined as such:

enter image description here

(page 74)

The CHOPCONF register is located at address 0x6C, and one of it's bytes named MRES contains the current microstep configuration defined as follows:

enter image description here

(page 110)

enter image description here

(page 111)

I first wanted to try and read the CHOPCONF register, using the following code which polls the register every 3 seconds:

#include "SPI.h"

const int PIN_EN = 0;
const int PIN_CS = 3;

void setup() 
{
  Serial.begin(9600);

  pinMode(PIN_EN, OUTPUT);
  pinMode(PIN_CS, OUTPUT);
  SPI.begin();

  digitalWrite(PIN_EN, LOW);
}

void loop() 
{
  uint8_t data[5] = {0x6C, 0x00, 0x00, 0x00, 0x00};
  SPIExchange(data);

  Serial.print(data[0], BIN); Serial.print(", ");
  Serial.print(data[1], BIN); Serial.print(", ");
  Serial.print(data[2], BIN); Serial.print(", ");
  Serial.print(data[3], BIN); Serial.print(", ");
  Serial.println(data[4], BIN);

  delay(3000);
}

void SPIExchange(uint8_t* data)
{
  digitalWrite(PIN_CS, LOW);
  SPI.transfer(data, 5);
  digitalWrite(PIN_CS, HIGH);
}

Here I'm using the address 0x6C defined in the datasheet, followed by all zeroes like in the example read command shown previously. The values I got were very inconsistent. It would alternate between the first byte showing some value, and all zeroes:

enter image description here

I tried polling several other registers to see whether the behaviour would change, but to no avail.


At this point I'm quite a bit confused as to how exactly one is supposed to operate this driver. Given that the TMC2240's predescessor, the TMC2209, was easily operated using the STEP and DIR pins, it's strange to me that this driver doesn't.

I wanted to ask for feedback and suggestions before trying other things, especially from others who have also tried using the TMC2240 driver.

Thanks for reading my post, any guidance is appreciated.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ It appears in photo that AIN pin connects to EN pin on the breadboard. Did you intend these to be common? Or did you cut the AIN pin shorter? It is unclear where the DIAG0 pin may connect (or not). \$\endgroup\$
    – glen_geek
    Commented Mar 19 at 1:31
  • \$\begingroup\$ @glen_geek thanks for your reply; That is a great point that I should have mentioned in the original post. I have cut both the AIN and DIAG0 pins, so they don't enter the breadboard and short with EN. \$\endgroup\$
    – Runsva
    Commented Mar 19 at 2:43
  • 1
    \$\begingroup\$ @Runsva - Hi, I can't find a clear & specific question, in that series of troubleshooting steps & results. People might guess something like "why doesn't that TMC2240 stepper motor driver, connected to my Arduino running the code in this question, cause any movement of my NEMA 17 stepper motor?" but is that actually correct? At the moment, I only see a request for "feedback and suggestions" which is really too broad & unfocused to be a good fit for this site (although it may be OK on a discussion forum) so it could be closed. || Please edit your post to add a clear & specific question. TY \$\endgroup\$
    – SamGibson
    Commented Mar 23 at 12:15

1 Answer 1

1
\$\begingroup\$

I think I figured it out;


At first I tried messing around with the SPI registers some more, and while I was able to view them and change some of them, I wasn't able to get the motor to run, even with a different microcontroller.

I also contacted BigTreeTech, who straight up told me: "TMC 2240 only has two modes, one is uart and the other is SPI. Setp/DIR mode cannot be used".

That response left me even more confused, about ready to give up with this driver.

However, after some more searching online, I finally found a library named TMC2240-LIB on GitHub: https://github.com/makerbase-mks/TMC2240-LIB

I downloaded it, and installed it manually into my Arduino IDE (since it's not listed in Arduino IDE's Library Manager). It appeared simillar to the existing TMCStepper library that's often used with BigTreeTech's other drivers, so I tried to compile a sketch with it.

However, initially it could not compile, because of missing included files. After taking a look at the files it was trying to include, it seemed like it was trying to include the TMCStepper library. I then realized that this is probably supposed to be an addition to the TMCStepper library, probably not yet included because the TMC2240 is a relatively new driver. Indeed, most of the functions defined in the TMC2240Stepper class are the same as the other drivers included in TMCStepper.

I then thought that I would probably have to merge TMC2240-LIB into TMCStepper, however I was able to get a sketch to compile by just including TMCStepper before TMC2240-LIB's main header file named TMC2240XStepper.h.

Using that setup, with all the SPI pins connected, I was able to get the motor running with the following testing code, using the STEP and DIR pins:

#include "TMCStepper.h"
#include "TMC2240XStepper.h"

const int PIN_EN = 21;
const int PIN_STEP = 5;
const int PIN_DIR = 10;
const int PIN_CS = 23;
const int PIN_CLK = 19;
const int PIN_MOSI = 22;
const int PIN_MISO = 18;

TMC2240Stepper Stepper(PIN_CS, PIN_MOSI, PIN_MISO, PIN_CLK);

void setup() 
{
  Serial.begin(9600);

  pinMode(PIN_EN, OUTPUT);
  pinMode(PIN_STEP, OUTPUT);
  pinMode(PIN_DIR, OUTPUT);

  digitalWrite(PIN_EN, LOW);

  Stepper.begin();
  Stepper.toff(5);
  Stepper.rms_current(600);
  Stepper.microsteps(2);
}

void loop() 
{
  digitalWrite(PIN_DIR, HIGH);
  for (int i = 0; i < 200; i++)
  {
    digitalWrite(PIN_STEP, HIGH);
    delayMicroseconds(500);
    digitalWrite(PIN_STEP, LOW);
    delayMicroseconds(500);
  }
  digitalWrite(PIN_DIR, LOW);
  for (int i = 0; i < 200; i++)
  {
    digitalWrite(PIN_STEP, HIGH);
    delayMicroseconds(500);
    digitalWrite(PIN_STEP, LOW);
    delayMicroseconds(500);
  }

  delay(1000);
}

The proper way to do this would probably be to merge the two libraries, to put TMC2240XStepper in with the rest of the drivers defined in TMCStepper, but the setup above worked for me.

enter image description here

Something interesting that I noticed while messing around with the SPI registers of the driver, is that this driver apparently requires BOTH the logic VIO and the motor VM pins to be connected to power in order for the registers to maintain their values. If either of the two power pins get disconnected, the registers get wiped shortly after. That struck me as strange, as the VM power I would assume would only get used to supply the motor. But I have also never had to read individual registers inside these drivers, so I couldn't say for certain whether this is the intended behaviour.

However, since the TMC2240Stepper class functions access the registers to configure the driver, that also means that this driver appears to only be able to properly configure itself at startup if BOTH the logic and motor power are supplied.


PS:

After some more time spent messing around with the driver, I was able to rewrite the code so that it doesn't rely on the mentioned libraries at all. TMC2240-LIB appears to be unfinished, and it doesn't appear to have the functions for the vast majority of the registers written properly.

For the simple purposes of just running the motor and setting the current & microsteps, using the libraries as shown in my previous reply works, but I wanted to be able to control the driver more directly.

This code works for me for running the motor, and also acquiring the temperature of the driver as an added bonus:

#include "SPI.h"

const int PIN_EN = 21;
const int PIN_STEP = 5;
const int PIN_DIR = 10;
const int PIN_CS = 23;
const int PIN_CLK = 19;
const int PIN_MOSI = 22;
const int PIN_MISO = 18;

uint8_t CurrentRun = 16;
uint8_t CurrentHold = 16;
uint8_t Microsteps = 4;
uint8_t TOff = 0x05;
float SPISpeed = 16000000 / 8;

bool Setup = false;

void setup() 
{
  Serial.begin(9600);

  pinMode(PIN_EN, OUTPUT);
  pinMode(PIN_STEP, OUTPUT);
  pinMode(PIN_DIR, OUTPUT);
  pinMode(PIN_CS, OUTPUT);

  digitalWrite(PIN_EN, LOW);

  SPI.begin(PIN_CLK, PIN_MISO, PIN_MOSI, PIN_CS);
  digitalWrite(PIN_CS, HIGH);
}

void loop() 
{
  if (!Setup)
  {
    Setup = MotorInit(CurrentRun, CurrentHold, Microsteps);

    if (!Setup)
    {
      Serial.println("Failed to initialize! Retrying...");
      delay(1000);
      return;
    }
    else
    {
      Serial.println("Successfully initialized!");
    }
  }
  
  Serial.println("Status: " + ByteToBin(MotorGetStatus()));
  Serial.println("Temp: " + String(MotorGetTemp()));

  MotorRun(400, 0, 1000);
  MotorRun(400, 1, 1000);

  delay(5000);
}

uint8_t MotorGetStatus()
{
  uint8_t status;
  uint32_t dataDummy;
  RegRead(0x00, &dataDummy, &status);

  return status;
}

float MotorGetTemp()
{
  uint8_t status;
  uint32_t data;
  RegRead(0x51, &data, &status);

  return (float)((uint16_t)(data & 0x00001FFF) - 2038) / 7.7;
}

void MotorRun(const uint32_t steps, const bool dir, const uint32_t stepDelay)
{
  digitalWrite(PIN_DIR, dir);

  for (int i = 0; i < steps; i++)
  {
    digitalWrite(PIN_STEP, HIGH);
    delayMicroseconds(stepDelay);
    digitalWrite(PIN_STEP, LOW);
    delayMicroseconds(stepDelay);
  }
}

bool MotorInit(const uint8_t currentRun, const uint8_t currentHold, const uint8_t microsteps)
{
  uint32_t ms;

  switch (microsteps)
  {
    case 128: ms = 0x1; break;
    case 64: ms = 0x2; break;
    case 32: ms = 0x3; break;
    case 16: ms = 0x4; break;
    case 8: ms = 0x5; break;
    case 4: ms = 0x6; break;
    case 2: ms = 0x7; break;
    case 1: ms = 0x8; break;
    default: ms = 0x0; break;
  }

  // Setting TOFF flag & microsteps...
  RegWrite(0x6C, 0x10410150 | (ms << 24) | TOff);
  // Setting running & holding current...
  RegWrite(0x10, 0x00060000 | ((uint32_t)(currentRun & 0x1F) << 8) | ((uint32_t)currentHold & 0x1F));

  uint8_t status;
  uint32_t data;
  RegRead(0x6C, &data, &status);

  if ((data & 0x0000000F) != TOff)
    return false;

  return true;
}

void RegWrite(const uint8_t address, const uint32_t data)
{
  uint8_t buff[5] = {address | 0x80, (data >> 24) & 0xFF, (data >> 16) & 0xFF, (data >> 8) & 0xFF, data & 0xFF};
  SPIExchange(buff, 5);
}

void RegRead(const uint8_t address, uint32_t* data, uint8_t* status)
{
  uint8_t buff[5];

  for (int i = 0; i < 2; i++)
  {
    buff[0] = address; buff[1] = 0x00; buff[2] = 0x00; buff[3] = 0x00; buff[4] = 0x00;
    SPIExchange(buff, 5);
  }

  *status = buff[0];
  *data = (buff[1] << 24) | (buff[2] << 16) | (buff[3] << 8) | buff[4];
}

void SPIExchange(uint8_t* data, const int size)
{
  digitalWrite(PIN_CS, LOW);
  delayMicroseconds(1);
  SPI.beginTransaction(SPISettings(SPISpeed, MSBFIRST, SPI_MODE3));
  delayMicroseconds(1);
  
  SPI.transfer(data, size);
  delayMicroseconds(1);

  SPI.endTransaction();
  delayMicroseconds(1);
  digitalWrite(PIN_CS, HIGH);
  delayMicroseconds(1);
}

String ByteToBin(const uint8_t num)
{
  String str = String(bitRead(num, 0)) + String(bitRead(num, 1)) + String(bitRead(num, 2)) + String(bitRead(num, 3)) +
               String(bitRead(num, 4)) + String(bitRead(num, 5)) + String(bitRead(num, 6)) + String(bitRead(num, 7));

  return StrReverse(str);
}

String Int16ToBin(const uint16_t num)
{
  String str = String(bitRead(num, 0)) + String(bitRead(num, 1)) + String(bitRead(num, 2)) + String(bitRead(num, 3)) +
               String(bitRead(num, 4)) + String(bitRead(num, 5)) + String(bitRead(num, 6)) + String(bitRead(num, 7)) + " " +
               String(bitRead(num, 8)) + String(bitRead(num, 9)) + String(bitRead(num, 10)) + String(bitRead(num, 11)) +
               String(bitRead(num, 12)) + String(bitRead(num, 13)) + String(bitRead(num, 14)) + String(bitRead(num, 15));

  return StrReverse(str);
}

String Int32ToBin(const uint32_t num)
{
  String str = String(bitRead(num, 0)) + String(bitRead(num, 1)) + String(bitRead(num, 2)) + String(bitRead(num, 3)) +
               String(bitRead(num, 4)) + String(bitRead(num, 5)) + String(bitRead(num, 6)) + String(bitRead(num, 7)) + " " +
               String(bitRead(num, 8)) + String(bitRead(num, 9)) + String(bitRead(num, 10)) + String(bitRead(num, 11)) +
               String(bitRead(num, 12)) + String(bitRead(num, 13)) + String(bitRead(num, 14)) + String(bitRead(num, 15)) + " " +
               String(bitRead(num, 16)) + String(bitRead(num, 17)) + String(bitRead(num, 18)) + String(bitRead(num, 19)) +
               String(bitRead(num, 20)) + String(bitRead(num, 21)) + String(bitRead(num, 22)) + String(bitRead(num, 23)) + " " +
               String(bitRead(num, 24)) + String(bitRead(num, 25)) + String(bitRead(num, 26)) + String(bitRead(num, 27)) +
               String(bitRead(num, 28)) + String(bitRead(num, 29)) + String(bitRead(num, 30)) + String(bitRead(num, 31));

  return StrReverse(str);
}

String StrReverse(String str)
{
  String strReversed = "";

  for (int i = (str.length() - 1); i >= 0; i--)
    strReversed += str[i];

  return strReversed;
}

Hope all this information helps out others working with this driver.

Cheers!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.