//-----------------------------------------------------------------------------
// GoPro Slave Test Fixture
// Copyright 2012 by Glen Akins.
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// The I2C bit-bang I/O routines are poached from the example source code
// for an accelerometer break out board from sparkfun.com and are subject
// to sparkfun's example source code licensing terms.
//
// sample cmd sequence to take two pictures:
//
// off (if camera on)
// init
// on
// on
// snaphi
// snaplo
//
// alternate snaphi and snaplo commands to continue taking pictures
// because initial state of id1 from camera changes from high to low
// or low to high after every picture.
//
// sample cmd sequence to record video:
// 
// off (if camera on)
// init
// on
// id2 0
// video is recording now
// id2 1
// video stops recording now
// 


//-----------------------------------------------------------------------------
// includes
//

#include <p24HJ128GP202.h>
#include "console.h"


//-----------------------------------------------------------------------------
// config bits
//

_FOSCSEL (FNOSC_PRI);
_FOSC    (FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_EC);
_FWDT    (FWDTEN_OFF & WINDIS_OFF)


//-----------------------------------------------------------------------------
// defines
//

// baud rates
#define FCY 40000000
#define U1BAUDRATE 115200
#define U1BRGVAL (((FCY/U1BAUDRATE)/16) - 1)

// command processor
#define ECHO			// define to echo characters on com port
#define PROMPT			// define to send prompt at beginning of lines
#define CMD_MAXLEN 72

// bit-banged i2c interface
#define SCL                         LATAbits.LATA0
#define WRITE_scl()					TRISAbits.TRISA0 = 0
#define SDA                         LATAbits.LATA4
#define SDARD						PORTAbits.RA4
#define READ_sda()                  TRISAbits.TRISA4 = 1
#define WRITE_sda()					TRISAbits.TRISA4 = 0
#define I2C_DELAY					10
#define ACK     					1
#define NO_ACK  					0

// camera read bits
#define GP4rd PORTBbits.RB8
#define POWERrd PORTAbits.RA1
#define ID1rd PORTBbits.RB7
#define ID2rd PORTBbits.RB6
#define ID3rd PORTBbits.RB3
#define ID4rd PORTBbits.RB2


//-----------------------------------------------------------------------------
// prototypes
//

void TickInit (void);

// command handling functions
void GetCommand (void);
void ProcessCommand (void);
unsigned char IsCommand (char *s1, unsigned char l1);
unsigned char fromhex (unsigned char ch);
void Get32BitsFromCommand (unsigned long *data);
void Get8BitsFromCommand (unsigned char *data);
void Get16BitsFromCommand (unsigned short *data);
void WriteCommand (void);
void ReadCommand (void);
void ProgramCommand (void);
void DumpCommand (void);
void PowerCommand (void);
void OnCommand (void);
void OffCommand (void);

// gpio functions
void SetGP4 (char a);
void SetMode (char a);
void SetTrig (char a);
void SetID1 (char a);
void SetID2 (char a);
void SetID3 (char a);
void SetID4 (char a);

// I2C
void delay_us (unsigned char x);
void i2c_start(void);
void i2c_stop (void);
void i2c_ack_polling(unsigned char device_address);
unsigned char i2c_send_byte(unsigned char out_byte);
unsigned char i2c_read_byte (void);


//-----------------------------------------------------------------------------
// globals
//

unsigned char flag50Hz = 0;
unsigned char timer1 = 0;

// command processing variables
unsigned char cmd_buffer[CMD_MAXLEN];
unsigned char cmd_length = 0;
unsigned char cmd_state = 0;
unsigned char word_begin = 0;
unsigned char word_end = 0;
unsigned char word_length = 0;


//-----------------------------------------------------------------------------
// constants
//


//-----------------------------------------------------------------------------
// main
//

int main (void)
{
	// set PLL to multiply external clock by 8
	// Fosc will be 80MHz and Fcyc will be 40MHz.
	PLLFBD = 30;
	CLKDIVbits.PLLPOST = 0;
	CLKDIVbits.PLLPRE  = 0;

	// switch to EC + PLL	
	__builtin_write_OSCCONH (0x03);
	__builtin_write_OSCCONL (0x01);

	// wait for PLL to lock
	while (OSCCONbits.LOCK != 0b1) {
	}

	// wait for oscillator to switch to EC + PLL
	while (OSCCONbits.COSC != 0b011) {
	}

	// unlock peripheral pin select and leave it unlocked
	__builtin_write_OSCCONL (OSCCON & ~(1<<6));

	// initialize timer 1 to generate 50Hz timebase
	TickInit ();

	// initialize uart1, 9600 bps at Fosc = 80MHz, 8N1
	// cts / rts hardware flow control
	TRISBbits.TRISB4 = 0;		// drive txd output
	TRISBbits.TRISB5 = 1;		// don't drive rxd input
	RPINR18bits.U1RXR = 5;		// connect RP5 to U1RXD
	RPOR2bits.RP4R = 3;			// connect U1TXD to RP4
	U1MODE = 0b1000000000000000;
	U1STA = 0b0000010000000000;
	U1BRG = U1BRGVAL;

	// initialize gpio
	// RB0  = not used	input		0 (don't care)
	// RB1  = not used 	input		0 (don't care)
	// RB2  = ID4_IN 	input		0 (don't care)
	// RB3  = ID3_IN	input		0 (don't care)
	// RB4  = U1_TXD	output		0 (don't care)
	// RB5  = U1_RXD	input		0 (don't care)
	// RB6  = ID2_IN	input		0 (don't care)
	// RB7  = ID1_IN	input		0 (don't care)
	// RB8  = GP4_IN	input		0 (don't care)
	// RB9  = MODE_OUT	output 		low
	// RB10 = ID2_OUT	output		high
	// RB11 = ID1_OUT	output		high (makes ID1 hi-z)
	// RB12 = ID3_OUT	output		low
	// RB13 = TRIG_OUT	output		high
	// RB14 = ID4_OUT	output		low
	// RB15 = GP4_OUT	output		high
	LATB  = 0b1010110000000000; 
	TRISB = 0b0000000111101111;

	// RA0  = J3-1       I2C SDA
	// RA1  = POWER_IN   input
	// RA2  = CLKI       input
	// RA3  = CLKO       output
	// RA4  = J3-2       I2C SCL
	LATA  = 0b1111111111111111;
	TRISA = 0b1111111111110111;

	AD1PCFGL = 0b0001111111111111;

	WRITE_scl ();
	WRITE_sda ();

	// intialize console I/O
	ConsoleInit ();

	// power on message
	putstringcrlf ("\x0d\x0a\x0d\x0aGoPro Slave Test Fixture");
	putstringcrlf ("Copyright 2012 by Glen Akins. All rights reserved.\x0d\x0a");

	while (1) {

		// transmit dmx, update dmx state, and process counters at 50Hz
		if (flag50Hz) {
			// clear flag
			flag50Hz = 0;

			// periodic tasks go here
			// TODO

/*
			// state
			puthex8 (state);
			putchar (' ');

			// GP4
			if (PORTBbits.RB8) putchar ('1'); else putchar ('0');			
			putchar (' ');

			// ID1,2,3,4
			if (PORTBbits.RB7) putchar ('1'); else putchar ('0');			
			if (PORTBbits.RB6) putchar ('1'); else putchar ('0');			
			if (PORTBbits.RB3) putchar ('1'); else putchar ('0');			
			if (PORTBbits.RB2) putchar ('1'); else putchar ('0');			
			putchar (' ');

			// POWER_IN
			if (PORTAbits.RA1) putchar ('1'); else putchar ('0');
			putchar (' ');

			putstringcrlf ("");

			if (state == 0) SetGP4(1); else SetGP4(0);
			if (state == 1) SetMode(1); else SetMode(0);
			if (state == 2) SetTrig(1); else SetTrig(0);
			if (state == 3) SetID1(1); else SetID1(0);
			if (state == 4) SetID2(1); else SetID2(0);
			if (state == 5) SetID3(1); else SetID3(0);
			if (state == 6) SetID4(1); else SetID4(0);

			if (state == 0) SetGP4(0); else SetGP4(1);
			if (state == 1) SetMode(0); else SetMode(1);
			if (state == 2) SetTrig(0); else SetTrig(1);
			if (state == 3) SetID1(0); else SetID1(1);
			if (state == 4) SetID2(0); else SetID2(1);
			if (state == 5) SetID3(0); else SetID3(1);
			if (state == 6) SetID4(0); else SetID4(1);

			state++;
			if (state == 8) state = 0;
*/
		}

		// check uart1 command buffer for new characters
		// and append any to current command buffer
		GetCommand ();
		if (cmd_state == 2) {
			ProcessCommand ();
		}

		// clear any receiver overrun conditions
		if (U1STAbits.OERR) {
			U1STAbits.OERR = 0;
		}
	}

	return 0;
}


