Driving Stepper Motor with Variable Frequency PWM

I'm considering using stepper motors for a balancing robot because the backlash is very low and torque is highest at low RPM. Been having backlash feedback issues with my banebots motors (RS454 motor with 26:1 gearbox). I measured 3 degrees of backlash.

Just not sure how to control them in my Arduino Due under a fixed time step program loop. Typically I use standard DC motors driven by a VNH2SP30 Motor Driver and I simply change the PWM duty cycle to change RPM without upsetting the program loop timing. I use the "pwm01.h" library to setup the hardware PWM frequency and the signal just runs in the background.

The problem: To drive a stepper motor I need to send a step command to a stepper driver. The command is in the form of a pulse, like a PWM wave with 50% duty cycle. To change RPM, I need to change the period or frequency of that wave. Changing hardware PWM frequency in a loop is not practical. Is there a way to setup variable frequency PWM signals with fixed 50% duty cycle in a loop without upsetting the program loop timing?

I haven't used stepper motors yet, but it sounds like you need signal(s) as described here: http://zone.ni.com/reference/en-XX/help/371093G-01/nimclvfb/axisconfig_stepper/

Earlier, I've tested the 2-bit Gray Up/Down Counter for Stepper Motor on the Due. All you need to do is update the Channel Period Register and Channel Duty Cycle Register to change the frequency and maintain 50% duty cycle. You could use pins 34 and 36, where pin 36 is phase shifted by 90°. http://forum.arduino.cc/index.php?topic=291435.msg2037460#msg2037460

My goal is to use a stepper motor like a standard DC motor and just control RPM and take advantage of the very low backlash stepper.

My problem... I have a good stepper motor driver that requires a pulse for stepping. I use a hardware PWM signal to continuously rotate the stepper with pwm_set_clockA_freq, PWMC_ConfigureChannel, PWMC_SetPeriod, etc. (library link below). Unlike a DC motor that requires changing PWM duty cycle to vary RPM, the stepper requires changing PWM frequency - this is the issue.

My program is a control system with a fixed time step per loop (0.005 sec) and constantly adjusts stepper RPM by changing the PWM frequency. It works great for high frequencies, but at low frequency (say 10hz), the timing of the entire Arduino board is affected.

Is there a way to actively change hardware PWM frequency without affecting the timing of the main loop?

http://forum.arduino.cc/index.php?topic=144446.msg1149044#msg1149044

randomvibe: Is there a way to actively change hardware PWM frequency without affecting the timing of the main loop?

Generally speaking, the PWM modules are not designed for variable frequency, so it's not ideal for stepper drivers. You can change the frequency by setting the clock frequency, that could get messy on the fly. You might need to disable the PWM while changing frequency for example.

I would use a timer, and use the compare registers to shape the waveform. See http://www.atmel.com/images/doc11057.pdf page 881, WAVSEL=10 for an example.

You don't need an exact 50% duty cycle for a stepper, just a pulse as small as 5us (depending on driver). So you would set RA or RB for the pulse width, then write RC to set the frequency.

Check your stepper driver datasheet for minimum pulse width, if the signal goes through optocoupler you will need to allow for that as they can be quite slow.

The timer approach for variable PWM frequency works! I found a very useful library specifically for the Due called "DueTimer" by "ivanseidel" (link below). The "timer1" and "timer3" libraries in the arduino playground do not function on the Due.

https://github.com/ivanseidel/DueTimer

I attach an interrupt function that is called at a certain frequency or period. The callback function generates a single pulse.

bobcousins: You don't need an exact 50% duty cycle for a stepper, just a pulse as small as 5us (depending on driver). So you would set RA or RB for the pulse width, then write RC to set the frequency.

That is true provided the duty cycle is fixed. Rather than shaping the pulse with ATMEL registers (out of my expertise), I did it with simple code: HIGH - DELAY - LOW - DELAY. The delay is 10 microseconds and the stepper driver handles it nicely.

The highest frequency I need for driving my stepper motor is only 10khz. So this Timer approach for PWM variable frequency works great. The tiny delay in the pulse generator function does not affect the fixed time step (0.005 sec or 200hz sample rate) control loop.

