Servo Motor Interfacing with PIC18F4550

Servo Motor Interfacing with PIC18F4550

Introduction

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 the topic Servo Motor in the sensors and modules section.

Generating PWM using PIC18F4550

SG90 servo has 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 PWM waveform of 50Hz with duty cycle variation from ~0.6ms to ~2.4ms. We can use fast PWM mode of PIC18F4550 using Timer1.
For more information about generating PWM in PIC18F4550 and how to use it, refer PIC18F4550 PWM.

Interfacing Diagram

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

Program for Servo

/* 
 * 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);
    }
}

Example
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.
PIC18F4550 Interface with Servo Motor
PIC18F4550 Interface with Servo Motor+POT

Program for Servo using POT

/* 
 * 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);
    }
}

No comments:

Post a Comment