Servo Motor Interfacing with PIC18F4550

Overview of Servo Motor

This is the picture of Servo Motor
Servo Motor

 

A servo motor is an electric device used for precise control of angular rotation. It is used where precise control is required, like in the case of control of the robotic arm.

  • It consists of a suitable motor with control circuitry for precise position control of the motor shaft.
  • It is a closed-loop system.
  • The rotation angle of the servo motor is controlled by applying a PWM signal to it.
  • By varying the width of the PWM signal, we can change the rotation angle and direction of the motor.

For more information about Servo Motor and how to use it, refer to the topic Servo Motor in the sensors and modules section.

 

Generating PWM using PIC18F4550

SG90 servo has a practical duty cycle time for -90° to +90 rotation that is different from ideal.

At ~0.6ms (3% duty cycle) we get shaft position at -90° of its rotation.

At ~1.4ms (7% duty cycle) we get shaft position at 0° (neutral) of its rotation.

At ~2.4ms (12% duty cycle) we get shaft position at +90° of its rotation.

 

To control servo motor in between -90° to +90° rotation. We need to generate a PWM waveform of 50Hz with duty cycle variation from ~0.6ms to ~2.4ms. We can use a fast PWM mode of PIC18F4550 using Timer1.

For more information about generating PWM in PIC18F4550 and how to use it, refer to PIC18F4550 PWM.

 

Connection Diagram of Servo Motor to PIC18F4550

This is the picture of PIC18F4550 Interface with Servo Motor
PIC18F4550 Interface with Servo Motor

 

Servo Motor Example using PIC18F4550

Now let’s program PIC18F4550 to generate 50Hz PWM to control Servo Motor in an angle between -90° to +90° rotation.

Servo Motor Code for PIC18F4550 

/* 
 * Servo control using PIC
 * http://www.electronicwings.com
 */

#include <pic18f4550.h>
#include <stdio.h>
#include <math.h>
#include "Configuration_header_file.h"

#define MINTHR              8000
#define RESOLUTION          488

#define InternalOsc_8MHz    8000000
#define InternalOsc_4MHz    4000000
#define InternalOsc_2MHz    2000000
#define InternalOsc_1MHz    1000000
#define InternalOsc_500KHz  500000
#define InternalOsc_250KHz  250000
#define InternalOsc_125KHz  125000
#define InternalOsc_31KHz   31000

#define Timer2Prescale_1    1
#define Timer2Prescale_4    4
#define Timer2Prescale_16   16

void PWM_Init()            /* Initialize PWM */
{
    TRISCbits.TRISC2 = 0;  /* Set CCP1 pin as output for PWM out */
    CCP1CON = 0x0C;        /* Set PWM mode */
}

int setPeriodTo(unsigned long FPWM)/* Set period */
{
    int clockSelectBits, TimerPrescaleBits;
    int TimerPrescaleValue;
    float period;
    unsigned long FOSC, _resolution = RESOLUTION;

    if (FPWM < MINTHR)    {TimerPrescaleBits = 2; TimerPrescaleValue = Timer2Prescale_16;}
    else                  {TimerPrescaleBits = 0; TimerPrescaleValue = Timer2Prescale_1;}

    if (FPWM > _resolution)               {clockSelectBits = 7; FOSC = InternalOsc_8MHz;}
    else if (FPWM > (_resolution >>= 1))  {clockSelectBits = 6; FOSC = InternalOsc_4MHz;}
    else if (FPWM > (_resolution >>= 1))  {clockSelectBits = 5; FOSC = InternalOsc_2MHz;}
    else if (FPWM > (_resolution >>= 1))  {clockSelectBits = 4; FOSC = InternalOsc_1MHz;}
    else if (FPWM > (_resolution >>= 1))  {clockSelectBits = 3; FOSC = InternalOsc_500KHz;}
    else if (FPWM > (_resolution >>= 1))  {clockSelectBits = 2; FOSC = InternalOsc_250KHz;}
    else if (FPWM > (_resolution >>= 1))  {clockSelectBits = 1; FOSC = InternalOsc_125KHz;}
    else                                  {clockSelectBits = 0; FOSC = InternalOsc_31KHz;}

    period = ((float)FOSC / (4.0 * (float)TimerPrescaleValue * (float)FPWM)) - 1.0;
    period = round(period);

    OSCCON = ((clockSelectBits & 0x07) << 4) | 0x02;
    PR2 = (int)period;
    T2CON = TimerPrescaleBits;
    TMR2 = 0;
    T2CONbits.TMR2ON = 1;  /* Turn ON Timer2 */
    return (int)period;
}

void SetDutyCycleTo(float Duty_cycle, int Period)
{
    int PWM10BitValue;

    PWM10BitValue = 4.0 * ((float)Period + 1.0) * (Duty_cycle/100.0);
    CCPR1L = (PWM10BitValue >> 2);
    CCP1CON = ((PWM10BitValue & 0x03) << 4) | 0x0C;
}


void delay(unsigned int val)
{
     unsigned int i,j;
        for(i=0;i<val;i++)
            for(j=0;j<10;j++);
}