I still need to figure out how to output very low PWM frequency. Since my program loops at 200hz, the lowest PWM frequency is 100hz. If I slow down the program loop to 2hz, I can easily achieve 1hz, but then my robot controller goes unstable. I may have to scale the frequency on the stepper driver. Any suggestions?

randomvibe:
The problem: To drive a stepper motor I need to send a step command to a stepper driver. The command is in the form of a pulse, like a PWM wave with 50% duty cycle. To change RPM, I need to change the period or frequency of that wave. Changing hardware PWM frequency in a loop is not practical. Is there a way to setup variable frequency PWM signals with fixed 50% duty cycle in a loop without upsetting the program loop timing?

If you have your PID loop output the desired velocity value then you can simply use
that to drive DDS in an interrupt routine that is fired off frequently.

So have a spare timer set up to overflow frequently (lets say 10kHz or similar). The code
in this routine just does DDS:

volatile long phase = 0L ;
volatile long frequency = 0L ;

ISR (TIMER2_OVF_vect)
{
  long new_phase = phase + frequency ;  // phase accumulate
  if ((phase ^ new_phase) < 0L)  // sign bit changed, time to step
  {
    if (frequency > 0L)
      step (1) ;
    else
      step (-1) ;
  }
  phase = new_phase ;
}

void set_speed (long freq)
{
  noInterrupts () ;
  frequency = freq ; 
  interrupts () ;
}

Of course the frequency value has to be scaled appropriately, and you might only
need int rather than long. The code above will generate upto one step pulse per
interrupt, as the value of frequency approaches is maximum or minimum limits.

The step rate (and thus motor speed) is linear in the frequency value which is
signed (what you want for balance control).

The step() function must set the direction pin according to its argument as well
as pulse the step pin.

Setting the relevant timer to interrupt at the right rate is an exercise left for you!

[Whoops, Due not AVR! As I’ve assumed ATmega code, not SAM38X code, you’ll have to change the
ISR declaration a bit too, and long’s can all be int’s since that’s 32 bit]

MarkT: If you have your PID loop output the desired velocity value then you can simply use that to drive DDS in an interrupt routine that is fired off frequently.

Thanks for your response. I don't wholly understand it though. What does DDS stand for? In the meantime, I got what I need using the DueTimer library as explained in my Feb. 14th message. I'm still interested in your solution if you can elaborate and simplify a bit more. Thanks again.

Direct Digital Synthesis - usually used for generating waveforms such as sinusoids, but easily adapted to generate step pulses.

Its very simple, you have a phase accumulator and add the current "frequency" variable to it on every sample period. The phase is then used to look up the waveform / trigger a step pulse, etc. You have to pay the price of a high frequency interrupt, but the result is a very simple interface for the rest of the system, in particular if driving more than one motor at once.

MarkT: Direct Digital Synthesis - usually used for generating waveforms such as sinusoids, but easily adapted to generate step pulses.

Thanks for that. I researched this subject and found this DDS method very clever. Now I know how those expensive signal generators function.

How many microseconds would it take to do the phase accumulator, waveform lookup, etc. as part of the DDS approach on the Arduino Due? Do you have example code for the Due?

With my current approach using the DueTimer and a simple bare bones function to make a square wave pulse, the operation takes about 100 microseconds. This is fast enough for my PID controller, which samples at 5,000 microseconds.

My approach only works for frequencies up to 50khz, which is more than adequate for driving my stepper (10khz max). But for high frequencies, the DDS method wins out.

Oh it’ll be a few dozen machine cycles I expect.

I wrapped a block of code with a pair of micros() to time it. The block makes a variable frequency PWM signal using a DueTimer object and a bare bones interrupt function to make a pulse. I use this signal to drive a stepper-motor like a standard DC motor. The problem: the pair of micros() says the block takes roughly 100 microseconds, but in reality it takes much longer. The problem is worse when I make a high frequency pulse (8khz). Does the micros() function work properly with interrupts and timers? What about the delayMicroseconds() function?

