In this roadtest I'm going to review the TMC2300-IOT-REF board and use the kit to build a dolly camera cart. Because the motor will drive directly the cart wheel, the motor itself will work at very low revs. For this reason, the feature I will evaluate during this test is StealthChop2, which, according to the datasheet, is a
"no-noise, high-precision chopper algorithm for inaudible motion"
The TMC2300-IOT-REF board is an evaluation based on Trinamic TMC2300 low-voltage stepper motor driver and the famous ESP32 processor.
Unboxing
In the kit, the following items were included
- the TMC2300-IOT-REF board
- a PM25S two-phases stepper motor
- a USB-C cable to connect the board to a PC
The board
The board looks very engineered. The ESP32 is clearly visible at center. The TMC2300 chip, which is mounted closed to the 4-pins header connector for the stepper motor, looks really small compared to ESP32. The board also features some LEDs, namely
- CHRG: connected to the CHRG output of the TP4054 (a linear Li-Ion battery charger). This LED is on when the battery connected to connector J4 is charging from USB
- IO18 and IO23: these tow LEDs are connected to the ESP32 GPIOs, so they can be freely used the application running on the processor
- +3.3V: reports the status of the 3.3V power supply
- DIAG: reports the status of the DIAG output pin of the TMC2300 chip. When the DIAG output is high, the motor driver has detected an abnormal condition
- LINK: shows the wifi link status
Also, the board features two pin hole headers, where you can connect your own external hardware. The signals available on these headers include the power supply (both 5 and 3.3V), several GPIOs and the I2C bus
Finally, there are two push buttons to start the ESP32 in bootloader mode, so that you can download a new bootloader using the tools provided by ESP32 manufacturer) and to reset (via the EN pin) the ESP32 itself
Installing the software
I decided to make all the development using the Arduino IDE. Since this is a ESP32-based board, you first need to install the board in the Arduino environment
Open the Arduino IDE and go to Files -> Preferences. In the Additional boards manager URLs, enter
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
Now go to Tools -> Boards -> Board manager and search for ESP32. You should see the board information. Select the latest version and click Install
To use the TMC2300 evaluation kit, you need to select the ESP32 Pico kit board
We are now ready to create a simple application to blink a LED. Two LED are available for user applications, and are connected to pins 18 and 23
Here is the sketch to blink the LEDs
#define LED_GREEN 18 #define LED_RED 23 // the setup function runs once when you press reset or power the board void setup() { pinMode(LED_RED, OUTPUT); pinMode(LED_GREEN, OUTPUT); } // this loop function runs over and over again forever void loop() { digitalWrite(LED_GREEN, HIGH); delay(1000); digitalWrite(LED_GREEN, LOW); delay(1000); digitalWrite(LED_RED, HIGH); delay(1000); digitalWrite(LED_RED, LOW); delay(1000); }
Connecting the motor
The stepper motor provided with the kit is a 2-phase stepper with a 7.5 degrees step angle. It has four wire (2 for each winding) that has to be connected to the 4-pins header of the TMC2300-IOT-REF board
The motor has a male connector that does not fit in the female connector on the board, so I had to check the schematics to make the proper wirings
This is the motor connector
Signal A1,A2 and B1,B2 are connected to OA1,OA2 and OB1,OB2 pins of the TMC2300
OA1,OA2 and OB1,OB2 are the outputs of the full bridges. So it comes out that the right connection was
- A2 --> brown
- A1 -->orange
- B2 --> red
- B1 --> yellow
Controlling the stepper motor
It's now time to make the motor turn. ESP32 can communicate with the TMC2300 in two different ways
1. using two pins STEP e DIR to ask the TMC2300 to make a single step in a certain direction. The background for Step-Direction is industrial motion control, where you need to bridge many meters with as few as possible control lines, while providing a simple and robust motor control interface. However, for high-end controls in industrial applications diagnostics and bus structure connections are new trends. This should be considered a a solution for legacy applications or for extremely resource-limited microcontrollers. After all, if the target of the TMC2300 are battery-operated devices, it should not be a surprise if the hardware designer selects a low-power microcontroller with less than 1k of RAM. Motor run current is fixed by sense resistor setting. The pin PDN_UART selects automatic standstill current reduction. Feedback from the driver to the CPU is granted by the DIAG output signal. Enable or disable the motor using the EN pin and VIO/NSTANDBY pin
2. using an UART. STEP-DIR can not give you information back from the driver, like error signals or StallGuard information. It lacks information about the actual microstep position, and you do not have free control over the motor current. All this is provided by UART interface.
To use a UART interface instead of STEP/DIR signals, some overhead has to be expected on the CPU side. You need a table look up for velocity, plus two writes to the UART interface. You can either do this for every (micro)step, or with a fixed rate, which should be some factor higher than your maximum full step rate, e.g. some kHz for a typical application. The real challenge is to generate accurate acceleration and deceleration ramps, in order to stop the motor at the target position. Quick and precise reaction to the reference and stop switches (if used) is essential. Motion ramp generation is a hard real time task, unless you want to go at motor start/stop velocity of the motor. Then everything you need is a slow few hundred hertz interrupt to do each one step.
It worth noting the UART interface is quite unusual for Trinamic, since all other products are based on SPI. In my opinion, this choice has been made to save pins. SPI requires at least 3 pins (Clock, MISO and MOSI) and, if you want to be able to control more than one driver from a single controller, a Chip select is also required. With a clever design, Trinamic is using just one single pin for UART Rx and Tx.
However, this design requires two external resistors to be mounted (R3 and R6 in schematic above). I agree: pins are very expensive but, nowadays, mounting external components is expensive, too. I think there would have been many other design options available to save pins while reducing the need for external components. For example, MS0 and MS1 pins (which are used to assign and "address" to the driver) could have been implemented as "strap option" pins (i.e. sampled at power-up, while the host controller pins are still in high-impedance, and then used for their specific functions).
Motion without external STEP pulses is provided by an internal programmable step pulse generator: Just set the desired motor velocity. However, no ramping is provided by the TMC2300. Access to multiple driver ICs is possible using 4 different address settings (through MS1 and MS2 pins) or via an analog multiplexer IC.
IMPORTANT NOTE
Steps to shut off the system (if something misbehaves):
- Push and hold the RESET pin
- Disconnect the battery
Disconnecting the battery while the motor is turning may destroy the voltage regulator or battery charger due to insufficient protection from back-EMF voltage spikes!
Steps to start turning the motor with the TMC2300. Only move the motor with the battery connected! If you try to run the motor using the USB as a power supply, the TP4054 will not be able to handle the current that the motor may absorb and will fry!
- Keep the EN pin pulled low
- Pull the IO5 pin low to enable VCC_IO supply for the TMC2300
- Read the IFCNT register (address 0x02) - and verify that you received a reply with a valid CRC
- Write the IHOLD_IRUN register (address 0x10). Suggested: Reduce the run current to a lower value for now, e.g. 10 instead of the default 31.
- Read the IFCNT register (address 0x02) again to verify that the write to IHOLD_IRUN was successful
- Pull the EN pin high
- Move the motor by either pulsing the STEP pin or setting a nonzero value in the VACTUAL register (address 0x22)
Reading and writing TMC2300 registers
The TMC2300 is fully configurable by writing a set of registers. Such registers can be accessed through a serial protocol whose details are provided in the datasheet
To communicate, you just need to configure the ESP32 serial port with 8 data bits and no parity. Any baudrate is ok, since the TMC2300 can detect actual baudrate by analyzing the first byte sent by the host. The first byte of the telegram is 0x05 (i.e. 0000 0101). The alternate 0s and 1s make it possible to measure bit time and. as a consequence, the baudrate
To write a register, the host has to send the following bytes
0: 0x05
1: Slave address, as configured by pins MS1 and MS2. In the TMC2300-IOT-REF, both pins are grounded (see screenshot below)
2: Register address (from 0x00 to 0x7F) OR-ed with 0x80 to set most significative bit
3: Data - byte 3 (MSB)
4: Data - byte 2
5: Data - byte 1
6: Data - byte 0 (LSB)
7: CRC-8
There is CRC8, according to the datasheet, is calculated with the following algorithm
However, this is not going to work. Actually, you need to refer to the provided source code and extract from there the source code for CRC8 calculation.
Another thing to take into account when developing the functions to communicate with the TMC2300, is that, because of one-wire UART, every byte sent is echoed back to the host controller
uint8_t datagram[20]; uint8_t i, idx, len, attempts; uint8_t expCrc; // create read frame datagram[0] = 0x05; datagram[1] = _address; datagram[2] = ubReg | RW_READ; datagram[3] = calcCRC(datagram, 3); len = 20; idx = 0; attempts = 0; // clear any pending character in the RX FIFO while (Serial1.available()) Serial1.read(); // send out frame Serial1.write(datagram, 4); // wait for an answer. Wait up to 10 ms between chars while ((idx < len) && (attempts < 10)) { if (Serial1.available()) { datagram[idx] = (uint8_t)Serial1.read(); idx++; } else { attempts ++; delay(1); } } // check for start-of-frame if (datagram[4] != 0x05) { Serial.print("Missing start of frame"); Serial.println(); return -1; } // check CRC expCrc = calcCRC(&datagram[4], idx-5); if (datagram[idx-1] != expCrc) { Serial.print("CRC Error "); Serial.print(datagram[idx-1], HEX); Serial.print("/"); Serial.print(expCrc, HEX); Serial.println(); return -1; }
To write a register, the following code has been implemented
uint8_t datagram[8]; uint8_t i, len, idx, attempts; datagram[0] = 0x05; datagram[1] = _address; datagram[2] = (ubReg << 1) || RW_WRITE; datagram[3] = ((ulVal >> 24) & 0xFF); datagram[4] = ((ulVal >> 16) & 0xFF); datagram[5] = ((ulVal >> 8) & 0xFF); datagram[6] = (ulVal & 0xFF); datagram[7] = calcCRC(datagram, 7); len = 8; idx = 0; attempts = 0; while (Serial1.available()) Serial1.read(); Serial1.write(datagram, 8); delay(10); while ((idx < len) && (attempts < 10)) { if (Serial1.available()) { datagram[idx] = (uint8_t)Serial1.read(); idx++; } else { attempts ++; delay(1); } }
Before any operation, the TMC2300 has to be put out of sleep by driving low VCC_IO pin. The pin is connect to a transistor driven by ESP32's IO5
The code to drive IO5 is very simple
#define PIN_VCC_IO 5 pinMode(PIN_VCC_IO, OUTPUT); digitalWrite(PIN_VCC_IO, LOW);
Simple velocity control
The easiest way to control the motor is velocity control.
The TMC2300 integrates a high-resolution step pulse generator, allowing motor motion via the UART interface. However, no velocity ramping is provided. Ramping is not required, if the target motion velocity is smaller than the start & stop frequency of the motor. For higher velocities, ramp up the frequency in small steps to accelerate the motor, and ramp down again to decelerate the motor. Figure 13.1 shows an example motion profile ramping up the motion velocity in discrete steps. Ramp velocity steps should be considerably smaller than the maximum start velocity of the motor, because motor torque drops at higher velocity, and motor load at higher velocity typically increases.
VACTUAL is the value to write in register 0x22 to control motor velocity.
The other registers involved in velocity control are listed in section 5.2 of the datasheet
In the documentation, it is not clear what the "n" column means. My educated guess is that it represents the number of meaningful bits in the register.
Also, I didn't found a clear indication of the fclk value. This is the frequency in the internal clock oscillator. In the calculation sheet (available on the Trinamic website) and in section 6.2 (StealthChop options) I found 12 MHz, so I will use this value in my calculations.
The value to write to register 0x22 (VACTUAL) is given by
v[Hz] = VACTUAL * ( fCLK[Hz]/2 / 2^23 )
With nominal oscillator (12 MHz) this means:
v[Hz] = VACTUAL * 0.715 [Hz]
For example, to set a speed of 10 Hz (which also means 10 rpm), VACTUAL register has to be written with the value
VACTUAL = 10 [Hz] / 0.715 [Hz] = 14
To monitor internal step pulse execution, program the DIAG output to provide step pulses (register GCONF, bit diag_step). When programmed like this, the DIAG output will toggle upon each step and thus shows half the microstep frequency. A timer input on the CPU can be used to count pulses. Alternatively, MSCNT register can be polled to grasp steps done in the previous polling interval. The register wraps around from 1023 to 0, so this condition has to be handled by control software.
Current limit
The current the TMC2300 sends to the motor can be limited by means of the following register
By writing this register, you can configure
- the current to send to the motor to keep the standstill position (IHOLD). When this parameter is 0, then the motor will be freewheeling
- the current to send to the motor when running (IRUN)
This parameter is important in a battery-powered device, since it can prevent the battery to be drawn when the motor stalls (even if we need to say that the TMC2300 integrates a StallGuard algorithm that can immediately detect a stall condition). In my case, this parameter is important to prevent any damage to the TP4054 charger IC when I was using the evaluating kit connected to the USB. By setting a low IRUN current, the TP4054 should not fry even if the motor starts while debugging your application
Fitting the motor
A chapter in the datasheet is dedicated to calculate the correct power supply for a motor whose electrical characteristics (coil current and coil resistance) are known. The following parameters needs to be known
ICOILNOM : Nominal (RMS) coil current [A]
RCOIL: Nominal coil resistance [Ω]
Holding torque at ICOILNOM
The datasheet for the motor provided with the kit is not completely clear to me, so I searched the web for the missing parameters. I think the following parameters applies
ICOILNOM 0.6 A
RCOIL:16 Ω
HoldingTorque: 0.0020 Nm
For standstill and slow motion, the formula is
For higher speed, the formula changes to
Which leads to
Ubat = 13,9 V for standstill and slow speed and
Ubat = 16,64 for higher speed
According to these results the motor provided is not a good choice for a battery powered device, since the optimal performances can only be achieved with a quite high voltage
StealthChop2
StealthChop2 is an extremely quiet mode of operation for stepper motors. It is based on a voltage mode PWM. In case of standstill and at low velocities, the motor is absolutely noiseless. Thus, StealthChop2 operated stepper motor applications are very suitable for indoor or home use. The motor operates absolutely free of vibration at low velocities. With StealthChop2, the motor current is applied by driving a certain effective voltage into the coil, using a voltage mode PWM. With the enhanced StealthChop2, the driver automatically adapts to the application for best performance. No more configurations are required. Optional configuration allows for tuning the setting in special cases, or for storing initial values for the automatic adaptation algorithm.
By default, an automatic tuning algorithm is active to optimize StealthChop2 parameters to the motor automatically. This way, StealthChop2 allows high motor dynamics and supports powering down the motor to very low currents. Just two steps have to be respected by the motion controller for best results:
- start with the motor in standstill but powered with nominal run current (this phase is called AT#1)
- Move the motor at medium velocity (e.g. as part of a homing procedure - this phase is called AT#2)
Information about the motor state is available with automatic scaling by reading out PWM_SCALE_SUM register. As this parameter reflects the actual voltage required to drive the target current into the motor, it depends on several factors: motor load, coil resistance, supply voltage and current setting. Therefore an evaluation of the PWM_SCALE_SUM value allows checking the motor operation point. When reaching the limit (255), the current regulator cannot sustain the full motor current
Building the cart
The TMC2300-IOT-REF evaluation kit will be roadtested while building a simple project: a dolly camera cart
The dolly cart is shown in pictures below. It has a three wheels configuration. The front wheel is the traction wheel. The base is made of plexiglass. On top of the base there is the TMC2300-IOT-REF board and the mobile phone holder, made of aluminum extruded bars.
Regarding the software, the application can be controlled using Blynk. The application for Android is available here. Installation and setup is very easy: there are just three steps to follow to activate your account and create your first Blynk application. To control the dolly camera cart, I create an application with the following widgets to control
- the run current (from 8 to 31)
- the direction (Forward and backward)
- the speed
- the motor start and stop
Here are some screenshot of the Blynk application with the widgets I added to the project
Thanks to the ESP32, it's easy to expand this project to support feature like line following, start&stop from external sensors, etc
Blynk is supposed to work with both Wifi and BLE connectivity. I tested both, but the BLE looks not to be supported by the app on my mobile, so I can only use the application with Wifi connectivity. Anyway, I can enable the BLE connectivity by uncommenting the line
//#define BLYNK_BLE
in the sketch
The sketch uses the BlynkSimpleEsp32 library. Blynk has the concept of virtual pins to transfer data that is not immediately related to an hardware component. There are some macros to implement the code that has to be executed when a command is given on the mobile app
/******************************************************************************/ // These functions are called whenever the corresponding virtual pin is updated // in the Blynk app BLYNK_WRITE(VIRTUAL_PIN_CURRENT) { Serial.print("New MaxCurrent set: "); Serial.println(param.asInt()); uint32_t value = 1 << TMC2300_IHOLDDELAY_SHIFT | ((param.asInt() << TMC2300_IRUN_SHIFT) & TMC2300_IRUN_MASK) | 8 << TMC2300_IHOLD_SHIFT; tmc2300_writeInt(TMC2300_IHOLD_IRUN, value); }
Blynk also offers the possibility to schedule a function at a user-defined rate (for example, every second)
BlynkTimer timer; [...] // Start the timer for the periodic function timer.setInterval(1000L, periodicJob);
The loop() function calls the Blynk.run() and timer.run() to let Blynk make their own process. The serial console supports some commands for debugging purposes
- 0: prints TMS2300 registers
- 1: increments target velocity
- 2: decrements target velocity
void loop() { Blynk.run(); timer.run(); if (Serial.available()) { int c = Serial.read(); if (c == '0') { int32_t reg1 = tmc2300_readInt(TMC2300_IFCNT); int32_t reg2 = tmc2300_readInt(TMC2300_GSTAT); int32_t reg3 = tmc2300_readInt(TMC2300_GCONF); int32_t reg4 = tmc2300_readInt(TMC2300_IOIN); int32_t reg5 = tmc2300_readInt(TMC2300_TSTEP); int32_t reg6 = tmc2300_readInt(TMC2300_SG_VALUE); int32_t reg7 = tmc2300_readInt(TMC2300_CHOPCONF); int32_t reg8 = tmc2300_readInt(TMC2300_DRVSTATUS); int32_t reg9 = tmc2300_readInt(TMC2300_PWMCONF); int32_t reg10 = tmc2300_readInt(TMC2300_PWMSCALE); int32_t reg11 = tmc2300_readInt(TMC2300_PWM_AUTO); Serial.println("--------------------------------------"); Serial.print("IFCNT: "); Serial.println(reg1, HEX); Serial.print("GSTAT: "); Serial.println(reg2, HEX); Serial.print("GCONF: "); Serial.println(reg3, HEX); Serial.print("IOIN: "); Serial.println(reg4, HEX); Serial.print("TSTEP: "); Serial.println(reg5, HEX); Serial.print("SG: "); Serial.println(reg6, HEX); Serial.print("CHOPCONF: "); Serial.println(reg7, HEX); Serial.print("DRV_STAT: "); Serial.println(reg8, HEX); Serial.print("PWM_CONF: "); Serial.println(reg9, HEX); Serial.print("PWM_SCALE:"); Serial.println(reg10, HEX); Serial.print("PWM_AUTO: "); Serial.println(reg11, HEX); } else if (c == '1') { Serial.println(targetVelocity); targetVelocity += 1; Serial.println(targetVelocity); targetVActual = tmc_compute_vactual(targetVelocity); tmc2300_writeInt(TMC2300_VACTUAL, direction? targetVActual : -targetVActual); } else if (c == '2') { Serial.println(targetVelocity); targetVelocity -= 1; Serial.println(targetVelocity); targetVActual = tmc_compute_vactual(targetVelocity); tmc2300_writeInt(TMC2300_VACTUAL, direction? targetVActual : -targetVActual); } } }
Evaluating StealthChop2
StealthChop2 is supposed to autotune PWM signals to make microsteps that reduce noise and vibrations. To test this feature, I made a simple test: I drove the motor at different speed (ranging from 10 to 200 RPM) and measured the noise the motor makes. To make things more difficult for the controller, the motor has nothing connected to the shaft (and that may act as a flywheel)
In this video, I show the dB reads out by a sound meter application at different speeds.
You can not expect to have an extremely quiet motor in every working condition: for example, in this scenario you get the best results at 10 RPM and 130 RPM, so selecting the proper motor for the application is of paramount importance.
Testing the dolly cart
Here is a final video of the dolly cart. As a prototype, must thinks should be improved. The most important are
- make a stiffer support for the camera
- implement a linear ramp for the motor velocity to make smoother starts and stops
Conclusions
The TMC2300-IOT-REF is a very interesting board. It integrates the programmability of the ESP32 with the TCM2300 IC, which is designed to control motors in battery-powered applications.
Pros
- The ESP32: it was really a great idea to integrate the ESP32 with the TMC2300. The union of the ESP32 (with its great community support) and the ability to drive motors in battery-powered applications, opens up many new opportunities to the makers
- The board layout: form a maker's point of view, the board layout with the ESP32 pins available on a easy-to-solder strip header, is really a plus
- The user manual of the TMC2300: the manual is really clear. It has all the technical details you may need to configure the registers and select the proper motor and Rsense, without being too complicated or overwhelming
Cons
- Weak electronic design: the board must be operated with care or the risk to fry any of the components on the board is real. Because the typical scenario of a user unboxing an evaluation kit is to plug and switch it on, I would suggest to pre-install a simple sketch that set the IHOLD_IRUN register to limit the run current to a value that is acceptable for the USB connection and make the motor run. I would also include a very clear warning about the risks of running the motor with the battery disconnected
- Not so user-friendly to tune: if you leave all the auto-tuning functionalities enabled, you can get good results by reading and writing a limited number of registers. However, for applications that requires a more precise tuning of the motor parameters, it may result a little bit difficult to find the correct values. I would have appreciated to ability to use one the software tools available for other products (for example, the TMCL-IDE)
- The connectors: battery and motor connectors may be difficult to find and may not be available in your lab. Terminal headers could be, in my opinion, a better option
Description | Link |
---|---|
Product home page | https://www.trinamic.com/support/eval-kits/details/tmc2300-iot-ref/ |
Product datasheet | https://www.trinamic.com/fileadmin/assets/Products/ICs_Documents/TMC2300_Datasheet_V104.pdf |
Product hardware manual | https://www.trinamic.com/fileadmin/assets/Products/Eval_Documents/TMC2300-IOT-REF_hardware_manual_hw4.0_rev1.2.pdf |
Product schematics | https://www.trinamic.com/fileadmin/assets/Products/Eval_Drawings/ECAD_TMC2300-IOT-REF_V40.ZIP |
Motor datasheet | https://www.trinamic.com/products/drives/stepper-motors-details/goot-motors/ |
Blynk "getting started" example | https://github.com/trinamic/TMC2300-IOT-REF |
Project source code | https://github.com/ambrogio-galbusera/tmc2300-iot-ref.git |
Top Comments