Servo motor control example for the PIC18F4620

This is a simple servo motor control example for the PIC18F4620 microcontroller.

  • The servo is controlled by the PWM signal from CCP1 (pin 17 on the PIC).
  • The PWM period is 20ms and the pulse width varies between 1ms and 2ms.
  • A button which is assumed to be connected to RD0 (pin 19 on the PIC) makes the voltage on RD0 go high when it’s pressed (it’s low otherwise).
  • Each time the button is pressed, the servo angle increased by 15 degress, until it reaches 180 degrees, at which point it resets to 0 degrees.

Here’s the code (NB I don’t have a PIC18F4620 here just now, so I still have to verify that this works correctly – in the meantime use with caution!):

// PIC18F4620 servo control example
// Written by Ted Burke
// Last update 17-11-2012
// A button is assumed to be connected to RD0.
// When the button is pressed the voltage on RD0
// should go high. It should be low otherwise.
// The servo control signal is output from CCP1
// which is pin 17. Most servos have three wires:
// positive and negative power supply and one
// control signal input. The control signal input
// should be connected to CCP1. Note that the
// 0V supply to the PIC and servo also need to be
// connected.

#include <p18f4620.h>
#include <delays.h>

#pragma config OSC = INTIO67
#pragma config MCLRE = OFF
#pragma config WDT = OFF
#pragma config LVP = OFF

void main(void)
	// Set Fosc = 8MHz, which gives Tcy = 0.5us
	OSCCON = 0b01110000;

	// Set up PWM (section 15 of the PIC18F4620 Data Sheet)
	CCP1CON = 0b00001100;   // PWM on CCP1
	TRISC = 0b11111011;     // CCP1 (pin 17) as output
	T2CON = 0b10010110;     // Enable TMR2 with prescale=16, postscale=10    
	PR2 = 255;   // period = (PR2+1) * prescale * postscale * Tcy = 20.48ms
	CCPR1L = 18; // pulse width = CCPR1L * prescale * postscale * Tcy = 1.52ms

	// Every time you press the button, the servo angle
	// will increase by 15 degrees until you get to 180
	// at which point it will reset to 0.
		// Wait for button press
		while (PORTDbits.RD0 == 0);
		Delay10KTCYx(2); // 10ms debounce delay

		// Change servo angle
		if (CCPR1L < 24) CCPR1L += 1; // Increment angle
		else CCPR1L = 12; // Set angle back to zero

		// Wait for button to be released
		while (PORTDbits.RD0 == 1);
		Delay10KTCYx(2); // 10ms debounce delay		
This entry was posted in PIC, Uncategorized and tagged , , , , , , , , , . Bookmark the permalink.

13 Responses to Servo motor control example for the PIC18F4620

  1. Richard Hayes says:

    As is didnt build for me
    RD0 should be PORTDbits.RD0.
    Next stage now get pulse out

  2. sharon says:

    hello Ted!! thank you for this list of code😀 it does work on my board! however, the servo motor keeps turning when the voltage (3.3V) is plugged in. and the push buttons can’t control the rotation of the servo motor.
    could u explain to me what’s wrong?

    but none the less, THANK YOU SO MUCH FOR YOUR HELP!! ((;

  3. batchloaf says:

    Hi Sharon,
    Are you using a development board? If so, please tell me exactly which one.
    Also, most importantly, precisely which servo motor are you using? (i.e. which make and model?) Can you give me a link to the datasheet, so that I can check if this program is configured correctly for it?

    • sharon says:

      hi Ted!
      i am using Hitec HS85MG (servo motor)

      information about the motor :
      Analog “Mighty Micro” Servo
      Aluminium Final Gear with one Ball Bearing
      Size (mm): 29x13x30
      Weight (g): 22
      Output Torque (Ncm 4,8V/6V): 29 / 34
      Output Torque (kgcm 4,8V/6V): 3,0 / 3,5
      Speed (sec 60° 4,8V/6V): 0,16 / 0,14
      Voltage (V): 4.8 – 6V

      and i am not using a a development board, but with PIC18f4620.
      hmm, maybe the pulse control should be programmed after the “while” command for the buttons?

      hope i am not confusing you…
      thank you!

      • batchloaf says:

        That servo motor should work fine with this program. However, from the datasheet it doesn’t look like this motor works at 3.3V. I would recommend powering the whole circuit at 5V, which may resolve your problem.

        If that doesn’t solve your problem, here are some other suggestions:

        Have you checked the pulse signal from CCP1 with an oscilloscope to check that you’re getting one pulse every 20ms and that the pulse width is between 1ms and 2ms? That’s worth checking.

        Also, is it possible that your button is connected wrong? If you connect RD0 directly to 0V (i.e. hold it constantly logic low) does the servo stop moving?

        When the button is hooked up normally (i.e. not held low), a resistor (e.g. 10k) should be connected between RD0 and 0v to “pull it down”. A “push-to-make” button should be connected between RD0 and 5V so that when it is pressed, RD0 gets short-circuited (pulled up) to 5V, making it logic high (it will be read as a 1 rather than as a 0).

        Finally, you can try increasing the debounce delays inside the while loop to the following:

        Delay10KTCYx(20); // 100ms debounce delay

  4. Georgina says:

    Hi Ted,

    Thanks for this piece of code, helped a lot with my hobby circuit. I’m fairly weak at coding so this helped a ton with configuring the PIC for PWM operation, which the manual isn’t that helpful with.

    A quick question if you don’t mind is if I was trying to control the speed of a DC motor, how would I go about changing the Pulse Width to vary the duty cycle (say from 5% to 95% based on different inputs)? Is it done by changing the value of CCPR1L register, and if so what do the values correspond to? In your example above you have it initally set to 18.

    Thanks again!

    • batchloaf says:

      Hi Georgina,

      Sorry for the delay in responding – I was out of the office for a couple of days and didn’t have my Windows laptop and PIC stuff. Now that I look at the example above again, I think there are some errors in the comments that describe the period and pulse width calculations. I’ll have to fix those! Anyway, I’ve started writing a better, simpler and more up-to-date explanation of PWM on the PIC18F4620, which you can read here:

      It’s not quite finished yet, but hopefully you’ll find the answers you’re looking for there. The C code provided in the new example is for Microchip’s newer XC8 C compiler. Let me know if that’s a problem (i.e. if you’re using version of MPLAB older than MPLABX). There are some small changes to the header files and delay functions that might be required if you’re compiling with C18 (the older C compiler).


  5. Racedriver says:

    which compiler is used for this example?

    • batchloaf says:

      I wrote this code for Microchip’s C18 compiler, but according to what I said at the end of the post, I didn’t actually try it out on a physical chip, so I suppose it’s possible that it may contain one or two small problems (hopefully not). Since I posted this, I have switched to using Microchip’s newer XC8 compiler for PIC18F4620 development. The code will probably compile fine with XC8 too – maybe just change the first included header file from “p18f4620.h” to “xc.h”.


  6. Noravid says:

    HI, I must say its an awesome tutorial and simple enough to understand for the beginners like me, A small video and schematic would would have been really great & helpfull for better understanding.

  7. Wanz says:

    Hey Ted!
    Nice code! i’m going to test on my board with it soon but i have a question. Is it possible to integrate the servo motor and the dc motor to use the same pwm pulse?


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s