randomvibe: The problem: the pair of micros() says the block takes roughly 100 microseconds, but in reality it takes much longer. The problem is worse when I make a high frequency pulse (8khz). Does the micros() function work properly with interrupts and timers? What about the delayMicroseconds() function?

What is 'reality'? How did you measure that?

Yes, micros() works properly with interrupts, so long as they are reasonably well-behaved and don't lock up the processor for long periods of time (a millisecond is long for an interrupt.)

What about it? It will block your program from executing for the specified microseconds. That probably isn't very useful in this application.

I found the problem. I'm using the DueTimer library (link below) to change the pulse frequency on the fly with the "setFrequency" command. The library does this by tweaking the Due's clock frequency. Doing this on the fly is disrupting the desired timing.

https://github.com/ivanseidel/DueTimer

At this point I have 2 options for making a variable frequency background signal: (1) implement Direct Digital Synthesis as suggested by @MarkT, or (2) find a separate module that will convert a variable duty cycle PWM signal to a variable frequency signal.

@MarkT, I still don't wholly understand how to implement your code. What do you mean by "...have a spare timer set up to overflow frequently (lets say 10kHz or similar)..."?

Okay, I’ve been doing more research on DDS. Digikey has really good tutorials.

I’m first implementing an example in Matlab to make sure I understand the main components, which are: the Phase or Angle Accumulator (N-bits), the Lookup table (P-bits), and the Digital-to-Analog converter (12-bits). I understand these basic elements.

The Lookup table size will match the DAC resolution, so P=12. The Accumulator will be at least 16-bits long (N=16). So phase truncation must occur here. Therein lies my confusion and my question. How does this truncation work? What does it mean to take the P significant bits of N?

I don't think you need the lookup table or DAC converter in this case. To generate a step pulse at a rate of once per cycle, you just need to detect when the phase wraps from 360 to 0 (or vice versa).

The snippet posted above shows that it just needs the phase accumulator. The only thing left for the reader is what value to set frequency to.

I think it is something like steps_per_sec * 2 ^32 / timer_freq.

For timers on Due, I would recommend DueTimer library, https://github.com/ivanseidel/DueTimer

True, the DAC is certainly not required for driving the stepper motor. I'm just trying to understanding the Direct Digital Synthesis method in general. Like I said, Digikey has excellent tutorials; for those interested, see intro below:

http://dkc1.digikey.com/us/en/TOD/ADI/Direct-Digital-Synthesis-Intro/Direct-Digital-Synthesis-Intro.html

I'm still hung up on phase truncation. I now understand what is meant by "keeping the most significant bits" in context to the resolution of the Phase Accumulator (N-bit), which is greater than the resolution of the Lookup table (P-bit). Just not sure what that means in terms of an Arduino C++ loop. I started a thread in stackoverflow:

http://stackoverflow.com/questions/29341664/direct-digital-synthesis-what-is-phase-truncation

"Phase truncation" is just a fancy way of saying divide!

If your phase variable went from 0 to 99, and your wavetable had 20 entries, then you need to divide phase by 5 to match the range of the wavetable - 0 to 19.

As we are using binary, we can make the phase variable a power of 2 and the wavetable also a power of 2. Then the divide can be done quickly with a bit shift. e.g with phase 16 bits, waveform 12 bits, it's a shift by 4, which is equivalent to a divide by 2^4 = 16.

Keeping the most significant is the normal case for a divide. In some cases there may be more efficient ways to get the MS bits than doing a shift, but it's the equivalent operation.

The same trick can be done to convert the wavetable resolution to the DAC resolution. For a 10 or 12 bit DAC the wavetable could be the same number of bits, unless you are short on memory.

This binary math and bitwise manipulation is new to me. Your information has been very insightful. Thank you. I implemented the Direct Digital Synthesis (DDS) method in a Matlab example, and it seems to work. Code below for others. May also run on Octave.

% INPUT PARAMETERS
%-----------------------
fs    = 2^14;   % Clock Sample Rate (hz)
NT    = 1e4;    % Total Samples for example 
foutd = 80;     % Desired Output Freq. (hz)
N     = 16;     % Accumulator resolution (bits)
P     = 12;     % Lookup Table resolution (bits)


