PIC16F877A servo circuit, RB0 control signal, RD0 RD1 buttons, Proteus servo simulation

Servo Motor Control with PIC Microcontroller

1.5M Views
40
700
60
25
60
PCBWay

Hello friends! Today we will control a servo motor with the PIC16F877A and observe its movement in Proteus ISIS. We will begin with the three servo connections, understand the command waveform, and then write a small program that selects positions using two pushbuttons.

In the previous tutorials, we explored DC motor direction control and stepper motor sequencing. A positional hobby servo is different because the motor, gearbox, position sensor, and controller are already assembled together. Our PIC requests a position rather than switching the motor windings itself.

We will keep the original PIC and button arrangement, but use a repeated control pulse with a sensible frame period. Along the way, we will separate what the simulated angle display tells us from what a physical servo must be calibrated to do. The example is deliberately small enough that you can trace every input and output.

PIC16F877A servo circuit, RB0 control signal, RD0 RD1 buttons, Proteus servo simulation
Figure: Original schematic showing RB0 control and active-low buttons; choose physical crystal capacitors from the component specifications.

How a Positional Servo Works

A conventional positional hobby servo contains a motor connected through a reduction gearbox to an output shaft. A position sensor measures that shaft's position. Internal electronics compare the requested position with the measured position and drive the motor to reduce the difference. This closed-loop arrangement is the reason we can request a position with one signal wire.

The signal does not provide motor power. The other two connections supply energy to the servo electronics and motor. A three-wire cable therefore normally contains supply, ground, and command, but their physical order and colours must be checked against the actual product. Do not identify the signal wire only by assuming that it is in the middle.

Also distinguish a positional servo from a continuous-rotation servo. A positional unit tries to reach an angle within its mechanical travel. A continuous-rotation unit generally interprets the pulse as a speed and direction request, with a neutral command near its stopped condition. The same firmware can therefore produce very different motion when the wrong type is selected.

Three motor-control approaches
Motor arrangementCommand supplied by the controllerPosition information
DC motor with an H-bridgeDirection and drive levelRequires an additional position sensor for closed-loop positioning
Stepper motor with a suitable driverWinding sequence or step and direction signalsOften inferred from commanded steps unless feedback is added
Positional hobby servoRepeated pulse-width position commandInternal feedback controls the shaft; ordinary three-wire control does not report the measured angle to the PIC

Understand the Servo Command Pulse

A common starting point for a hobby servo is a pulse repeated about every 20 ms, with a neutral pulse near 1.5 ms. The width of the high portion carries the position request. The exact acceptable range, direction of motion, neutral point, and travel depend on the servo. Pololu's measurements of the servo interface illustrate why a universal pulse-to-angle conversion should not be assumed.

For our initial simulation, we will select 1.3 ms, 1.5 ms, and 1.7 ms commands. These give three nearby requests without immediately using wide endpoint pulses. On physical hardware, even this starting range must be checked against the servo specification and the attached mechanism. First movement should be observed with the linkage unloaded.

The frame frequency is f = 1 / T. For T = 0.020 s, this becomes f = 50 Hz. A 1.5 ms high pulse in that frame has duty cycle D = 1.5 / 20 × 100 = 7.5%. Duty cycle is useful when measuring the waveform, but pulse width is the command quantity we intend to control.

Timing used by the demonstration
Requested stateHigh timeNominal low timeTotal programmed delay
Position A1.3 ms18.7 ms20 ms
Centre request1.5 ms18.5 ms20 ms
Position B1.7 ms18.3 ms20 ms

Instruction execution adds a little overhead around these delays. We will measure the generated waveform rather than claiming that a blocking software loop is an exact hardware pulse generator. The example has no interrupt-driven background tasks that could unexpectedly stretch the high pulse.

Prepare the PIC Circuit in Proteus ISIS

Add a PIC16F877A, a positional servo model, a crystal, reset and button resistors, two pushbuttons, and the required supply connections. The original component-selection screenshot is retained below. Component names and selector categories can vary between Proteus versions, so identify the part and its model rather than relying only on its position in the list.

PIC16F877A selection, positional servo model, Proteus component list, button control circuit
Figure: Original component picker for the PIC servo demonstration.