//-----------------------------------------------------------------------------
// TickInit
//

void TickInit(void)
{
	// 1:256 prescale
	T1CONbits.TCKPS = 3;
	// Base
	PR1 = 3125;
	// Clear counter
	TMR1 = 0;

	// Enable timer interrupt
	IPC0bits.T1IP = 2;	// Interrupt priority 2 (low)
	IFS0bits.T1IF = 0;
	IEC0bits.T1IE = 1;

	// Start timer
	T1CONbits.TON = 1;
}


//-----------------------------------------------------------------------------
// T1 ISR
//

void _ISR __attribute__((__no_auto_psv__)) _T1Interrupt(void)
{
	// Set 50Hz flag
	flag50Hz = 1;

	// decrement timers
	if (timer1 != 0) timer1--;

	// Reset interrupt flag
	IFS0bits.T1IF = 0;
}


//-----------------------------------------------------------------------------
// command processing
//

void GetCommand (void)
{
	short ch;

	if (cmd_state == 0) {
		cmd_length = 0;
		cmd_buffer[cmd_length] = 0;
#ifdef PROMPT
		putstring ("GPSlave> ");
#endif
		cmd_state++;
	} else if (cmd_state == 1) {
		// get character
		ch = getchar ();
		
		// process character
		if (ch >= 0) {
			if (ch == 0x0d) {							// return
				// carriage return and linefeed
#ifdef ECHO
				putchar (0x0d);
				putchar (0x0a);
#endif
				cmd_state++;
			} else if (ch == 0x08) {					// backspace
				if (cmd_length > 0) {
#ifdef ECHO
					putchar (0x08);
					putchar (' ');
					putchar (0x08);
#endif
					cmd_buffer[--cmd_length] = 0;
				}
			} else if (ch == 0x15) {					// ctrl-u is rub out
				while (cmd_length > 0) {
#ifdef ECHO
					putchar (0x08);
					putchar (' ');
					putchar (0x08);
#endif
					cmd_buffer[--cmd_length] = 0;
				}
			} else if (ch >= 0x20 && ch <= 0x7e) {		// printable characters
				if (cmd_length < (CMD_MAXLEN - 1)) {
#ifdef ECHO
					putchar (ch);
#endif
					cmd_buffer[cmd_length++] = ch;
					cmd_buffer[cmd_length] = 0;
				}
			}
		}
	}
}


void ProcessCommand (void)
{
	unsigned char data;

	// find beginning of first word
	word_begin = 0;
	while ((cmd_buffer[word_begin] == ' ') && (word_begin < cmd_length)) {
		word_begin++;
	}

	// find end of first word
	word_end = word_begin;
	while ((cmd_buffer[word_end] != ' ') && (word_end < cmd_length)) {
		word_end++;
	}

	// find length of first word
	word_length = word_end - word_begin;

	// IsCommand accesses the global variables word_begin and word_length
	if (IsCommand ("wr", 2)) {				// write one byte in eeprom
		WriteCommand ();
	} else if (IsCommand ("rd", 2)) {		// read one byte from eeprom
		ReadCommand ();
	} else if (IsCommand ("prog", 4)) {		// program entire eeprom
		ProgramCommand ();
	} else if (IsCommand ("dump", 4)) {		// dump entire eeprom
		DumpCommand ();
	} else if (IsCommand ("power", 5)) {	// get power state
		PowerCommand ();
	} else if (IsCommand ("on", 2)) {		// short mode button press
		OnCommand ();
	} else if (IsCommand ("off", 3)) {		// long mode button press
		OffCommand ();
	} else if (IsCommand ("init", 4)) {
		InitCommand ();
	} else if (IsCommand ("snaphi", 6)) {
		SnapWhileHighCommand ();
	} else if (IsCommand ("snaplo", 6)) {
		SnapWhileLowCommand ();
	} else if (IsCommand ("high", 4)) {
		SetGP4 (1);
		SetTrig (1);
		SetID1 (1);
		SetID2 (1);
		SetID3 (1);
		SetID4 (1);
	} else if (IsCommand ("id2", 3)) {
		Get8BitsFromCommand (&data);
		if (data == 0) SetID2 (0);
		else SetID2 (1);
		DumpState ();
	} else if (IsCommand ("id3", 3)) {
		Get8BitsFromCommand (&data);
		if (data == 0) SetID3 (0);
		else SetID3 (1);
		DumpState ();
	} else if (IsCommand ("id4", 3)) {
		Get8BitsFromCommand (&data);
		if (data == 0) SetID4 (0);
		else SetID4 (1);
		DumpState ();
	} else if (IsCommand ("trig", 4)) {
		Get8BitsFromCommand (&data);
		if (data == 0) SetTrig (0);
		else SetTrig (1);
		DumpState ();
	} else if (IsCommand ("gp4", 3)) {
		Get8BitsFromCommand (&data);
		if (data == 0) SetGP4 (0);
		else SetGP4 (1);
		DumpState ();
	} else if (IsCommand ("state", 5)) {
		DumpState ();
	} else if (IsCommand ("sim", 3)) {
		putstringcrlf ("sim");
		while (1) {
			if (ID2rd == 0) {
				SetID1 (0);
				break;
			}
		}
		while (1) {
			if (ID2rd == 1) {
				SetID1 (1);
				break;
			}
		}
	}

	cmd_state = 0;
}

