Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC 18 and Servo Motor Control HELP

Status
Not open for further replies.

badkitty

New Member
Greetings Programming Gurus

My project consists of 2 servo motors which will receive position commands serially from PC. I have decided to use PWM of CCP1 and ECCP1 of PIC18f458.

I plan to build a "Pan and Tilt" and not use gears at all. I would be using servo motors with -90 to +90 degree angles. At the bottom would be Servo 1 rotating in X plane, and on top of that would be the Servo 2 rotating in Y plane.

My algorithm as yet is (Please comment or improve)

My main routine would be to monitor serially received data. In parralell I would run the CCP and ECCP modules. Timer flag of CCP and ECCP is reset using interrupt.

My query is that lets say servo point -90 at 1ms and +90 at 2ms. If I produce a 20ms square wave, I would have very limited positions between 1ms and 2ms. So should I produce a square wave of 2ms and then varry its 256 PR2 values to produce unique positions or stick with 20ms or is there a better algo, instead of using PWM, should I use Capture mode ? (I have no experience with servo motors)

PLEASE HELP ME ALL THOSE WITH KNOWLEDGE OF SERVOs
 
You can't get good enough resolution with the PWM modules. I described a better way in the thread, Junebug Servo C18 code

Have a good read and come back with any questions. Note, the IRQ code has the wrong vector (should be 0x18 not 0x08).

Mike.
 
Last edited:
hi Mike.
Your link seems to have a problem.?
 
Hi Eric,

Looks like I copied the wrong link. Fixed now.

Thanks,

Mike.
 
Thanks a lot Mike ! I have got a very good idea of your technique and I am going to extend it for two Servos.

I have got some basic questions regarding C programming


volatile int ServoPos; //used to hold the servo position

#pragma code low_vector=0x18 //setup the ISR vector
void low_interrupt (){
_asm GOTO ccp1_isr _endasm //jump to interrupt handler
}
#pragma code

