/****************************************************************************
Light_seek_&_disp
Derbot seeks light. PWM applied. Speed is dependent on light difference 
(front to back), so Derbot comes to a halt when light difference is minimal.
Indicative value of light reading on each LDR is displayed. 
Microswitches used for bump detection.
Files c018i.o and p18f242.lib are included by the Linker Script. 
TJW 12.11.05											Tested 6.12.05
****************************************************************************/
//Clock is 4MHz. 

	#pragma config OSC = HS, OSCS = OFF
	#pragma config PWRT = ON, BOR = OFF
	#pragma config WDT = OFF
	#pragma config STVR = ON, LVP = OFF

	#include <p18F242.h>
	#include <adc.h>
	#include <timers.h>
	#include <pwm.h>
	#include <i2c.h>
	#include <delays.h>	
	#include <stdlib.h>			//for itoa function
	#include <string.h>			//for strlen function
	#define slave_addr1 0xA4 	//Adress of Derbot Hand Controller I2C node.	

//User-defined function prototypes
	void leftmot_fwd (void);
	void rtmot_fwd (void);
	void leftmot_rev (void);
	void rtmot_rev (void);
	void rev_left (void);
	void rev_rt (void);
	void fwd_left (void);
	void fwd_rt (void);
	void rotate_rt (void);
	void rotate_left (void);
	void diagnostic (void);
	void disp_int (int op_int);			//Converts an integer to string, and sends to lcd via I2C 
	void send_space (char space_no);	//Sends space to lcd via I2C

//Declare Variables
	char loop_cntr;		//counts iterations of super-loop
	int ldr_rt;   	    //right ldr value
	int ldr_left;   	//left ldr value
	int ldr_rear;   	//rear ldr value
	int ldr_ave;	  	//computed average of front ldrs
	int ldr_diff;  	    //difference between front ldrs, left - right
	int ldr_fwd;	  	//ave fwd speed required
	int fwd_dr_left;	//offset added to left PWM for fwd motion
	int fwd_dr_rt;	  	//offset added to right PWM for fwd motion

/**********************************************************************
Main Program
**********************************************************************/
	void main (void)
{
//Initialise
	TRISA = 0b00001011;	// ADC channels set as inputs
	TRISB = 0b11111000;
	TRISC = 0b10011000; //Port C bits. I2C bits are both set as ip
	OpenTimer2 (TIMER_INT_OFF & T2_PS_1_1 & T2_POST_1_1);
	OpenPWM1 (0xFF);    //Enable PWM1 and set period
	OpenPWM2 (0xFF);    //Enable PWM2 and set period (same as for PWM1)
	loop_cntr = 0;

//Port A bits 0,1,3 set for analog input, internal reference, right justify result
	OpenADC(ADC_FOSC_8 & ADC_RIGHT_JUST & ADC_3ANA_0REF,ADC_CH0 & ADC_INT_OFF); 
//60Switch all outputs off
	PORTA = PORTB = PORTC = 0;
//enable motors at idle speed
	CCPR1L = CCPR2L = 0x80; 
	PORTAbits.RA5 = 1;	
	PORTAbits.RA2 = 1;
//Initialise I2C
	OpenI2C (MASTER, SLEW_OFF);
	SSPADD = 0x07; 				//set up 125kHz baud rate
	diagnostic();				//call diagnostic function
/**************************************************
Program from here is structured as "super-loop" 
**************************************************/
while (1)
  {
	loop_cntr++;
//check first for collisions 
	if (PORTBbits.RB4 == 0) //Test right uswitch
	rev_left ();
	if (PORTBbits.RB5 == 0) //Test left uswitch
	rev_rt ();
//Read and store all ldr values
	//left channel 
	SetChanADC (ADC_CH0);	
	Delay10TCYx (2);		//delay for 20us approx
	ConvertADC();
	while (BusyADC());		//wait for conversion to complete
	ldr_left = ReadADC()&0x03FF;  // read it, AND out unwanted bits 
	ldr_left = 1024 - ldr_left;   //reverse polarity
	//right channel 
	SetChanADC (ADC_CH1);	
	Delay10KTCYx (2);
	ConvertADC();
	while (BusyADC());
	ldr_rt = ReadADC()&0x03FF;  // read it, AND out unwanted bits
	ldr_rt = 1024 - ldr_rt; //reverse polarity
	//rear channel 
	SetChanADC (ADC_CH3);	
	Delay10KTCYx (2);
	ConvertADC();
	while (BusyADC());
	ldr_rear = ReadADC()&0x03FF;  // read it, AND out unwanted bits ;
	ldr_rear = 1024 - ldr_rear; //reverse polarity

//Display ldr values every 10 loops
	if (loop_cntr == 10)
	{
	disp_int (ldr_left);	//display left ldr value on lcd
	disp_int (ldr_rt);		//display right ldr value on lcd
	send_space (2);			//centre rear display
	disp_int (ldr_rear);	//display rear ldr value on lcd
	send_space (2);			//fills second line, forcing line feed
	loop_cntr = 0;
	}

//Compute some intermediate variables
	ldr_diff = (ldr_left - ldr_rt); //difference between ldrs
	ldr_ave = (ldr_left + ldr_rt);  //average front two ldrs
	ldr_ave = (ldr_ave>>1);  		//divide this +ve no. by 2
	ldr_fwd = ldr_ave - ldr_rear;	//front to back differential - to set forward speed
	if (ldr_fwd < 0) ldr_fwd = 0;   //set minimum value
//determine action, by comparing LDR readings
	if (ldr_left > ldr_rt)
	{if (ldr_left > ldr_rear)
		fwd_left();				//ldr_left is brightest, go forward left
	  else rotate_left ();		//rear is brightest, rotate towards light
	}
	else 
	{if (ldr_rt > ldr_rear)
		fwd_rt();				//ldr_rt is brightest, go forward left
		else rotate_rt ();
	}
	Delay10KTCYx (5);			//50ms delay (4MHz clock)		
  }				//end of superloop while
}				//end of main