void DumpState (void)
{
	putstringcrlf ("G P T 4321");
	puthex4(GP4rd);
	putchar (' ');
	puthex4(POWERrd);
	putchar (' ');
	puthex4(LATBbits.LATB13);
	putchar (' ');
	puthex4(ID4rd);
	puthex4(ID3rd);
	puthex4(ID2rd);
	puthex4(ID1rd);
	putstringcrlf ("");
}


unsigned char IsCommand (char *s1, unsigned char l1)
{
	unsigned char s2;
	unsigned char l2;

	s2 = word_begin;
	l2 = word_length;

	if (l1 != l2) {
		return 0;
	}

	while (l1--) {
		if (*s1++ != cmd_buffer[s2++]) {
			return 0;
		}
	}

	return 1;
}


unsigned char fromhex (unsigned char ch)
{
	if (ch >= '0' && ch <= '9')
		return ch - '0';
	if (ch >= 'a' && ch <= 'f')
		return ch - 'a' + 10;
	if (ch >= 'A' && ch <= 'F')
		return ch - 'A' + 10;
	return 0;
}


void Get32BitsFromCommand (unsigned long *data)
{
	unsigned char i;

	// find beginning of parameter
	word_begin = word_end;
	while ((cmd_buffer[word_begin] == ' ') && (word_begin < cmd_length)) {
		word_begin++;
	}

	// find end of parameter
	word_end = word_begin;
	while ((cmd_buffer[word_end] != ' ') && (word_end < cmd_length)) {
		word_end++;
	}

	// find length of parameter
	word_length = word_end - word_begin;

	*data = 0;

	for (i = word_begin; i < word_end; i++) {
		*data <<= 4;
		*data |= fromhex (cmd_buffer[i]);
	}
}

void Get8BitsFromCommand (unsigned char *data)
{
	unsigned char i;

	// find beginning of key
	word_begin = word_end;
	while ((cmd_buffer[word_begin] == ' ') && (word_begin < cmd_length)) {
		word_begin++;
	}

	// find end of key
	word_end = word_begin;
	while ((cmd_buffer[word_end] != ' ') && (word_end < cmd_length)) {
		word_end++;
	}

	// find length of key
	word_length = word_end - word_begin;

	*data = 0;

	for (i = word_begin; i < word_end; i++) {
		*data <<= 4;
		*data |= fromhex (cmd_buffer[i]);
	}
}


void Get16BitsFromCommand (unsigned short *data)
{
	unsigned char i;

	// find beginning of key
	word_begin = word_end;
	while ((cmd_buffer[word_begin] == ' ') && (word_begin < cmd_length)) {
		word_begin++;
	}

	// find end of key
	word_end = word_begin;
	while ((cmd_buffer[word_end] != ' ') && (word_end < cmd_length)) {
		word_end++;
	}

	// find length of key
	word_length = word_end - word_begin;

	*data = 0;

	for (i = word_begin; i < word_end; i++) {
		*data <<= 4;
		*data |= fromhex (cmd_buffer[i]);
	}
}

void SetGP4 (char a)
{
	LATBbits.LATB15 = a;
}

void SetMode (char a)
{
	LATBbits.LATB9 = a;
}

void SetTrig (char a)
{
	LATBbits.LATB13 = a;
}

void SetID1 (char a)
{
	LATBbits.LATB11 = a;
}

void SetID2 (char a)
{
	LATBbits.LATB10 = a;
}

void SetID3 (char a)
{
	LATBbits.LATB12 = a;
}

