//======================================================
// abacom.c    written by Aaron Ramsey, Jan. 25th, 2000
//
// This program is a trial run of a serial transmission
// routine that is interrupt driven. It is designed to
// work with the ABACOM transmitters and receivers, which
// are designed to work at 10000bps. As we are merely
// receiving data on the other end with another pic,
// our baud rates do not need to be standard baud rates,
// but instead merely need to be the same on either end.
// This program will be written for a 9765.625bps
// rate, which is close to 9600 (under 2% error). This
// baud rate comes around from the fact that we are using
// a 20MHz clock. That gives a 5000000 instruction rate.
// We will be using timer0 which is a 8 bit timer, which
// will overflow at a rate of 5MHz/256=19531.25 which we
// will then divide in two.
//
// This program is written to use pin B0 as the receiving
// input and B1 as the transmitting output. B0 is used as
// it has an interrupt that will tell us when an input signal
// has begun to arrive.
//
// This program uses timer0 (RTCC) for timing the rx and tx
// routines, so we could send and receive at the same time
// with a little care taken. There are some subtle things
// to consider before doing it this way. This may be
// implemented at a later date.
//
// The abacom transceivers were acting up with the old code
// and so we are trying something new. It seems like the
// abacom receivers take about 30ms to 'turn on'. This means
// that we need to tx meaningless crap for 30ms or so before
// we can start transmitting the real data. This is because
// we are using bursts of data rather than a continuous
// stream of data. This is obviously not acceptable, as it
// drastically reduces bits per second that we can achieve.
//
// This forces us to change things quite a bit. We will tx
// a stream of 1's and 0's whenever the channel is not being
// used for data. The line will be kept active by whoever
// received the last packet of data. The main CPU will keep
// the line active in the beginning, before communication with
// any of the robots. When it sends data to one of the robots,
// after receiving the data, that robot will keep the line
// active until it send data back to the main CPU. The main
// CPU then takes over until it sends the next packet of
// data to the next robot... and on and on it goes.
//
// The start bit is 4 high bits, and is easily distinguished
// from the high/lows that keep the line active. The receiver
// will be interrupted on every low to high change on the
// RX_PIN line (pinB0). The ISR will first check a flag to
// see if we are already receiving or sending data. If we
// are, it just returns. If not, it loads the timer with a
// 253. This causes the timer0 interrupt to interrupt in 
// 258 (256+3) clocks from then. This places the timer0
// in the middle of the next possible bit... This synchronizes
// the receiver with the transmitter constantly.
//
// In the timer0 interrupt service routine, we take care of
// the transmitting and receiving functions. The transmitter
// is easy to understand... it first sends the 4 bit start 
// header, then it simply takes the manchester encoded data
// and shifts it out the TX_PIN. When it has tx'd one byte,
// it encodes the next byte, and tx's it too... etc... until
// it has sent all the data in the buffer. It then sends the
// 4 bit stop bit (4 lows). The receiver side of the ISR
// first watches for the start header bits. If we get 4 
// highs in a row, we have received a valid start bit. We then
// bring in the 16 bits of data. Then we bring in the
// next 16 bits... etc... until we have read in the total
// number of bytes that constitutes our packet. (likely 
// 30 to 40 bytes). We don't send any start bits/stop bits
// to resynch our clocks like the previous protocol. We
// resynch our clock instead everytime that we get a
// low to high level change on the RX pin by loading the
// clock with a value.
//
//============================================================

#include <16C77.h>

#fuses HS, NOWDT, NOPROTECT, PUT
#use delay(clock=10000000)
#zero_ram
#use fast_io(B)

//in CCS, short is 1 bit, char is 8 bits and long is 16 bits
//redefine these to something more readable
#define bit short
#define uint8 char
#define uint16 long

// define the rx and tx pins. TX can be any pin, but RX must be
// the B0 pin due to use of the EXT interrupt. If it needs to
// be moved, the interrupt handler and corresponding code
// must also be rewritten
#define RX_PIN pin_B0
#define TX_PIN pin_B1

#define ROBOT_ID 1
#define BUFFER_SIZE 15

//----=GLOBAL VARIABLES=---------

