When the timer value is greater than the
expected value, then a low value is output on the
I/O pin. The ATmega used in the Arduino has two
PWM “channels” dedicated to Timer1 (other
microcontrollers can have as many as eight PWM
channels dedicated to a single timer, and have
multiple timers with multiple PWM channels).
The PWM generation does not require any I/O
cycles of the processor at all, so you could argue
that the improvement over the other versions is
infinite. How’s that for better?
Figure 10. Arduino servo connection with separate power supply and
inverting buffered servo control signals.
“Application_08_-_One_Servo_PWM_Output”
and “Application_09_-_Two_Servo_PWM_Output”
implements the servo control over the two Timer1
channels. When they run, you should not see any
difference compared to the timer interrupt versions
of the servo control programs. However, they do
not take up any processor cycles during execution.
The pseudo-code for these applications is:
Enable Servo IO Pin(s) for Output
Start Timer with “Timer1IRQ” Interrupt
Handler
state = finalDelay;
while (1 == 1) { // Loop forever
Servo1PWMvalue = left Pot
Servo2PWMvalue = right Pot
Delay 10ms
}
Figure 11. Breadboard wiring of Arduino servo connection with
separate power supply and inverting buffered servo control signals.
All the execution time is devoted to reading the
sensors and writing the new value to the PWM
registers. It doesn’t get any simpler than this.
Depending on the MCU, the timer will immediately
cause an interrupt which will have it jump to the next state
immediately, or will wait for the timer to count through and
overflow. Neither option is desirable, so while I could have
gone with 2,401 µs, I decided to go with 2,500 µs which
gives me a maximum of eight outputs over 20 ms, with
each one spaced exactly 2,500 µs apart.
When you look at some servo datasheets, they
will indicate that the “Control System” is “PWM.” This is
something of a misnomer, as we’ve just demonstrated. It is
possible to use a PWM signal to create a valid series of
control pulses for a servo, but they should not be
considered to be a PWM signal.
By using the timer and its interrupt, I can comfortably
generate control signals for eight servos with only a 1% loss
of machine cycles, as well as execute completely
asynchronously so the mainline code (where sensor
operations and high level execution takes place) has
minimal disruption.
A PWM signal is used to directly control the power to a
motor or to set an analog level, whereas the control signals
here are used to set the position of the servo. This is
splitting semantic hairs, but it’s important to think about
exactly how the signals being produced are used.
Would it surprise you if we could do better?
For those people who are very familiar with the
Arduino, you probably had an idea why I picked pins 9 and
10; these are the PWM outputs for Timer1. The PWM
generator in a microcontroller continually compares the
current value of a timer to an expected value, and when
the timer value is less than the expected value, it outputs a
high value on an I/O pin.
There is a potential issue with using PWM to drive
servos, and that is the number of bits available to the
PWM’s timer. In the Arduino’s Timer1, there are 16 bits for
a total count value of 65,536. However, the timer runs off
the system clock divided by two (which is 8 MHz), which
means that at full speed it takes 8. 2 ms to run through the
clock. So, to be able to support a 20 ms cycle time, the
Timer1 input is passed into a “prescaler” which divides it by
eight. That means the maximum time for Timer1 now to
cycle through is 65. 5 ms (or 1 µs for each timer click); for
20 ms, 15 of the 16 available bits are required (the high bit
74 SERVO 05.2015