The original design uses a 16 MHz oscillator and two buttons on RD0 and RD1. We will use RB0 for the command signal because that is the pin connected to the servo in the original schematic. In the 40-pin DIP package, RB0 is pin 33, RD0 is pin 19, and RD1 is pin 20.

PIC16F877A servo circuit, RB0 control signal, RD0 RD1 buttons, Proteus servo simulation
Figure: Original schematic showing RB0 control and active-low buttons; choose physical crystal capacitors from the component specifications.

Treat this older screenshot as a connection reference. Its crystal capacitors are labelled 1 nF, which should not be copied as a general-purpose physical oscillator design. Select the crystal capacitors from the crystal load specification and the PIC oscillator guidance. Likewise, the supply symbols in the drawing do not specify the current capability of a real power supply.

Connections for the revised example
ItemConnectionReason
Servo signalPIC RB0Receives the position-command pulse
Servo supplySupply rated for the selected servoPowers its internal motor and controller
Servo groundCommon ground with the PICProvides the command's voltage reference
Button ARD0 to ground when pressed, with a 10 kΩ pull-upActive-low request for position A
Button BRD1 to ground when pressed, with a 10 kΩ pull-upActive-low request for position B
MCLRAppropriate reset pull-up; 10 kΩ in this exampleKeeps the PIC out of reset during normal operation
Oscillator16 MHz crystal and suitable load capacitorsEstablishes the instruction timing used by the compiler

Connect both PIC supply pins and both ground pins in a physical 40-pin device: VDD is on pins 11 and 32, and VSS on pins 12 and 31. Add local decoupling near these pairs. Proteus may hide power pins behind named nets; check those nets instead of assuming the missing pins mean that power connections are unnecessary.

The Microchip PIC16F87XA datasheet provides the authoritative pinout, oscillator configuration, and port behaviour. Configure PORTD as ordinary digital I/O rather than as a parallel slave port. The firmware below clears PSPMODE through TRISE and makes RD0 and RD1 inputs.

Write the mikroC Program

Create a mikroC project for PIC16F877A with a 16 MHz clock. Select the appropriate HS oscillator configuration for this crystal arrangement and disable the watchdog for this simple delay-based demonstration. Set the simulated processor frequency to 16 MHz too. A different compiler clock setting changes the pulse widths even when the delay numbers in the source remain unchanged.

With neither button pressed, the program retains the last requested position. Button A selects the shorter pulse; button B selects the longer pulse. Pressing both selects the centre request. Two consecutive matching button samples are required before accepting a change, providing a basic debounce step for this demonstration.

unsigned char position = 1;
unsigned char previousButtons = 3;
unsigned char buttons;

void main() {
    PORTB = 0;
    TRISB = 0;
    TRISE = 0x07;  // PSPMODE off
    TRISD = 0xFF;

    while (1) {
        buttons = PORTD & 0x03;
        if (buttons == previousButtons) {
            if (buttons == 2) position = 0; // RD0 pressed
            if (buttons == 1) position = 2; // RD1 pressed
            if (buttons == 0) position = 1; // both pressed
        }
        previousButtons = buttons;

        PORTB.F0 = 1;
        if (position == 0) Delay_us(1300);
        else if (position == 2) Delay_us(1700);
        else Delay_us(1500);
        PORTB.F0 = 0;

        if (position == 0) Delay_us(18700);
        else if (position == 2) Delay_us(18300);
        else Delay_us(18500);
    }
}

The source uses mikroC's device-register syntax and delay routines. It is not an Arduino sketch or an XC8 project. Build it using the matching compiler, select the generated HEX file in the PIC's Program File property, and restart the simulation after loading the new firmware.

Read the button masks carefully: 2 means binary 10, so RD1 is high and RD0 is low. Because the switches connect to ground, the low input is the pressed button. The pull-ups make the unpressed state deterministic instead of leaving the input floating.

The debouncing rule samples approximately once per frame. A stable change is therefore accepted after roughly one to two frame intervals, depending on when the button changes. This is sufficient for understanding the example, but a product should choose a debounce policy that matches its switches and responsiveness requirements.