int main() 
{
    int Period;
    PWM_Init();                 /* Initialize PWM */
    Period = setPeriodTo(50);   /* 50Hz PWM frequency */
    /* Note that period step size will gradually increase with PWM frequency */
    while(1)
    {
        SetDutyCycleTo(3.0, Period);    /* 3% duty cycle */
        delay(1000);
        SetDutyCycleTo(7.0, Period);    /* 7% duty cycle */
        delay(1000);
        SetDutyCycleTo(12.0, Period);    /* 12% duty cycle */
        delay(1000);
    }
}

 

Change the Angle of Servo Motor with Potentiometer using PIC18F4550

Now let’s program PIC18F4550 to generate 50Hz PWM to control Servo Motor in an angle between -90° to +90° rotation using external potentiometer knob.

  • Here we are using ADC channel 0 of PIC18F4550 to read external potentiometer knob and according to the ADC value, we are varying duty cycle of PWM.
  • Refer ADC in PIC18F4550 for more information on ADC in PIC18F4550.

 

Connection Diagram of Servo Motor and Pot to PIC18F4550

This is the picture of PIC18F4550 Interface with Servo Motor
PIC18F4550 Interface with Servo Motor and POT

 

Code for Servo control using POT with PIC18F4550

/* 
 * Servo control using POT with PIC
 * http://www.electronicwings.com
 */

#include <pic18f4550.h>
#include <stdio.h>
#include <math.h>
#include "Configuration_header_file.h"
#include "ADC_Header_File.h"

#define MINTHR              8000
#define RESOLUTION          488

#define InternalOsc_8MHz    8000000
#define InternalOsc_4MHz    4000000
#define InternalOsc_2MHz    2000000
#define InternalOsc_1MHz    1000000
#define InternalOsc_500KHz  500000
#define InternalOsc_250KHz  250000
#define InternalOsc_125KHz  125000
#define InternalOsc_31KHz   31000

#define Timer2Prescale_1    1
#define Timer2Prescale_4    4
#define Timer2Prescale_16   16

void PWM_Init()             /* Initialize PWM */
{
    TRISCbits.TRISC2 = 0;   /* Set CCP1 pin as output for PWM out */
    CCP1CON = 0x0C;         /* Set PWM mode */
}

int setPeriodTo(unsigned long FPWM)/* Set period */
{
    int clockSelectBits, TimerPrescaleBits;
    int TimerPrescaleValue;
    float period;
    unsigned long FOSC, _resolution = RESOLUTION;

    if (FPWM < MINTHR)    {TimerPrescaleBits = 2; TimerPrescaleValue = Timer2Prescale_16;}
    else                  {TimerPrescaleBits = 0; TimerPrescaleValue = Timer2Prescale_1;}

    if (FPWM > _resolution)               {clockSelectBits = 7; FOSC = InternalOsc_8MHz;}
    else if (FPWM > (_resolution >>= 1))  {clockSelectBits = 6; FOSC = InternalOsc_4MHz;}
    else if (FPWM > (_resolution >>= 1))  {clockSelectBits = 5; FOSC = InternalOsc_2MHz;}
    else if (FPWM > (_resolution >>= 1))  {clockSelectBits = 4; FOSC = InternalOsc_1MHz;}
    else if (FPWM > (_resolution >>= 1))  {clockSelectBits = 3; FOSC = InternalOsc_500KHz;}
    else if (FPWM > (_resolution >>= 1))  {clockSelectBits = 2; FOSC = InternalOsc_250KHz;}
    else if (FPWM > (_resolution >>= 1))  {clockSelectBits = 1; FOSC = InternalOsc_125KHz;}
    else                                  {clockSelectBits = 0; FOSC = InternalOsc_31KHz;}

    period = ((float)FOSC / (4.0 * (float)TimerPrescaleValue * (float)FPWM)) - 1.0;
    period = round(period);

    OSCCON = ((clockSelectBits & 0x07) << 4) | 0x02;
    PR2 = (int)period;
    T2CON = TimerPrescaleBits;
    TMR2 = 0;
    T2CONbits.TMR2ON = 1;  /* Turn ON Timer2 */
    return (int)period;
}

void SetDutyCycleTo(float Duty_cycle, int Period)/* Set Duty cycle for given period */
{
    int PWM10BitValue;
    
    PWM10BitValue = 4.0 * ((float)Period + 1.0) * (Duty_cycle/100.0);
    CCPR1L = (PWM10BitValue >> 2);
    CCP1CON = ((PWM10BitValue & 0x03) << 4) | 0x0C;
}

int main() 
{
    float Duty_Scale;
    int Period;
    ADC_Init();
    PWM_Init();                 /* Initialize PWM */
    Period = setPeriodTo(50);   /* 50Hz PWM frequency */
    /* Note that period step size will gradually increase with PWM frequency */
    while(1)
    {
        /* Scale Duty Cycle in between 3.0-12.0 */
        Duty_Scale = (((float)(ADC_Read(0)/4.0)*9.0)/255.0) + 3.0;
        SetDutyCycleTo(Duty_Scale, Period);
    }
}

 

Video of Controlling Servo Motor using Potentiometer with PIC18F4550


Components Used

Servo Motor MG995
Servo Motor MG995
1
PIC18f4550
PIC18f4550
1
PICKit 4 MPLAB
PICKit 4 MPLAB
1

Downloads

PIC Interface with Servo Motor Project Files Download
Ad