Stepper motor step signal timing calculation

While we use stepper motors in many devices, the timing of the step signals is not always well understood. Most of us can see how a fixed speed is a bad idea unless the speed of motion is really slow, but it is fixed speed what is really to generate with simple code. You just can use the Arduino Blink example to send a fixed frequency of pulses to a stepper motor driver. If too slow, just reduce the delay value.

But for most real-life applications variable speed controls will perform better. These systems will speed up the motor to a given maximum speed and it will slow it down to stop at the desired step count. But understanding the math behind this process is sometimes not obvious.

Let us start with the simple case of the so-called trapezoidal speed motion profile. Here motion takes place in three phases: acceleration, constant speed (cruising) and deceleration. But the point is how can our code create such a behavior.

Stepper motor drivers usually accept two signals to control motor motion: a direction signal, that will make the motor rotor CW or CCW depending on whether it is LOW or HIGH, and a second signal called step that will make the motor move one step more every time a pulse is applied to it.

For a given moment, and after setting the right direction for it, the tricky part is to provide the exact number of pulses required (N) so each one is properly timed according to each motion phase. But in order to provide the right timing, we need to understand that each step pulse makes the motor move one discrete angle. That means each pulse causes a fixed amount of displacement no matter the timing between the pulses.

The easy part

When the motor is coasting at a constant speed, pulses are happening at a fixed rate, so the time interval between them remains constant. For example, for a speed of 100 steps/second, the time in between pulses is 1/100 seconds or 10 milliseconds. Providing a new pulse after this exact time will keep the motor spinning at a constant speed.

The ugly part

So for the simple case of a constant acceleration (a), and assuming we express the acceleration in steps/second2, the first step should happen at the time that will be equal to the motion of a single step. If you recall from High School physics, the distance traveled from a standstill can be calculated as d=(a*t2)/2, so if distance==1 then the time will be t=(2*1/a). For the second step, the time will be t=(2*2/a) and for pulse 'i' the time will be t=(2*i/a).

But please note that for our code, more than the absolute value of the time when a certain step happens, what we need is the time interval we will use for our timer. So, for the first step pulse, we can just use the value for distance of one step, that is (2*1/a) but for the second step, what we use is the time difference between the moment of the first and the second pulse, that is  (2*2/a)- (2*1/a). And for the pulse 'i', the time interval will be  (2*(i)/a)-(2*(i-1)/a).

This is what is kind of fast forwarded in AVR446, that otherwise contains a lot of useful information for coding stepper timing on a microcontroller. (BTW, You may see that many links to AVR application notes are now gone due to the acquisition of Atmel by Microchip). That is why they tell you there that the timing of a pulse is proportional to (i)-(i-1).

Processing time considerations

Unfortunately, if you are calculating the next interval time value in between pulses, the amount of time available for calculations quickly diminishes as the motor accelerates. If your code runs on an 8-bit microcontroller processing time can become an issue.

To speed things up, an approximation can be made so each new time interval between pulses during acceleration (or deceleration) phase can be calculated with the following expression:
Which represents the timer value calculated based on the timer value of the previous step. So only a couple of shifts (product by 2 or 4) and a division are needed and therefore the calculation can be done quite quickly and using integers. 

This is exactly what I used for my own code in here.

Going further

But what if you do not use a constant acceleration, as you want to use some type of s-curve motion profile? Well, the acceleration solution above is of no use then, and you need to look somewhere else. 

What we have is that a given acceleration will shape the way speed changes over time while acceleration and that, in turn, will affect how distance is traveled as the motor accelerates, shaping the time intervals in between pulses in a particular way.

But things can get messy quickly. For example: let us assume our acceleration is a(t)=1-cos(t) then, the speed expression will be the integral of the acceleration over time so s(t)=t-sin(t). We have traded the speed ramp for a smoother transition, but let us have a look at the timing of the step pulses.

The expression of the traveled distance over time will then be d(t)=t2/2+cos(t)-1 but when we try to determine the time that will correspond to distance==1 we realize there is no way we can find the inverse function in the form of t=...

wxMaxima to the rescue

Do not panic, there must be a way of figuring out when exactly a distance of 1 step has been covered. But given we only have the equation of know the distance traveled after a certain amount of time, let us try to get some math tool to do the heavy lifting: I do use, like and recommend wxMaxima and wolframalpha.com, both free to use and quite powerful. 

So we just need to solve the equation where d=1 for the variable t. And while the solve command won't help us here (as we are dealing with a transcendental expression), we can obtain an approximate result with the find_root command:
We even could use a loop to obtain the time corresponding to each integer value of the distance, and the time between a pulse and the next one is the difference between the time of this step and the time of the next step.  But this process may not be very convenient if, for example, we want to change the acceleration.