/*****************************************************
Display Functions
******************************************************/
//Converts an integer to string, and sends to lcd via I2C, filling with spaces up to 4 digits 
	void disp_int (int op_int)
{	
	char disp_val[5];	 	//will hold the string representation of any value
	char *disp_val_ptr; 	//pointer to disp_val[]
	char space_no;			//number of spaces to be inserted
	disp_val_ptr = &disp_val[0];
	itoa (op_int, disp_val_ptr);//first convert to a BCD string
	space_no = 4 - strlen(disp_val_ptr); //find how many spaces needed
//Now send the message
	StartI2C();			   		//send start condition
	WriteI2C (slave_addr1); 	//send address word	
	while (space_no)			//fill up with leading spaces
	{WriteI2C (' ');
	Delay1KTCYx(5);				//delay needed for hand controller
	space_no--;
	}
//send the string
	while (*disp_val_ptr)
	{
	WriteI2C (*disp_val_ptr);
	disp_val_ptr++;
	Delay1KTCYx(5);				//delay needed for hand controller
	}	
	StopI2C();					//send stop condition
}

//Sends space to lcd via I2C
	void send_space (char space_no)
{	
	StartI2C();			   		//send start condition
	WriteI2C (slave_addr1); 	//send address word, function waits until write is complete
//send space
	while (space_no)
	{
	WriteI2C (' ');
	Delay1KTCYx(5);				//delay needed for hand controller
	space_no--;
 	}
	StopI2C();					//send stop condition
}

/**************************************************************
Movement Functions
One of these four functions selected every loop iteration
***************************************************************/
/*light is front left, hence move forward left. Algorithm is:
fwd drive left = ldr_fwd - ldr_diff, fwd drive right = ldr_diff + ldr_fwd*/
	void fwd_left (void)	
	{
	fwd_dr_left = ldr_fwd - ldr_diff;
	if (fwd_dr_left < 0) fwd_dr_left = 0; //set to zero if -ve
	fwd_dr_left = fwd_dr_left>>1;         //rotate right to scale down drive value
	if (fwd_dr_left > 127) fwd_dr_left = 127; //limit maximum value
	fwd_dr_rt = ldr_fwd + ldr_diff;
	if (fwd_dr_rt < 0) fwd_dr_rt = 0;  		//set to zero if -ve
	fwd_dr_rt = fwd_dr_rt>>1;         		//rotate right to scale down drive value
	if (fwd_dr_rt >127) fwd_dr_rt = 127;  	//limit maximum value
	CCPR1L = 0x80 + fwd_dr_rt;  	//set right motor, which is greater
	CCPR2L = 0x80 + fwd_dr_left; 	//set left motor, which is less
	}
