PIC servocontroller (in C)

May 2, 2009 at 4:38 pm 1 comment

In this article I’ll explain how to control a simple servo motor from a PIC18F4455 microcontroller. I am using a real cheap servo (an ES-30, see Conrad site), but it should be enough for hobby (read: robot) purposes.

Servo Controller

A DC motor is controlled using a DC voltage, but a servo motor requires some more work as it is controlled using pulses. By varying the width of a pulse you can set the angle the servo motor has to turn to. A servo motor can usually turn from -90 degrees to +90 degrees, but my ES-30 seems to be limited to -70 to +70 degrees or so.. 

Connections

The servo motor has three connections: ground (black), supply voltage (red) and the connection for the input pulses (usually yellow). We will connect the black wire to ground, the red one to our PIC supply voltage (5V) and the yellow wire to the D3 output (PORTDbits.RD3). So D3 will be the PIC output pin that controls the servo.

servo

Pulses

The servo expects a pulse every 20ms. The width of this pulse varies in the range 1.0 – 2.2ms, this range could be different with your specific servo so consult the datasheet (or just try some values)! A pulse width of 1.0ms sets the servo to the far left, a pulse of 2.2ms to the far right, and everything in between should map on an angle somewhere in between. 

Timing with the PIC uC

We are going to use interrupts to ensure our pulses appear every 20ms. A timer interrupt happens when the timer register overflows. The timer register (can be configured as 8bit or 16bit) is increased on every clock tick. If the prescaler is set it happens less than every clock tick. A spreadsheet with my calculations is available at http://spreadsheets.google.com/pub?key=p9YPS93eEHH3W7dx5fxQEMQ&output=xls, you can see the interrupt rate for various settings of the prescaler value. 

You can enable the interrupts like this:

OpenTimer0(TIMER_INT_ON & T0_SOURCE_INT & T0_16BIT & T0_PS_1_4);	//enable TMR0, 16bit, CLK0 src, low-to-high trans, prescale on, prescale 1:4 

So your servo initialization will look like this:

void startServo(void) {		//initialize TIMER0 + enable interrupts
	TRISD				=	0;	//D is output
	PORTD				=	0;
	INTCONbits.RBIF		=	0;
	INTCONbits.TMR0IE	=	0;
	RCONbits.IPEN		=	0;
	OpenTimer0(TIMER_INT_ON & T0_SOURCE_INT & T0_16BIT & T0_PS_1_4);	//enable TMR0, 16bit, CLK0 src, low-to-high trans, prescale on, prescale 1:4 
	INTCONbits.GIE 		= 	1;	//enable global interrupts
	INTCONbits.PEIE 	= 	1;	// enable peripheral interrupts
}

Define the interrupt function like this:

 

 

#pragma interruptlow timerFtie
void 
timerFtie(void)  {
	if (INTCONbits.TMR0IF) {	//interrupt timer 0
		INTCONbits.TMR0IF = 0;	//interrupt flag off
		servoInterrupt();
	}
}

 

Now we only need to define what will happen inside the servoInterrup() function, i.e. the function that is called every time TIMER0 overflows. We will set D3 to the correct value (1 or 0) depending on whether the pulse is starting or ending. We will also initialize TMR0H and TMR0L to values so that the next overflow appears when a pulse needs to start/stop. This is explained in the following note:

servo1

This results in the following code:

void servoInterrupt(void) {	//called on overflow of TIMER0
	//create pulse (begin or end of pulse)
	PORTDbits.RD3 = (pulseOn == 1);
	//set new TIMER0 value
	if (pulseOn == 1) {
		pulseOn = 0;
		TMR0H = 0xFF & (pulseOnLoad >> 8);
		TMR0L = 0xFF & pulseOnLoad ;
	} else {
		pulseOn = 1;
		TMR0H = 0xFF & (pulseOffLoad >> 8);
		TMR0L = 0xFF & pulseOffLoad;
	}
}

PulseOnLoad and PulseOffLoad are set according to the servo angle:

void setServoAngle(short angle) {	//set new servo value (called from external code)
	short loadTimer0BeginPulse 	= 0;
	short loadTimer0EndPulse 	= 0;
	short usPulse;
	short timerCount;	// [2727,6363] =~ [900,2100]*3
	servoAngle = angle;
	usPulse = MIN_PULSE_DURATION + angle*((MAX_PULSE_DURATION-MIN_PULSE_DURATION)/180);
	timerCount = usPulse * 3;	//0.33us = 1/3 us per tick
	pulseOnLoad = 65534 - timerCount+2;
	pulseOffLoad = 5538 + timerCount +2;
}

Code download

You can download the complete code here. This is an Eclipse project (see my other article on how to use Eclipse for PIC development), but you can import the .c and .h files into MPlab if you like. Note that this project assumes to be loaded onto the PIC with an USB bootloader (see this article for details)!

References

 

  • This site gives a very thorough introduction to using a servo with a PIC12F675, though all code is not freely available. My logic is also a bit different: I don’t wait inside the interrupt routine untill the end of the pulse but I set it low using a second interrupt. 
  • http://www.mcmanis.com/chuck/robotics/projects/servo.html gives another introduction to using the servo with a PIC, the code is in assembler.
  • The PIC datasheet, chapter about TIMER0. 
Advertisements

Entry filed under: Electronics, PIC18F. Tags: , , , , , .

Poor man’s virtualization (a rich experience!) Distance scanner using servomotor and distance sensor

1 Comment Add your own

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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

Trackback this post  |  Subscribe to the comments via RSS Feed


Feeds

Articles to be written…

Twitter – kr3l

my del.icio.us

RSS Google Reader Shared Stuff

  • An error has occurred; the feed is probably down. Try again later.

RSS Listening to..

  • An error has occurred; the feed is probably down. Try again later.

%d bloggers like this: