How To **
Addressable Serial Servo Controllers
article will show you how to use the tiny 8-pin PIC12C671 to create your own
serial servo controller chips.
A dedicated servo motor control IC
has several obvious advantages, and is considerably less expensive than
purchasing a large motor control board. This is especially true if you
only need a single servo controller. Why buy a board with 8 or more
motor capacity if you only need to control a single servo..?
For the rest of this article, I'll
just refer to them as servo-pods. This seemed like an appropriate name
at the time, and has no other real meaning. Servo-pods have several
advantages. Here's just a few.
Each PIC is totally dedicated
to the specific function of controlling a single servo motor.
Individual servo-pods can be
placed anywhere -- even attached to the servo motor itself.
Multiple servo-pods can have
the same address allowing control of several motors at "exactly"
the same time with a single data transmission.
There are no wasted servo
controller output ports.
Hundreds of servo motors can
be controlled on a single serial data-bus.
blab all-day-long about how handy these little doo-dads are, but that would
make for a pretty boring article. You'll see soon enough just how cool
these things really are once you start using them anyhow, so let's just dive
right on in to the good stuff...!
The code for the servo-pod is very
simple, and modifications to suit your specific needs shouldn't be much of a
problem. My motto is to keep it simple if at all possible, and always
leave plenty of room for expansion. This project does exactly that....
define osccal_1k 1 'Set osccal for OTP chips
pos var byte 'Servo position
servo1 var gpio.5 'Servo control output-pin
serpin var gpio.4 'Serial control input-pin
ID var byte 'ID number storage variable
ID = 5 'Assign unique ID to servo-pod
adcon1 = 7 'All pins digital I/O, not AD
trisio = %00010000 'gpio.4 = input
pos = 125 'Center servo on power-up
low servo1 'Setup for positive pulse
serin serpin,N9600,15,nxt,[ID],pos 'Get input
Now you're going to ask if this can be designed using another PIC micro since
you don't have any of the 8-pin PIC12C671s in your parts bin.
bet. Simply remove the define osccal_1k 1, and the adcon1 = 7
and you're in business. This project will work on any PIC that you can
program with PicBasic Pro.
Make sure to change the trisio to trisB
= %00010000 to setup portB [or use another port]. For PortB bit 4, simply do this:
trisb = %00010000. Redefine the port pins you want to use for the serial input
pin, and pulsout pin.
trisb = %00010000
servo1 var PortB.5 ' Servo control output-pin
serpin var PortB.4 ' Serial control input-pin
Note: If you're using a PIC that
has A/D pins, and you want to use one or all of these pins as digital I/O,
you'll need to setup the on-chip A/D configuration register to configure
them. This is the purpose of the adcon1 = 7 in my sample code.
The PIC12C671 powers-up with these pins in the default A/D mode.
A Look Under The Hood:
The first line include "modedefs.bas"
simply includes the mode definitions file allowing us to use the pre-defined mode
N2400 to specify the baud rate. This isn't 100% necessary, but it makes
our code much more readable. The alternate way to indicate the baud rate
would be to simply indicate the mode by using the mode number. In this
case N9600 would be mode 6. See the PBP manuals serin command
page for detailed information on the different serial modes.
The ID number hard-coded into each servo-pod
forces each PIC to respond only when it receives its assigned ID
number. Once the correct ID number is received, position data is then
accepted, and stored in the byte-variable pos.
The serial input line uses the built-in timeout
and label capabilities of PicBasic Pro to allow program execution to
continue if no serial data is received within a pre-set time frame. In
this case, the timeout has been set to 15mS as shown below.
This forces program execution to wait for 15
milliseconds. If no serial data is received within 15mS, then goto label
nxt. The nxt label holds a goto instruction that
directs program flow back to the main label which resumes sending
control pulses to the servo motor, and looking for more incoming serial data.
Using the timeout & label method we can use
the timeout for setting up the time between each control pulse, and to make
sure we aren't locked in the serial receiving routine waiting for incoming
serial data. We have pretty much killed two birds with one stone by
using this method.
Wait For The Servo ID #:
The next thing we need is to force the
servo-pod to respond only to positional data that is specifically meant for a
certain address by using the data qualifier capabilities of
PBP. The qualifier is the actual ID value we have assigned to
each servo-pod as shown below.
ID var byte 'ID number storage variable
ID = 5 'Assign unique ID to servo-pod
Line 1 assigns the byte-variable storage space
to hold the physical value or number for the servo-pod ID. Line 2 writes
the physical value of #5 into this RAM byte-variable storage
location. The last part of our serial input line will now wait until the
ID number 5 arrives before accepting servo positional data and placing
it into the pre-defined RAM storage location pos.
Here's a complete breakdown of how the serial
input line functions.
serin serpin,N9600,15,nxt,[ID],pos 'serial in on gpio.4
|Get serial input on serpin (GPIO.4).|
|Receive this data at 9600 baud -- inverted.|
|Wait 15mS for incoming data.|
|If no data is received within 15mS, jump to
|Wait for only 15mS to receive the ID.|
|ID received, store servo position
data in byte-variable pos.|
|Return to "Main" and send the pulse to the servo.|
PicBasic Pro allows us to create some very
powerful command lines with the unique capability of waiting for specific
data, and the timeout, jump to label features. This single line of code
handles many various requirements that are necessary to make the servo-pod
the internal control electronics of each servo motor expects to have position
data updates at intervals of approximately 60 times per-second (60Hz), the
timeout/label helps establish this timing without using additional pause
commands or other methods.
Notice from the scope trace below that we have
15mS delays between the servo pulses. This corresponds to our 15mS delay
time waiting for incoming serial data in the serial-input code line.
Above is a screen-capture of a
scope trace showing the servo control pulse from a servo-pod. The
frequency changes slightly depending on the actual position data, but stays
well within the requirements of the servo. Normally it will fluctuate
between 57-70Hz, (cycles-per-second)...
There are other methods to achieve better timing, but why
over-complicate a simple device if it works..?
The PULSOUT Command:
The pulsout command uses the
positional data received/stored in the byte-variable pos to generate
the servo control pulses. The PIC12C671 is limited to a maximum
oscillator speed of 4MHz, hence, the period of the generated pulses will be in
Pulsout portb.0, 100 ' Sends a
1mSec pulse out pin b.0. 100 x 0.000010 = 1mS.
must first establish the initial logic state of the output pin to be used with
pulsout to make the pulse a positive polarity. Since the pulsout command
will toggle the output pin twice, we need to establish the
required initial logic state prior to using the pulsout command for our
Setting the output-pin to logic 0,
we know that the pulsout command will toggle the pin from 0 to 1 then back to 0, and meet our requirements for a positive going pulse.
If we start with our output pin at logic 1, the outgoing pulse will be the compliment
of the pulse shown above in the scope trace - or inverted....
Servo Positional Data:
Now that we have the servo-pod
ready to use, we need to figure out how to send positional data to the
servo-pod that will let us position the servo motor. As stated
previously -- we know that the pulsout command is dependent on the oscillator
frequency, and the pulse duration will be in 10uS increments when used in
conjunction with an oscillator frequency of 4MHz. Since the PIC12C671
maximum operating frequency is 4MHz, we'll have to make due with this
To determine the values to send a
servo-pod -- we need to take the minimum and maximum position values we can
send a servo, and divide each one by our actual pulse resolution of 10uS as
The full left position for
a servo requires a pulse of 2mS or 0.002. For full right we need a pulse of
1mS or 0.001.
Data for full left
= 2mS/10uS = 0.002/0.000010 = 200.
Data for full right = 1mS/10uS
= 0.001/0.000010 = 100.
Sending a value of 100 for the
positional data will cause the servo motor to go to the full right position by
loading the pos variable with this value. A value of 200 will
position the servo to the extreme left. What will 150 do..?
No Two Servos Are Created
I have yet to see two servos that
are exactly the same. Position data for one servo, may not send
another similar servo to exactly the same position. You may have to send
position data from 20 to 225. This is really not important, since you
can send anything you like that will fit into a single byte-variable storage
space such as 0 to 255.
Test your servo motors
individually to establish the minimum & maximum rotation of each one. This
is the only way to make sure you're not over-driving some servo motors.
Trying to send a servo farther than it's supposed to go in any direction will
cause jittering, above average current consumption, and drain your batteries
The Test Program:
Now that you're ready to start
using the servo-pods, some test code to cycle the servo motors will be
necessary. Here's a quick & simple Basic Stamp routine that will
cycle a servo motor attached to a servo-pod hard-coded with the unique ID #5.
' Program to cycle the 8-pin PIC12C671 serial
' servo controller (servo-pod).
cont var byte
pos var byte
N9600 con 16468
ID con 5
pos = 20
for cont = 1 to 210 'Cycle right to left
pos = pos + 1 'From 20 to 230
for cont = 1 to 210 'Cycle left to right
pos = pos - 1 'From 230 to 20
serout 0,N9600,[ID,230] 'Quick left
serout 0,N9600,[ID,125] 'Center
serout 0,N9600,[ID,20] 'Quick right
goto loop 'Repeat forever
how the ID variable has been assigned a value of 5...? Try changing this
to any other number and see what happens. If the servo-pod doesn't
receive its own unique ID# first, any new positional data is simply ignored,
and the servo motor is held firmly in place by the last stored
position data in variable pos. You may also notice that I had to
alter the min/max values. Instead of 100 to 200, I had to use 20 to 230
for full rotation with my particular servos. You'll see how this changes
depending on the servo brands you're using...
This little 8-pin PIC micro makes
building these servo control gadgets a breeze, is a very powerful solution for addressable servo motor control, and only requires
a single I/O-pin to control as many servo motors as you can afford.
Theoretically, the only real
problem you'll have is the total number of devices you can drive with a single
Line driver ICs to boost the serial signal to accommodate more devices on the
Be sure to use a separate power
supply for the motor & servo-pod with both circuit grounds tied
together. To interface the servo-pod to the Basic Stamp, the 220 ohm
resistor will limit current between the input-pin of the servo-pod and Basic
Stamp I/O-pins to 22mA. For more protection, simply increase this value.
For interfacing directly to the PC
serial port, replace the resistor on GP.4 with a 22K. This will allow
direct interface of the servo-pod to your PC serial port.
TIP: Some internal
oscillators (especially for the 12C671) may not maintain an oscillator
frequency that's as stable as an external solution. If your servo pod acts
funny, you may want to use an external 4MHz ceramic resonator or crystal. This
will help insure you're getting the correct baud rate. If the baud rate is
slightly off - the servo pod will not function 100%. Normally the internal
oscillator works fine - but if your servo pod acts weird, give this a
unique feature of the servo-pod is the ability to have many pods with the same
address, and to control multiple servos at exactly the same time.
If you have a large animation that requires moving multiple servos to the
exact same position, at the exact same time, this is incredibly simple.
Program as many servo-pods with the same address as you need, and address each
one at the same time by sending its unique ID followed by the position to move
the servo to.
The ID is a byte variable -- so up
to 255 unique individual address numbers are available for an ID. A
number from 1 to 255 can be used for an ID, and you can have as many
servo-pods with the same ID as you like. You can have an incredible
number of serial servo-pods all sharing a single serial bus data line.
Until the next
project -- have fun, and thanks for stopping by....:o]
to purchase PicBasic Compilers - and hardware.
to learn more about the PicBasic compiler.
to return to the PicBasic projects page.