/******* T1DP.c  for PIC18LF2321
*
* Use Fosc = 4 MHz for Fcpu = Fosc/4 = 1 MHz.
* Sleep for 16 ms (nominal), using watchdog timeout for wakeup.
* Toggle RC2 output every 16 milliseconds for measuring looptime with scope.
* Blink LED on RA7 for 16 ms every four seconds.
*
*             Current draw = 4 uA
*
******* Program hierarchy *****
*
* main
*  Initial
*  BlinkAlive
*
*******************************
*/

#include <p18f2321.h>           // Define PIC18LF4321 registers and bits
#include <usart.h>

/*******************************
* Configuration selections
*******************************
*/



#pragma config OSC = INTIO1     // Use internal osc, RA6=Fosc/4, RA7=I/O
#pragma config PWRT = ON        // Enable power-up delay
#pragma config LVP = OFF        // Disable low-voltage programming
#pragma config WDT = OFF        // Disable watchdog timer initially
#pragma config WDTPS = 4        // 16 millisecond WDT timeout period, nominal
#pragma config MCLRE = ON       // Enable master clear pin
#pragma config PBADEN = DIG     // PORTB<4:0> = digital
#pragma config CCP2MX = RB3     // Connect CCP2 internally to RB3 pin
#pragma config BOR = SOFT       // Brown-out reset controlled by software
#pragma config BORV = 3         // Brown-out voltage set for 2.1V, nominal
#pragma config LPT1OSC = OFF    // Deselect low-power Timer1 oscillator


/*******************************
* Global variables
*******************************
*/
#define MAX_BYTES 5

unsigned int DELAY;             // Counter for obtaining a delay
unsigned char ALIVECNT;         // Counter for blinking "Alive" LED

unsigned int flagDone;

unsigned char databits[MAX_BYTES];
unsigned char maskbit;
unsigned char index;
unsigned int bitCount;
unsigned int ledCounter;


	unsigned char sendbits[MAX_BYTES+2];
	unsigned int sendMask;
	unsigned int sendIndex;
	unsigned int dataMask;
	unsigned int dataIndex;
	unsigned int offset;

unsigned char EEADDRESS;        // Starting address in EEPROM
unsigned char EECNT;            // Number of bytes to copy

char GIEHCOPY;                  // Copy of GIEH bit for EEPROM writes
char SBORENCOPY;                // Copy of SBOREN bit for EEPROM writes
char* RAMPTR;                   // Pointer to RAM array to be copied to EEPROM

unsigned char EEStackPtr;

/*******************************
* Constant strings
*******************************
*/

const char HEXITS[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};


/*******************************
* Variable strings
*******************************
*/

/*******************************
* Function prototypes
*******************************
*/

void Initial(void);
void BlinkAlive(void);
void Pushbutton(void);
void HiPriISR(void);
void LoPriISR(void);
void xmitGO(void);
void InitTX(void);
void txHex(unsigned char);

/*******************************
* Macros
*******************************
*/

#define Delay(x) DELAY = x; while(--DELAY){ Nop(); Nop(); }
#define TXascii(in)  TXREG = in; while(!TXSTAbits.TRMT)

/////// Main program //////////////////////////////////////////////////////////

/*******************************
* main
*******************************
*/

void main()
{
	Initial();                   // Initialize everything
	InitTX();
	while (1)
	{
		flagDone = 1;

		PORTCbits.RC2 ^= 1;       // Toggle pin, for measuring loop time
		Sleep();
		Nop();
		
		if (PORTAbits.RA2 == 1)
			++ledCounter;
		if (ledCounter >= 43)		// leaves the LED on for about .5 sec with 16ms watchdog
			PORTAbits.RA2 = 0;

		if (bitCount > 0 && flagDone) {
			PORTAbits.RA2 = 1;	// turn LED on
			ledCounter = 0;  // reset counter

			//TXascii('\r');
			//TXascii('\n');
			xmitGO();

			// cleanup
			for( index = 0; index < MAX_BYTES; index++){
				databits[index] = 0;
			}
			
			maskbit = 128;
			index = 0;
			bitCount = 0;
		}
	}
}

