Serial Servo Control using a 16F84

Another solution for controlling up to six servos with a PIC. This solution may not be for everyone, but it meets most of my needs - it doesn't cost an arm and a leg, and it works using anything that can transmit serial data and monitor a CTS signal.

The controller is a 16F84 (not important as long as the PIC has 8 pins free and interrupt capability) that uses two lines for serial control. One for serial input and one for DTR/CTS (pin 8 is CTS on a 9 pin connector if you are hooking to a PC). It will take back-to-back updates and doesn't have jitter. The prototype seems robust enough - I successfully used it to drive a 5 axis robot arm smoothly with both a Stamp and a 200 Mhz pentium. The nice part is that it uses a 4 Mhz clock and works reliably at 9600 bps. The less attractive part is that it isn't clean to add more controllers (16F84s) that use the same serial lines. This controller would probably work with 8 servos (the timing would be a bit tight with 8) - but I limited it to a comfortable six servos.

I used CCS C mixed with assembler to code it.


Main function:

1. Set servo positions using an array - init array on start up.

2. Start servo timing (can be varied depending on port A configuration to either 90 or 180 degrees) for all servos.

3. Assert DTR

4. Enable interrupts

5. Pause 6 ms - external terminals can now send data

6. Bring DTR low. Due to overhead of processing all that came before, the total delay is about 6 ms and then it's time to refresh the servos. This would be the limiting factor for adding more servos. You need to have a little time for the terminal to talk to the PIC. I've decreased the interrupt availability time to 1 MS and it worked with a fast PC, but that is too short for a Stamp to detect it and then fire off a serial output. Six ms seems to be very stable with the Stamp. You could sacrifice some refresh interval time making the refresh interval longer than 25 ms when being serviced - but then you are hoping you have a good servo that won't jitter.

7. Disable interrupts

8. goto 2

Interrupt Function (if to here, then external interrupt occurred, meaning serial input):

Use one of the many examples of timed reception of serial data (the square one books have examples, has examples, and so does a bunch of others. If the input byte(s) doesn't (don't) show within two ms, then time out and return from interrupt. This will stop the controller from hanging when noise or a bad transmission hose the inbound data.

1. Get input from terminal - my code expects three bytes: Synch, servo number, position.

2. Bring DTR low - this stops any more transmissions by a cooperative terminal

3. do sanity check on the two data bytes to be sure they are in range expected.

4. put them in the array that is used to time the servos.

5. return from interrupt

In practice and with back to back updates, the actual time between servo refresh grows to about 25 ms. But servos are very forgiving and it doesn't seem to bother their functioning. With no updates coming in, the time between refreshes is about 15 ms.

If my lame psuedo code doesn't make much sense, then click on the following link and look at my initial C code.