Measure the Signal Before Interpreting the Angle

  1. Run the simulation with neither button pressed and connect the oscilloscope to RB0.
  2. Check for a high pulse near 1.5 ms and a repetition period near 20 ms.
  3. Press button A and confirm that the high pulse becomes approximately 1.3 ms.
  4. Press button B and check for approximately 1.7 ms.
  5. Press both buttons and confirm the return to the centre command.
  6. Release the buttons and verify that the last command continues to repeat.

The original simulation screenshot displays an angle such as +90 degrees. That is a result of the model's configuration and its reference convention; it does not prove that a particular physical servo reaches the same angle for the same pulse. Check pulse timing first, then examine how the selected model maps that timing into movement.

A repeated position request does not mean the shaft instantly reaches that position. Travel takes time and depends on the servo's speed, load, supply, and controller. A simulation may simplify those effects. Avoid interpreting the timing of a screen animation as a measured mechanical specification.

Calibrate Pulse Width to Your Required Movement

Suppose a particular servo and linkage have been measured to move from 20 degrees at 1.2 ms to 140 degrees at 1.8 ms. Within that measured region, a first linear approximation is t = tA + (θ - θA)(tB - tA)/(θB - θA). These are illustrative calibration points, not universal servo endpoints.

For a requested 80 degrees, the calculation gives t = 1.2 + (80 - 20)(1.8 - 1.2)/(140 - 20) = 1.5 ms. If the measured response is nonlinear, use more calibration points instead of assuming a straight line describes the entire travel. Mechanical linkage geometry can also make output movement nonlinear even when the servo shaft response is approximately linear.

Clamp software requests to the calibrated range. A requested angle beyond the mechanism's travel should not silently become a wider pulse. If you replace the servo or change the horn position, repeat the calibration because the mechanical reference may have changed.

Power, Timing, and Practical Improvements

A servo can draw short current peaks when starting, changing direction, or resisting a load. Supply it from a source rated for its operating voltage and expected current, including the manufacturer's stall-current information. Keep motor-current paths away from sensitive PIC supply wiring, use suitable local energy storage, and maintain a clear ground reference for the command.

A capacitor cannot compensate indefinitely for an undersized supply. The simple relationship ΔV = IΔt/C shows why: if a capacitor alone supplied 0.5 A for 1 ms, limiting its voltage drop to 0.25 V would require C = 0.5 × 0.001 / 0.25 = 0.002 F, or 2000 µF, ignoring resistance and other effects. The supply must still provide the average demand and recharge it.

The blocking loop is easy to follow but spends nearly all its time waiting. For a larger project, schedule the rising and falling edges with a timer or compare peripheral. That lets the main program process communications and sensors without inserting unpredictable work inside the high pulse. Check the available peripheral's timing range before assuming a standard high-frequency PWM setup can generate a 20 ms frame directly.

Troubleshooting and Review

Plan a Timer-Based Version

We can estimate the timing resources before writing an interrupt-driven version. With a 16 MHz oscillator, the PIC's internal instruction clock is 4 MHz, so one instruction-clock period is 0.25 µs. If Timer1 uses that internal clock with a 1:8 prescaler, one timer count represents 2 µs. This calculation concerns the timer tick; it does not mean that the entire program executes one useful operation every tick.

A 1.5 ms interval then requires 1500 / 2 = 750 timer counts. A 20 ms frame requires 20000 / 2 = 10000 counts. Both fit within a 16-bit timer's counting range. For an overflow-based approach, an ideal preload for the high interval would be 65536 - 750 = 64786. The remaining 18.5 ms corresponds to 9250 counts and an ideal preload of 56286.

Those values are a design calculation, not a complete interrupt routine. A real implementation must account for the time between overflow, interrupt entry, output switching, and timer reload. Repeatedly adding that latency can lengthen the frame and vary the command. A hardware compare event or a schedule based on a continuously running timebase can reduce dependence on software execution time.

When a button changes the requested width, copy that request into the active timing state at a defined frame boundary. Otherwise, the rising half of one command and the falling half of another can accidentally form a pulse you never intended. Keep interrupt work short and leave text formatting, display updates, and communications processing outside the time-critical path.

Choose What the Application Should Report

