////////////////////////////////////////////////////////////////////////////////
//
//     PIC16F877 + GPIO14 + LCD03 example
//     Written November 2005 by Gerald Coe, using HITECH PIC16 compiler
// 
//		 General Purpose I/O using same I2C bus as the LCD03 is using
//		 Note - assumes a 20MHz crystal
//		 
//     This code is Freeware - Use it for any purpose you like.
//
///////////////////////////////////////////////////////////////////////////////


#include <pic.h>
#include <stdio.h>
	 
__CONFIG(0x3b32);								// configuration register - see PIC data sheet for details		

#define NO_OP		0x00						// does nothing
#define PULLB_ON	0x01						// PORTB pull-ups
#define PULLB_OFF	0x02
#define GET_AD0	0x03						// Read A/D channels
#define GET_AD1	0x04
#define GET_AD2	0x05
#define GET_AD3	0x06
#define GET_AD4	0x07
#define GET_S4A	0x08						// SRF04 - Trig on RB0, Echo on RB2
#define GET_S4B	0x09						// SRF04 - Trig on RB6, Echo on RB7
#define GET_S5A	0x0A						// SRF05 - Trig + Echo on RB6
#define GET_S5B	0x0B						// SRF05 - Trig + Echo on RB7
#define SET_US		0x0C						// sonar ranging in uS
#define SET_CM		0x0D						// sonar ranging in cm
#define SET_IN		0x0E						// sonar ranging in inches

#define GPIO_ADDR	0x40						// Factory supplied default I2C address
#define CMD				0						// write only - command register
#define REVISION		0						// read only - firmware revision
#define MASK_A			1						// write only - TRISA reg
#define RESULT			1						// read only - result of A/D
#define MASK_B			2						// write only - TRISB reg
#define AD_CONTROL	3						// read/write - ADCON1
#define PORT_A			4						// read/write
#define PORT_B			5						// read/write
#define PWM				6						// read/write
#define ADDR_CHANGE	7						// write only

#define SET_RA0	0x10						// define bit set commands for easier use
#define SET_RA1	0x11
#define SET_RA2	0x12
#define SET_RA3	0x13
#define SET_RA4	0x14
#define SET_RA5	0x15						// not available on GPIO14 (Input only)
#define SET_RA6	0x16
#define SET_RA7	0x17
#define SET_RB0	0x18
#define SET_RB1	0x19						// not available on GPIO14	(SDA)
#define SET_RB2	0x1a
#define SET_RB3	0x1b
#define SET_RB4	0x1c						// not available on GPIO14 (SCL)
#define SET_RB5	0x1d
#define SET_RB6	0x1e
#define SET_RB7	0x1f

#define CLR_RA0	0x20						// define bit clear commands for easier use
#define CLR_RA1	0x21
#define CLR_RA2	0x22
#define CLR_RA3	0x23
#define CLR_RA4	0x24
#define CLR_RA5	0x25						// not available on GPIO14 (Input only)
#define CLR_RA6	0x26
#define CLR_RA7	0x27
#define CLR_RB0	0x28
#define CLR_RB1	0x29						// not available on GPIO14	(SDA)
#define CLR_RB2	0x2a
#define CLR_RB3	0x2b
#define CLR_RB4	0x2c						// not available on GPIO14 (SCL)
#define CLR_RB5	0x2d
#define CLR_RB6	0x2e
#define CLR_RB7	0x2f

#define TOG_RA0	0x30						// define bit toggle commands for easier use
#define TOG_RA1	0x31
#define TOG_RA2	0x32
#define TOG_RA3	0x33
#define TOG_RA4	0x34
#define TOG_RA5	0x35						// not available on GPIO14 (Input only)
#define TOG_RA6	0x36
#define TOG_RA7	0x37
#define TOG_RB0	0x38
#define TOG_RB1	0x39						// not available on GPIO14	(SDA)
#define TOG_RB2	0x3a
#define TOG_RB3	0x3b
#define TOG_RB4	0x3c						// not available on GPIO14 (SCL)
#define TOG_RB5	0x3d
#define TOG_RB6	0x3e
#define TOG_RB7	0x3f

void clrscn(void);							// prototypes
void cursor(char pos);
void print(char *p);
void setup(void);
char read1_gpio(char reg);
int read2_gpio(char reg);
void write_gpio(char reg, char data);

