4. Variable Speed Control Using Hardware PWMs

The ATmega48/168/328P used on all four controllers discussed in this app note has two eight-bit timers: Timer0 and Timer2. Each of these timers has two PWM output pins. The Timer0 PWM output pins are OC0A on PD6 and OC0B on PD5; these are the control lines for motor 1. The Timer2 PWM output pins are OC2A on PB3 and OC2B on PD3; these are the control lines for motor 2. Our general motor control strategy is as follows:

Step 1. Configure Timer0 and Timer2 to generate inverted PWM outputs on all four motor control lines. An inverted PWM is a signal that is low for the specified duty cycle and high the remainder of the period. As such, a duty cycle of 0% produces a constant high output and a duty cycle of 100% produces a constant low output.

This is accomplished by setting the Timer/Counter Control Registers (TCCRxx) to select the appropriate timer mode and timer clock (note that these registers are defined in <avr/io.h>, so you must first include this before the code below will work):

// see the ATmega48/168/328P datasheet for detailed register info

// configure for inverted PWM output on motor control pins
TCCR0A = TCCR2A = 0xF3;

// use the system clock / 8 (2.5 MHz) as the timer clock
TCCR0B = TCCR2B = 0x02;

These register values configure Timer0 and Timer2 to run in fast PWM mode using the system clock divided by 8, setting output pin OCxx when the timer counter matches the value in register OCRxx and clearing output pin OCxx when the timer counter exceeds its maximum value of 255 and overflows.

This gives us four inverted PWM outputs, one on each of our motor control lines. Each PWM is running at a frequency of 20 MHz/8/256 = 9.8 kHz and each has a duty cycle determined by the value in its associated eight-bit OCR0A, OCR0B, OCR2A, or OCR2B register. For example, if OCR0B = 127, the duty cycle of the PWM output on pin OC0B (PD5) is 128/256 = 50%. For the fast PWM mode used in the above code, the relationship between the OCRxx value and the PWM duty cycle is given by:

duty cycle of PWM on pin OCxx (%) = ((OCRxx + 1) / 256) * 100%

Note that one consequence of this formula is that you can never achieve a clean 0% duty cycle; the closest you can come to 0% when using a fast PWM is 1/256 = 0.4%, which we approximate as 0% in this document. If you use phase-correct PWMs, you can achieve both clean 0% and 100% signals at the expense of halving your PWM frequency. Please see the ATmega48/168/328P datasheet for more information if you wish to use phase-correct PWMs.

Step 2. For each motor, you can select rotation direction by holding one of its control PWMs fixed at 0% duty cycle (i.e. holding the control line high) while PWMing the other control line at some arbitrary duty cycle. The duty cycle of this second control line determines the speed of the motor, with 100% resulting in full speed (since now one motor input is a constant high while the other is a constant low).