/*******************************
* Interrupt vectors
*******************************
*/
// For high priority interrupts:
#pragma code high_vector=0x08
void interrupt_at_high_vector(void)
{
	_asm GOTO HiPriISR _endasm}
#pragma code
#pragma interrupt HiPriISR

/*******************************
* HiPriISR
*
* Respond to rising and falling edges on INT2 input from RPG.
*******************************
*/
void HiPriISR()
{
	flagDone = 0;
	++bitCount;

	if(INTCON3bits.INT2IF){
		databits[index] |= maskbit;
		INTCON3bits.INT2IF = 0;
		//TXascii('1');
	}
	
	if(INTCON3bits.INT1IF){
		INTCON3bits.INT1IF = 0;
		//TXascii('0');
	}
	
	maskbit >>= 1;
	
	if(!maskbit){
		index++;
		maskbit = 128;
	}
	
	//if( !index && maskbit == 8)
	
}

/*******************************
* xmitGO
*
* 
*******************************
*/

void xmitGO()
{
	INTCONbits.GIEH = 0;
	
	// this block of code will send the number of bits read over wiegand
	/*TXascii((char)(bitCount/10 + 48));
	TXascii((char)(bitCount%10 + 48));
	TXascii(' '); */

	offset = (MAX_BYTES * 8) - bitCount;	// difference between max bits and bitCount
	sendIndex = 1 + offset / 8;				// first sendBits char array index to use (starting at least at the second byte)
	sendMask = 128 >> (offset % 8);			// first bit to use in first char being used
	dataIndex = 0;							// start at the beginning of databits
	dataMask = 128;							// first bit in databits

	if (MAX_BYTES * 8 < bitCount)	// if we have too many bits, gtfo
	{
		TXascii('X');		// send just an X
		TXascii('\r');
		TXascii('\n');
		return;
	}

	for( index = 0; index < MAX_BYTES+2; index++){	// initialize the send bits to 0
		sendbits[index] = 0;
	}

	if (sendMask < 128)	// add 1 bit BEFORE all of the other bits.  why?  i don't know.  ask HID.
		sendbits[sendIndex] = (sendMask << 1) & 0xFF;

	for (index = 0; index < bitCount; index++) {	// loop through all bits
		if (databits[dataIndex] & dataMask)			// if databits is 1 at current dataMask
			sendbits[sendIndex] |= sendMask;		// set sendbits to 1 at current sendMask
		sendMask >>= 1;		// move masks to the right
		dataMask >>= 1;
		if(!sendMask){		// if at the end of this byte, move to the next byte
			sendIndex++;
			sendMask = 128;
		}
		if(!dataMask){
			dataIndex++;
			dataMask = 128;
		}
	}

	// compute checksum byte for the last byte
	for (index = 0; index < MAX_BYTES+1; index++)	// checksum is the 8 least signigicant 
		sendbits[MAX_BYTES+1] += sendbits[index];	// bits of the sum of all the other bytes

	for(index = 0; index < MAX_BYTES+2; index++){	// send the sendbits as hex!
    	txHex(sendbits[index]);
	}

	TXascii('\r');
	TXascii('\n');
	
	
	INTCONbits.GIEH = 1;

}

/*******************************
* xmitGO
*
* 
*******************************
*/

void txHex(unsigned char in)
{
	// send a char as two hexits
	TXascii( HEXITS[((0xF0 & in)>>4)] );
	TXascii( HEXITS[(0x0F & in)] );
}

/*******************************
* Initial
*
* This function performs all initializations of variables and registers.
*******************************
*/

