In this blog article we wire up a Arduino Mega2560 with a TMC5160-EVAL board to run in it STEP/DIR mode, use a timer to generate a fixed frequency for the STEP input. The diagnostic diag0 output pin of the TMC5160 will be used to indicate a defined load condition we want to react on. In this case we stop the motor respectively stop the step output of the Arduino.
Only ten wires are required to control the TMC5160-EVAL with your Arduino. Here are the steps to get started.
Preparation
If your Arduino is a 5V type you have to desolder one resistor on the TMC5130-EVAL from position R211 and solder it to position R212. This sets the logic level of the TMC5130 to +5V as seen in the circuitry below.
Illustration 1 – TMC5160-EVAL – Voltage selection
While by default the resistor is soldered to the lower position, you have to move it to the upper position as shown in the red area below for 5V operation.
Illustration 2 – TMC5160-EVAL resistor selector for logic supply voltage
Wiring
The wiring is very simple. You will need 10 jumper wires. To make the wiring more easy you can print out the TMC5130-EVAL_Pinning.pdf and cut out the template to mount it on the connector header of the TMC5160-EVAL (As seen on illustration 4). As a reference you can use the TMC5160-EVAL schematic. Here you’ll find the signals that are on each pin. The configuration is documented in the comment section of the Arduino code.
Illustration 3 – Pin header of TMC5160-EVAL
Illustration 4 – Arduino Mega 2560 wired up to TMC5160-EVAL for STEP/DIR mode
Cable colors of illustration 4 +5V –> red GND –> blue SDO –> yellow SDI –> orange SCK –> white CSN –> grey DRV_ENN –> black CLK16 –> green
Using the internal CLK of the TMC5160
This example uses the internal CLK of the TMC5160. To use the internal CLK simply connect the CLK16 pin to GND.
Step pulse generation
To generate the step pulses a timer is used with a pre scaler of 1024. With a system CLK of the Arduino ATMEGA2560 of 16MHz that results in a 7.8125kHz frequency. As there is no ramping a jump start from velocity 0 to 7812.5 pps is done which should work with most motors. If not a acceleration ramp needs to be implemented.
The TMC5160 offers an internal Motion Controller that can be used as described with the TMC5130 blog post. As some people like to start with a step direction approach this blog post does cover this. Usually when people do try out the Motion Controller they will see the big benefits and make use of it. It does take off a lot of software work! The TMC5160 either starting in step direction and then explore the Motion Controller or directly evaluate the Motion Controller is a great procedure.
Arduino Code
The Arduino Code below does not need any additional libraries. The SPI library comes with the Arduino IDE. The program initializes the TMC5160. Please use either the TMC5160 datasheet or the TMCL IDE as a reference for the different registers.
#include <SPI.h>
/* The trinamic TMC5160 motor controller and driver operates through an
* SPI interface. Each datagram is sent to the device as an address byte
* followed by 4 data bytes. This is 40 bits (8 bit address and 32 bit word).
* Each register is specified by a one byte (MSB) address: 0 for read, 1 for
* write. The MSB is transmitted first on the rising edge of SCK.
*
* Arduino Pins Eval Board Pins
* 51 MOSI 32 SPI1_SDI
* 50 MISO 33 SPI1_SDO
* 52 SCK 31 SPI1_SCK
* 25 CS 30 SPI1_CSN
*
* 17 DIO 8 DIO0 (DRV_ENN)
* 11 DIO REFL_STEP pin header
* 4 DIO 22 SPI_MODE
* 5 DIO 20 SD_MODE
* 20 DIO DIAG0 pin header
* GND 2 GND
* +5V 5 +5V
*/
#define diag0_interrupt
int chipCS = 25;
const byte STEPOUT = 11;
int enable = 17;
int SPImode = 4;
int SDmode = 5;
int DIAG0interruptpin = 20;
unsigned long SGvalue = 0;
unsigned long SGflag = 0;
int cnt = 0;
void setup()
{
// put your setup code here, to run once:
pinMode(SPImode,OUTPUT);
pinMode(SDmode,OUTPUT);
pinMode(chipCS,OUTPUT);
pinMode(STEPOUT,OUTPUT);
pinMode(enable, OUTPUT);
digitalWrite(SPImode,HIGH);
digitalWrite(SDmode,HIGH);
digitalWrite(chipCS,HIGH);
digitalWrite(enable,HIGH);
#ifdef diag0_interrupt
pinMode(DIAG0interruptpin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(DIAG0interruptpin), stallguardstop, FALLING);
#endif
//set up Timer1 for step generation
TCCR1A = bit (COM1A0); // toggle OC1A on Compare Match
TCCR1B = bit (WGM12) // CTC, no prescaling
| bit (CS10)
| bit (CS12); // Pre scaler set to 1024 resulting in 7.8125kHz
OCR1A = 0; // output every cycle
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV8);
SPI.setDataMode(SPI_MODE3);
SPI.begin();
Serial.begin(9600);
sendData(0x80,0x00000080); // GCONF -> Activate diag0_stall (Datasheet Page 31)
sendData(0xED,0x00000000); // SGT -> Needs to be adapted to get a StallGuard2 event
sendData(0x94,0x00000040); // TCOOLTHRS -> TSTEP based threshold = 55 (Datasheet Page 38)
sendData(0x89,0x00010606); // SHORTCONF
sendData(0x8A,0x00080400); // DRV_CONF
sendData(0x90,0x00080303); // IHOLD_IRUN
sendData(0x91,0x0000000A); // TPOWERDOWN
sendData(0xAB,0x00000001); // VSTOP
sendData(0xBA,0x00000001); // ENC_CONST
sendData(0xEC,0x15410153); // CHOPCONF
sendData(0xF0,0xC40C001E); // PWMCONF
digitalWrite(enable,LOW);
}
void loop()
{
SGvalue = sendData(0x6F,0x00000000); // Read DRV_STATUS register
SGflag = (SGvalue & 0x1000000) >> 24; // Check SG2 flag
SGvalue = (SGvalue & 0x3FF); // Extract SG2 value bits
Serial.print("StallGuard2 value: ");
Serial.println(SGvalue, DEC);
Serial.print("StallGuard2 event:");
Serial.println(SGflag);
}
unsigned long sendData(unsigned long address, unsigned long datagram)
{
//TMC5130 takes 40 bit data: 8 address and 32 data
unsigned long i_datagram = 0;
digitalWrite(chipCS,LOW);
delayMicroseconds(10);
SPI.transfer(address);
i_datagram |= SPI.transfer((datagram >> 24) & 0xff);
i_datagram <<= 8;
i_datagram |= SPI.transfer((datagram >> 16) & 0xff);
i_datagram <<= 8;
i_datagram |= SPI.transfer((datagram >> 8) & 0xff);
i_datagram <<= 8;
i_datagram |= SPI.transfer((datagram) & 0xff);
digitalWrite(chipCS,HIGH);
Serial.print("Received: ");
Serial.println(i_datagram,HEX);
Serial.print(" from register: ");
Serial.println(address,HEX);
return i_datagram;
}
void stallguardstop()
{
cnt++; // To avoid false detection. When motor starts diag0 pin goes low.
if(cnt > 1)
{
TCCR1B &= (0 << CS12);
TCCR1B &= (0 << CS11);
TCCR1B &= (0 << CS10);
}
}
Download
The TMC5160-EVAL_Arduino.zip file includes the pinning template, the TMC5160-EVAL schematic and the Arduino project.
Well, not here on our blog, but you can now easily download the CAD files you need for all our active chips. Simply visit the product page, for examplethe TMC2160 product page or the TMC4671 product page, scroll down till you see the tool for downloading CAD symbols, choose your preferred format and hit the download button.
Using the Ultra Librarian Reference Design Cloud, you have easy, web-based access to Trinamic’s reference design schematics and CAD symbols for fast design starts. All designs are verified so you won’t have to worry about a thing when downloading the preferred the files in the CAD format of your choice.
The formats include the most popular formats such as Altium, OrCAD, Eagle, Mentor PADS Logic, and Mentor DxDesigner, enabling the fastest method of getting verified circuitry into your design.
Let’s say we’re working together in a shared office space where you can rent some desk for a certain time. For your convenience, the tables can be adjusted in height. What if the system already learned your preferred settings for standing and sitting and automatically adjusts as you wish as soon as you walk up to the table? Slight correction? Just grab your phone and make some finer adjustments.
This tutorial shall give a simple starter project for web-accessible motion applications. We’re working on a Raspberry Pi 3B+ with Raspian installed in combination with a TMC5130-BOB stepper driver breakout board. The basic principle will be the same for different drivers. The driver strength of this particular chip is probably too small for the shown application but it’s perfect for a first test on the desk.
The TMC5130 is a stepper motor driver with integrated motion controller, also called a cDriver . Therefore, it’s absolutely sufficient to command motions. The movement itself will not require any processing power from the Raspberry Pi, while the motor moves smoothly with outstanding performance.
Below you can find the 10 steps to take. It might be that some of them are already done in your case or you know a better way of doing it. Please feel free to share your wisdom
1. Set up Network & Interfacing
First we connect keyboard, mouse & display to Raspberry Pi and connect it to a network via wifi or cable. In the following steps we assume that the Raspi has access to the internet.
Now we should set static IP addresses for networks in order to access the Raspberry via networks & sub networks. In this tutorial, we use the following:
Now we can make the Raspi accessible through your favorite machine. Therefore we open the configuration on the console:
sudo raspi-config
With the Interfacing options we can enable SSH, SPI and GPIOs:
The SPI and the GPIOs will be required in order to access the driver chip.
Enabling SSH allows us to address the Raspi from our preferred device via network by its static IP in an SSH console like Putty. Even more convenient might be the access via remote desktop. The XRDP tool enables graphical access via tools like Windows Remote Desktop. Just install and connect to the IP:
sudo apt-get install xrdp # install remote desktop service
For both you can log in with your standard Raspi credits.
2. Update
For the best user experience, we should make sure that all software is up to date. Run update/upgrade/dist-upgrade one after the other and reboot if packages were installed when running one of these commands. Start over until there are no updates left to install:
The SPI driver can be found in the bcm2835 library. For this guide version “1.58” was used. In this case X.XX is replaced by “1.58”. You can check the latest version by scrolling to the bottom of the following page: http://www.airspayce.com/mikem/bcm2835/. Run one line after the other.
wget http://www.airspayce.com/mikem/bcm2835/bcm2835-X.XX.tar.gz
gunzip bcm2835-X.XX.tar.gz
tar xvf bcm2835-X.XX.tar
cd bcm2835-X.XX
./configure
make
sudo make check # This should show you a pass message
sudo make install
sudo reboot -h 0
4. Download and install Apache Server, PHP and common Libraries
There are countless tutorials of that on the web. Here are the required lines for the most common setup for PHP7. Run one line after the other:
We put the project files directly to the web server directory. The following lines will navigate there, unpack the files, remove the archive and replace the default ‘hello world’ landing page:
cd /var/www/html/
sudo wget http://blog.trinamic.com/wp-content/uploads/2019/03/TMC5130_Raspi_Webserver.zip # download project archive
sudo unzip TMC5130_Raspi_Webserver.zip
sudo rm TMC5130_Raspi_Webserver.zip #remove archive
sudo rm index.html # remove old hello world
sudo chown -R $USER ./ # set current user as owner
Of course you can download the package directly in order to have a look upfront:
RASPBERRY PI | BOB
--------------------------
(17) +3V3 | VCC_IO
(19) SPI_MOSI | SDI
(21) SPI_MISO | SDO
(23) SPI_SCK | SCK
(24) SPI_CE0_N | CSN
(25) GND | GND
Put connect following pins of the BOB to GND, too:
CLK16 (enables internal clock)
DRV_ENN (enables driver)
Now we can also connect the motor. Make sure that the motor is never disconnected when the driver is active. Therefore I highly recommend to always connect the motor first and then power the system. In this case you can choose something like 5…46V. At 24V, your power supply should supply 1A at least. For lower voltages a bit more.
7. Test TMC5130
Now we can compile the C source code that parametrizes the driver:
cd /var/www/html/TMC5130/
gcc ./src/TMC5130.c ./src/SPI.c -lbcm2835 -o TMC5130 # compile
You can find everything in the src sub-directory. I suggest to access it with your favorite text editor that has built-in SSH (SFTP) support.
cd /var/www/html/TMC5130/src/
The code includes the access to the chip registers, simple functions for movements and a standard initialization. It utilizes the SPI interface in order to communicate with the chip. All in all it’s just around 120 lines plus defines in the header.
In the TMC5130_init function you can find the basic initialization. Please check whether the programmed current of around 0.7A RMS is suitable for your motor. There is also a second example for 0.25 A RMS.
Make sure the system is powered for the test. Otherwise, nothing will happen. If everything is OK, the motor starts moving with constant velocity.
Our program is not clever enough to stop the motor yet – that’s the next step. If you want to stop it, just unpower it. This will also reset the driver chip.
8. Demo Program
Before starting, we have to comment the main function in the TMC5130.c, because now we want to run the main of the project.c file.
Now that is done we can compile the whole project:
cd /var/www/html/TMC5130/
gcc ./src/*.c -lbcm2835 -o project # compile all c files
Everything worked well? Now we can test the program with different parameters. Please try one line after the other:
sudo ./project i #initialize motor
sudo ./project r 100000 # rotate motor with velocity 100000
sudo ./project r -100000 # rotate motor with velocity -100000
sudo ./project 0 # stop motor
sudo ./project t 51200 100000 # move to target 51200 with velocity 100000
sudo ./project b 51200 100000 # move by 51200 steps with velocity 100000
sudo ./project b -51200 100000 # move by -51200 steps with velocity 100000
sudo ./project g 33 # read register with decimal address 33
sudo ./project s 33 0 # write register with decimal address 33 to 0
In the main loop of the project.c file, we are looking at the different ASCII commands (i,r,0,t,b,g,s,d) and the corresponding parameters and use the TMC5130 code to perform different movements.
By the way, the number of 51200 steps is exactly one revolution for the most common hybrid stepper motors. Typically these motors have 200 full steps. The driver runs with 256 microsteps per default. 200*256=51200.
The given velocity of 100000 is here around 77500pps. You can calculate it with the following formula:
v[pps]=v*fclk/2^24
As we are using the internal clock of the chip of around 13MHz, this gives:
v[pps] = 100000 * 13000000 / 2^24
As we know that one reveolution has 51200 micro steps, we are moving 77500/51200 or around 1.5 revolutions per second (rps). This is around 1.5*60 = 90 revolutions per minute (rpm).
Further information regarding real world units can be found in the datasheet.
9. Make Programs Accessible
Our two motor control programs work well now. But as they are using important hardware blocks of the Raspi you need certain rights. Therefore we need to allow the www-data user to access our programs as sudo. All we need to do is add following to the sudoers file. As the slightest mistake in this modification might mean that we lock ourselves out of the Raspi, we’re doing well in using the visudo helper, which performs a syntax check:
sudo visudo
Here we can add the following lines to the bottom:
You can find all web relevant files in the web subfolder:
cd /var/www/html/TMC5130/web
We linked the index.html before. This means we can now open our browser and access the web project on the Raspi via its IP address (whichever you use http://192.168.178.184 or http://192.168.178.185) directly from our browser. (If you call it from the Raspi directly it’s of course http://localhost/)
All the forms are defined in the index.html file. Submitting a form by clicking a button will call the run.php with all given parameters. The run.php is called in an iframe, which is shown in the feedback field. Therefore all responses will be shown there.
The run.php translates the commands from the web interface into calls of our little project program, which will move the motor. Most commands are triggering or stopping a motion and inform that the end of the program is reached.
There is one additional command d (|<>|), which will start the demo, where the motor is moved back and forth. In mode the program stays active. In a state machine the actual status of the motor is monitored. As soon as the motor arrives at a target position the motor goes back where it came from. Even though the program is active, everything still needs only around 1% of processing power.
Summary
Of course you can build way way fancier programs, utilize java script and
sweet graphics. But I hope this project gives you a brief idea of how to get
started.
This project is meant as a starter for different projects around automation. You can shut your window blinds, open your cat flap or rock your baby to sleep. Very similar implementations allow stronger drivers like the TMC5160 or TMC5161, but also smaller two-axis systems that are e.g. based on the TMC5041.
As the actual driving of the motors doesn’t consume processing power of the Raspberry Pi You can combine different motors for several tasks and still have easily enough processing capabilities for powerful application layers.
For the driver itself, we’re working with standard C code. Therefore you can also use it on any microcontroller or platform in order to make it work in different environments.
Please feel free to add a comment if this post was helpful, about what you made out of it or if there is anything I can improve.
Small electrical motors are ubiquitous. The human environment is being automated in an increasingly fast pace, driven by the desire for safety, security and health, fueled by an aging population in the highest developed countries, gadgets that go mainstream, and by new manufacturing trends that characterize the 4th Industrial Revolution.
Every day, we see new, ingenious applications for small electric motors. And while this series on myths about motor control is supposed to cover all aspects on how to control a motor, the booming market for small motors is quite different compared to the huge industrial motors, meaning there are too many myths out in the world. So, instead of talking about electric motors in general, we’ll focus on myths in the small motor market, tackling one myth a week.
Note that some aspects are true for bigger motors as well.
Myth 1: Brushed DC motors will be replaced by brushless DC motors
DC motors with an electromechanical commutation system are still highly popular in toys and simple gadgets – due to low cost – as well as in Mars rovers: Expensive, high-quality motors need only simple electronics – or none at all – and are resistant to radiation.
However: The high-volume production of all kinds of BLDC motors (brushless DC) and PMSM (Permanent Magnet Synchronous Motors) in China goes together with a sharp decline with regard to cost of the necessary motor control electronics. Technologies and algorithms formerly reserved for high-end industrial drives are now embedded in ICs. Brushless motors are far more durable, compared to their mechanical commutated ancestors – the life is only limited by the bearings, not by the brushes. This is an important factor, just because there will be more motors around, driving the need for every unit to be extremely dependable. BLDC motors do not generate electromagnetic emission caused by sparks and no dust – both side effects that do not go well in our wireless and clean tech future.
So – like the Diesel engines in cars, brushed DC motors will still be around for some time, but for every new design, the pressure of replacement by the new technology will increase. Finally, they will become niche products –unless the Mars colonization proceeds faster than expected…
Small electrical motors are ubiquitous. The human environment is being automated in an increasingly fast pace, driven by the desire for safety, security and health, fueled by an aging population in the highest developed countries, gadgets that go mainstream, and by new manufacturing trends that characterize the 4th Industrial Revolution.
Every day, we see new, ingenious applications for small electric motors. And while this series on myths about motor control is supposed to cover all aspects on how to control a motor, the booming market for small motors is quite different compared to the huge industrial motors, meaning there are too many myths out in the world. So, instead of talking about electric motors in general, we’ll focus on myths in the small motor market, tackling one myth a week.
Note that some aspects are true for bigger motors as well.
Myth 2: Stepper motors are inefficient and there is no reason to use them…
Stepper motors are not a good choice if you are looking for the ultimate power/weight or power/volume ratio, or efficiency – you would never choose them to power a flying drone or the joints of a prosthetic arm. So, will they be replaced by brushless servos, sooner or later? Why do engineers choose stepper motors, of all possible solutions?
The answer is quite simple: The stepper motor – usually designed as a 2-phase synchronous motor with a high number of poles – offers high torque at low rotational speed. A hybrid stepper motor typically has 50 poles, that’s kind of a magnetic gearbox. And this enables direct drive systems, which means: You get rid of the mechanical gearbox. A gearbox stands either for gear loose and limited lifetime – or significant cost.
So, in case you design a medical analyzer, a 3D printer or a motorized security camera, the stepper might be just the right choice to enable a simple and reliable design architecture. The stepper motor is a component that is mass manufactured at low cost, but with ultimate precision and – due to the low speed – a lifetime that is de facto unlimited, as the bearings will not wear out.
Small electrical motors are ubiquitous. The human environment is being automated in an increasingly fast pace, driven by the desire for safety, security and health, fueled by an aging population in the highest developed countries, gadgets that go mainstream, and by new manufacturing trends that characterize the 4th Industrial Revolution.
Every day, we see new, ingenious applications for small electric motors. And while this series on myths about motor control is supposed to cover all aspects on how to control a motor, the booming market for small motors is quite different compared to the huge industrial motors, meaning there are too many myths out in the world. So, instead of talking about electric motors in general, we’ll focus on myths in the small motor market, tackling one myth a week.
Note that some aspects are true for bigger motors as well.
Myth 3: All you need is a microcontroller and some libraries: Control algorithms for BLDC / Servo are easy to implement.
Motor and motion control is a growing market and almost every microcontroller manufacturer offers solutions for motor control. Typically, these are controllers with optimized internal peripherals (e.g. PWM units, ADCs…) and they come with libraries, application examples and – sometimes – dedicated toolkits. The promise is always a short time to market, combined with ultimate performance & flexibility.
The reality looks slightly different: Depending on the level of the support of motor control by the semiconductor company, the libraries are sometimes either very generic, so it takes time to adapt them to your application, or very specific for a dedicated motor and microcontroller making it hard to tune to your application – you might end up with a huge investment in time (been there, done that…). So, motor control is not rocket science, but – if done right – it requires more than some mouse clicks…
However, that doesn’t mean you have to be a motor or motion control expert to use the world’s best solutions. Few manufacturers offer solutions that make motion control as easy as possible – a bit more complicated than some mouse clicks, but easily understandable with good documentation and walk-throughs.
What came first… technologies
resulting in shorter development cycles, or the need to shorten development
cycles resulting in those technologies?
Of course, the question doesn’t matter as much as the ongoing result: a technological revolution “that will fundamentally alter the way we live, work, and relate to one another. In its scale, scope, and complexity, the transformation will be unlike anything humankind has experienced before,” to put it in the words of Klaus Schwab. “The speed of current breakthroughs has no historical precedent. When compared with previous industrial revolutions, the Fourth is evolving at an exponential rather than a linear pace. Moreover, it is disrupting almost every industry in every country,”
As to why companies
want to shorten their design cycles and launch their products as fast as possible,
several arguments are made that all boil down to one goal: show market
leadership.
To support companies around
the world in cutting down their development time, Trinamic products embed
motion control in silicon. This means you no longer have to write endless lines
of code to move your application the way you want it to. Instead, you only have
to set a few parameters, for example using the single wire UART interface or
step/direction interface, to enjoy the most advanced motion control.
For more information on the dedicated motion control ICs and other building blocks shortening development time and resources needed, visit www.trinamic.com/products.
Embedded systems are used in various industries and applications for years now, making work – and increasingly everyday life – easier by transforming digital information to physical motion. Lately, companies are focusing their efforts toward further integration of these systems with sensors and copious amounts of data. By doing so, they create cyber-physical systems by merging pervasive computing and information with hardware and software, which in turn enables predictive maintenance, increases speed and intelligence, and improves the user experience.
This trend towards further integration that’s taking place in the 4th Industrial Revolution calls for a fast, secure, and reliable link between master and slaves, creating a robust bridge between the cloud and the physical edge. And while this takes place both within one single device and between different devices or machines, the paradigm is shifting towards the communication between devices, or machine-to-machine communication (M2M). Not only between devices used on the factory floor, also lab automation, office automation, and even homes see an increase in M2M communication. While M2M communication at home mostly takes place via a wireless network, professional environments will always rely on bus systems for secure, real-time communication.
However, different real-time requirements demand different approaches. Read more about these requirements and what different bus systems have to offer in our white paper on field bus systems here.
When I bought my 3D printer, a Prusa-like construction, I started upgrading it the same day it arrived. Aside from the mechanical parts (which are completely replaced at this point), the obvious and much needed upgrade was the mainboard, or to be precise – stepper motor drivers. The A4982s are powerful, but noisy – and making my printer as quiet as possible was my main goal.
After a quick research, it was clear
that the Trinamic integrated circuits are the best drivers for a 3D printer –
the only thing left to do was to choose between TMC2130 or TMC2208. I chose the
latter, because of the newer version of the StealthChop mode.
The easy thing to do would be buying
popular stepsticks and a suitable mainboard… but I really dislike the concept
of these small modules. Why? Well, being an engineer, I just saw all the
compromises and limitations of that system. So, the not-easy-but-fun thing to
do was designing my own PCB.
What issues do I see in the stepstick module actually?
Heat
dissipation – PCB so little simply cannot dissipate enough heat.
Decoupling
– not enough space for good decoupling.
Routing
– again, board is too tiny to properly route all the signals and VIAs.
Goldpins
– they are usually rated for 1A, so they are working at their limit, not to
mention unnecessary resistance and inductance.
Having these flaws does not make the stepsticks
really that bad, but there is plenty of room for improvements and this is what
I tried to do.
So, here it is – 5 axis, independent stepper motor driver module
Large, low ESR capacitors, linear
regulator for logic supply with step down pre-regulator, buffered
inputs/outputs, optional buck module for fans supply, LEDs for diagnostics… and
an integrated heatsink.
The Layer Stackup
The layer stackup goes as follows: signal layer + ground plane; main ground plane; power plane; logic power plane + ground plane. The internal layers are untouchable for me, no traces are routed on them, so the supply/return current is not obstructed in any way.
Picture below shows one driver
section: EAGLE screenshot with ground polygon turned off, ground highlighted,
photo of the bare PCB and finally, fully assembled board.
Each supply pin is decoupled with
100nF + 10uF ceramic capacitors, which are known for their low series
resistance. Thermal relief is turned off, so there is more copper, meaning
smaller thermal and electrical resistance. VIAs are placed directly in the pads
and there is a lot of them, again for lower resistance and inductance. The
ground return for the sense resistors is separated, as the datasheet suggests.
One unusual thing is a very big VIA under the die attach pad. It is 2.9mm in diameter and is completely filled with solder during assembly, which essentially forms a big chunk of metal, taking up the heat from the chips and spreading it over the ground planes. Usually, there is a grid of small VIAs under the pad, but as this board was intended for manual assembly anyway, I wanted to try something different.
Heatsink
Getting back to the “heatsink” – as the PCB price does not change with dimensions up to 100x100mm, I thought… why not? So – four ground planes with plenty of VIAs and exposed copper on top/bottom layers. How does it perform? We need a thermal camera to find out. In this case, FLIR ONE, a smartphone attached camera was used and the result can be seen below.
When taking measurements with a thermal camera, one must take into account emissivity of the object. The heatsink area on the picture above seems to be much colder than the middle part of the board expect for the two small squares – that is where Kapton tape is glued. Bare metal is highly reflective, which throws off the IR camera. Solder under the die attach pad is covered with thermal conductive tape too, in order to make the readout true. It is clear that the areas directly beneath the drivers are the hottest and the heat is spread over the entire board.
The tests were performed with five motors running at 1A RMS each and the board was powered from a laboratory power supply with output set to 24V. Small, aluminium heatsinks were glued to the Trinamic ICs and a low RPM, 80mm fan was blowing over the board, which was mounted vertically on a (obviously) 3D printed bracket. Finally, square wave from a simple signal generator connected to the Step input did put the motors in motion.
Final conclusions:
Stepper
motors really DO operate much more silent with Trinamic drivers. Previously,
frame made from 3mm steel sheets was resonating horribly, now, I can sleep in
the same room as the printer.
Even
a smallest airflow dramatically decreases temperature of the drivers, up to 20
degrees!
The
“heatsink” actually works, but its effect is moderate – temperature with
aluminum heatsinks removed/exposed copper area covered with a piece of plastic
is comparable.
And
last, but not least – the drivers are stable at 1.5A RMS. That is 25% over the
datasheet limit!
I can definitely say that this project was successful and the TMC2208s are happily making my printer quiet, yet very alive. But that is not the end of the story – since their premiere, the TCM2209s have quickly become very popular (if not the most) motor drivers in the 3D printing community. I was no exception and wanted to test them too, but more on that in the next part.
In the past two years, much has happened regarding ROCINANTE and R&D is currently testing the 5th MPW run, which contains the 3rd iteration of a functional motor driver with integrated processor. There is even limited availability of a ROCINANTE evaluation board, named DOCK5.
Be sure to sign up to the ROCINANTE Newsletter to stay up to date on the latest developments and receive notifications when the final products are about to hit the market.
If you like to try out one of the DOCK5 boards which are limited available, be sure to request one at www.trinamic.com.
The TMC4671 is Trinamic‘s FOC servo controller IC, capable of controlling BLDC, DC and stepper motors. In this post, I’d like to show you how to configure the TMC4671-Eval Board with our Landungsbrücke interface board to drive a linear stage.
In my hardware setup I use a QSH4218-51-10-049-10000-AT, which has 50 pole pairs (200 full steps) and a rated current of 1A. The encoder has 10,000 lines resulting in 40,000 PPR. I also use the Landungsbrücke, a TMC4671-Eval board, and the UPS_2A_24V-Eval power stage board. My belt drive linear stage is of type igus ZLW-0630 Basic and travels at 54 mm/revolution.
To monitor and tune my system I use the USB-2-RTMI adapter. My goal is to initialize the TMC4671 – especially the encoder and the current measurement.
Step 1: Preparation
At the beginning of this project I download and install the software toolchain according to Appnote 038 and download the Evalsystem Firmware sources from Trinamic’s github accordingly. I can compile the latest firmware and upload the .hex file to my Landungsbrücke via the TMCL-IDE using the BL (bootloader) option.
Next, I set up my evaluation kit and power it with 24V.
Step 2: Finding initial settings with the Configuration Wizard
As I need to set up the TMC4671 for correct operation, I use the Configuration Wizard inside the TMCL-IDE to find basic parameters. It’s similar to the approach used in this video by Onno Martens.
In the first step, I choose the stepper motor and power stage.
Step 1: Wizard settings
In the second step, I click on the “set defaults” button and ramp up the slider to 3500. The motor spins now until it reaches the end of the track. I reduce the velocity to 1 rpm so it takes some time to make the distance. If the travel distance is too short in your application, turning the motor in open-loop mode is not required. This is just an example. Furthermore, we don’t need that at every startup once we have configured our parameters.
Step 2: Starting the motor in open-loop mode
In the next step of the wizard, I calibrate the current measurement by letting some periods pass to find good values for the offsets. For this, I click on the set button and check the phase. Later on, I will implement a different offset calculation algorithm.
Step 3: Calibrating the current measurement
I check for correct mapping of ADC channels to phase currents by checking phase alignment of voltages and currents in the lower graphs of the ADC configuration step. On the TMC4671, phase V is measured by ADC_I2 and phase WY by ADC_I1. This means I have to adjust the default selections as depicted in the image below in order to align the phases. You can see this in the following video as well:
Step 4: Adjusting the Phases
Step 4: Adjusting phases
In the next step, I set the encoder parameters, encoder PPR (=40,000 PPR = 4*10,000 encoder lines), the direction bit if necessary, and I perform the encoder initialization as I click on the button “Init with offset estimation (wizard)”. Later, I want to do this in the firmware.
Step 5: Setting ABN encoder parameters
What happens during encoder initialization?
The firmware enables the open-loop mode and sets the commutation angle PHI_E to zero. By setting a voltage U_D, the motor current rises and the rotor aligns with the external field generated by the current. The firmware waits for the motor to settle and writes a zero on the ABN_DECODER_COUNT register. The procedure forces the motor to go to the zero position, which is where we set our zero position. Unfortunately, this procedure requires the motor to move during initialization. We will explain other alignment procedures in future blog posts.
Sometimes this encoder alignment procedure fails. For example, if the rotor is stuck and cannot align. For my linear drive stage, this would be the case if I am already at the end stop. So, it is always good to do a plausibility check. But how do I do that?
I could set a different commutation angle then the zero value and see if my measured encoder angle follows my commanded position. I can do that for a full rotation. If the rotor does not follow or if the offset is too big, I can correct my offset or at least raise a flag that the motor is stuck.
With my PWM, current measurement, and encoder feedback configured, I can operate the “drive in closed-loop torque mode”. I do so in the test drive wizard slide and click the “Set Defaults” button. The IDE automatically sets the PHI_E to PHI_E_ABN, the motion mode to TORQUE_MODE and the controller parameters to 256 as these are small values that should be ok for most motors. It also sets the target torque to 500.
Step 6: Setting torque mode
We can now choose which angle we want to use on each cascade level. In my example, I use phi_e_abn for PHI_E_SELECTION. The selectors dialog in the device tree shows all major selections I made. I need to choose a higher value for PID_TORQUE_FLUX_LIMITS because our motor is already connected to a load and won’t move with the default value of 1000 due to limited torque.
Step 6: Setting torque mode values
At the end of the wizard, I can generate a TMCL PC script or C-Code for initialization. I choose C-Code, save the generated code for later usage, and proceed with controller tuning.
You can choose either the option “Open loop” or “ABN Encoder” for the generated code.
Step 7: Generating the code as TMCL PC script or C-Code for initialization
This concludes the first steps within the wizard. In the next post, I will show you how to tune your P-I-parameters for torque/flux mode, velocity mode, and position mode.
Welcome back to my blog post series! I previously described how I set everything up in the post: Driving a Linear Stage With the TMC4671 – Setup. Today I will show you how I tuned my controller gains for torque/flux-, velocity- and position-mode.
In order to do this, I use the USB-2-RTMI adapter and the TMCL-IDE. I start with the current controllers and use the torque/flux tuning tool.
I set a target voltage of 1000 and monitor the step response. The red line displays my voltage reference while the blue line shows the current rising with lowpass behavior. The algorithm estimates the inductance and resistance of the motor from the step response. The green line displays the fitting result for a first order lowpass with estimated parameters. Basically, the green line should fit well to the noisy blue line. If I see a bad fitting result, I can either perform the measurement again or modify the data range.
In the first picture, we see the step response for the default values. The second picture shows a bad fitting result because I reduced the data range. When I increase the data range again I get good values as seen in the third picture.
Figure 1: Step response for default values
Figure 2: Step response with reduced data range
Figure 3: Step response with increased data range
My controller parameters are automatically calculated and displayed in the bottom right. I can set them directly with the button “Set Parameters”.
I can check my
controller performance with the step response tool by setting a small but good
visible flux target. I chose 1000 here and measurement time of 1000 samples.
Current controllers are pretty dynamic and will reach their target value after
a few PWM cycles if the voltage limitation is not tripped. Here are two
different step responses for different controller parameters.
Figure 4: Step response Flux untuned
Figure 5: Step response Flux tuned (1000 samples)
Figure 6: Step response Flux tuned (100 samples)
Figure 7: PI control torque – tuned
I can now proceed with my velocity controller tuning. For this, I need to change my settings in the step response tool to velocity target and velocity actual. My reference value is now a little lower at 100 rpm. Since I selected PHI_M_ABN for velocity selection, the real mechanical velocity is displayed. I set PID_VELOCITY_P to 100 as a starting value and increase the value until I end up at 50% of the step response. After that, I increase the I-parameter concluding with P = 3000 and I = 2200.
Figure 8: Velocity PHI_M_ABN selection
Figure 9: Velocity P100
Figure 10: Velocity P500
Figure 11: Velocity P1000
Figure 12: Velocity tuning good
With that tuning result, I start tuning my position control loop. I set a small P parameter (e.g. 10) while velocity mode is still active. Then I open the position mode dialog to reset the actual position with the “Clear” button. In order to not drive into the end stop, I do this in the middle of my traveling distance.
Figure 13: Position p start
Figure 14: Resetting the actual position with the ‘clear’ button
Now I use the step response tool to tune my position controller. Before applying a higher gain, I check my velocity limit in PID_VELOCITY_LIMIT. This should be a value the drive should actually be capable of driving. For this setup, it is 200 rpm as I chose mechanical angle PHI_M_ABN for velocity selection.
Now I can set up the step response tool for position control loop. I set POSITION_SELECTION in the selectors-section to phi_m_abn and choose a target value that represents one full revolution of my motor: 65536.
Figure 16: Setting the position selector to phi_m_abn
I open the step response tool again and evaluate the result of P = 10. As I’m not satisfied with the result, I increase P to 30. As seen below, the result already looks better but there is room for improvement since the target value is not yet reached. Thus, I set the gain to 60. This yields very good results as we reach our target value of one revolution and do not overshoot the target position.
Figure 17: Position step P10
Figure 18: Position step P30
Figure 19: Position step P60
Figure 20: Position mode unstable
If I increase the gain too much (e.g. P = 30,000 as seen above), the cascade gets unstable and the drive gets noisy. This resulted in the slide not reaching the actual target position as the feedback of the controller is too strong. This is not acceptable. Think about a CNC-mill for example. What you absolutely do not want is a cut where none should exist. This is why I stick to my previous results of P = 60.
Now I can do some tests with the position mode dialog driving back and forth. I can monitor the position in the position graph.
Monitoring the position using the position graph
With these results, I can start developing some C-code for my firmware using the Trinamic API. I will see you in the next blog post, where I will show you my code and talk a little bit about the functions I used. See you there!
In our last blog post, we were able to determine stable P-I-parameters for our cascaded controller loop. With that knowledge in mind I want to show you how I created a small example program which I will include in the firmware of the Landungsbrücke.
What this code basically does is initializing the TMC4671 for stepper motor control. This is done in init_Basics(…) by writing several configuration-registers whereas reset_Basics(…) resets all registers used in this work to a defined state of 0. This replaces the first steps in the configuration wizard of our IDE where we used the default buttons.
The configure_ABN(…)-function will set the ABN-count to 0 after PHI_E aligned the rotor to 0. For this to happen we need to apply a voltage to the flux as well.
init_PosMode(…) sets the correct registers for the TMC4671 to enter position mode.
Using tmc4671_setAbsolutTargetPosition(…) sets the target position of the controller to the desired value, making the motor turn until the target position is reached. Here the absolute position will always refer to our zero position configured in the configure_ABN(…) function.
Side note: If you want movement relative to your actual position you can use the function tmc4671_setRelativeTargetPosition(…).
In order to find the minimum and maximum positions of my setup, I select two target positions in find_targetPositions(…) that are impossible to reach. For me, these are 500000 and –500000. The controller waits a certain amount of time to be sure that it reached an end position. Then it will save the actual position with the function tmc4671_getActualPosition(…). We do that for the other direction as well. The values of both endings can now be used for new target positions which can be set by using tmc4671_setAbsolutTargetPosition(…) again.
I can also use linear ramping for acceleration and deceleration while driving. I use the function tmc_ramp_linear_compute(…) and a struct of type TMC_LinearRamp for this (you can find more information in the API in the files LinearRamp1.h and LinearRamp1.c). This creates a ramp with the information of our track in software. I also set some of the parameters of the struct using the corresponding functions from the aforementioned files. I can set the target position for my ramp using tmc_ramp_linear_set_targetPosition(…) in the struct. After that, I use the calculated ramp position in tmc4671_setAbsolutTargetPosition(calculated_Ramp). This will be looped until the actual target position is reached.
What does ramping do? Instead of setting an absolute target position to be reached with one instruction, we divide our track in multiple sub-target positions. In each iteration of the loop, we increase the target position and the controller will adjust to the new target. However, the velocity of the Landungsbrücke increasing the target position is higher than the adaption of our controller, thus increasing the difference between target position and actual motor position over time. This leads to the motor accelerating as the offset between actual and target position increases.
Decelerating works the other way around. The Landungsbrücke will decrease the velocity in which the target position is set and the controller will slowly catch up to it until it reaches the end position.
In order to increase the speed of the motor, I multiply the actual ramp position by a factor of 1024. This must be compensated by dividing the target position of the software ramp by 1024. With this we scale the ramp to our application and increase the acceleration and velocity of the motor.
Why do I want to use a (software) ramp? Basically, it gives us a more stable way to reach our target position as we have a determined way of accelerating and decelerating and it reduces the possibility of overshooting the target significantly.
The Landungsbrücke alternates these target positions in the TMC4671 register so the motor will be alternating the position of the slide.
With the finished code I generate a hex-file for the bootloader of the Landungsbrücke and update it using the TMCL-IDE.
I hope you can utilize some of the shown examples in order for you to transform digital information into physical motion for your projects! Thank you for keeping up with these blog posts and keep your eyes open for more to come!
Figure 1: Flashing the firmware
Code and explanation
#include "boards/Board.h"
#include "hal/derivative.h"
#include "hal/HAL.h"
#include "tmc/IdDetection.h"
#include "tmc/TMCL.h"
#include "tmc/VitalSignsMonitor.h"
#include "tmc/BoardAssignment.h"
#include "tmc/ramp/LinearRamp1.h"
#include "TMC-API/tmc/ic/TMC4671/TMC4671.h"
#include "TMC-API/tmc/ic/TMC4671/TMC4671_Register.h" //As I use the names of its registers this needs to be included
//mydefines
#define STATE_START_INIT 1
#define STATE_RUN_1 2
#define STATE_RUN_2 3
#define UD_EXT 3202
//Torque-Flux Limits 15000
#define TORQUE_FLUX_LIMIT 10000
//Velocity Limit 10000
#define VELOCITY_LIMIT 10000
//P-I-defines
#define TORQUE_FLUX_P 4952
#define TORQUE_FLUX_I 10939
#define VELOCITY_P 2200
#define VELOCITY_I 2000
#define POSITION_P 45
#define POSITION_I 0
//for checking motor supply voltage
#define VM_FACTOR 713
#define ADC_VM_RES 65535 //Landungsbruecke define -- change for Startrampe
//Software-Ramp defines
#define STEPDIR_FREQUENCY (1 << 17)
#define STEPDIR_MAX_VELOCITY (1 << 20)
#define STEPDIR_MAX_ACCELERATION 2147418111
#define STEPDIR_DEFAULT_ACCELERATION 100000
#define STEPDIR_DEFAULT_VELOCITY STEPDIR_MAX_VELOCITY
#define RAMP_SPEED_FACTOR 1024
//end mydefines
void init_PosMode(uint8_t motor)
{ //Set registers needed for position mode
//tmc4671_writeInt(motor, TMC4671_VELOCITY_SELECTION, TMC4671_VELOCITY_PHI_M_ABN);
tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_TARGET_DDT_LIMITS, 32767); //max
tmc4671_writeInt(motor, TMC4671_PIDOUT_UQ_UD_LIMITS, 32767); //max
tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_LIMITS, TORQUE_FLUX_LIMIT);
tmc4671_writeInt(motor, TMC4671_PID_ACCELERATION_LIMIT, 2147483647);
tmc4671_writeInt(motor, TMC4671_PID_VELOCITY_LIMIT, VELOCITY_LIMIT);
tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_LOW, 0x80000001);
tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_HIGH, 0x7FFFFFFF);
tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0x00000003); //phi_e_abn
tmc4671_writeInt(motor, TMC4671_VELOCITY_SELECTION, TMC4671_VELOCITY_PHI_M_ABN);
tmc4671_writeInt(motor, TMC4671_POSITION_SELECTION, TMC4671_POSITION_PHI_M_ABN); //phi_m_abn
tmc4671_writeInt(motor, TMC4671_MODE_RAMP_MODE_MOTION, 0x00000003); //position mode
//The following gain-values were determined using the USB-2-RTMI and following the PI-tuning guide
tmc4671_setTorqueFluxPI(motor, TORQUE_FLUX_P, TORQUE_FLUX_I); //(motor, P, I)
tmc4671_setVelocityPI(motor, VELOCITY_P, VELOCITY_I);
tmc4671_setPositionPI(motor, POSITION_P, POSITION_I);
}
//Check VM
uint32_t current_VM()
{
uint32_t VM;
VM = *HAL.ADCs->VM; // read ADC value for motor supply VM
VM = (VM*VM_FACTOR)/ADC_VM_RES; // calculate voltage from ADC value
return VM;
}
//End Check VM
void init_Basics(uint8_t motor)
{
//Configure basic registers like motor type, adc-offset
tmc4671_writeInt(motor, TMC4671_MOTOR_TYPE_N_POLE_PAIRS, 0x00020032); //x0002 - Stepper, x0032 = 50 polepairs
tmc4671_writeInt(motor, TMC4671_PWM_POLARITIES, 0x00000000);
tmc4671_writeInt(motor, TMC4671_PWM_MAXCNT, 0x00000F9F); //Maxcount of PWM-counter
tmc4671_writeInt(motor, TMC4671_PWM_BBM_H_BBM_L, 0x00001919); //Break before make times
tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0x00000007); //turn on PWM
//Configure ADC -- Values were taken from the ide after the set offset option was used
tmc4671_writeInt(motor, TMC4671_ADC_I_SELECT, 0x18000100); // configure ADC-Inputs
tmc4671_writeInt(motor, TMC4671_dsADC_MCFG_B_MCFG_A, 0x00100010); // configure ADCs
tmc4671_writeInt(motor, TMC4671_dsADC_MCLK_A, 0x20000000); // turn on clock-signal for group-A ADCs
tmc4671_writeInt(motor, TMC4671_dsADC_MCLK_B, 0x00000000); // turn off clock-signal for group-B ADCs
tmc4671_writeInt(motor, TMC4671_dsADC_MDEC_B_MDEC_A, 0x014E014E); // Constants for filter
tmc4671_writeInt(motor, TMC4671_ADC_I0_SCALE_OFFSET, 0x01005D80); // Fill in the offsets determined in the IDE
tmc4671_writeInt(motor, TMC4671_ADC_I1_SCALE_OFFSET, 0x01005CEF); // Fill in the offsets determined in the IDE
// Open loop settings
tmc4671_writeInt(motor, TMC4671_OPENLOOP_MODE, 0x00000000);
tmc4671_writeInt(motor, TMC4671_OPENLOOP_ACCELERATION, 0x0000003C);
tmc4671_writeInt(motor, TMC4671_OPENLOOP_VELOCITY_TARGET, 0x00000000);
tmc4671_writeInt(motor, TMC4671_OPENLOOP_PHI, 0x00000000);
// Feedback selection
tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0x00000000);
tmc4671_writeInt(motor, TMC4671_MODE_RAMP_MODE_MOTION, 0x00000008);
//tmc4671_writeInt(motor, TMC4671_UQ_UD_EXT, 0x00000F9F);
tmc4671_writeInt(motor, TMC4671_ABN_DECODER_PPR, 0x00009C40);
//Set Limits
tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_TARGET_DDT_LIMITS, 0x7FFF); //max
tmc4671_writeInt(motor, TMC4671_PIDOUT_UQ_UD_LIMITS, 32767); //max
tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_LIMITS, TORQUE_FLUX_LIMIT);
tmc4671_writeInt(motor, TMC4671_PID_ACCELERATION_LIMIT, 2147483647);
tmc4671_writeInt(motor, TMC4671_PID_VELOCITY_LIMIT, VELOCITY_LIMIT);
tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_LOW, 0x80000001);
tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_HIGH, 0x7FFFFFFF);
}
void reset_Basics(uint8_t motor)
{
//reset registers of TMC4671 that will later be used to 0
wait(1500);
tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0);
tmc4671_writeInt(motor, TMC4671_MOTOR_TYPE_N_POLE_PAIRS, 0);
tmc4671_writeInt(motor, TMC4671_PWM_POLARITIES, 0);
tmc4671_writeInt(motor, TMC4671_PWM_MAXCNT, 0);
tmc4671_writeInt(motor, TMC4671_PWM_BBM_H_BBM_L, 0);
tmc4671_writeInt(motor, TMC4671_ADC_I_SELECT, 0);
tmc4671_writeInt(motor, TMC4671_dsADC_MCFG_B_MCFG_A, 0);
tmc4671_writeInt(motor, TMC4671_dsADC_MCLK_A, 0);
tmc4671_writeInt(motor, TMC4671_dsADC_MCLK_B, 0);
tmc4671_writeInt(motor, TMC4671_dsADC_MDEC_B_MDEC_A, 0);
tmc4671_writeInt(motor, TMC4671_ADC_I0_SCALE_OFFSET, 0);
tmc4671_writeInt(motor, TMC4671_ADC_I1_SCALE_OFFSET, 0);
tmc4671_writeInt(motor, TMC4671_OPENLOOP_MODE, 0);
tmc4671_writeInt(motor, TMC4671_OPENLOOP_ACCELERATION, 0);
tmc4671_writeInt(motor, TMC4671_OPENLOOP_VELOCITY_TARGET, 0);
tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0);
tmc4671_writeInt(motor, TMC4671_MODE_RAMP_MODE_MOTION, 0);
tmc4671_writeInt(motor, TMC4671_UQ_UD_EXT, 0);
tmc4671_writeInt(motor, TMC4671_ABN_DECODER_PPR, 0);
tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_TARGET_DDT_LIMITS, 0);
tmc4671_writeInt(motor, TMC4671_PIDOUT_UQ_UD_LIMITS, 0);
tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_LIMITS, 0);
tmc4671_writeInt(motor, TMC4671_PID_ACCELERATION_LIMIT, 0);
tmc4671_writeInt(motor, TMC4671_PID_VELOCITY_LIMIT, 0);
tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_LOW, 0);
tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_HIGH, 0);
tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0);
tmc4671_writeInt(motor, TMC4671_VELOCITY_SELECTION, 0);
tmc4671_writeInt(motor, TMC4671_POSITION_SELECTION, 0);
tmc4671_writeInt(motor, TMC4671_MODE_RAMP_MODE_MOTION, 0);
tmc4671_writeInt(motor, TMC4671_PID_POSITION_TARGET, 0);
tmc4671_writeInt(motor, TMC4671_PID_POSITION_ACTUAL, 0);
tmc4671_setTorqueFluxPI(motor, 0, 0); //(motor, P, I)
tmc4671_setVelocityPI(motor, 0, 0); // (motor, P, I)
tmc4671_setPositionPI(motor, 0, 0); // (motor, P, I)
}
void init_softwareRamp(TMC_LinearRamp *linearRamp)
{
tmc_ramp_linear_init(linearRamp);
tmc_ramp_linear_set_mode(linearRamp, TMC_RAMP_LINEAR_MODE_POSITION);
tmc_ramp_linear_set_maxVelocity(linearRamp, STEPDIR_DEFAULT_VELOCITY);
tmc_ramp_linear_set_acceleration(linearRamp, STEPDIR_DEFAULT_ACCELERATION);
}
void find_targetPositions(uint8_t motor, int32_t *lower_End, int32_t*upper_End)
{
tmc4671_setAbsolutTargetPosition(motor, 500000); // ~ 7.5 motor turns
wait(1500);
/* After slide reached one end of the setup the absolute position is stored in memory
For convenience I reduce the value of the target position to be saved a little,
* as driving into the endposition with full force is annoyingly loud :)
* if this is your goal just remove the +/- 1000 */
*lower_End = (tmc4671_getActualPosition(motor)-1000);
tmc4671_setAbsolutTargetPosition(motor, 0); //return to the starting position
wait(1000);
// Assumed an unreachable position so the slide reaches the other side of the setup
tmc4671_setAbsolutTargetPosition(motor, -500000); // ~ -7.5 motor turns
wait(1500);
// After slide reached the other end of the setup the absolute position is stored in memory
*upper_End = (tmc4671_getActualPosition(motor)+1000);
tmc4671_setAbsolutTargetPosition(motor, 0);
wait(1500);
}
void configure_ABN(uint8_t motor, uint16_t startVoltage)
{
// set ABN_DECODER_PHI_E_OFFSET to zero
tmc4671_writeRegister16BitValue(motor, TMC4671_ABN_DECODER_PHI_E_PHI_M_OFFSET, BIT_16_TO_31, 0);
// select phi_e_ext
tmc4671_writeRegister16BitValue(motor, TMC4671_PHI_E_SELECTION, BIT_0_TO_15, 1);
// set an initialization voltage on UD_EXT (to the flux, not the torque!)
tmc4671_writeRegister16BitValue(motor, TMC4671_UQ_UD_EXT, BIT_16_TO_31, 0);
tmc4671_writeRegister16BitValue(motor, TMC4671_UQ_UD_EXT, BIT_0_TO_15, startVoltage);
// set the "zero" angle
tmc4671_writeRegister16BitValue(motor, TMC4671_PHI_E_EXT, BIT_0_TO_15, 0);
tmc4671_writeInt(motor, TMC4671_ABN_DECODER_MODE, 0x00001000);
//Set PHI_E to 0° angle so the rotor alignes itself with it
tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0x00000000);
wait(4000);
tmc4671_readRegister16BitValue(motor, TMC4671_ABN_DECODER_PHI_E_PHI_M, BIT_16_TO_31);
//After the rotor has aligned this will set the ABN_DECODER_COUNT to 0 so there is a defined 0-position
tmc4671_writeInt(motor, TMC4671_ABN_DECODER_COUNT, 0);
}
void tmc4671setPositionModeAndRun(uint8_t motor, uint8_t *initState, uint16_t startVoltage, int32_t *upper_End, int32_t *lower_End, TMC_LinearRamp *linearRamp)
{
/*State Machine with state-pointer *initState and 3 states
* STATE_START_INIT: Sets TMC4671-registers for my application and configures the ABN-Encoder
* Drives to the ends of the track to get the actual min/max position values
* Drives to the first end position
*
* STATE_RUN_1: Drives to one end and changes direction when the *lower_End is reached
*
* STATE_RUN_2: Drives to the other end and changes direction when *upper_End is reached
*/
switch (*initState)
{
case STATE_START_INIT:
//reset TMC4671 registers
reset_Basics(motor);
//set TMC4671 registers with basic data like motortype, number of polepairs and so on
init_Basics(motor);
//initialize the software ramp
init_softwareRamp(linearRamp);
//configure ABN
configure_ABN(motor, startVoltage);
//Enter position-Mode
init_PosMode(motor);
//configure the end positions and store them in *lower_End and *upper_End
find_targetPositions(motor, lower_End, upper_End);
*initState = STATE_RUN_1;
//set the first target position using the software ramp
tmc_ramp_linear_set_targetPosition(linearRamp, (*lower_End/RAMP_SPEED_FACTOR));
break;
case STATE_RUN_1:
//drive from one end to the other: positive direction
tmc_ramp_linear_compute(linearRamp);
tmc4671_setAbsolutTargetPosition(motor, (RAMP_SPEED_FACTOR*tmc_ramp_linear_get_rampPosition(linearRamp)));
//When the target position is reached we will change direction
if(tmc4671_getActualPosition(motor) >= (*lower_End-RAMP_SPEED_FACTOR))
{
wait(50);
*initState = STATE_RUN_2;
tmc_ramp_linear_set_targetPosition(linearRamp, (*upper_End/RAMP_SPEED_FACTOR));
break;
}
*initState = STATE_RUN_1;
break;
case STATE_RUN_2:
//drive from one end to the other: negative direction
tmc_ramp_linear_compute(linearRamp);
tmc4671_setAbsolutTargetPosition(motor, (RAMP_SPEED_FACTOR*tmc_ramp_linear_get_rampPosition(linearRamp)));
//When the target position is reached we will change direction
if(tmc4671_getActualPosition(motor) <= (*upper_End + RAMP_SPEED_FACTOR))
{
wait(50);
*initState = STATE_RUN_1;
tmc_ramp_linear_set_targetPosition(linearRamp, (*lower_End/RAMP_SPEED_FACTOR));
break;
}
*initState = STATE_RUN_2;
break;
default:
*initState = 0;
break;
}
}
const char *VersionString = MODULE_ID"V308"; // module id and version of the firmware shown in the TMCL-IDE
/* Keep as is! These lines are important for the update functionality. */
#if defined(Landungsbruecke)
const uint8_t Protection[] __attribute__ ((section(".cfmconfig")))=
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //Backdoor key
0xFF, 0xFF, 0xFF, 0xFF, //Flash protection (FPPROT)
0x7E, //Flash security (FSEC)
0xF9, //Flash option (FOPT)
0xFF, //reserved
0xFF //reserved
};
struct BootloaderConfig {
uint32_t BLMagic;
uint32_t drvEnableResetValue;
};
// This struct gets placed at a specific address by the linker
struct BootloaderConfig __attribute__ ((section(".bldata"))) BLConfig;
#endif
/* Check if jumping into bootloader is forced */
/* */
/* In order to jump to bootloader e.g. because of an accidental infinite loop */
/* in a modified firmware you may short ID_CLK and ID_CH0 pins on start up. */
/* This will force the entrance into bootloader mode and allow to replace bad firmware. */
void shallForceBoot()
{
// toggle each pin and see if you can read the state on the other
// leave if not, because this means that the pins are not tied together
HAL.IOs->config->toOutput(&HAL.IOs->pins->ID_CLK);
HAL.IOs->config->toInput(&HAL.IOs->pins->ID_CH0);
HAL.IOs->config->setHigh(&HAL.IOs->pins->ID_CLK);
if(!HAL.IOs->config->isHigh(&HAL.IOs->pins->ID_CH0))
return;
HAL.IOs->config->setLow(&HAL.IOs->pins->ID_CLK);
if(HAL.IOs->config->isHigh(&HAL.IOs->pins->ID_CH0))
return;
HAL.IOs->config->toOutput(&HAL.IOs->pins->ID_CH0);
HAL.IOs->config->toInput(&HAL.IOs->pins->ID_CLK);
HAL.IOs->config->setHigh(&HAL.IOs->pins->ID_CH0);
if(!HAL.IOs->config->isHigh(&HAL.IOs->pins->ID_CLK))
return;
HAL.IOs->config->setLow(&HAL.IOs->pins->ID_CH0);
if(HAL.IOs->config->isHigh(&HAL.IOs->pins->ID_CLK))
return;
// not returned, this means pins are tied together
tmcl_boot();
}
/* Call all standard initialization routines. */
static void init()
{
#if defined(Landungsbruecke)
// Default value: Driver enable gets set high by the bootloader
BLConfig.drvEnableResetValue = 1;
#endif
HAL.init(); // Initialize Hardware Abstraction Layer
IDDetection_init(); // Initialize board detection
tmcl_init(); // Initialize TMCL communication
tmcdriver_init(); // Initialize dummy driver board --> preset EvalBoards.ch2
tmcmotioncontroller_init(); // Initialize dummy motion controller board --> preset EvalBoards.ch1
VitalSignsMonitor.busy = 1; // Put state to busy
Evalboards.driverEnable = DRIVER_ENABLE;
Evalboards.ch1.id = 0; // preset id for driver board to 0 --> error/not found
Evalboards.ch2.id = 0; // preset id for driver board to 0 --> error/not found
// We disable the drivers before configurating anything
HAL.IOs->config->toOutput(&HAL.IOs->pins->DIO0);
HAL.IOs->config->setHigh(&HAL.IOs->pins->DIO0);
IdAssignmentTypeDef ids = { 0 };
IDDetection_initialScan(&ids); // start initial board detection
IDDetection_initialScan(&ids); // start second time, first time not 100% reliable, not sure why - too fast after startup?
if(!ids.ch1.id && !ids.ch2.id)
{
shallForceBoot(); // only checking to force jump into bootloader if there are no boards attached
HAL.IOs->config->toOutput(&HAL.IOs->pins->ID_CLK);
HAL.IOs->config->toInput(&HAL.IOs->pins->ID_CH0);
}
Board_assign(&ids); // assign boards with detected id
VitalSignsMonitor.busy = 0; // not busy any more!
}
/* main function */
int main(void)
{
//initialise some pointers
//state pointer for fsm
uint8_t init_State = STATE_START_INIT;
uint8_t *initState;
initState = &init_State;
//store one end of the track
int32_t upper_end = 0;
int32_t *upper_End;
upper_End= &upper_end;
//store the other end
int32_t lower_end = 0;
int32_t *lower_End;
lower_End= &lower_end;
//convenience
uint8_t motor = 0;
//"flipflop"
uint8_t block = 0;
//struct used for the software ramp - see \TMC-API\tmc\ramp\LinearRamp1.h for typedef etc.
TMC_LinearRamp adressRamp;
TMC_LinearRamp *aRamp;
aRamp = &adressRamp;
init();
// Main loop
while(1)
{
// Check all parameters and life signs and mark errors
vitalsignsmonitor_checkVitalSigns();
// Perodic jobs of Motion controller/Driver boards
Evalboards.ch1.periodicJob(systick_getTick());
Evalboards.ch2.periodicJob(systick_getTick());
// Process TMCL communication
tmcl_process();
if (Evalboards.ch1.id != ID_TMC4671)
continue;
if(current_VM()<20){
tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0); // Turn off PWM after unexpected reset
reset_Basics(motor); // Set the registers needed in this example to a defined default state
continue;
}
if(block == 0)
{
tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0); //Turn of PWM-Register after SPI was initialized
reset_Basics(motor);
block = 1;
}
tmc4671setPositionModeAndRun(motor, initState, UD_EXT, lower_End, upper_End, aRamp);
}
return 0;
}
With this code my drive is initialized, the encoder initialization is performed and homing on both end stops is performed. Afterwards, a back and forth movement is implemented.
I can upload the firmware to the Landungsbrücke with the TMCL-IDE and it starts automatically after power-up. I can monitor the system behavior over the RTMI.
Trinamic recently launched a new line of battery-powered motor driver chips. The TMC7300 is one of them, capable of driving 1 DC motor up to 2A, or 2 DC motors up to 2.4A (peak). In this post, I’ll show you a small example script to drive a DC motor using the TMC7300-EVAL-KIT.
First, I assemble the Evaluation Kit by using the Eselsbrücke connector board to connect the TMC7300-EVAL board with the Landungsbrücke interface board. Second, I connect a motor to the evaluation board. Finally, I plug in the USB-C cable for direct access with my computer using the TMCL-IDE.
With the setup complete, I can tune the TMC7300 DC driver IC and immediately see the motor’s behavior in real life, and in the graphical user interface.
Figure 1: Screenshot on how the movement looks like
Using the TMCL-IDE, I create a short example script that moves the DC motor as shown in figure 1. The motor PWM goes from 0% duty cycle to 30%, to 50% and back to zero. Afterwards, it shows the same values in negative direction.
Below is the script I used.
#module 1 COM16/USB/id1/Landungsbruecke [Landungsbruecke]
CALC ADD, 1 // Add 1 to accu
STOA 0, 0 // Store local variable to accumulator
SAP 7, 0, 0, 1 // Deactivate Standby of TMC7300
Loop:
SAP 0, 0, 30, 1 // Set duty cycle for Motor 1 to 30%
WAIT TICKS, 0, 1000 // Wait 1 second
SAP 0, 0, 50, 1 // Set duty cycle for Motor 1 to 50%
WAIT TICKS, 0, 1000 // Wait 1 second
SAP 0, 0, 30, 1 // Set duty cycle for Motor 1 to 30%
WAIT TICKS, 0, 1000 // Wait 1 second
SAP 0, 0, 0, 1 // Set duty cycle for Motor 1 to 0%
WAIT TICKS, 0, 1000 // Wait 1 second
SAP 0, 0, -30, 1 // Set duty cycle for Motor 1 to -30%
WAIT TICKS, 0, 1000 // Wait 1 second
SAP 0, 0, -50, 1 // Set duty cycle for Motor 1 to -50%
WAIT TICKS, 0, 1000 // Wait 1 second
SAP 0, 0, -30, 1 // Set duty cycle for Motor 1 to -30%
WAIT TICKS, 0, 1000 // Wait 1 second
SAP 0, 0, 0, 1 // Set duty cycle for Motor 1 to 0%
WAIT TICKS, 0, 1000 // Wait 1 second
CALC ADD, 1 // Add 1 to accu
STOA 0, 0 // Store value to accumulator
COMP 100 // <----- Amount of loops set here
JC LE, Loop // Jump to label Loop
SAP 7, 0, 1, 1 // Activate Standby of TMC7300
STOP
At the moment, all spacecraft are developed, tested, and assembled on earth before being transported by rockets to their respective mission locations. Each component must withstand the high loads of the rocket launch, while the loads of the actual mission are often relatively low. These oversized components cause high space transportation costs due to the high system weight and volume as well as the complex test procedures required for a rocket flight. In order to reduce these costs, spacecraft components could be manufactured and used directly in orbit using generative manufacturing methods.
With this idea in mind, a team of eight students from the University of Applied Sciences Munich decided to prototype a 3D-printing technology through which structures for solar panels, antennas, or any other installation can be created directly in space. Named AIMIS-FYT – AIMIS for Additive Manufacturing in Space – they decided on a printing method in which photoreactive resin is extruded and cured by UV-light.
The Application
The setup basically consists of a primary structure that has a cartesian 3D-print kinematic mounted inside. The printer has two translational axes and one rotational axis. Thus, the printer can move and rotate in one fixed plane. This enables the system to create freeform structures. The print head is the workhorse of the team’s experiment and consists of an extruder driven by a stepper motor, that dispenses a viscous resin when in zero-gravity. During the extrusion process, the resin is simultaneously cured by UV-Light behind the nozzle.
AIMIS-FYT explains how it works: “For our process, we use a so-called “direct robotic extrusion of photopolymers”. It essentially consists of an extruder through which a viscous photopolymer can be dispensed. This allows the resin to be ejected through a nozzle and afterward cured by UV-light. By moving the nozzle externally, it is possible to produce three-dimensional structures. In our case, this is not done layer by layer as in conventional 3D-printers, but directly via a three-dimensional movement in combination with a volumetric extrusion of the resin.”
Solving the Challenge
Unlike conventional FDM printers, the printing process used by AIMIS-FYT prints with a UV-curing resin. This resin needs to be dispensed in a controlled and very accurate manner in order to produce 3D-structures. To meet these requirements, an extruder with a precise stepper motor is used. In addition, the whole setup is required to fit into a small compartment and be useable with the team’s software.
In order to fully benefit from the advantages of the stepper motor, the team decided to use the TMCM-1070 module by Trinamic. “We came across the Trinamic driver module TMCM-1070 already after a short research. This driver module is easy to use, controlled via a step and direction interface, has a very small footprint and is a reliable solution. Furthermore, the module is in a box which easy complies with the requirements for our experimental setup on the Zero-G aircraft,” according to the team from Munich.
First Results
Experiments are based on four basic operations, which have been identified for printing structures (e.g. truss structures) in microgravity. They are ordered according to increasing complexity as followed:
Straight rod
Straight rod with start/stop points
Freeform rod
Connections between rods
By combining these basic operations, it should be possible in the future to create any customized structural element directly in space.
Zero-Gravity Testing
In November 2019 the AIMIS-FYT team has been selected for the FlyYourThesis2020! campaign, a program of the European Space Agency (ESA), allowing university students to perform scientific and technological experiments under microgravity conditions during several parabolic flights. Throughout the flight campaign in Bordeaux, France, in November 2020, the team from Munich has a total of 90 parabolas to test their technology. During each parabola, they’ll be floating in zero-gravity for about 20 seconds together with their printer.
On
three flight days, a total of 90 parabolas will be flown and therefore we can carry
out a total of 90 experiments. The experiments are divided into the mentioned
four basic operations and within each basic function we test different
parameters in order to identify their influence on the printing process.
Therefore, we equip our experiment with a variety of sensors, such as thermal
imaging cameras, air pressure sensors, temperature sensors, etc. The goal is to
print 90 rods of various sizes and shapes, which are analysed in detail
afterwards. The results of the experiments will be used to further optimize the
printing process and to demonstrate that our additive manufacturing method
works under microgravity conditions. In the future, this technology can then be
further improved and perhaps even tested in space. The technology offers the
opportunity to drastically reduce the costs of satellites and other space
missions.
One of the challenges while developing any robotic hardware is to handle the communications with the motors along with sending commands to them as per the generated trajectory in real time. Since the motor accepting trajectories should respect the time limits, it becomes important that the communication between the motor driver and the controller is seamless. Furthermore, the controller should also receive the latest joint state (position, velocity, effort) information from the motor so that the trajectory execution can be verified. The final challenge that remains afterward is to actually generate the trajectory and send it to the motor driver.
To achieve these requirements ROS framework provides us with the several tools such as ROS-control and ROS-Moveit. ROS (Robot Operating System) is a software framework that implement state-of-the-art algorithms and tools to simplify the development of robotic hardware and software with minimal efforts.
To control any joint actuator there can be multiple methods, such as, commanding joint positions, joint velocity, or, joint efforts. It completely depend on the application that, what level of control is required. Things become easier if the joint positions are directly used to command the actuator since all the low level processing such as velocity and torque control loop are executed within the drive hardware. However, if the joint velocities or joint efforts are used to command the actuator then the position interface has to be generated by running control loops on the commanding device i.e. in our case a linux machine running an instance of ROS control.
ROS-control
ROS-control is a ROS package capable of interfacing with multiple hardware instances mentioned above. To do so, it provides abstraction over the implementation of control loops such that any motor or actuator can be controlled irrespective of its hardware interface.
As per the official documentation, there can be four types of hardware interfaces that ROS-control can natively support:
Joint Command Interface
Effort Joint Interface
Velocity Joint Interface
Position Joint Interface
Joint State Interface
The Interfaces can be best understood by the following image,
As evident from the image, ROS control communicate with the actuators and the motors using the memory locations. For example, if the motor has to be commanded using the position interface, ROS control would simply write the position demand value at “pos_cmd_” memory location which would be then transmitted to the motor via the the communication interface of the motor. In case of TMC4671 eval kit with Landungsbrücke, these interfaces can be RS232, USB serial, or RN171XV/RN42XV wireless modules. Similarly, for velocity and effort interfaces the demand values get written to these memory locations which are pre-defined. Now, the question that remains is, how does the joint states are read from the motor. It is also anologous to the joint command interface where the joint values are read into the “pos_”, “vel_” and “eff_” memory locations.
Now its just matter of updating these memory locations at certain interval to command and read the joint interfaces. This process can be easily explained using following code snippet,
class trinamic: public hardware_interface::RobotHW
{
public:
trinamic();
void read();
void write();
};
Here, the constructor can be used to initialize for example, communication interfaces (Serial in case of our implementation with Landungsbrücke). Moving further, both trinamic::read() and trinamic::write() functions are called periodically in a loop so that the memory locations are updated at a fixed interval.
ROS controllers
Once the hardware Interfaces are up and running, we can spawn one of the many controllers such as position_controllers, velocity_controllers, joint_state_controller, joint_trajectory_controller. These controllers are nothing but an abstraction of hardware interfaces as ROS topics. Here, as mentioned above, position_controller can be started with velocity joint interface such that the control loop is running on the host machine. Furthermore, joint_trajectory_controller is compatible with all types of joint command interfaces. One thing to note is that we should always check for conficts such that no two controllers are accessing the same hardware interfaces.
Establishing communication between ROS control and TMC4671
Thanks to TMCL API, establishing communication between ROS and TMC4671 via Landungsbrücke become effortless. As mentioned previously, the communication is established using USB serial communication that accepts command position (since we are creating hardware interface) and transmits joint position. Once Landungsbrücke receive this command position, it writes this value in register 0x68, PID_POSITION_TARGET via SPI. Furthermore, the current position value is taken from register 0x6B, PID_POSITION_ACTUAL and transmitted via USB serial. Since all these values are 32 bit signed integers, the data is marshalled into 4 single byte unsigned integers for both sending and receiving.
How to setup everything
Initially setup TMC4671 according to the motor using TMCL-IDE, the process is nicely explained here. Update the Register settings received from IDE in init_motor() function of ROS_control.c. Now download the ROS package in any workspace and start the hardware interface using launch file. Any compatible ROS controller can be spawned along with the ROS control node in the launch file. The controller can be configured in the controller.yaml.
The most important part while using position control with TMC4671 is to tune PI gains for velocity and position control loops. These loops can be manually tuned using 0x58 and 0x5A registers. However, it is strongly not advised.
References
S. Chitta, E. Marder-Eppstein, W. Meeussen, V. Pradeep, A. Rodríguez Tsouroukdissian, J. Bohren, D. Coleman, B. Magyar, G. Raiola, M. Lüdtke and E. Fernandez Perdomo
“ros_control: A generic and simple control framework for ROS”
Access Trinamic
modules with a simple Python script using Trinamic’s TMCM-0960-MotionPy,
providing a clear insight into system communications regardless of the
interface or bus system used, speeding up automated testing and analysis in the
field or on the factory floor.
The simple module replaces computers and application software for debugging of automation applications running TMCL, for updating them, or for uploading new scripts to industrial drives on the factory floor. By plugging the module directly into the application, it can log all communication or targeted communication on an SD card without using additional tools.
The small TMCM-0960-MotionPy offers a total of 13 functions via Python programming language so commands can be executed directly into Python for ease of use. Scripts can be placed directly on the SD card and run automatically once plugged in to the embedded system, regardless of the bus system used.
The TMC2300-IOT-REF is a completely open-source reference design for easy evaluation of the TMC2300-LA stepper motor driver IC. Whether it’s a POS device, toys, office and home automation, or mobile medical devices, Engineers can quickly prototype IoT applications running on 3.5 – 6 V batteries.
Besides the low-voltage stepper motor driver, the board features a Li-Ion cell charger via USB-C, an ESP32-PICO-D4 processor with integrated WiFi and BLE capabilities, as well as UART and USB-C for serial communication and programming. Other features including StealthChop2, StallGuard4 and CoolStep bring industrial-grade solutions to convenient, handheld devices.
Each year, the Formula Student Team named High-Octane Motorsports e.V. of the Friedrich-Alexander-University Erlangen-Nürnberg, designs, constructs, and builds innovative and unique racing cars. These are then entered in the Formula Student Competition in three different categories, competing teams from all over the world.
All three categories, Electric, Driverless, and Combustion, come with their challenges. This article explains how the students tackled power steering for the driverless vehicle. The concept describes all the necessary forces that are required in order to perform a turn on the steering column. To ensure the safety of all persons involved, and especially the drivers, strict rules generally apply to the autonomous steering system in Formula Student.
Since the autonomous racing car must also be steerable in manual mode, the vehicle has a manually operated steering wheel with a classic steering column. For transmitting the torque of the electric motor to the steering rod, it was decided to integrate a second steering rod, which is coupled to the manual steering rod by means of a bent metal plate. The BLDC motor is geared by a planetary gearbox, after which the output shaft of the planetary gearbox is transferred to our self-developed steering gearbox, in which a pinion goes to a rack.
Through various measuring techniques, the team could determine a relatively precise torque at the steering column and thus at the pinion of the manual steering gear, which is required for steering when the vehicle is stationary. Since the gear ratio in High-Octane Motorsports e.V.’s manual steering gear is the same as in the “autonomous steering gear”, the same torques are required at the output shaft of the compact drive as the required torques and rotational speeds determined by means of sensors. The required torque is 15 Nm at a standstill. However, this is a value that’s seldom found in the history of a race car, as it’s hardly ever necessary to maneuver at a standstill. Since the torque is reduced many times over when driving, this fact may well be taken into account in the design of the drive.
From the average steering speeds, the rotational speed for the servo motor can be determined. The average value (in absolute value) amounts to 403.2°/s, which is equivalent to 67.2rpm. For an autonomous race car, however, the average value can be set lower.
As mentioned above, a maximum torque of 15Nm is required at the output shaft at a speed of approx. 100rpm. According to the datasheet of the brushless DC motor, a nominal torque of Mnenn = 0.47Nm may be assumed, which results in an output torque of: Mab = 0,47Nm ∗ 32,72 = 15,4 Nm. Furthermore, it can be found in the datasheet that a nominal speed of nnenn=3500rpm is present at 24V. However, since only a maximum voltage of 16.5V is available with the battery used, a reduced speed of approximately nred~3500 * 16/24=2333rpm can be calculated. With nred the rotational speed at the output shaft of the planetary gear can then be calculated: nab=2333rpm / 32,72=71,3rpm. The calculation shows that both the maximum torque and a sufficient nominal rotational speed of the motor are achieved, resulting in a sufficient rotational speed at the output shaft. Also, the planetary gear with a specified maximum output torque of 42.7Nm, as well as a maximum output speed of 10913rpm, is not overloaded at any time.
On the part of the electronics, a functioning control and regulation for a stepper motor has already been developed. This season, however, a switch was to be made to a brushless DC motor (BLDC), which generally makes it possible to control the steering more quickly and accurately with a corresponding reduction ratio. In order to be able to evaluate the switch to a BLDC, a new, more powerful stepper motor and a BLDC were requested from Nanotec. In order to be able to test both engines, the team used the TMC4671-10A70V-EV-KIT.
Since this concerns the first BLDC control board, all conductor track thicknesses were designed for their maximum peak current. In addition, more measurement options of the motor currents (phase measurement and total current measurement) were included, so that as many measured values as possible are available for the validation of the overall system on the first board. The TMC4671-LA from the already mentioned evaluation kit was chosen as the basis.
The design of the board is separated into two parts. It consists of the section where the high motor currents (up to 30A) can flow in paths as short as possible. This reduces disturbance characteristics like a “ground bounce” or unwanted voltage collapse of the small-signal electronics. In order for the currents to gain the widest possible flow range, the board was developed on a 4-layer design with a copper thickness of 70μm.
The measurements of the board have become quite large due to the many current measurements and the prototype production and are to be reduced in future seasons. For this purpose, a new layout has already been considered. Also, in future seasons, the team’s plug problem of this year will be solved and all plugs will be integrated in a smaller format on the board again.
To make sure that the circuit board can be stored water and dustproof in the monocoque, High-Octane Motorsports e.V. designed a housing which is 3D-printed and then attached to the monocoque via a vibration-proof dual lock connection.
In the 3D printing community, newcomers are often having a hard time understanding how stepper motors are really driven. Questions like “voltage rating for my motor is 4.6V, can I use it if my printer has a 12/24V power supply?” and similar pop out from time to time.
This is because most of the electronics we use every day use constant voltage supply with variable current and that’s what we are used to think about. A 12V LED strip will be powered by a stable, controlled 12V and the current consumption will increase as the number of diodes – the load – gets bigger.
Stepper
motors are powered in a reverse way – the current is constant/controlled (more
on that later) and the voltage required varies together with the load. That’s
why in 3D printing 12V power supplies were replaced by 24V or even higher
voltage supplies – because (apart from other benefits) this way the printer can
deliver more power to the motors, reach higher movement speeds and be more
dynamic, despite the set motor current staying at the same level.
But
the typical power supply delivers constant voltage. How is it converted to
regulated, controlled current? It’s the job of the stepper motor driver, like
the TMC2208.
The current regulation is achieved using a technique called PWM (Pulse Width Modulation). Voltage is very quickly switched on and off using MOSFET transistors in a manner that results in the current flowing at a desired level. But this current control method will not work with a simple resistive load – the current regulation can only be achieved when driving coils – and they, together with magnets or other coils are making the stepper motor rotate. A coil – an inductor – has an interesting property – it “slows down” the current flow, adding “inertia” to it. That means that if a voltage is applied, the current flowing through an inductor will not rise immediately – but slowly. The same thing happens when the voltage is disabled – the current will not immediately drop to zero amperes but will decrease over time.
By the way, LEDs are actually current-controlled too – but in the case of a simple LED strip, a resistor is enough to regulate the current, so finally a LED strip is working as a constant voltage device.
Practical measurements
The described current control method can be
seen clearly on actual measurements:
The yellow curve represents current flowing through a motor coil, the cyan line shows the voltage being switched on/off. This measurement was taken during standby, when the motor was not rotating, but holding its position. The current is pretty much constant and the voltage is regularly switched on for a short period of time and then turned off again. Please note that this switching is happening over 30.000 times a second!
When
the motor starts moving, that’s when the interesting stuff happens. The shape
of the current waveform is not flat anymore – it’s a sine wave. To rotate the
motor, the current needs to be changing to alternate the magnetic field, which
results in motion. This principle is true for all brushless electric motors.
The TMC2208 is actively measuring and regulating the current, creating a sine
current shape with set amplitude and the effective voltage varies accordingly.
Rotation speed depends on the frequency of the current sine wave.
Don’t worry about the waviness of the voltage measurement – it’s an artifact. The amplitude – seen at the bottom of the screen – is more or less equal to the supply voltage which I was using (32V). The RMS value is an indicator of “how much” effective voltage is delivered to the motor coil. In this case, the measured/calculated value is not very precise, but it shows that at this speed we deliver less than 40% of the nominal supply voltage.
When we zoom in, we
can clearly see the mentioned special property of an inductor:
When the voltage is on, the current is rising, but quite slowly compared to how fast the voltage is rising/falling. When we turn off the voltage, current flowing through the coil falls, but again – quite slowly. Before it goes too low, the driver turns on the supply and the current goes up again. This is essentially how we keep the current at the required level. Please also note that the time how long the MOSFET switch is conducting (how long the voltage stays ON) depends on the “position” on the sine wave. When we look at the sine wave, we can see regions that change slowly (near the top/bottom) and which change faster (near zero on the Y-axis). If we want the current to follow this shape, we simply need to apply the voltage for a bit longer in the “fast region” of the sine wave!
The small
irregularities, deviations from ideal, smooth sine shape are called ripples and
are always present when a coil current is controlled with PWM.
Effects of the motor load
At this point, one very important question arises – what causes the required voltage (actual power delivered to the motor) to vary with changing load? That thing is called BEMF – Back ElectroMotoric Force – the inherent property of every electric motor. I don’t want to get into the physics details of this phenomenon in this article – simply speaking, the motor coil during spinning is generating a “counter” voltage, which is fighting with the voltage that we are applying from the power supply to the motor, that’s why it is called Back EMF. The higher the speed (or load), the higher the BEMF that we need to fight with.
BEMF is affected by three main factors:
motor coil inductance – the smaller, the better;
set current – the higher the current, the stronger the motor is, but so is generated BEMF;
speed/mechanical load – of course the BEMF increases together with the load. That’s how the sensorless homing using StallGuard works – it measures the BEMF!
Actual influence of BEMF
On
the measurement below, we can see an acceleration move and close-ups of two
regions – with low/high speed.
When the speed was still quite low, the motor controller still had plenty of headroom to nicely regulate the current, so the sine wave can be considered ideal. But if we zoom in a bit later, we can see that the current looks more like a triangle, and the voltage is applied not very precisely. That’s because the controller had no voltage headroom to properly regulate the current and in effect, the sine wave gets distorted, although the motor is still operating.
Now that we understand how a stepper motor is controlled, we can get to the next point and answer the last question – what happens when the BEMF is so high that the mentioned “counter” voltage is getting too close to the supply voltage? One might guess that the motor will start losing steps – and that’s true, but not immediately! I was honestly surprised by how well the drivers and motors are handling extreme speeds. Let’s take a look:
This is the current flowing through the motor coil during a full move using a 24V power supply. The printer starts at standstill, then it accelerates to 900 mm/s at 9000 mm/s2 and then stops.
So, what is actually happening? In the beginning, the driver is able to maintain a prover sine wave, but a bit later, when the BEMF gets close to supply voltage, the waveform degrades, as we have seen above. But at that point the printer still did not reach the desired speed – soon the back voltage generated by the motor was so high that it was impossible to reach the set current value. It drops until the desired speed is achieved and then the amplitude gets stable, but we are not looking at a sine wave anymore – at that point, it is much closer to a square wave.
These results look pretty bad, but actually – they are ok! The machine was running with settings like that for over a year without problems. It is quite normal in high-speed applications. Of course, the torque is greatly decreased and the accuracy may not be perfect, but after slowing down, the motors regain nominal torque and the position is accurate. 900mm/s is the maximum speed that I considered safe before starting to lose steps.
I
also tried to use raw data from the oscilloscope to calculate and show the
averaged “voltage consumption” during operation. It proved to be a bit harder
than I’d anticipated, so the results are only indicative – that’s why there are
no numbers presented. Anyway:
The
two graphs present voltage and current with “Local RMS”, which is more or less
the averaged effective value. We can see that as the speed increases, we need
to apply more and more voltage until we reach the limit and at that point the
current is falling a bit. Two important conclusions come from these graphs:
We can never deliver 100% of the supply voltage because we need the current to be changing -> we need some time for it to fall down;
At high speeds, we are unable to deliver full power to the motor.
Benefits of higher supply voltage
Some
of you could have realised that in most of the measurements I was using 32V,
not 24V power supply. That’s true – I recently upgraded my machine to 32V and
that’s why I decided to play around with my oscilloscope and compare both
options.
Was
it worth it? Definitely!
With
move parameters like previously, the waveform shape looks much better and
current amplitude is ~60% higher than before, which means better stability and
higher margin before motors start losing steps. On the other hand, instead of a
higher safety margin, I can print with quite high accelerations and even reach
a speed of 1200mm/s! Not that it makes much sense for a filament
printer.. but I’m really happy with the results.
Summary and recommendations
Even
a few volts of difference will improve operation of our stepper motor driver or
allow us to reach higher speeds. Sometimes higher printing speed will come with
decreased print quality, but that’s often not a big issue and at the very least
we can increase the travel speed, which will not only reduce print time but is
also helpful for retraction tuning.
With
all the knowledge we got, now we can choose motors for our machine more
confidently. So:
Keep the motor rated inductance and resistance as low as possible;
For drivers like TMC2208 or TMC2130, 1.5 – 1.7A rated motor current should be optimal;
For TMC2209, TMC2660 and TMC51X0, 2.0 – 2.5A rated motors will be OK;
Choose motor power supply voltage as high as possible, but double-check ratings of your drivers and mainboard!
Personally, I think that in the next few years we will see more and more 36V and later 48V – ready motherboards for Reprap/commercial 3D printers, as our machines keep getting better and could make use of a speed boost. The only downside to it is that heaters are usually designed for 24V – but maybe that will change too! We will see.