PIC Based Serial Port Servo Controller
By Ashley Roll
Ever wanted to use Servos in a robotics project? This project uses a PIC microcontroller and a handful of other parts to drive four servos and four digital outputs from the serial port on your computer. It can also be used as a development platform for your other PIC projects.
Introduction
I have been working on a robotics project recently and decided that I needed to use servo motors from a remote control aircraft to control parts of the mechanism, unfortunately I had no simple way to control them so I designed this small circuit.
It uses a PIC16F84 microcontroller to generate the PCM control signals for up to four servos. The PIC is controlled by a host computer with a standard RS232 serial port (COM1 etc). The commands allow the controlling program to specify the position and neutral point of each of the servos independently and also control four digital outputs.
The commands are simple one or two byte sequences that are easily constructed and transmitted to the PIC.
The Circuit
Figure 1 (pdf) shows the complete schematic for the servo controller, there is not much to it as all the hard work is done in the PIC software.
Power for the circuit is derived from a 9V dc plug pack and regulated down to 5V by the 7805. The centre connector of the plug is positive but diode D1 provides reverse polarity protection just in case. C9 bypasses the supply and helps stabilise the regulator while C6, the 47uF capacitor, helps filter the power from the plug pack.
The interface to the host computer's serial port begins at P1, a female DB9 connector. Pins 1, 4 and 6 (CD, DTR and DSR) are connected together to ensure the host computer will accept serial data from the PIC at any time. The signal ground (Pin 5) is connected to the circuit ground and the remaining signals are sent to the MAX232.
The MAX232 is a single power rail RS232 interface circuit. It takes care of converting the RS232 signal voltages to CMOS logic levels that the PIC can use. It has two receivers that convert RS232 levels to CMOS levels and two transmitters that convert CMOS to RS232 levels. It uses an on chip dc-dc converter and the five 10uF capacitors C1, C2, C3, C4 and C5 to generate +10 volts and –10 volts. C3 is also used to smooth the 5 volt power rail.
The RX line (data going to the host computer) from the serial port is connected to the first transmitter's output, which is driven by the 'SerialOut' signal from the PIC's RA0 pin. The TX line (data coming from the host) is connected to the second receiver, which then drives the 'SerialIn' signal connected to RA3 on the PIC. Similarly the incoming RTS signal is routed via the first receiver to RA1 and the CTS signal is sent from RA2 to the second transmitter and on to the host's serial port.
All of Port B on the PIC is dedicated to driving the servos and digital outputs. RB0 to RB3 drive the digital outputs 0 to 3 respectively, RB4 to RB7 drive servo outputs 0 to 3. You may note that the connectors for the digital outputs and the servos are wired identically; this is so you can add more servos by simply changing the PIC code. It would also be possible to convert some of the pins to inputs that can report status of other sensors to the host computer.
The 4 MHz crystal Y1 and the two 22pF ceramic capacitors are connected to the PIC to form its clock oscillator. The clock signal is divided by four internally to derive the PIC's instruction clock resulting in a 1ns execution time for each instruction. This is important for the timing of both the serial communications and the servo control pulses.
D2, an LED is connected to RA4 on the PIC and to the power rail via R1 a 1K current limiting resistor. The PIC software lights the LED whenever a data byte is received from the host. It is lit when RA4 is driven low. The 100K resistor R2 apparently shorting it out is very important! RA4 is an 'Open Drain' output meaning that is can only pull the pin low and cannot make the pin positive, it relies on external components to do this. This is important when modifying the value of individual bits on the Port as the PIC will first read the state of all the pins, do the modification on the one it is interested in and write back state for all the pins.
If R2 weren't there the value of the pin would be 'floating' when the pin is supposed to be high (LED off) so when the PIC reads the value of the pin it could read low which when written back would turn on the LED. A long and fruitless night was spent trying to debug the PIC code only to read about this in the PIC datasheet hours later. Argh!! The moral of the story – RTFM (Read The #@%#&$ Manual!)
The remaining connections to the PIC are C10, a supply bypass capacitor and tying the MCLR (reset) pin of the PIC high, so it actually runs.
Construction
The circuit is quite straightforward and there are not many components. You could construct the circuit on 'veroboard' or similar without too much difficulty if you keep the crystal and its 22pF capacitors close each other and the PIC. However the PCB design accompanying this article makes it a breeze.
First inspect the PCB for any missing or broken tracks and while your trusty soldering iron warms up, identify all the components.
It is best to start with the lowest components and work your way up. I suggest that you use IC sockets, especially for the PIC. If your not going to use IC sockets, don't fit the ICs as they will need to be removed for testing. If you intend reprogramming the PIC I'd also suggest that you invest in a machine pin IC socket for it. You can then 'zip' tie the PIC to the machine pin socket and then place it into the standard IC socket. This allows you to easily remove and refit the PIC without fear of bending pins.
Refer to Figure 2 during construction to aid you in positioning the components. Start with the two resistors, and use the excess lead from one for the link on the board above the PIC. Next fit the two 22pF ceramic capacitors to the right of the PIC.
Insert the two IC sockets now as it is going to be fiddly to get the pins to line up with the holes later when the other components are in place. Remember, don't insert the ICs yet as you'll need to remove them before testing.
Next, fit the two 0.1uF MKT capacitors, one below the PIC the other below the 7805 regulator. These are not polarised so may be inserted either way around.
Now place the 47uF electrolytic capacitor and the five 10uF electrolytic capacitors taking care that they are inserted the correct way around.
Break up the lengths header pins into bunches of three. You can use a pair of side cutters if you like, but I just pressed by thumbnail into the notch between the pins where I wanted to break them. You will need eight lots of three, which you can then insert and solder.
I found that it is best to start from one end; insert one and solder it before inserting the next so you can get the tip of your soldering iron to the pins.
Next insert the LED, remember that the flat side on the case or the short leg is the cathode and goes towards the PIC.
You can now insert the crystal, which is not polarised, so can be insert either way around. Then insert the 7805 regulator. The metal tab should be towards the edge of the board.
Lastly solder in the power socket and the DB9 connector. I found that a piece of tape to hold the power socket in place while I turned over the board and soldered it worked well. If you're using a 90 degree DB9 connecter it should have holes to allow it to be bolted to the PCB or clips that penetrate the PCB. It is a good idea to bolt it in before you solder its pins as it stops you putting pressure on the joint and ripping up the pads and tracks. It also stops it falling out when you flip the board over to solder it. If you have metal clips, solder them in.
Done, take a break. Come back and inspect all your joints, especially the ones on the MAX232 and the DB9 connector where a track goes between the pads to make sure you didn't accidentally short anything out.
Testing
You will need a 9V dc power supply with a 2.5mm power plug, centre positive and a serial cable to connect the controller to your computer. You will also need a serial communications (sometimes called a 'Terminal') program. I used 'HyperTerminal' as it comes with Windows.
First, without the serial cable or ICs plugged in, power up the board and measure the voltage across pins 5 and 14 on the PIC and across pins 15 and 16 on the MAX232. They should both be around 5 volts.
Remove the power and insert the MAX232, pin 1 faces the DB9 connector. Connect the power again and test the voltage between ground (pin 15) and pin 2 of the MAX232. You should read close to 10 volts. Now check between ground (pin 15) and pin 6 and you should get –10 volts. If not, check you inserted all the 10uF capacitors the correct way. If all is well, we know the dc-dc converter is working in the MAX232 so we can move on to testing the serial port.
Without the PIC installed, put a jumper wire between pins 3 and 5 of the PIC socket, the LED should light. If not, check you installed it the correct way around. Remove the jumper.
Now put a jumper between pins 1 and 5 and between pins 2 and 17 of the PIC socket. This loops the serial data back to the host and sets the CTS signal active (low) so the host is allowed to send data. Plug in the serial cable to the computer and the DB9 connector and apply power to the controller board again.
Run your terminal program and open the serial port that you connected to the controller. Set the serial port speed to 2400 baud, eight data bits, and one stop bit, no parity and 'Hardware' flow control.
You should now be able to type into the terminal program and see the characters appear as you type. Try moving the link from pins 1 to 5 and placing it from 1 to 14 and type a few characters. You shouldn't get any characters displayed as the serial port is buffering them because the CTS line is inactive. If you move the link back to pins 1 to 5 again what you typed should appear. This means that your serial port, the MAX232 and the hardware flow control are working properly.
If moving the link from pins 1 to 5 doesn't seem to stop you seeing the characters you type, you probably haven't set the port to use hardware flow control. Sometimes it is labelled CTS/RTS flow control.
Remove the power, unplug the serial cable from the controller and remove the links in the PIC socket then insert the PIC. Again pin 1 faces the DB9 connector.
Connect a servo to the servo 0 output, remembering that the white wire on the servo lead is the control signal and goes closest to the PIC. Connect the serial cable and open your serial port in your terminal program again.
When you connect the power, the PIC will send a message to the computer, which should come up in the terminal window. It should send "PIC Servo Controller" then "Reset". You may also notice the servo twitches a little when you first connect the power. You should be able to turn the servo with your fingers and it won't resist or return to a point. This means it is not getting any position signals.
Lets send it some commands while we're here. Type a '0' (zero), this is actually the command to enable the output of servo 0, so the servo should move with a satisfying whirr! It should also resist your attempts to move it, but don't use too much force.
Now type an 'at' sign (@) and the servo should be deactivated so you can move it again. You should notice the LED light and will remain on for about a quarter of a second after the each character. You won't get anything displayed in the terminal program however as the PIC is not echoing back the characters. It is in fact trying to read them as commands.
Close your terminal program, and power off the controller.
Congratulations it works!
Host Program
A host program can be written in any language that can access the serial port. It must configure the port for 2400 baud 8 data bits, no parity and 1 stop bit with hardware flow control. It should then send a reset command to reset the controller to bring everything back to a known state.
The reset state is where all digital outputs are off, all servo outputs are disabled and all servo offsets and positions are set to 128.
From there the program builds commands and sends them to the serial port to control the hardware, probably under control of some other input (a joystick for instance).
Refer to the side bar for details of the commands the servo controller will respond to.
[Side Panel – Servo Controller Commands]
Servo Controller Commands
To control the servos and outputs we need to send commands to the PIC. Some of the commands are single byte commands; some however require two bytes.
The first byte always contains the command and the channel to which the command applies. We will call this the 'Command' byte. The second byte when needed will contain data for the command; we will call this the 'Data' byte and is used when we need to set the servo offset or position for example.
The command byte is split into two nibbles (4 bits), the upper one defines the command to execute and the lower defines the channel (which servo or digital output) is to be affected.
| Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
| Command | Channel Select | ||||||
Currently, the software recognises only channels zero, one, two and three. The following are the commands and their numerical value. To build command bytes add the command value to the channel number.
| Hex | Decimal | Data Byte | Description |
|---|---|---|---|
| 00 | 0 | Yes | Reset the controller. All outputs off, all servos disabled and all servos position and offset values are set to 128 (midrange). The data byte following this command MUST be zero. To ensure that a reset command it actually executed, send three consecutive zero bytes. |
| 10 | 16 | Yes | Set the servo output 'Position' value for the servo specified in the channel nibble. The data byte contains the new position value between 0 and 255. |
| 20 | 32 | Yes | Set the servo output 'Offset' value for the servo specified in the channel nibble. The data byte contains the new offset value between 0 and 255. |
| 30 | 48 | No | Enable Servo Output. Start generating the servo control pulse for the servo specified in the channel nibble. |
| 40 | 64 | No | Disable Servo Output. Stop generating the servo control pulse for the servo specified in the channel nibble. |
| 50 | 80 | No | Set Digital Output On (High). Set the digital output specified in the channel nibble high (5 volts). |
| 60 | 96 | No | Set Digital Output Off (Low). Set the digital output specified in the channel nibble low (0 volts). |
Special consideration needs to be given to the reset command as the state of the command parser in the PIC is not known when it is issued we actually need to be a little tricky. The parser could be waiting for a command in which case it would work, or it could be waiting for a data byte in which case it would get a little confused.
Therefore we will make the reset command a series of three zeros and have the parser on the PIC detect that there is an extra byte and ignore it.
When the servo controller receives a reset command it will turn everything off, set all the servos position and offset values to midrange and send the string "Reset" to the serial port with a 'carriage return' and a 'new line' character appended.
Example Commands
Enable Servo 0: 48 + 0 = 48 (ASCII '0')Enable Servo 2: 48 + 2 = 50 (ASCII '2')
Disable Servo 0: 64 + 0 = 64 (ASCII '@')
Disable Servo 2: 64 + 2 = 66 (ASCII 'B')
Set Servo 1 Position to 180: 16 + 1 = 17 then 180
Parts List
| Resistors (Metal Film 1% or 5%, 0.25 W) | |
|---|---|
| R1 | 1K |
| R2 | 100K |
| Capacitors | |
| C1, C2, C3, C4, C5 | 10uF 25 Volt Electrolytic |
| C6 | 47uF 25 Volt Electrolytic |
| C7, C8 | 22pF Ceramic |
| C9, C10 | 0.1uF MKT Polyester |
| Semiconductors | |
| U1 | MAX232 |
| U2 | 7805 voltage regulator |
| U3 | PIC16F84 programmed with SERVOCTL.HEX |
| D1 | IN4004 |
| D2 | 5mm Red LED |
| Miscellaneous | |
| Y1 | 4 MHz Crystal |
| 8 | 3 pin PCB Header Pins. Buy a longer length and bread them up. |
| 1 | 16 Pin IC Socket |
| 1 | 18 Pin IC Socket |
| 1 | DB9 90 degree female connector |
| 1 | 2.5mm PCB Mount DC Power socket |
| 3mm bolts, nuts and PCB standoffs for mounting | |