Servo Motor Interfacing with PIC18F4550
Introduction
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
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+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