uint8 transmitting_header=0;	// tx'ing the high/low thing
bit transmitting_data=0;	// in tx mode if high
bit transmitted_data=0;	// tx mode completed if high
bit receiving_data=0;		// in rx mode if high
bit received_data=0;		// we have received data
bit receive_error=0;		// There is a problem with the byte received
bit receive_buffer_error=0;	// we had a rx buffer overrun
uint16 rx_data,tx_data=0;		// holds the data being received or transmitted
uint8 rx_counter,tx_counter=0;	//counts the # of bits sent or received
uint8 start_bit_counter=0;	// valid start bit counter
bit blip=0;				// high/low thing for start header
bit send_start_bit=0;		// whether we are tx'ing the start 'bit'
bit send_stop_bit=0;		// yadda yadda yadda yadda  stop 'bit'
uint8 stop_bit_counter=0;		// number of stop bits tx'd
uint8 rxtx_buffer[BUFFER_SIZE];	// a buffer to hold the RX data
uint8 rxtx_buf_counter=0;		// current position in the buffer
bit first_byte=0;			// the first byte of rf data = TRUE
							

//-----=function definition=-------

uint16 man_encode(uint8 unenc);
uint8 man_decode(uint16 enc);
bit transmit_data(void);
bit receive_data(void);

//()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()
//  RX MAIN ROUTINE
void main(void) {
	uint8 test1,test2,counter=0;

	set_tris_b(0b01000001); // B0,B6 input, the rest outputs

	disable_interrupts(GLOBAL);
	ext_int_edge(L_TO_H);
	// set up the RTCC and EXT_B0 interrupts
	setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
	enable_interrupts(INT_RTCC);
	enable_interrupts(INT_EXT);

	//MASTER SETUP
	// for now, we just fill buffer with some numbers....
	for (counter=0;counter<BUFFER_SIZE;counter++) {
		rxtx_buffer[counter]=counter;
	}
	enable_interrupts(GLOBAL);
	transmitting_header=TRUE;
	delay_ms(5000);
	transmit_data();

	//SLAVE SETUP
//	for (counter=0;counter<BUFFER_SIZE;counter++) {
//		rxtx_buffer[counter]=0;
//	}
//	delay_ms(2000);
//	enable_interrupts(GLOBAL);	

	// this rs232 port is for debugging on the computer
	#use rs232 (baud=9600,xmit=pin_B7,rcv=pin_B6)
	// infinite loop waiting for things to happen in the interrupt routine
	// we basically just transmit stuff out, then wait for things to come
	// back, and then transmit again. It's like a ping-pong game with the
	// other robot
	do {
		//waiting for received data
		if (received_data) {
			// received data... pretend that we did something with it by
			// clearing the received_data and received_error flags
			// normally we would check for a receive_error and request
			// a retransmission if necessary
			received_data=0;
			receive_error=0;
			//wait for 2 seconds
			delay_ms(2000);
			//fill the buffer with some data
			for (counter=0;counter<BUFFER_SIZE;counter++) {
				rxtx_buffer[counter]++;
			}
			//transmit that data out
			transmit_data();
		}
	} while (TRUE);
}

//---------------------------------------------------------------------
// ext_isr()
//
// If a B0 interrupt occurs, then we are may be starting to receive a
// transmission. If we are transmitting, we ignore anything on the
// RX pin. If not, we load the clock so that it interrupts in half
// the time. This resynchs our rx'r clk with the tx'r clk.
//----------------------------------------------------------------------
#INT_EXT
ext_isr() {
	if ((transmitting_data)|(transmitting_header)) return;

	// else resynch the clock just in case.. let the rtcc take care of
	// the rest of the nasty business. We are using a prescale of 2
	// on the rtcc, and when we load the clock with a value, it resets
	// the prescaler too... by setting it at 253 or so, it will roll over
	// right away, and then only have to do one more set of 256 counts
	// before interrupting
	set_rtcc(253);
}