void Initial()
{
	int i;

	OSCCON = 0b01100010;         // Use Fosc = 4 MHz (Fcpu = 1 MHz)
	ADCON1 = 0b00001111;         // RA0,RA1,RA2,RA3 pins analog; others digital
	TRISA = 0b00001011;          // Set I/O for PORTA
	TRISB = 0b11100110;          // Set I/O for PORTB
	TRISC = 0b10000000;          // Set I/O for PORTC
	PORTA = 0;                   // Set initial state for all outputs low
	PORTB = 0b00000000;
	PORTC = 0;
	Delay(50000);                // Pause for half a second
	RCONbits.SBOREN = 0;         // Now disable brown-out reset
	ALIVECNT = 247;              // Blink immediately
	WDTCONbits.SWDTEN = 1;       // Enable watchdog timer

	maskbit = 128;
	index = 0;
	flagDone = 1;
	bitCount = 0;
	ledCounter = 0;

	for( i = 0; i < MAX_BYTES; i++){
		databits[i] = 0;
	}

	INTCON2bits.INTEDG1 = 0;
	INTCON3bits.INT1IP = 1;
	INTCON3bits.INT1IE = 1;
	INTCON3bits.INT1IF = 0;

	INTCON2bits.INTEDG2 = 0;
	INTCON3bits.INT2IP = 1;
	INTCON3bits.INT2IE = 1;
	INTCON3bits.INT2IF = 0;

	RCONbits.IPEN = 1;
	INTCONbits.GIEL = 1;
	INTCONbits.GIEH = 1;

}

/*******************************
 * InitTX
 *
 * This function initializes the UART for its TX output function.  It assumes
 * Fosc = 4 MHz.  For a different oscillator frequency, use Figure 6-3c to
 * change BRGH and SPBRG appropriately.
 *******************************
 */
void InitTX()
{
   RCSTA = 0b10010000;          // Enable UART
   TXSTA = 0b00100100;          // Enable TX
   SPBRG = 12; //12                 // Set baud rate
   BAUDCON = 0b00000000;        // Invert TX output
	// 19200 BAUD RATE

}

/****************************
 * EEArrayWrite
 *
 * Each time this function is called with EECNT != 0, it writes one byte
 * from the RAM char[] pointed by RAMPTR, into the EEPROM location whose
 * address is in EEADDRESS. Then it increments RAMPTR and EEADDRESS and
 * decrements EECNT. RAMPTR, EEADDRESS and EECNT should not be modified
 * by other code until EECNT is 0.
 * Variable declaration:
 * char* RAMPTR;         unsigned char EEADDRESS;        unsigned char EECNT;
 * Example usage:
 *   char TEMPARRAY[] = {'a','b','c','d'};
 *   RAMPTR = TEMPARRAY + 1;
 *   EEADDRESS = 0x20;
 *   EECNT = 2;
 *   After 2 calls of EEArrayWrite, EECNT = 0;
 *   and EEPROM has 'b' and 'c' in address 0x20 and 0x21 respectively.
 ****************************
 *
void EEArrayWrite()
{
   if(EECNT && !EECON1bits.WR)                                              // Skip to next loop time for second write
   {
      EEDATA = *(RAMPTR++);
      EEADR = EEADDRESS++;
      EEwrite();
      EECNT--;
   }
}

/****************************
 * EEread
 *
 * This function reads from the EEPROM address identified by EEADR
 * into EEDATA.
 ****************************
 *
void EEread()
{
   while (EECON1bits.WR);   // Wait on the completion of any write
   EECON1bits.RD = 1;       // Set the RD bit to read from EEADR into EEDATA
}

/****************************
 * EEwrite
 *
 * This function writes the data contained in EEDATA into the EEPROM
 * address identified by EEADR.
 * The write is self-timed and takes about 4 milliseconds.
 ****************************
 *
void EEwrite()
{
   while (EECON1bits.WR);       // Wait on the completion of any write
   SBORENCOPY = RCONbits.SBOREN;  // Copy SBOREN for subsequent restore
   RCONbits.SBOREN = 1;         // Enable brown-out reset
   GIEHCOPY = INTCONbits.GIEH;  // Copy GIEH for subsequent restore
   INTCONbits.GIEH = 0;         // Disable all interrupts
   EECON1bits.WREN = 1;         // Enable write operation
   EECON2 = 0x55;               // Write first key
   EECON2 = 0xAA;               // Write second key
   EECON1bits.WR = 1;           // Set WR bit to initiate write
   INTCONbits.GIEH = GIEHCOPY;  // Restore global interrupt state
   RCONbits.SBOREN = SBORENCOPY;  // Restore brown-out reset state

   PORTCbits.RC2 ^= 1;                                                      // Toggle pin to flag where write occurs
   Delay(100);                                                              // Add a delay to show sleep after 1 ms of writing
}
*/