properly timed.
As an aside, the SoftwareServo library brings up an
interesting philosophical question which goes back to the
analog versus digital nature of servos that was discussed
previously: How many “steps” are there for a servo?
“Application_07_-_Two_Servos_Timer_Interrupt” uses a
state machine to determine what are the I/O operations
along with timing, and can be described using the pseudo-code:
If you use SoftwareServo, you have 180 discrete
degrees, but if you consider that the Arduino’s processor is
running at 4,000,000 instructions per second in the 1,800
µs range of the servo pulse, there are 7,200 instruction
cycles. Doesn’t this mean that there are 40 possible
positions between each “degree” in the Arduino?
As I said, it’s a philosophical question, but an important
one to think about as we discuss different methods of
driving servos below.
With the “SoftwareServo” as a reference point, I want
to be able to create servo control signals that:
1. Do not have an impact on the operation of the
application code.
2. Provide a stable 20 ms pulse train.
The answer goes back to my first class in assembly
language programming in University. If you are looking to
provide a consistently timed event, use the processor’s built-in timer and interrupt.
The “TimerOne” Arduino library (available at the article
link) provides access to the ATmega Timer1 module and its
built-in peripherals (including the PWM, which we will be
taking advantage of in the next application). This library is
used in “Application_06_-_One_Servo_Timer_Interrupt” and
556 Servo Driver Bill of Materials
1x 556 Dual Timer
1x 0.033 μF Capacitor
1x 0.01 μF Capacitor
1x 2.7M 1/4W Resistor
1x 100K 1/4W Resistor
Enable Servo IO Pin(s) for Output
Start Timer with “Timer1IRQ” Interrupt Handler
state = finalDelay;
while (1 == 1) { // Loop forever
Servo1value = left Pot
Servo2value = right Pot
Delay 10ms
}
Timer1IRQ { // Timer output state machine
switch (state)
case leftServoStart:
Servo1Pin High
setTimer = Servo1value
state = leftServoEnd
case leftServoEnd:
Servo1Pin Low
setTimer = 2,500us - Servo1value
state = rightServoStart
case rightServoStart:
Servo2Pin High
setTimer = Servo2value
state = rightServoEnd
case rightServoEnd:
Servo1Pin Low
setTimer = 2,500us - Servo1value
state = finalDelay
case finalDelay:
setTimer = 20,000us – (2,500us *
#servos)
state = leftServoStart
}
1x 18K 1/4W Resistor
1x 50K Single Turn, PCB Mountable Potentiometer
1x Servo Header (see text)
1x 4 AA Battery Holder with Batteries
Misc. Breadboard/Prototying PCB with Wires
Arduino Driver - Non-Inverting Buffers Bill of Materials
1x Arduino Uno
4x 2N3904 NPN Transistors
6x 1K 1/4W Resistors
2x 50K Single Turn, PCB Mountable Potentiometers
2x Servo Header (see text)
1x 4 AA Battery Holder with Batteries
Misc. Breadboard with Wires
Notice that in this application, the mainline code is
completely independent of the servo control signal
generation. If we were to assume that every 20 ms, each
Timer1IRQ instance took 50 instruction cycles (and there
are five of them), and in 20 ms, there are 80,000
instruction cycles, the percentage of cycles lost to
generating the servo control signals is 0.3125%. This is
constant, however, and does not affect the operation of
the mainline code. If we expand this to eight servo outputs
(at which point there would be 16 states), this loss
increases to a constant 1.0% — still a lot better than the
Arduino Driver - Inverting Buffers Bill of Materials
3.5% to 12.5% of the SoftwareServo library.
1x Arduino Uno
2x 2N3904 NPN Transistors
4x 1K 1/4W Resistors
2x 50K Single Turn, PCB Mountable Potentiometers
2x Servo Header (see text)
1x 4 AA Battery Holder with Batteries
Misc. Breadboard with Wires
You may question why the application uses 2,500 µs
for the length of time for the code for each servo when the
maximum length of time that a pulse can be active is 2,400
µs. The reason is a practical one to eliminate the problems
when the timer is loaded with 2,400 µs – that is, 2,400 µs
or zero.
SERVO 05.2015 73