A user interface should distinguish requested position from measured position. The ordinary three-wire connection in this tutorial sends a command but returns no angle measurement. A label reading “Position B requested” accurately describes the available information. A label claiming “Position B reached” would require additional evidence, such as a feedback output, encoder, or limit sensor appropriate to the mechanism.

This distinction becomes useful when a linkage can jam or the supply can dip. The PIC may continue producing excellent pulses while the shaft fails to move. Add feedback when the application needs confirmation of movement, and define how the controller should respond when the requested and observed states disagree.

Common servo-control problems
SymptomUseful check
No response to buttonsCheck RD0 and RD1 pull-ups, common ground, and the selected HEX file.
Wrong pulse widthsCompare the compiler clock with the Proteus processor frequency.
Servo jitters while other code runsMeasure high-time variation and move pulse generation to a predictable timer schedule.
PIC resets when the servo movesMeasure the PIC supply during motion and review current paths and decoupling.
Angle differs from the screenshotCheck the model mapping or calibrate the actual servo and linkage.
Servo rotates continuouslyConfirm that it is a positional model rather than a continuous-rotation type.

This tutorial now provides a complete learning path from command timing to a button-controlled simulation. The useful result is understanding what RB0 must generate and how the motor assembly interprets it. Simulation checks the control logic; physical measurements establish real travel, current demand, and mechanical behaviour.

Frequently Asked Questions

Does a 1.5 ms pulse always mean exactly 90 degrees?

No. It is commonly used as a neutral request, but the angle depends on the servo and its installed mechanical reference. Use measured calibration points for meaningful angle commands.

Can I stop sending pulses after the servo reaches its position?

Do not assume that this preserves holding behaviour. Response to a missing signal varies between servo designs. Keep sending valid commands when the application requires continued position control, and check the selected servo's documented behaviour.

Why not connect the servo motor through an L293D here?

The hobby servo already contains a motor driver and feedback controller. Its external signal wire expects a position command. An H-bridge is appropriate when you are controlling a separate bare motor.

Can I use the same idea with Arduino?

Yes. See the related Arduino servo control tutorial. The command waveform remains the central idea, even though the programming tools and pin configuration differ.

You can also compare the 555 timer servo-control circuit or continue to relay simulation in Proteus ISIS. In each case, begin by identifying the signal the device actually expects.


Comments

7

Join the conversation

Reply1

I was suggested this blog by my cousin. I am not sure whether this post
is written by him as no one else know such detailed about my difficulty.
You are amazing! Thanks!

Reply2

wonderul post, very informative. I ponder why the opposite specialists of this sector don't understand this.
You must proceed your writing. I'm confident, you've a great readers' base already!

Reply3

I NEED VEDIO TUTOR FOR PROTEUS 8.1
BEFORE I USED 7.2 SP2
U TUBE IS NORKING ON MY COMLAPTOP
IS THER ANY TUTOR IN OTHER THEN UUBE?
tHANKYOU
a m tAHIR

Reply4

Note:
The code and the Proteus Simulation has already been emailed to all the subscribed members. If you are new here then subscribe to our newsletter and it will be emailed to you as well.

!! Did not received anything by email !!

1 reply
Reply5

Bom, montei o projeto e na hora de testar observei que o servo obedecia apenas ao comando de B1 e em seguida não obedecia a mais comandos!
fiz uma pequena alteração no código e o sistema passou a responder aos comandos de B2 assim como o s comandos de B1!
Sendo assim, o servo motor gira para +90.0 e também para -90.0
veja como ficou o código depois de alterado.

#define M1 PORTB.F7
#define M2 PORTB.F6
#define B1 PORTD.F0
#define B2 PORTD.F1

int a;

void main() {
TRISB=0;
TRISD=0xFF;
PORTB = 0;
a = 1;
while(1){
if((B1 == 0) && (a==1)){
PortB=0xFF;
delay_ms(100);
PortB=0x00;
delay_ms(1);
PortB=0xFF;
a=0;
}
if((B2 == 0) && (a == 0 )){
PortB=0x00;
delay_ms(100);
PortB=0xFF;
delay_ms(1);
PortB=0x00;
a=1;
}

}
}