Introduction: Use a PIC Microcontroller to Control a Hobby Servo
This instructable describes how to integrate hobby servos (the kind used in RC planes, cars, etc.) into your microcontroller projects.
Step 1: How Servos Are Different From Regular Motors
In a regular DC motor, the amount of torque the motor exerts on the shaft is proportional to the amount of current flowing through the motor. A simple way to control it is by varying the voltage across the motor; more voltage means more current which means the motor pushes harder against its load which means the shaft turns faster.
When using a servo, however, you don't control torque or velocity. Instead, you specify what angle you want the shaft at. In other words, you have positional control of the motor.
Inside a servo is a traditional DC motor, a potentiometer (variable resistor), and control circuitry. The potentiometer is connected to the motor such that when the motor shaft turns it also turns the potentiometer. The controller can then measure the voltage at the center pin of the potentiometer and get an indication of the shaft's position. The controller receives a signal (see next step) from the user that sets a desired position. The controller compares the desired position to the current position of the motor and uses that information to turn the motor in a direction that minimizes the error.
The way this works in practice is you specify the angle you want the shaft at using your PIC, the shaft turns to that position, and then holds there. The further it gets pushed away from that position, the harder it tries to turn back. Hobby servos are usually geared way down, so even a wimpy $15 or $20 one can hold its position reasonably well.
Step 2: The Control Signal
In addition to power (red wire, 5 volts works fine) and ground (black wire) connections, the servo accepts a control signal on the third wire (usually white or yellow). The signal is almost pulse width modulated, except that it doesn't have a fixed period.
It is composed of pulses of voltage, the duration of which determine the angle of the output shaft. The pulses can be from 0.9 ms to 2.1 ms long, 1.5 ms being the center position (in other words, pulse duration varies linearly with shaft angle). I don't remember whether 0.9 ms means all the way clockwise or all the way counter clockwise, but it's not a big deal to test our your project, see that the servo is turning the wrong way, and reverse it in your code. A new pulse must be sent to the servo once every 10 to 20 ms (50 to 100 Hz).
These timing/voltage values are for a typical servo. You should be able to find documentation for you specific servo from the manufacturer.
Step 3: The Circuit
This schematic is actually from something else, but it's ridiculously similar to the one I'll use for a quick demonstration.
The only change that needs to be made for this project is to replace the LED and its resistor with the wire for the control signal. So instead of connecting pin 3 to a resistor and then to an LED and then to ground, just connect pin 3 to the white/yellow wire from the servo. The ICD2 block represents the PIC programmer I used, substitute your own if you want. Also, don't forget to hook up the servo's power and ground connections.
The potentiometer (wiper connected to pin 2) is used here as a voltage divider. A voltage divider is simply two resistors in series with a contact in the middle at which to measure voltage. In this case, the potentiometer is both of the resistors and we adjust their relative proportions by adjusting the potentiometer. It will be used in this project as an analogue input to let us vary the duration of the control signal pulses.
The second image is a bigger version of the schematic. The smaller one just displays better at the size shown on the page.
Step 4: The Firmware
Attached is the firmware I used for this little demo. I used the free MPLAB IDE with the C18 compiler (free to students). Both are available to download from the Microchip website. The logic goes like this:
1. Delay for somewhere between 10 and 20 ms (in this case, 12)
2. If the button (pin 4) is pushed, measure the voltage of the potentiometer (pin 2)
3. Set the control signal high (5 v)
4. Delay for a length of time proportional to the voltage measured in step 2
5. Set the control signal low (ground)
6. Loop back to step 1
Maybe a little more information on the analog-to-digital conversion is in order. So we already know that the voltage at pin 2 is going to be somewhere between 0 and 5 volts because of the voltage divider. The PIC's A/D conversion works (conceptually) sort of like a reverse voltage divider. It reports the voltage as a proportion of its supply voltage. However, the proportion is scaled to be between 0 and 255 (which is 8 bits of information, the size of an unsigned character). In other words, the following equality is set up:
(pin 2 voltage) / 5v = POT_VALUE / 255
or:
POT_VALUE = 255 * ((pin 2 voltage) / 5v)
The PIC is actually doing a 10-bit A/D conversion, but 8 bits is plenty granular enough for our purposes, so the two least significant bits of the A/D conversion aren’t used in this code. Besides, the last couple of digits are probably measuring noise instead of the signal itself, so we're not missing all that much by ignoring them.
The delays are calculated based on the time it takes to complete one instruction on the PIC with a 4 MHz clock. I don't remember why, but somehow this works out to 1/6 us per instruction cycle. So if we delay for 5400 cycles, that's 5400 cycles * 1/6 us/cycle = 900 us = 0.9 ms. Another important piece is finding the constant of proportionality that scales POT_VALUE (on a range of 255 - 0 = 255) to our pulse width (on a range of 2.1 ms - 0.9 ms = 1200 us); or, to frame the question a different way: How many cycles should we delay for each count of POT_VALUE?
(1200 us * 6 cycles/us) / 255 counts = 28 cycles/count
Those of you following along with the code will notice that I actually delay 20 cycles per count, not 28. This is an experimentally determined value (I looked at the signal with an oscilloscope) that adjusts for all the factors that effect the timing, mainly the overhead from the for-loop on line 68.
Step 5: Improvements
Which brings me to my next point: this code sucks. You can do hardly any processing other than signaling to the servo, which is ridiculous because the vast majority of that time is spent locked up in a delay function. Also, it's hard to predict just how much overhead you have from other parts of the code that could be throwing the timing off; that constant of proportionality shouldn't have required an experiment to get it working correctly.
What this code should use instead of delays is the PIC's built in timers. Code for this might look something like:
OpenTimer0( TIMER_INT_OFF &
T0_SOURCE_INT & T0_16BIT &
T0_PS_1_2 ); // i think this makes a timer that raises a flag every 21.8 ms
while (1) {
while(!INTCONbits.TMR0IF){ // wait for timer to set flag
// do useful things
}
INTCONbits.TMR0IF=0; // reset timer flag
// send pulse to servo
}
I know I said there was supposed to be a pulse every 10 to 20 ms, but 21.8 ms is pretty close and hobby servos are generally very forgiving.
Step 6: Aside: Power Issues
I've run two or three small servos and a PIC off of a line-powered USB connection before, so there shouldn't be any issues using the same power supply for the servo and the logic. However, if your servo isn't as powerful as you know it should be or your PIC keeps randomly resetting or even breaking, these are indications that you have power issues. It might mean that your power supply can't source enough current or can't deal with the rate at which the current through the servo changes or that the servo is polluting the power lines with back EMF noise. Possible solutions include using a separate power supply for the PIC and for the servo (they'll have to have a common ground because that's the reference for the control signal), getting a beefier power supply, or making the decoupling capacitor (between pins 19 and 20) a little bigger.
I honestly don't know what best practice is in terms of decoupling and avoiding ground loops and such, but it's something I'm interested in. Can some one help me out here?
19 Comments
10 years ago on Step 4
do you have a version of using assembly codes? most of the codes were in C language... please?
14 years ago on Step 5
actually what this code which sucks should be using is the built in hardware PWM pins on most PICs. it will let you do whatever you want while the pwm runs in the background.
google pic hardware pwm
here's a good page on it:
http://www.winpicprog.co.uk/pic_tutorial8.htm
(you can usually use assembler in line with other higher level code if you're using PICBasic or PICC)
Reply 14 years ago on Introduction
No, you're wrong. The code in the example you cite is to control normal, DC motors - not steppers. Most people who know, will tell you that trying to control a servo from using the PICs hardware PWM is hard, as you can't get the delay times long enough for the 20mSec pulses (unless you run at a very low clock speed). As a consequence, experienced PIC programmers will code the servo control explicitly - not using the hardware PWM. The code shown doesn't suck - it's a nice, simple example that couples a pot. to a servo, so you can control the servo's position by rotating the pot.
Reply 14 years ago on Introduction
first of all I said it 'sucks' because the person that wrote the code said it 'sucks'. secondly, a servo motor is not the same as a stepper motor, completely different things. thirdly, you're probably right about hardware pwm incompatibility with servos, I've used hardware pwm only for regular dc motors and assumed it would work with servos. Sorry for the bad suggestion.
Reply 11 years ago on Step 5
sir , would you help me in constructing a servo contoller using pic 18f.. i'm just a beginner.
Reply 12 years ago on Introduction
Usually the pwm pins are not good for servos, they are usually set up to cover almost 0 to 100 % duty cycle and have poor resolution in the 1 to 2 % used by servos. To have code that does not suck you should use interrupt processing. Managing each servo in say 3 ms lets you easily fit 6 servos in 20 ms. And since the processing is by interrupts I would guess you use less than 10 % of the processing cycles. Examples of interrupt processing are not too hard to find on the net,
12 years ago on Introduction
um looking for elevator pic program. Do you have example program ?
12 years ago on Introduction
I think a PIC running at 4MHz clock speed executes one instruction cycle in 1 usec. This is consistent with what you see on your oscilloscope, with a delay of 20 cycles.
13 years ago on Step 4
DELAY.ASM?
14 years ago on Introduction
this is baisic not intended question for this specific project but my arduino duemilaove has a error for ANY program i put in there. it always finds a error..help????????????????????????????????????
15 years ago on Introduction
i usually put a 1000uF capacitor (electrolytic) across the power terminals, as this smoothes out the big spikes in the power supply. if you use assembly code to program the device, every 4 oscillations it advances one clock cycle, most instructions take one cycle but some instructions take cycles (e.g. goto or return). using this it approximately (because of the instructions take 2 cycles) takes a millionth of a second for each instruction and therefore you can calculate delays using instructions rather than the onboard timers. this way you can calculate the pulses accurately.
15 years ago on Introduction
Brilliant, this is fascinating thank you! However a schematic and a basic explanation your PIC/USB interface for controlling multiple servos would really harden my nipples. Uh, if you wanted to. \) _\) (o.0) ('")('")
15 years ago on Introduction
Awesome projects. I got my MS in Computer Science and have been working as Software Engineer. I'm going back to school in PHd in Computer Engineering @ UC Berkeley next year--got accepted. I'll specialize in robotics. I'll try to learn and purchase the products you have before I start my school and learn it. My goal is to create java libraries for microcontroller programming. Oh boy...It's going to be fun fun fun.
15 years ago on Introduction
I was wondering if either of these two microcontrollers would work or if anyone has any information on them, or what type they are... they are 40 pins each, heres the writing on each of them. 1: Front: C34451BE CHICONY VER-L 105-08049-250 9708 CTI (c) INTEL 1980 Back: TC7903.1 2: Front: CHICONY (r) ver-c 105-06868-031 6868A-10063 0212M E2NE3 Back: [none] any help would be appreciated, and pin outs/manuals would be great too.
16 years ago on Introduction
in this servo the potentiometer shaft is the shaft through the gear at the bottom of the parts picture!!!
16 years ago on Introduction
I'm afraid you're mistaken about where the potentiometer is inside the servo!!!! the thing you indicated was the output shaft!!!! the potentiometer is actually under one of the gears next to the motor!!!!
16 years ago
Excellent tutorial. You mention the commands should arrive between 50 to 100x per second. Is 100hz the fastest you can send the commands? I wanted to use it with a PIC PWM @ 8MHz (you can view the schematic at site under updates, see Firefly) www.blueroomelectronics.com
17 years ago on Step 6
re: best practices: 1. Don't count on a higher level language like 'C' to execute quickly. If you have a deterministic time-frame in the millisecond domain, use assembler (c'mon, it's easy and deterministic, and doesn't eat up 50K for a 'HELLO WORLD' demonstration). 2. Nothing less than a 0.1uF cap across the supply pins of every device, placed right next to it. 3. The USB specification defines a unit load as 100mA, so watch out for your power demands, especially the little power spikes. Per 2 (above) more capacitance will help average these out. 4. Great Instructable!
17 years ago on Step 6
Thanks for making this. Have dealth with servos before but you go pretty in depth. Understood most of it. Keep up the good work!