//------------------------------------------------------------------------
// rtcc_isr()
//
// This routine decides whether we are transmitting or receiving, and
// acts on that. Receiving takes precedence over transmitting. If for
// some reason we are trying to do both at the same time (shouldn't 
// happen as we will attempt to trap for that in the main code), the
// receive section will be executed and the transmit will be ignored.
//------------------------------------------------------------------------
#INT_RTCC
rtcc_isr() {
	// --------LOOKING FOR START BIT---------
	if ((receiving_data==FALSE)&(transmitting_data==FALSE)&(transmitting_header==FALSE)) {
		// not receiving or transmitting, therefore we must check for
		// a valid start bit (4 highs in a row)
		if (input(RX_PIN)) {
			start_bit_counter++;
			if (start_bit_counter==4) {
				receiving_data=TRUE; //valid start bit
				start_bit_counter=0; //reset counter for next time
				first_byte=TRUE; // we will be getting the first byte
				return;
			}
		} else start_bit_counter=0;
		return;
	}

	// ------ TRANSMITTING HEADER--------
	if (transmitting_header) {
		output_bit(TX_PIN,blip);
		blip=!blip;
		return;
	}

	// ------RECEIVING-----
	if (receiving_data) {
		// we are in receiving mode
		rx_counter++;

		// shift the data on pin B0 into the rx_data variable
		// Basically we shift rx_data 1 to the left, then
		// OR it with the input of pin B0 (which has been cast
		// uint16 so that it will either be a 0x0001 or 0x0000)
		rx_data=(rx_data<<1)|((uint16)input(RX_PIN));

		// we need to check if we have received 16 bits of data yet
		if (rx_counter==16) {
			// we have all 16 bits of data, so we need to decode it
			// from the manchester encoding and put it into the
			// rx_buffer.
			rxtx_buffer[rxtx_buf_counter]=man_decode(rx_data);
			if (first_byte) {
				// we just received the first byte of data from
				// the tx'r... the first byte contains the number
				// of the robot that the tx'r is trying to communicate
				// with... we check it, and if that robot isn't this
				// one, we ignore the rest of the data
				first_byte=false;
				if (!(rxtx_buffer[0]==ROBOT_ID)) {
					// first byte doesn't match our robot ID
					rx_counter=0;
					rxtx_buf_counter=0;
					receiving_data=FALSE;
					return;
				}
			}
			// if the received_data flag is still high at this point,
			// then we need to report a receive buffer error, as we
			// haven't read the previous data from the rx_data_buffer
			// yet, and we just overwrote it... whoops!
			if (received_data) receive_buffer_error=TRUE;

			rx_counter=0;
			rxtx_buf_counter++;
			if (rxtx_buf_counter==BUFFER_SIZE) {
				// we have all the data
				receiving_data=FALSE;
				received_data=TRUE;
				transmitting_header=TRUE;
				rxtx_buf_counter=0;
			}
			return;
		}
		return;
	}

	// -------TRANSMITTING--------
	if (transmitting_data) {
		// We are in transmitting mode
		if (send_start_bit) {
			// CAREFUL... if the last start header bit is a high,
			// our rx detector will see it as the first bit of
			// the 4 start bits, and all data after that will
			// be mis-detected
			start_bit_counter++;
			if (start_bit_counter==1) {
				output_low(TX_PIN);
			} else {
				output_high(TX_PIN);
			}
			if (start_bit_counter==5) {
				// we have now sent the 4 start bits, so now tx data next interrupt
				start_bit_counter=0;
				send_start_bit=FALSE;
			}
			return;
		}
		if (send_stop_bit) {
			stop_bit_counter++;
			output_low(TX_PIN);
			if (stop_bit_counter==4) {
				stop_bit_counter=0;
				send_stop_bit=FALSE;
				tx_counter=0;
				rxtx_buf_counter=0;
				transmitting_data=FALSE;
				transmitted_data=TRUE;
			}
			return;
		}
		// If the tx_counter is equal to 16, then we need to
		// prepare for the next byte of data after sending the last
		// bit of data from the current byte.
		// Otherwise we just send out the next bit of data for the
		// current byte.
		tx_counter++;
		// transmit MSB first
		output_bit(TX_PIN,bit_test(tx_data,15));
		// shift out the MSB and get tx_data ready for next time
		tx_data=tx_data<<1;

		if (tx_counter==16) {
			// we just tx'd the last bit of data from the current byte
			// Set up for the next byte
			tx_counter=0;
			rxtx_buf_counter++;
			if (rxtx_buf_counter==BUFFER_SIZE) {
				// we have sent all the data in the buffer
				// and now we need to send the stop bit
				send_stop_bit=TRUE;
				return;
			}
			tx_data=man_encode(rxtx_buffer[rxtx_buf_counter]);
			return;
		}
		return;
	}
}