char s[21];										// buffer used to hold text to print

void main(void)
{
int result1, result2;

	setup();										// sets up the PIC16F877 I2C port
	clrscn();									// clears the LCD03 disply
	cursor(5);									// sets cursor to 1st row of LCD03
	sprintf(s,"GPIO14 Test ");				// text, printed into our buffer
	print(s);									// send it to the LCD03
	
	write_gpio(CMD, PULLB_ON);				// turn pull-up resistors on
	write_gpio(AD_CONTROL, 0x80);			// select right justification and all AN0-AN4 as analogue inputs
	write_gpio(CMD, SET_CM);				// set ranging in centimeters

		while(1) {								// loop forever
		write_gpio(CMD, GET_AD0);
		result1 = read2_gpio(RESULT);
		write_gpio(CMD, GET_AD1);
		result2 = read2_gpio(RESULT);
		cursor(21);									// sets cursor to 2nd row of LCD03
		sprintf(s,"An0 =%04d, An1 =%04d", result1, result2);	// set text
		print(s);									// send it to the LCD03	

		write_gpio(CMD, GET_AD2);
		result1 = read2_gpio(RESULT);
		result2 = read2_gpio(PORT_A);			// reads two bytes - Ports A and B
		cursor(41);									// sets cursor to 4th row of LCD03
		sprintf(s,"An2 =%04d, AB = %04X", result1, result2);	// set text
		print(s);									// send it to the LCD03	

		write_gpio(CMD, GET_S5A);
// now wait for the ranging to complete. This delay is a little over 100mS
		TMR1H = 0;									// delay while the srf05 is ranging
		TMR1L = 0;							
		T1CON = 0x31;								// 1:4 prescale and running (0.8uS/clk)
		TMR1IF = 0;
		while(!TMR1IF);							// wait for delay time
		TMR1ON = 0;									// stop timer	
		result1 = read2_gpio(RESULT);

		write_gpio(CMD, GET_S5B);
// now wait for the ranging to complete. This delay is a little over 100mS
		TMR1H = 0;									// delay while the srf05 is ranging
		TMR1L = 0;							
		T1CON = 0x31;								// 1:4 prescale and running (0.8uS/clk)
		TMR1IF = 0;
		while(!TMR1IF);							// wait for delay time
		TMR1ON = 0;									// stop timer	
		result2 = read2_gpio(RESULT);
		cursor(61);									// sets cursor to 3rd row of LCD03
		sprintf(s,"SR1 =%04d, SR2 =%04d", result1, result2);	// set text
		print(s);									// send it to the LCD03	
	}
}


