//===================================================================//
//                         SERVOTST.C                                //
//===================================================================//
//	By:		Dale Botkin <dale@botkin.org>					    //
//	Date:	3/15/2000                                             //
//-------------------------------------------------------------------//
// Simple C program for the PIC MPU, pretty much any variety.  Will  //
// exercise up to four R/C servos connected to pins RB0:3.  I tested //
// this with three, but it's pretty obvious you need to power the    //
// servos separately from the PIC to cut down on glitches.  Very     //
// easy to modify for up to 8 servos, though every servo you add     //
// sucks up another 10% of available CPU time.  Since all we're      //
// doing is exercising them, though, using 80% of CPU time running   //
// the pulses is no biggie.                                          //
//-------------------------------------------------------------------//
// Copyright (C)2000, Dale Botkin, all rights reserved.  You may use //
// this code freely for personal, non-commercial use.  In the highly //
// unlikely event that you want to use this code for commercial use, //
// email me to make arrangements.                                    //
//-------------------------------------------------------------------//


#include "16C711.H"					// Change for your CPU
#include "int16CXX.h"

#pragma config WDTE=off, FOSC=XT, BODEN=on
#pragma config |= 0x3FB0  			// Code protect off

char	servo_pin;					// Variable for port pins
char	servo[4];						// Servo position values
char	servo_num;					// Pointer to current servo position value
char	position;						// Working servo position value
char	window;						// 2ms - position value
char	boogies;						// Interrupt counter
char	loops;						// Loop counter
char	current;						// same as servo_num, but outside of interrupt
bit	direction;					// direction of travel flag

#pragma	origin 4

		// With .1ms resolution, we can control the servo in 10% steps:
		// 10 = 0 degrees (or 100% backward motion)		1.0ms
		// 11 = 18 degrees (or 80% backward motion)		1.1ms
		// 12 = 36 degrees (or 60% backward motion)		1.2ms
		// 13 = 54 degrees (or 40% backward motion)		1.3ms
		// 14 = 72 degrees (or 20% backward motion)		1.4ms
		// 15 = 90 degrees (or stopped)				1.5ms
		// 16 = 108 degrees (or 20% forward motion)		1.6ms
		// 17 = 126 degrees (or 40% forward motion)		1.7ms
		// 18 = 144 degrees (or 60% forward motion)		1.8ms
		// 19 = 162 degrees (or 80% forward motion)		1.9ms
		// 20 = 180 degrees (or 100% forward motion)		2.0ms
		// So our pulse routine needs to run 1ms + x tenths.
		// Next version will (maybe) use hundredths, from 100 to 200.

interrupt scan_servos() {
	PORTA = 2;									// indicate where we are
	T0IF = 0;										// Clear TMR0 interrupt flag
	int_save_registers								// Save status & W reg
	char fsr; fsr = FSR;							// Save FSR
	OPTION = 8;									// No TMR0 prescaler
	servo_pin = 1;									// Set up for first servo 
	for(servo_num = 0; servo_num <=3; servo_num++) {
		position = servo[servo_num];					// get pulse width value
		window = 30 - position;						// Figure out remainder of 2ms window
		PORTB = servo_pin;							// Turn on servo pin
	   	TMR0 = 0;									// Clear TMR0
		do {
			TMR0 = 11;							// Adjust for execution time
			while ( TMR0 < 100 );					// wait 100 uS
		} while( --position > 0 );					// for as many as we need
		PORTB = 0;								// Turn off all servo pins
			// Now we have to burn up the remainder of the 2ms total pulse
			// window...
		if(window > 0) {
			do {
					TMR0 = 11;					// Adjust for execution time
				while ( TMR0 < 100 );				// wait 100 uS
			} while(--window > 0);					// for as many as we need
		}
			// And set us up for the next servo in line...
		servo_pin = servo_pin*2;						// Set up for next servo in line
	}
		
		// Now we set up for an 8ms delay until the ext interrupt.
		// We just spent 8ms pulsing the servos, so in another 12 we
		// need to do it again.
	int_restore_registers							// Retrieve the registers
	FSR = fsr;									// Restore FSR
	OPTION = 5;									// 256 prescaler for TMR0
	TMR0 = 187;									// Set up for 12ms interrupt
	boogies++;									// Increment boogie counter
	PORTA = 0;
}
	

void main(){
	T0CS = 0;										// T0 on instruction cycle
	T0SE = 1;										// rising edge
	clearRAM();									// start with clear RAM
	PORTA = 0;						
	PORTB = 0;									// All outputs off
	TRISA = 0;
	TRISB = 0;									// Port B is all output
	OPTION = 5;									// Prescaler set to 256
	direction = 0;										
	
		// Set all servo position registers to the 90 degree mid point.
	for(current = 0; current < 4; current++) {
		servo[current] = 15;
	} 
	TMR0 = 131;									// Set up timer for 8ms
	T0IE = 1;										// Enable TMR0 interrupt
	GIE = 1;										// Duh.

		// The boogie counter gets updated every 20ms, so 50 boogies
		// equals one second.  Tip of the hat to bogomips.

		// First wait 2 seconds to let us see how the servos do at deadband
	while(boogies < 100);							// Loop for 2 seconds

		// Now do 60 loops of 10% steps once a second, full motion
		// range from 10 to 20 and back.  Should take 1 minute.
	direction = 1;
	loops = 0;
	PORTA = 1;
	do {
		boogies = 0;								// clear boogie counter
		while( boogies < 10 );						// Wait 1/5 second	
		for(current=0; current < 4; current++) {
			if(servo[current] == 25) direction = 0;
			if(servo[current] == 5) direction = 1;
			if(direction == 0) servo[current]--;
			if(direction == 1) servo[current]++;
		}
	} while(++loops < 60);

		// Now recenter the servos for 2 seconds 
	for(current=0; current < 4; current++) {
		servo[current] = 15;
	}		
	boogies = 0;
	while(boogies < 100);

		// Now we finish up with 10 full-travel swings, 10 to 20,
		// with a 2-second delay in between. 

	loops = 0;
	PORTA = 0xD;
	do {
		boogies = 0;
		while(boogies < 100);						// 2 second delay
		for(current=0; current < 4; current++) {
			if(servo[current] == 25) direction = 0;
			else if(servo[current] == 5) direction = 1;
			else if(servo[current] > 5 && servo[current] < 25) direction = 0;
			if(direction == 0) servo[current] = 5;
			if(direction == 1) servo[current] = 25;
		}
	} while(++loops < 10);

		// And loop forever.
	main();
}