//--------------------------------------------------------------
// bit transmit_data(void)
//
// This routine sets up the buffer to be transmitted and the
// various flags that need to be set in order to TX stuff...
// This code assumes that we are at the robot and tx'ing 
// position data, etc... back to the main CPU
//--------------------------------------------------------------
bit transmit_data(void) {
	uint8 counter=0;

	// Check to make sure we are not already tx'ing or rx'ing
	if ((transmitting_data)|(receiving_data))
		//already transmitting or receiving
		return(0);
	else {

		// code to set up rxtx_buffer[] here - not implemented in this demo

		// load tx_data with the first byte to send
		tx_data=man_encode(rxtx_buffer[0]);

		// enable the tx'ing by setting transmitting_data high
		transmitting_header=FALSE;	
		send_start_bit=TRUE;
		set_rtcc(0); //make sure timer0 starts at 0
		transmitting_data=TRUE;
		
		return(1);
	}
}

//-------------------------------------------------------------
// bit receive_data(void)
//
// This procedure will get called by the main loop when 
// the received_data flag goes high. It should check the
// CRC to make sure that everything was received correctly
// break everything apart into the instructions or data that
// the buffer now contains....
//-------------------------------------------------------------
bit receive_data(void) {
	// do something with the data in the receive buffer
}	


//-------------------------------------------------------------
// uint16 man_encode(int enc)
//
// This function encodes a 8 bit value into a 16 bit manchester
// encoded variable which it then returns.
//
// This function encodes the bits in a unique way. The odd
// bits (7,5,3,1) are encoded into the msb 8 bits of data to
// return and the even bits (6,4,2,0) are encoded into the
// lsb 8 bits of data to return. Encoding this way saves alot
// of bit testing and flipping.
//
// 0x0E (00001110) encodes into 0x5AA6 (0101101010100110)
// instead of the more natural 0x55A9 (0101010110101001)
//
//-------------------------------------------------------------
uint16 man_encode(uint8 unenc) {
	uint8 odd_byte,even_byte,temp;

	// what I really want to do was this...
	//		odd_byte=(unenc&0xAA)|(~unenc&0xAA)>>1);
	// but the CCS compiler didn't like it all on one line
	// so I had to use an intermediate variable to get around it
	
	odd_byte=unenc&0xAA;
	temp=(~unenc&0xAA)>>1;
	odd_byte=odd_byte|temp;
	
	// what I really want to do was this...
	//		even_byte=(unenc&0x55)|(~unenc&0x55)>>1);
	// but the CCS compiler didn't like it all on one line
	// so I had to use an intermediate variable to get around it

	even_byte=unenc&0x55;
	temp=(~unenc&0x55)<<1;
	even_byte=even_byte|temp;
	return((uint16)odd_byte<<8)|even_byte;
}


//-------------------------------------------------------------
// uint8 man_decode(uint16 enc)
//
// This function decodes a 16 bit manchester encoded variable
// and returns the actual 8 bit unencoded value.
// See man_encode for a description of the encoding technique.
//-------------------------------------------------------------
uint8 man_decode(uint16 enc) {
	uint8 odd_byte,even_byte,temp;
	
	odd_byte=(uint8)(enc>>8);
	if((odd_byte&0xAA)^((~odd_byte&0x55)<<1)) {
		receive_error=1;
		return(0);
	} else odd_byte&=0xAA;
	even_byte=(uint8)enc;
	if((even_byte&0x55)^((~even_byte&0xAA)>>1)) {
		receive_error=1;
		return(0);
	} else even_byte&=0x55;
	receive_error=0;
	return(odd_byte|even_byte);
}



