Servo
Control
Servo
Operation:
Hobby RC servo motors turn to some
angle, based on a PWM control signal.
The servo PWM signal consists of a 50Hz (20ms period) pulse train, in
which the pulses may vary from 1ms - 2ms in width (I find 0.5-2.5ms is needed for
full range of motion).
A pulse of 1.5ms is “center”
value. Deviation from 1.5ms pulse width
turns the servo as follows (but take these angles with a grain of salt):

(from http://www.seattlerobotics.org/guide/servos.html )
Different manufacturers’ servos behave
slightly differently. For example, most
servos claim to have a 90° range of motion. These servos
will respond as follows:
1ms pulse -45°
1.5ms pulse 0°
2ms pulse +45°
The above pulselengths (1-2ms) are typical of the
output from hobby RC radio receivers.
However, most motors that claim to be 90° will happily turn through 180° if you supply them with appropriately shorter and
longer pulses. I’ve achieved 180° control of Futaba servos using pulse lengths varying
from 0.5ms(-90°) to 2.5ms(+90°). It seems that outside of the
–45°-45° range, there is some loss of linearity.
The hardstops for the Futaba servos
actually permit more than 180° of motion.
You can get an extra 20° range of motion on both ends (from -110° to +110°) by using pulses between nearly zero length and 3ms.
Be careful of that “nearly zero” pulse
length. Some servos have a failsafe
feature that powers down the motor if the servo stops receiving pulses. Other motors interpret “no pulse” as “zero
length pulse”. When the control signal
is switched off, these motors will turn as far counterclockwise as they possibly
can, until they collide with and press against their internal hardstop. High torque servos can self destruct this
way. Even if the servo isn’t strong
enough to damage itself, the DC motor inside will draw a very large current
while it is stalled out.
I worked around this “feature” of hobby servos by limiting my pulses to a minimum length. I found that a minimum pulse of 50μs was enough to save a Futaba servo from itself. Similarly, there will be a maximum pulselength, beyond which the servo collides with its other hard stop.
The seattlerobotics.org page on modifying a servo for
speed (instead of position) control
has
a good description of the innards of a hobby servo. The block diagram, below, illustrates how the major functional
components inside a servo interact.
The DC motor inside the servo is controlled by the
H-bridge at top, right. When a control
pulse comes in to the “Input” node at left, it triggers the Linear Pulse
Generator, which outputs a pulse of length determined by the analog voltage of
the “Feedback Signal.” The Feedback Signal
is not just the voltage from the position-feedback potentiometer, it is also
affected by “back EMF,” the voltage output by the spinning DC motor. The back EMF is a measure of velocity and is used to damp the servo's motion by
preventing it from going too fast and overshooting its target position. The difference between the control pulse and
the LPG pulse is used to generate a PWM signal for the DC motor.
The
speed control modification firstly eliminates position-dependence by
replacing the potentiometer with a pair of fixed 220K resistors.
Secondly, it drastically reduces the
resistance on the speed side of the feedback resistor network. After
modification the back EMF, not position, primarily determines the
voltage at the LPG input. There is also a plastic tab that must be
filed away to permit continuous rotation.

(from http://www.seattlerobotics.org/encoder/200009/S3003C.html )
Mark’s
Multiple Servo Controller:
Mark Smorul has written a program for
the PIC16F628 to control up to 9 servo motors, via a serial port with hardware
(CTS/RTS) handshaking. The servo
controller’s serial port has a CTS (Clear to Send) output that goes high when
the chip is ready to receive new serial data.
Sending data to the chip when it is not ready (ie. when CTS is low) will
hang the chip.
This can be avoided by placing a
serial buffer (another of Mark’s creations) between your program and the servo
controller chip. The buffer chips will
accept up to 64 bytes of data, as fast as you can send it over a single Tx
wire. The buffer chips monitor the
servo controller’s CTS signal and send the buffered data to the servo
controller when it’s ready.
Serial instructions to the servo
controller should be sent in 3-byte packets like this:
255,
address, data
“255” is a synch byte that lets the
chip know a new instruction is about to come in. “Address” is the address of the motor to be adjusted. “Address” is a binary number from 0-8
inclusive. “Address” is NOT an ASCII
character, but a binary number. Lastly,
“data” is the position to turn the servo at “address” to. “Data” may have any value between 0 and 250,
inclusive. The values 251-255 are
reserved for internal use by the servo controller chip. This means that “center” value is 125.
Interfacing
to Mark’s Servo Controller:
A simple interface to Mark’s servo
controller has been provided in Sam Scheinman’s
servocontroller_interface.c. This file
contains a #use rs-232 directive for communicating with the servo
controller. As such, it must be
#included AFTER your “#use delay” directive.
The interface gives you the
SC_SetServo function to control your motors:
short SC_SetServo(int address, int
data, short buffer_status). The function accepts an integer from 0-8
(inclusive) for the address, 0-250 (inclusive) for data, and the #defined
symbols BUFFERED and NOT_BUFFERED for buffer_status, which tells the program
whether or not there is a serial buffer between it and the servo
controller. If the buffer is in place,
SC_SetServo doesn’t have to monitor CTS itself as this is done by the buffer.
/*
This program makes use of servocontroller_interface.c functions to talk to Mark Smorul's
servo controller chips. It will set the positions of all 9 servos controlled by the chip, based
on the position of a potentiometer connected to Pin_A0.
*/
#include <16F877.h>
#fuses HS,NOWDT,NOBROWNOUT,NOLVP
#use delay(clock = 20000000)
#include "servocontroller_interface.c"
#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
void main(){
int analog=128, i=0;
setup_port_a( RA0_ANALOG );
setup_adc( ADC_CLOCK_INTERNAL );
set_adc_channel( 0 );
set_tris_a(0b00010001); //PorA inputs on pin0, analog, nad A4, serial Rx.
set_tris_b(0);
set_tris_c(0);
while(true){
analog = read_adc();
if(analog > 250) analog = 250; //full sweep for the servo chips is 0-250, NOT 255.
for(i=0;i<9;i++){
SC_SetServo(i, analog, NOT_BUFFERED);
}
}
}