void write_gpio(char reg, char data)
{
	SEN = 1;										// send start bit
	while(SEN);									// and wait for it to clear
	SSPIF = 0;
	SSPBUF = GPIO_ADDR;						// GPIO I2C address
	while(!SSPIF);								// wait for interrupt
	SSPIF = 0;									// then clear it.
	SSPBUF = reg;								// address of register to write to 
	while(!SSPIF);								// 
	SSPIF = 0;									//
	SSPBUF = data;								// write the data
	while(!SSPIF);								// 
	SSPIF = 0;									//
	PEN = 1;										// send stop bit
	while(PEN);									//
}
	
	
int read2_gpio(char reg)
{
int data;

	SEN = 1;										// send start bit
	while(SEN);									// and wait for it to clear
	ACKDT = 0;									// acknowledge bit
	SSPIF = 0;
	SSPBUF = GPIO_ADDR;						// I2C address
	while(!SSPIF);								// wait for interrupt
	SSPIF = 0;									// then clear it.
	SSPBUF = reg;								// address of register to read from
	while(!SSPIF);								// 
	SSPIF = 0;									//
	RSEN = 1;									// send repeated start bit
	while(RSEN);								// and wait for it to clear
	SSPIF = 0;									//
	SSPBUF = GPIO_ADDR+1;					// I2C address - the read bit is set this time
	while(!SSPIF);								// wait for interrupt
	SSPIF = 0;									// then clear it.
	RCEN = 1;									// start receiving
	while(!STAT_BF);							// wait for high byte
	data = SSPBUF<<8;							// and get it
	ACKEN = 1;									// start acknowledge sequence
	while(ACKEN);								// wait for ack. sequence to end
	RCEN = 1;									// start receiving
	while(!STAT_BF);							// wait for low byte
	data += SSPBUF;							// and get it
	ACKDT = 1;									// not acknowledge for last byte
	ACKEN = 1;									// start acknowledge sequence
	while(ACKEN);								// wait for ack. sequence to end
	PEN = 1;										// send stop bit
	while(PEN);									//
	return data;
}

	
char read1_gpio(char reg)
{
char data;

	SEN = 1;										// send start bit
	while(SEN);									// and wait for it to clear
	ACKDT = 0;									// acknowledge bit
	SSPIF = 0;
	SSPBUF = GPIO_ADDR;						// I2C address
	while(!SSPIF);								// wait for interrupt
	SSPIF = 0;									// then clear it.
	SSPBUF = reg;								// address of register to read from
	while(!SSPIF);								// 
	SSPIF = 0;									//
	RSEN = 1;									// send repeated start bit
	while(RSEN);								// and wait for it to clear
	SSPIF = 0;									//
	SSPBUF = GPIO_ADDR+1;					// I2C address - the read bit is set this time
	while(!SSPIF);								// wait for interrupt
	SSPIF = 0;									// then clear it.
	RCEN = 1;									// start receiving
	while(!STAT_BF);							// wait for low byte
	data += SSPBUF;							// and get it
	ACKDT = 1;									// not acknowledge for last byte
	ACKEN = 1;									// start acknowledge sequence
	while(ACKEN);								// wait for ack. sequence to end
	PEN = 1;										// send stop bit
	while(PEN);									//
	return data;
}

	
void clrscn(void)
{
	SEN = 1;								// send start bit
	while(SEN);							// and wait for it to clear

	SSPIF = 0;
	SSPBUF = 0xc6;						// LCD02 I2C address
	while(!SSPIF);						// wait for interrupt
	SSPIF = 0;							// then clear it.

	SSPBUF = 0;							// address of register to write to 
	while(!SSPIF);						// 
	SSPIF = 0;							//

	SSPBUF = 12;						// clear screen 
	while(!SSPIF);						// 
	SSPIF = 0;							//

	SSPBUF = 4;							// cursor off 
	while(!SSPIF);						// 
	SSPIF = 0;							//
	 
	PEN = 1;								// send stop bit
	while(PEN);							//
}

		
void cursor(char pos)
{
	SEN = 1;								// send start bit
	while(SEN);							// and wait for it to clear

	SSPIF = 0;
	SSPBUF = 0xc6;						// LCD02 I2C address
	while(!SSPIF);						// wait for interrupt
	SSPIF = 0;							// then clear it.

	SSPBUF = 0;							// address of register to write to 
	while(!SSPIF);						// 
	SSPIF = 0;							//

	SSPBUF = 2;							// set cursor 
	while(!SSPIF);						// 
	SSPIF = 0;							//
	SSPBUF = pos;						//  
	while(!SSPIF);						// 
	SSPIF = 0;							//
	 
	PEN = 1;								// send stop bit
	while(PEN);							//
}

		
void print(char *p)
{
	SEN = 1;								// send start bit
	while(SEN);							// and wait for it to clear

	SSPIF = 0;
	SSPBUF = 0xc6;						// LCD02 I2C address
	while(!SSPIF);						// wait for interrupt
	SSPIF = 0;							// then clear it.

	SSPBUF = 0;							// address of register to write to 
	while(!SSPIF);						// 
	SSPIF = 0;							//

	while(*p) {
		SSPBUF = *p++;					// write the data 
		while(!SSPIF);					// 
		SSPIF = 0;						// 
	}

	PEN = 1;								// send stop bit
	while(PEN);							//
}


void setup(void)
{
unsigned long x;

	PORTB = 0xfe;						// RB0 (trig) is output
	TRISB = 0xfe;						// and starts low

	TRISC = 0xff;
	PORTC = 0xff;

	SSPSTAT = 0x80;
	SSPCON = 0x38;
	SSPCON2 = 0x00;
	SSPADD = 50;						// SCL = 91khz with 20Mhz Osc

	for(x=0; x<300000L; x++);		// wait for LCD03 to initialise		
}