% FREQUENCY TUNING WORD
%------------------------------
FTWf = foutd/fs*2^N;          % float
FTW  = round(foutd/fs*2^N);   % actual integer term for accumulator
fout = FTW*fs/2^N;            % actual output frequency


% SINE WAVE LOOKUP TABLE
%---------------------------
x = [0:2^P-1]';
y = sin(x*2*pi / (2^P-1) );


% INITIALIZE
%--------------------------
dt    = 1/fs;                % Time Step (sec)
t     = [0:dt:dt*(NT-1)]';   % Time Vector
phase = zeros(NT,1);         % Save phase vs. time
wave  = zeros(NT,1);         % Save wave amplitude vs. time


% SAMPLE THROUGH TIME
%-----------------------
pa = 0;   % Phase accumulator

for ii=1:length(t)

    % ACCUMULATE PHASE
    pa = pa + FTW;
    
    % ROLLOVER PHASE
    if pa > 2^N
        pa = 1;
    end 
    
    % PHASE TRUNCATION
    indx = ceil(pa * 2^(P-N));
        
    % LOOKUP
    phase(ii) = pa;
    wave(ii)  = y( indx );
       
end

figure;
subplot(211),  plot(t, phase); grid on;
subplot(212),  plot(t, wave);  grid on; hold on
               plot(t,sin(foutd*2*pi*t), 'r-.');

My next step is to implement this on the ArduinoDue. This algorithm will be the basis of the ISR interrupt function. I will definitely use binary operators to speed up the division (i.e., "Phase Truncation").

After that, I need to figure out how to handle non-integer frequency inputs.

Again, thanks for your help.

Okay, finally got the Direct Digital Synthesis (DDS) function working as an ISR on the Arduino Due. I use the DueTimer library to call the DDS function as an interrupt at a rate of 16,384 Hz. This rate is required to drive the stepper motor to about 8.000 Hz. I'm now able to drive the stepper with a variable frequency signal synthesized by the DDS.

However, I'm surprised how slow the DueTimer is. I have a fixed time-step loop running at 5,000 microsecond intervals. The DDS function only requires 6 microseconds per call. But the DueTimer calling the DDS takes about 715 microseconds. Even if I comment out all the contents of the DDS function, it's still slow. If I reduce the interrupt rate to say 1,000 Hz (too slow for my application), this definitely speeds it up.

Is there a way to setup a DDS to work like a hardware PWM signal, which just runs in the background without slowing the main program?

Anyone aware of a stepper driver that takes a PWM signal directly and steps at rate proportional to the PWM duty cycle?

Thank you.

As it happens, I needed a frequency generator for some testing at work, so I also wrote a little DDS sketch.

randomvibe: However, I'm surprised how slow the DueTimer is. I have a fixed time-step loop running at 5,000 microsecond intervals. The DDS function only requires 6 microseconds per call. But the DueTimer calling the DDS takes about 715 microseconds. Even if I comment out all the contents of the DDS function, it's still slow. If I reduce the interrupt rate to say 1,000 Hz (too slow for my application), this definitely speeds it up.

That's puzzling, because I measured about 7.4 us including the DueTimer. I can't see how there could possibly be 715 us overhead, given the code!

Is there a way to setup a DDS to work like a hardware PWM signal, which just runs in the background without slowing the main program?

You get what you get with the Timer and PWM modules. There might be a way to do something with DMA, and perhaps sneaky use of SPI, or you could use external hardware. Of course any software solution will always take up cycles.

I've never seen a micro with a "stepper driver" peripheral. Creating acceleration profiles in hardware would be the really useful thing. There are some motor driver chips that can do accel profiles, e.g. from Trinamic.

Anyone aware of a stepper driver that takes a PWM signal directly and steps at rate proportional to the PWM duty cycle?

Never heard of one! There is no need really because the step pulse method works fine for most applications.

Tbh, DDS is not the way I would drive stepper motors, not really sure why it was suggested in the first place. There are many more conventional methods of creating step pulses. I would have a look at AccelStepper library for example.

Unfortunately driving steppers at high rates in software can end up eating a lot of cycles, that might mean using a faster CPU, or offloading the work to a second CPU.