void SetID4 (char a)
{
	LATBbits.LATB14 = a;
}

void WriteCommand (void)
{
	unsigned char address;
	unsigned char data;

	Get8BitsFromCommand (&address);
	Get8BitsFromCommand (&data);

	i2c_start ();
	// control code = 1010, block address = XXX, R/W_n = 0
	i2c_send_byte (0b10100000);			
	i2c_send_byte (address);
	i2c_send_byte (data);
	i2c_stop ();

	putstring ("addr: ");
	puthex8 (address);
	putstring (", ");
	putstring ("data: ");
	puthex8 (data);
	putstringcrlf ("");
}

void ReadCommand (void)
{
	unsigned char address;
	unsigned char data;
	Get8BitsFromCommand (&address);

	i2c_start ();
	// control code = 1010, block address = XXX, R/W_n = 0
	i2c_send_byte (0b10100000);			
	i2c_send_byte (address);
	i2c_start ();
	// control code = 1010, block address = XXX, R/W_n = 1
	i2c_send_byte (0b10100001);			
	data = i2c_read_byte ();
	i2c_stop ();

	putstring ("addr: ");
	puthex8 (address);
	putstring (", ");
	putstring ("data: ");
	puthex8 (data);
	putstringcrlf ("");
}

void ProgramCommand (void)
{
	unsigned char address;
	unsigned char i;

	for (address = 0; address < 128; address++) {
		i2c_start ();
		i2c_send_byte (0b10100000);			
		i2c_send_byte (address);
		if (address == 0) i2c_send_byte (0x05);
		else i2c_send_byte (0xFF);
		i2c_stop ();
		putchar ('.');
		for (i = 0; i < 100; i++) {
			delay_us (255);
		}
	}
	putstringcrlf ("");
}

void DumpCommand (void)
{
	unsigned char address;
	unsigned char data;

	for (address = 0; address < 128; address++) {
		i2c_start ();
		// control code = 1010, block address = XXX, R/W_n = 0
		i2c_send_byte (0b10100000);			
		i2c_send_byte (address);
		i2c_start ();
		// control code = 1010, block address = XXX, R/W_n = 1
		i2c_send_byte (0b10100001);			
		data = i2c_read_byte ();
		i2c_stop ();
		puthex8 (data);
		putchar (' ');
		if ((address & 0xf) == 0xf) {
			putstringcrlf ("");
		}
	}
}

void PowerCommand (void)
{
	if (POWERrd) {
		putstringcrlf ("Camera is ON");
	} else {
		putstringcrlf ("Camera is OFF");
	}
}

void OnCommand (void)
{
	SetMode (1);
	timer1 = 50;
	while (timer1 != 0) {
	}
	SetMode (0);
}

void OffCommand (void)
{
	SetMode (1);
	timer1 = 200;
	while (timer1 != 0) {
	}
	SetMode (0);
}

void InitCommand (void)
{
	SetMode (0);
	SetGP4 (1);
	SetTrig (1);
	SetID1 (1);
	SetID2 (1);
	SetID3 (0);
	SetID4 (0);
	putstringcrlf ("inited.");
}

void SnapWhileHighCommand (void)
{
	unsigned char i, j;

	// set id3 high
	SetID3 (1);

	// delay about 20ms
	for (i = 0; i < 150; i++) {
		delay_us (255);
	}

	// set id2 low
	SetID2 (0);

	// id1 will immediately go low to indicate the camera is busy
	// after about 50 - 100 ms, id1 will return high to indicate
	// the camera is ready

	// delay about 2ms to allow id1 to go low
	for (i = 0; i < 15; i++) {
		delay_us (255);
	}

	// wait for id1 to go high again
	// this is where we'd wait for all attached cameras to return to high
	while (ID1rd == 0) {
		if (U1STAbits.URXDA) { 
			putstringcrlf ("aborted while waiting for id1 --> low.");
			return;
		}
	}

	// for simulation of waiting on all cameras, delay about 800ms
	for (j = 0; j < 40; j++) {
		for (i = 0; i < 150; i++) {
			delay_us (255);
		}
	}

	// pulse trigger low to take the picture
	SetTrig (0);

	// delay about 2ms to make a short pulse
	for (i = 0; i < 15; i++) {
		delay_us (255);
	}

	// set trigger high to finish the pulse
	SetTrig (1);

	// delay about 20ms
	for (i = 0; i < 150; i++) {
		delay_us (255);
	}

	// set id2 high
	SetID2 (1);

	// delay about 20ms
	for (i = 0; i < 150; i++) {
		delay_us (255);
	}

	// set id3 low
	SetID3 (0);

	putstringcrlf ("snapped.");
}

