Instructables
Picture of Arduino Hardware PWM for stepper motor drives
With our new project, JustAddSharks, we are very keen to support the development of open source control software for laser cutters. We are more than happy for people to modify our laser cutters, replace the control system with something suitably open source and then let us know how it goes. Realistically though we're going to have to figure these things out for ourselves so we can guide other people through the process.

There are a lot of tutorials online detailing the hardware PWM features of the arduino and how it relates to servo motor control. This instructable will show you how to use the hardware PWM to control a stepper motor instead. We'll start with the basics of PWM, show briefly how it relates to servo motors and then show the differences for use with stepper motors.
 
Remove these adsRemove these ads by Signing Up

Step 1: Pulse Width Modulation

Picture of Pulse Width Modulation
Pulse Width Modulation (PWM) is a technique for creating a digital square wave signal. A square wave has 3 main characteristics
  • Amplitude - The amount the signal changes between On and Off states
  • Frequency - The number of times the signal repeats in a given time frame
  • Duty Cycle -  The proportion of On time to Off time usually expressed as a percentage
In these digital systems the amplitude is fixed between 0 and 1, between 0V and VCC. In my testing this was +5V.

In it's simplest form a PWM square wave can be created with a few lines of code. The following code would produce a signal with a 1 second Frequency and a 10% duty cycle. This would be functional but the Arduino would be dedicated to producing this signal the whole time and would not do anything else. This is why the blink without delay example is so important to learn.

digitalWrite(IOPin, HIGH);
delayMicroseconds(100);
digitalWrite(IOPin, LOW);
delayMicroseconds(900);


With a software generated signal any variation in the code execution time can cause variations in the output. Conditional statements may not execute on every loop so it is hard to guarantee that each loop will take exactly the same amount of time. Modern microcontrollers have dedicated hardware modules to generate a code independent square wave. Values for Duty cycle and Frequency are written to registers within the controller and the hardware generates the appropriate signal on the desired output pin.

The registers for the Hardware PWM module can be written to directly as described here, but the joy of the Arduino environment is that somebody is likely to have written a library to simplify that process. The TimerOne library provides control over the HardwarePWM module connected to timer 1 (aptly). This library has easy functions for setting the frequency and duty cycle of the square wave being generated
  • pwm(pin, duty, period) - Generates a PWM waveform on the specified pin.
The minimum freqeuncy supported by the library is 1ms and the maximum is 8.3s. The duty is specified as a 10 bit value, between 0 and 1023, and this correlates to the 0% and 100% values respectively.



Joe_M2 months ago

I found this while looking for others who thought that PWM was a good way to run stepper controllers. Losing the count might be a good trade off in many situations. Even printers use other ways to position the head than counting pulses. Pulse counting in printers and so many other things can fall short of expectations when the motor skips, stutters, or slips. I don't mean a lot, I mean just a tiny bit, just enough so the count is wrong, and that error will be cumlative, and increase over time. I expect that I could use the interrupts to control limits. My thought on using PWM to control Steppers is that it offloads the process from the processor, and gives it time to do other things faster than it would if it was counting pulses, as well as generating them. This is a great instructable. I downloaded the library too, Thanks

racataca6 months ago

?

I need to convert 2 PWM inputs (RC signal) and turn it into 2 analog outputs 5VDC. (0v-to 5 Vdc).

.

stratos13pao6 months ago

im sure you know it but frequency idoes not mesure time, i think that would be the period, frequency is the number of occurrences of a repeating event per unit time

Orngrimm6 months ago

Recently i had to make a much faster PWM for a project but still preserve the capability of 1%-steps in the duty.

In fact, i had to reach FULL speed --> 16MHz clock of the Arduino --> 160kHz frequency with 1% stepping of dutycycle.
A lot of forums and boards said: No dice. That wont work, too fast.
I fiddled around a bit and got it finally working as intended.

The important parts are those setting of Timer1 and registers to it:

// The definitions of the PWM. Borders, initial dutys and such stuff
int duty1 = 80;// The desired dutycycle #1 initialisation.
int duty2 = 1;// The desired dutycycle #2. Lets make 1% // Not needed... // Also here: 0..49 = 50 steps
int PWM_maximal = 100; // 100% is the TOP limit
int PWM_minimal = 0; // 0% is the lower limit. However 0% is a tad > than 0. In fact, 0% == 60ns high (1 clockcycle). ATM no way around this. Sorry.
int PWMfreq = 160; // kHz of the PWM
int period = 16000 / PWMfreq; // How many clockticks shall = one period? (16000kHz Quarz / desired PWMfreq in kHz)

// Set the Pins to output. Pins 13-2 are PWM-capable on my Arduino mega (Atmega1280)
pinMode(11, OUTPUT);
// pinMode(12, OUTPUT); // enable this if yu want to use the second PWM!

// Init timer1
// STOP & clear
TCCR1B = TCCR1A = B00000000; // Clear TCCR1A (for a known start) andTCCR1B (to stop timer1 clock for register updates)

// Define the pins and theyr behaviour / linking
TCCR1A = TCCR1A | B10000000; // Clear OC1A on match, set OC1A at BOTTOM (Non-Inverting-Mode) --> Pin11
// TCCR1A = TCCR1A | B00100000; // Clear OC1B on match, set OC1B at BOTTOM (Non-Inverting-Mode) --> Pin12 // Enable this to get a second PWM on Pin 12! His duty is set by OCR1B further below in this setup

// Waveform Generation Mode: I want WGM12-WGM10 set to binary 110 --> Fast PWM, Top = ICR1 , Immediate update of OCR1A
TCCR1A = TCCR1A | B00000010; // WGM11 = 1 & WGM10 = 0
TCCR1B = TCCR1B | B00001000; // WGM12 = 1

// Define TOP and the Compare
ICR1 = period;// ICR1 = TOP --> FastPWM with TOP = ICR1: Counts 0 to ICR1: --> drive PWM period
TCNT1 = duty1 - 1;// force immediate OCR1x compare on next tick

// Set the prescaler and an unknown (to me) bit which seems important
TCCR1B = TCCR1B | B00000001;// last 3 bits = B001 --> prescaler = 1
TCCR1B = TCCR1B | B00010000;// Seems to be a read-only-bit? why set it? dont know. was this way online...
// Without it, PWM-Duty HIGH is OK, but LOW is WAY too long... 30.4us instead of 4.6us.



// Define the dury-cycles (Those can be set later on in te loop() as well!)
proportional_duty1 = (unsigned long)period * (unsigned long)duty1 * 1UL;
proportional_duty1_osca = (unsigned long)proportional_duty1 / 100 * 1UL;
OCR1A = proportional_duty1_osca; // ON duration = drive pulse width of OCR1A --> PIN11
// OCR1B = duty2; // ON duration = drive pulse width of OCR1B --> PIN12 // enable this to define the duty of the second PWM. See above for the TCCR1A-setting to enable the PWM as it.

I hope this code helps some people who need to make a FAST pwm.