/*light is front right, hence move forward right. Algorithm is:
fwd drive right = ldr_fwd + ldr_diff, fwd drive left = ldr_fwd - ldr_diff 
(noting "polarity" of ldr_diff*/
	void fwd_rt (void)
	{
	fwd_dr_rt = ldr_fwd + ldr_diff;	
	if (fwd_dr_rt < 0) fwd_dr_rt = 0; 		//set to zero if -ve
	fwd_dr_rt = fwd_dr_rt>>1;         	//rotate right to scale down drive value
	if (fwd_dr_rt > 127) fwd_dr_rt = 127; 	//limit maximum value
	fwd_dr_left = ldr_fwd - ldr_diff;
	if (fwd_dr_left < 0) fwd_dr_left = 0;  	//set to zero if -ve
	fwd_dr_left = fwd_dr_left>>1;         	//rotate right to scale down drive value
	if (fwd_dr_left >127) fwd_dr_left = 127;  //limit maximum value
	CCPR1L = 0x80 + fwd_dr_rt;  	//set right motor, which is less
	CCPR2L = 0x80 + fwd_dr_left; 	//set left motor, which is greater
	}

//fixed speed left rotation (light is at rear left)
	void rotate_left (void)
	{
	rtmot_fwd ();
	leftmot_rev ();
	}

//fixed speed right rotation (light is at rear right)
	void rotate_rt (void)	
	{
	leftmot_fwd ();
	rtmot_rev ();
	}

/**************************************************************
Motor Drive Functions
***************************************************************/
	void leftmot_fwd (void)		/*sets left motor running forward*/
	{	CCPR2L = 192;
		PORTAbits.RA5 = 1;		/*enable motor*/
	}

	void rtmot_fwd (void)		/*sets right motor running forward*/
	{	CCPR1L = 192;
		PORTAbits.RA2 = 1;		/*enable motor*/
	}

	void leftmot_rev (void)		/*sets left motor running in reverse*/
	{	CCPR2L = 64;
		PORTAbits.RA5 = 1;		/*enable motor*/
	}

	void rtmot_rev (void)		/*80 sets right motor running in reverse*/
	{	CCPR1L = 64;
		PORTAbits.RA2 = 1;		/*enable motor*/
	}

	void rev_rt (void)			/*reverses and then turns to right*/
	{PORTCbits.RC6 = 1;			//set led
	PORTAbits.RA5 = 0;		/*stop motors*/
	PORTAbits.RA2 = 0;		
	PORTBbits.RB1 = 1;		//small bleep from sounder
	Delay10KTCYx (100);
	PORTBbits.RB1 = 0;		//clear sounder
	leftmot_rev (); 		//reverse both motors
	rtmot_rev ();
	Delay10KTCYx (200);
	leftmot_fwd (); 		//left motor forward to turn
	Delay10KTCYx (200);
	PORTCbits.RC6 = 0;		//clear led
	}

	void rev_left (void)	/*reverses and then turns to left*/
	{PORTCbits.RC5 = 1;		//set led
	PORTAbits.RA5 = 0;		/*stop motors*/
	PORTAbits.RA2 = 0;		
	PORTBbits.RB1 = 1;		//small bleep from sounder
	Delay10KTCYx (100);
	PORTBbits.RB1 = 0;	
	leftmot_rev (); 		//reverse both motors
	rtmot_rev ();
	Delay10KTCYx (200);
	rtmot_fwd (); 			//right motor forward to turn
	Delay10KTCYx (200);
	PORTCbits.RC5 = 0;		//clear led
	}
//Diagnostic: switches leds on for 1s (Tcy = 1us)
void diagnostic (void)
{	Delay10KTCYx (100);
	PORTCbits.RC6 = 1;
	PORTCbits.RC5 = 1;
	Delay10KTCYx (100);
	PORTCbits.RC6 = 0;
	PORTCbits.RC5 = 0;
	Delay10KTCYx (100);
}