void SnapWhileLowCommand (void)
{
	unsigned char i, j;

	// set id3 high
	SetID3 (1);

	// delay about 20ms
	for (i = 0; i < 150; i++) {
		delay_us (255);
	}

	// set id2 low
	SetID2 (0);

	// id1 will immediately go high to indicate the camera is busy
	// after about 50 - 100 ms, id1 will return low to indicate
	// the camera is ready

	// delay about 2ms to allow id1 to go high
	for (i = 0; i < 15; i++) {
		delay_us (255);
	}

	// wait for id1 to go low again
	// this is where we'd wait for all attached cameras to return to low
	while (ID1rd != 0) {
		if (U1STAbits.URXDA) { 
			putstringcrlf ("aborted while waiting for id1 --> high.");
			return;
		}
	}

	// for simulation of waiting on all cameras, delay about 800ms
	for (j = 0; j < 40; j++) {
		for (i = 0; i < 150; i++) {
			delay_us (255);
		}
	}

	// pulse trigger low to take the picture
	SetTrig (0);

	// delay about 2ms to make a short pulse
	for (i = 0; i < 15; i++) {
		delay_us (255);
	}

	// set trigger high to finish the pulse
	SetTrig (1);

	// delay about 20ms
	for (i = 0; i < 150; i++) {
		delay_us (255);
	}

	// set id2 high
	SetID2 (1);

	// delay about 20ms
	for (i = 0; i < 150; i++) {
		delay_us (255);
	}

	// set id3 low
	SetID3 (0);

	putstringcrlf ("snapped.");
}
void i2c_ack_polling(unsigned char device_address)
{
    while(1)
    {
        i2c_start();
        if (i2c_send_byte(device_address) == ACK) break;
    }
    i2c_stop();
}


void i2c_start(void)
{
    WRITE_sda();
    SDA = 1;
    delay_us(I2C_DELAY);

    SCL = 1;
    delay_us(I2C_DELAY);

    SDA = 0;
    delay_us(I2C_DELAY);
}

//The I2C Clock has a minimum of 2us high time and 2us low time
void i2c_stop(void)
{
    SCL = 0;
    delay_us(I2C_DELAY);

    WRITE_sda();

    SDA = 0;
    delay_us(I2C_DELAY);

    SCL = 1;
    delay_us(I2C_DELAY);

    SDA = 1;
    delay_us(I2C_DELAY);
}

//The I2C Clock has a minimum of 2us high time and 2us low time
//8MHz = 0.5us per instruction
unsigned char i2c_read_byte(void)
{
    int j, in_byte;

    SCL = 0;

    READ_sda();

    for(j = 0 ; j < 8 ; j++)
    {
        SCL = 0;
        delay_us(I2C_DELAY);

        SCL = 1;
        delay_us(I2C_DELAY);

        in_byte = in_byte << 1;
        in_byte |= SDARD;
    }

    return(in_byte);
}

//The I2C Clock has a minimum of 2us high time and 2us low time
//8MHz = 500ns per instruction
unsigned char i2c_send_byte(unsigned char out_byte)
{
    unsigned char i;

    WRITE_sda();

    for(i = 0 ; i < 8 ; i++)
    {
        SCL = 0;
        delay_us(I2C_DELAY);

		if (out_byte & 0x80) {
			SDA = 1;
		} else {
			SDA = 0;
		}
        out_byte = out_byte << 1;
        delay_us(5);

        SCL = 1;
        delay_us(I2C_DELAY);
    }

    //Read ack
    SCL = 0;
    delay_us(I2C_DELAY);

    READ_sda();

    SCL = 1;
    delay_us(I2C_DELAY);

    //Wait for IC to acknowledge
    for(i = 0 ; i < 255 ; i++)
        if(SDARD == 0) break;

    SCL = 0;
    delay_us(I2C_DELAY);

    if (i == 255) return(NO_ACK);
    
    return(ACK);
}

//====================================

void delay_us (unsigned char x)
{
	while (x--) {
		Nop ();
		Nop ();
		Nop ();
		Nop ();
		Nop ();
		Nop ();
		Nop ();
		Nop ();
		Nop ();
		Nop ();
		Nop ();
		Nop ();
		Nop ();
		Nop ();
		Nop ();
		Nop ();
	}
}