Turning numbers into timer values

Once we have time values for d=1, d=2, ... we have to turn them into timer values for the steps to take place at the right time. However, there is a catch. No matter how many values we have precalculated, a given movement may require more or fewer steps than that value, so we need a way to adapt our discrete set of values to a general case.

On the other hand, another way to think about the acceleration is to consider it can be represented by a certain amount of time (the time while acceleration takes place). So the problem we need to solve here is to find out a way to properly spread a number if steps (M) across a time period (T). 

The reference time in our acceleration pattern goes from 0 to 2Π and the maximum distance traveled after that time is 2Π2

So when the distance to cover is M steps, then we will calculate the time that corresponds to each distance=2*PI*PI*i/M where 'i' goes from 0 to M. The difference between each pair of adjacent values P=t(i)-t(i-1) is the delay between pulse i-1 and pulse i. We just need to turn these values into timer counts.

However, as the total time the acceleration phase lasts is T and the time value used for the table was 2Π then we need to scale that value to P*T/2/PI. Though this value is still in seconds. Now, depending on the timer frequency, the integer value for the timer count will be k*P*T/2/PI. For example, k=1000000, if timer count frequency is 1Mhz, as one count, is then one microsecond.

 From distance to time

I intentionally left unexplained how the code is to obtain the time that corresponds to a distance. The solution that works for me requires two different steps. 

The first part is to obtain a lookup table of distance and time pairs. If obtained using the d(t) expression above, the pairs can be equally spaced in time easily. But for best results, we want the values to be equally spaced in the distance axis (you could do that using the wxMaxima route for example).

The second part is that, for 'i' step to lookup for the time that corresponds to distance 2*PI*PI*i/M and subtract it the value for the previous step (i-1). Then this time value is scaled properly according to the acceleration time and converted into a timer count. Once that time has expired, the pulse is fired and the process repeated till M pulses have been generated. Next, we start the coast phase, where speed is just maintained, till deceleration starts.

Using a lookup table takes care of removing any complexity in the expression of the curves out of the real-time operation of the code, as the linear interpolation can be done very quickly each step.

Comments

Eric S. said…
Hello,

I am trying to figure out the Stepper motor pulse settings for my Delta Wasp 3MT. I'll be honest you have lost me in your calculations. I have access to the motor specs and torque curves, but don't know how to get the settings to drive them. The settings I need are minimum driver step pulse width, step pulse interval, direction setup time and direction hold time. Can you please reply or email me at my address, I've been looking to hire someone to solve this and haven't had luck yet. Thank you!
misan said…
Hi Eric,

If you want to control a Delta 3D printer you can use an existing firmware like Reprap Firmware or Marlin. It handles not only the timing but all the rest of the tasks needed to get a 3D printer up and running.

It depends on the driver chip, but step pulse should be 1-2 us duration (or longer). The interval in between the pulses (or its inverse, aka pulse frequency) will determine the rotational speed of the motor. Direction setup time 1-2 us (again, it depends on the controller chip, a typical one is A4988 https://www.pololu.com/file/0J450/A4988.pdf ).
Eric S. said…
Thank you so much for your reply! Yes I am using a duet3d board which is RepRap and have my config.g all sorted except for M569 which is the step pulse setup. Its a duet board, sending signal to Pololu Tic36v4's which use DRV8711 (https://www.ti.com/lit/ds/symlink/drv8711.pdf) and then on to the 48v 2.5amp motors (p85-2h-2-b-200-4-a14). (https://www.pamoco.it/catalogo-prodotti/motori-rotativi/passo-passo/download/241_9fe34b2ff38f0cc2ae650b4d162838cf)

So looking at page 9 of the DRV8711 document, it looks like 1.9 us for pulse duration (so that means width and interval?), and 200ms (.2us) for step setup and hold. I will try the settings

M569 P5 R0 S0 T1.9:1.9:.2:.2

for all of my motors and see if this resolves my movement issues. I have a $100 bounty out right now on Fiver and craigslist gigs for anyone who can help me resolve this. If your advice pans out I will paypal/venmo or whatever form of payment you want. You are the most knowledgeable person I've heard back from. I've been trying to get this printer to work since Nov. I normally work with smaller kit printers and this one is a frankenstien from previous owner. Thanks again for your help.
misan said…
Page 9 of datasheets details the 1.9 usec as the minimum pulse time, and another 1.9 usec for the minimum time between pulses. Round that to 2usec and you get the 250Khz of the step frequency. (You better use 2 usec than 1.9 usec to get some leeway).

Dir pin setup time is a much lower 200 nsec.

Duet forum brought me one of the best support experiences ever: https://forum.duet3d.com/

Popular posts from this blog

VFD control with Arduino using RS485 link

How to get sinusoidal s-curve for a stepper motor