#pragma interruptlow ccp1_isr //the ISR
void ccp1_isr(){

In the above code why do we go to x08 address for ISR and please explain how interrupt occur and how the flow of program changes, I mean why do we have an ASM function here. Also the use of #pragma code so many times. I know these are the basics but help explain :)
 
Due to the fact that Microcontrollers aren't standard, they use #pragmas for any none standard C stuff. The ASM is really just a bodge to make it use the interrupt vector and then jump to the actual routine. The interrupt happens when Timer1 matches CCPR1L/H, it's called the "Special event trigger" and you'll find an explanation in the PWM section of the data sheet.

Mike.
 
Hey Mike, Ive modified the code please check it. I cant varry the duty cycle even if I change the value in ServoPos :(

//using CCP to produce PWM and interrupt
#include <p18f458.h>
#pragma config WDT = OFF, LVP = OFF
#define ServoTris TRISBbits.TRISB3
#define ServoPin LATBbits.LATB3

void ccp1_isr();
volatile int ServoPos;


#pragma code low_vector= 0x08
void low_interrupt()
{
_asm
GOTO ccp1_isr
_endasm
}

#pragma code
#pragma interruptlow ccp1_isr
void ccp1_isr()
{
if (ServoPin == 1)
{
ServoPin = 0;
CCPR1L = 20000-ServoPos;
}
else
{
ServoPin = 1;
CCPR1L = ServoPos;
}
PIR1bits.CCP1IF = 0;
}
#pragma code
void main(void)
{
// OSCCON = 0x00; //OSC = 8MHZ
ServoTris = 0;
ServoPin = 0;
CCP1CON = 0b00001011;
T1CON = 0b10010001;
ServoPos = 1000;
CCPR1L = ServoPos;
PIE1bits.CCP1IE = 1;
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;
INTCON2bits.RBPU = 0;
while(1)
{
while(!ServoPin);
while(ServoPin);
}

}


Further more the Compiler gives and error for "CCPR1 = 1000" and i know Ive made a mistake moving 1000 into CCPR1L ? ... Why is this ?
 
Last edited:
Which compiler are you using? In C18, CCPR1 (no L) is defined as a int (16 bit) at address CCPR1L. What you may have to do is,
Code:
    CCPR1L=ServoPos&255;
    CCPR1H=ServoPos/256;
//and
    CCPR1L = (20000-ServoPos)&255;
    CCPR1H = (20000-ServoPos)/256;
Or, your compiler may have a way to define CCPR1 at the correct address.

Mike.
 
Last edited:
Hey mike . I figured it out. Instead of #include <p18f458> I used #include<p18f4580> and it did it. And I had linker file of p18f4580 so I think that was the error. And Yeah I have done it for a single servo, also included 2 Push Buttons, 1 for increasing duty cycle and 1 for decreasing. I am simulating it on Proteus ! Thanks

I was modifying my algo for 2 servos. I have decided to put a sequence of producing pulses. Like when at the start of 20ms period, I produce first pulse for servo 1 and then servo 2, this repeats every time after 20ms ... first load CCPR with ServoPos1 and make ServoPin1 = 1, when flag raised, make ServoPin1 = 0, load CCPR with ServoPos2 and make ServoPin2 = 1. When flag raised make ServoPin2 = 0, and load CCPR = 20000 - ServoPos1 - ServoPos2 . When Flag is raised again, the whole process repeats. I will add Sequence variable to keep track of which servo to update

What do you say about this ?

////////////////////UPDATE:///////////////

Mike I ran the above algo and it works great. I also introduced two more buttons for motor 2 and now its running perfect. I have got 1 query though

when I load 1500 in servopos1 and servopos2 , the angles of the motor are -0.09 and -0.036. Is that sort of error expected because of programming in high level language ? or should they be exactly 0 ?

I am posting the code :

//using CCP to produce PWM and interrupt
Code:
#include <p18f4580.h>
#pragma config WDT = OFF, LVP = OFF
#define	Servo1Tris	TRISBbits.TRISB2
#define	ServoPin1	LATBbits.LATB2
#define	Servo2Tris	TRISBbits.TRISB3
#define ServoPin2	LATBbits.LATB3	
#define	IncPin1Tris	TRISBbits.TRISB4
#define DecPin1Tris	TRISBbits.TRISB5
#define	IncPin2Tris	TRISBbits.TRISB6
#define DecPin2Tris	TRISBbits.TRISB7
#define	IncPin1		PORTBbits.RB4
#define	DecPin1		PORTBbits.RB5
#define	IncPin2		PORTBbits.RB6
#define	DecPin2		PORTBbits.RB7
		
void	ccp1_isr();
unsigned int ServoPos2;
unsigned int ServoPos1;
unsigned int seq;

#pragma code low_vector= 0x08
void	low_interrupt()
{
	_asm
	GOTO	ccp1_isr
	_endasm
}

#pragma code
#pragma	interruptlow ccp1_isr
void	ccp1_isr()
{
	if(seq == 0)
	{
		if	(ServoPin1 == 1)
			{
				ServoPin1 = 0;
				CCPR1 = ServoPos2;
				ServoPin2 = 1;
				seq = 1;
			}	
		else
			{
				ServoPin1 = 1;
				CCPR1 = ServoPos1;
			}
	}
	else
	{
		if (ServoPin2 == 1)
			{
				ServoPin2 = 0;
				CCPR1 = (20000 - (ServoPos1 + ServoPos2));
				seq = 0;
			}
	}
	PIR1bits.CCP1IF = 0;
}

#pragma code
void main(void)
{
	seq =0;
	IncPin1Tris = 1;
	DecPin2Tris = 1;
	IncPin2Tris = 1;
	DecPin2Tris = 1;	
	Servo1Tris = 0;
	ServoPin1 = 0;
	Servo2Tris = 0;
	ServoPin2 = 0;
	IncPin1 = 1;
	DecPin1 = 1;
	IncPin2 = 1;
	DecPin2 = 1;
//
	CCP1CON = 0b00001011;			//Activate CCP1 mode with interrupt trigger
	T1CON = 0b10010001;				//Set timer 1 on with 2 prescaler
	ServoPos1 = 1500;				
	ServoPos2 = 1500;
	CCPR1 = ServoPos1;				//Compare register value loaded
	PIE1bits.CCP1IE = 1;			//Peripheral interrupts enable
	INTCONbits.PEIE = 1;			
	INTCONbits.GIE = 1;
	//INTCON2bits.RBPU = 0;
	//while(!ServoPin1);
	//while(ServoPos1);	
	while(1)
	{	
		if(IncPin1 == 0)
		{	
			while(IncPin1 == 0);
			ServoPos1 = ServoPos1+50;
		}
		if(DecPin1 == 0)
		{
			while(DecPin1 == 0);
			ServoPos1 = ServoPos1-50;
		}

		if(IncPin2 == 0)
		{	
			while(IncPin2 == 0);
			ServoPos2 = ServoPos2+50;
		}
		if(DecPin2 == 0)
		{
			while(DecPin2 == 0);
			ServoPos2 = ServoPos2-50;
		}
	
	}
	
}


The output
**broken link removed**
 
Last edited:
Mike is it necessary to connect external pull ups to ports. Are weak pull ups sufficient. And why in the code you enabled weak pull ups when if we make any port as output, the pull ups are turned off ?

When I connect external Pull ups to the output of 1 of the servos, the servo makes a deflection to 18 degree then returns back to -0.09.

Further more, the Pins on which I am producing these pulses are always blue in Proteus rather they should be toggling. Is it some simulation error or does it have to do with wrong initialization of the Port B

Also please clear why are we using LAT instead of PORT ? It does not make difference if I use PORT !
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top