/********************************************************************
 * FileName:        reference code.c
 * Processor:       PIC18F4520
 * Compiler:        MPLAB C18 v.8.36
 *
 * This file is a compilation of code snippets used in ME430 for reference purposes
 *  Note: Copy and Paste only gets you so far.  Make sure you understand things!
 *
 *       Author               Date              Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
// David Fisher 			10-28-08
// David Fisher				10-21-09        Add UART reference info

/**  Header Files **************************************************/
#include <p18f4520.h> 
#include <delays.h>
#include "LCD Module.h"
#include <stdio.h>
#include <adc.h>
#include <timers.h>
#include <pwm.h>
#include <portb.h>
#include <usart.h>

/** Configuration Bits *********************************************/
#pragma config OSC = INTIO67
#pragma config WDT = OFF
#pragma config LVP = OFF
#pragma config BOREN = OFF
#pragma config XINST = OFF

/** Define Constants Here ******************************************/
#define PRESSED 0
#define UNPRESSED 1
#define DELAY_TIME 100

// Buzzer 
#define VOLUME 100
#define NO_VOLUME 0

// Some mustic note #defines to use with OpenPWM1 on the buzzer.  Only works for...
//     4 MHz with a 16 prescaler on Timer 2
//     1 MHz with a 4 prescaler on Timer 2
//or 250 kHz with a 1 prescaler on Timer 2
#define A_UP_880_HZ 70
#define G_UP_784_HZ 78
#define F_UP_698_HZ 88
#define E_UP_659_HZ 93
#define D_UP_587_HZ 105
#define C_UP_523_HZ 118
#define B_MID_494_HZ 125
#define A_MID_440_HZ 141
#define G_MID_392_HZ 158
#define F_MID_349_HZ 177
#define E_MID_330_HZ 188
#define D_MID_294_HZ 211
#define C_MID_262_HZ 237

// UART constants
#define MAX_RX_MESSAGE_SIZE 32
#define MESSAGE_TERMINATOR  10

/** Local Function Prototypes **************************************/
void low_isr(void);
void high_isr(void);

// Remember no UART on Exam 2, but still a useful reference. 
void clearBuffer(char bufferToClear[]); // UART helper function
void loadNewMessage(void); // UART helper function

/** Declare Interrupt Vector Sections ****************************/
#pragma code high_vector=0x08

void interrupt_at_high_vector(void) {
    _asm goto high_isr _endasm
}

#pragma code low_vector=0x18

void interrupt_at_low_vector(void) {
    _asm goto low_isr _endasm
}

/** Global Variables ***********************************************/
char recentButtonState = UNPRESSED;     // Digital IO
int RA0result = 0;                      // ADC variables
char line1[17];                         // LCD variables
char line2[17];

char tempRxBuffer[MAX_RX_MESSAGE_SIZE]; // UART variables
char newRxMessage[MAX_RX_MESSAGE_SIZE];
char newMessageAvailable = 0;

/*******************************************************************
 * Function:        void main(void)
 ********************************************************************/
#pragma code
void main(void) {

// -------------------------- General Pin I/O -----------------------------
// REQUIRES: #include <p18f4520.h> 
/*
        ADCON1 = 0x0F;			// All pins digital IO (simple 1 liner instead of the OpenADC command if not using the ADC)
        TRISC = 0x00;			// All of PORTC is an output
        TRISAbits.TRISA4 = 1; 		// Makes RA4 an input
        PORTC = 0b0110;			// Turn on RC2 and RC3 others off
        PORTCbits.RC5 = 1;		// Turn on RC5 others no change

        // Optional PRESSED/UNPRESSED code
        // To do stuff once on a button press
        if(PORTAbits.RA4 == PRESSED) {
                if(recentButtonState == UNPRESSED) {
                        // Do stuff that happens once per press
                        PORTBbits.RB0 = 1;
                }
                recentButtonState = PRESSED;
        } else {
                recentButtonState = UNPRESSED;
                PORTB = 0x00;
        }
	
	    // Same code as above but using RB0 and refactored to look like the version in Lab 4.
		if ((PORTBbits.RB0 == PRESSED) && (recentButtonState == UNPRESSED)) {
			 // This is a new press of the button
			 // Stuff for a new button press goes below
			 //
			 // new button press stuff
			 // new button press stuff
			 //
			 recentButtonState = PRESSED;
		} else if ((PORTBbits.RB0 == PRESSED) && (recentButtonState == PRESSED)) {
			 // This is an old press of the button
			 // We don't need to do anything
		} else {
			 // The button isn't pressed.
			 // Record that the button isn't pressed and put stuff for an unpressed button below
			 //
			 // unpressed button stuff
			 // unpressed button stuff
			 //
			 recentButtonState = UNPRESSED;
		}
*/

// ---------------------- Using Delay Functions ---------------------------
// REQUIRES: #include <delays.h>
/*
    OSCCONbits.IRCF2 = 1;  // 4 MHz
    OSCCONbits.IRCF1 = 1;
    OSCCONbits.IRCF0 = 0;

    PORTB = 0b00000001;
    Delay1KTCYx(DELAY_TIME);
    PORTB = 0b00000010;
    Delay1KTCYx(DELAY_TIME);
    PORTB = 0b00000100;
    Delay1KTCYx(DELAY_TIME);
    PORTB = 0b00001000;
    Delay1KTCYx(DELAY_TIME);
    PORTB = 0b00000100;
    Delay1KTCYx(DELAY_TIME);
    PORTB = 0b00000010;
    Delay1KTCYx(DELAY_TIME);

    // If clock is at 4 MHz each instruction takes 1 uS (0.000001 seconds)
    // If DELAY_TIME = 100 then Delay1KTCYx(DELAY_TIME) would be 1000 * 100 * 1 uS = 0.1 second
*/

// ----------------  Displaying information to the LCD --------------------
// REQUIRES: #include "LCD Module.h"
// REQUIRES: #include <stdio.h>
/*
    XLCDInit();
    XLCDClear();

    sprintf(line1, "RA0 ADC -> %d", RA0result);
    XLCDL1home();
    XLCDPutRamString(line1);

    sprintf(line2,"RA1 ADC -> %d",RA1result);
    XLCDL2home();
    XLCDPutRamString(line2);
*/

// --------------------------- Using Timers -------------------------------
// REQUIRES: #include <timers.h> 
/*
    OSCCONbits.IRCF2 = 1;  // 4 MHz
    OSCCONbits.IRCF1 = 1;
    OSCCONbits.IRCF0 = 0;

    // Note, this section is for Timers WITHOUT interrupts.
	// See the interrupts section later for Timers with interrupts
    OpenTimer0( TIMER_INT_OFF & T0_16BIT & T0_SOURCE_INT & T0_PS_1_128 );
    OpenTimer1( TIMER_INT_OFF & T1_16BIT_RW & T1_SOURCE_INT & T1_PS_1_8 & T1_OSC1EN_OFF);
	// Note T1_OSC1EN_OFF was added to free up RC0 to use for digital IO (see video 7-3 12:00)

    OpenTimer2(TIMER_INT_OFF & T2_PS_1_16);
    // If using PWM you must open Timer 2
    // PWM period =[(timer ticks) + 1] x 4 x TOSC x TMR2 prescaler

	OpenTimer3(TIMER_INT_OFF & T3_16BIT_RW & T3_SOURCE_INT & T3_PS_1_8);
	
    // Sample calculation:
    // Clock Frequency = 4 MHz
    // Instruction Cycle Frequency = 1 MHz
    // Timer 0 Frequency = 1 MHz / 128 = 7812.5 Hz
    // Timer 0 Period =  128 uSeconds <-- Always equals prescaler when clock is 4 MHz
    // Overflow every 128 uSecond * 2^16 ticks = 8.4 seconds

    // Set timer value
    WriteTimer0(60000);  // Example here goes 5536 ticks before overflow

    // Read Timer value
    unsigned int readTimer = ReadTimer0();  // Less commonly used, but functional
*/

// -------------------------- Using the ADC -------------------------------
// REQUIRES: #include <adc.h>
/*
        OpenADC(ADC_FOSC_8 & ADC_RIGHT_JUST & ADC_12_TAD,
                        ADC_CH0 & ADC_INT_OFF & ADC_REF_VDD_VSS,
                        0x0E);

        SetChanADC(ADC_CH0);	// Select the pin
        ConvertADC(); 			// Start conversion
        while( BusyADC() ); 	// Wait for completion
        RA0result = ReadADC(); 	// Read result
*/

// -------------------------- Using the PWM -------------------------------
// REQUIRES: #include <timers.h>
// REQUIRES: #include <pwm.h>
/*
        OSCCONbits.IRCF2 = 1;  // 4 MHz
        OSCCONbits.IRCF1 = 1;
        OSCCONbits.IRCF0 = 0;

        OpenTimer2(TIMER_INT_OFF & T2_PS_1_16);

        OpenPWM1(162);				// 163 Timer ticks per period
        SetDCPWM1(512);				// 50% duty cycle

        // PWM period =[timer ticks + 1] x 4 x TOSC x TMR2 prescaler
        // Create an Excel spreadsheet and use that to find frequencies quickly
        // or use #defines for specific notes (setup

        OpenPWM1(A_MID_440_HZ);		// Use the middle A #define to set the frequency
        SetDCPWM1(VOLUME);  		// set the duty cycle (provides volume)

        SetDCPWM1(NO_VOLUME);		// Stop the buzzer
*/

// -------------------- Using UART Communication --------------------------
// REQUIRES: #include <usart.h>
// REQUIRES: #include <stdio.h>
/*
        OSCCONbits.IRCF2 = 1;  // 4 MHz
        OSCCONbits.IRCF1 = 1;
        OSCCONbits.IRCF0 = 0;

        // configure USART for 19200 buad rate (from 4 MHz) with receive interrupts
        RCONbits.IPEN = 1;      // Priority mode interrupts
        OpenUSART( USART_TX_INT_OFF &
                USART_RX_INT_ON &
                USART_ASYNCH_MODE &
                USART_EIGHT_BIT &
                USART_BRGH_HIGH,
                12 );

        clearBuffer(newRxMessage);  // Clear the Rx buffers
        clearBuffer(tempRxBuffer);

        IPR1bits.RCIP = 1;  // Make the UART Rx interrupt a high priority interrupt
        INTCONbits.GIEH = 1;   //Turn on high priority interrupts

        printf("Hello\n");
        while(1)
        {
                if( newMessageAvailable )
                {
                        // Do stuff with new message
                        newMessageAvailable = 0;
                }
        }
 */

// ------------------------ Using Interrupts ------------------------------
// REQUIRES: #include <portb.h>
// REQUIRES: #include <timers.h>
/*
        RCONbits.IPEN = 1;					// Put the interrupts into Priority Mode
	
// Pushbuttons
        // Turn on the RB0 interrupt INT0, falling edge, always high priority
        OpenRB0INT( PORTB_CHANGE_INT_ON & FALLING_EDGE_INT & PORTB_PULLUPS_OFF);
		// Note, setting the priority is not necessary since INT0 is always High priority
        // -- OR --
        INTCONbits.INT0IE = 1;			// Same thing using the two SFRs directly
        INTCON2bits.INTEDG0 = 0;		// Sets the INT0 to look for falling edges

        INTCONbits.INT0IF = 0;			// Optional. Clearing the flag MIGHT help avoid initially calling isr at startup

		// Turn on the RB1 interrupt INT1, falling edge
        OpenRB1INT(PORTB_CHANGE_INT_ON & FALLING_EDGE_INT & PORTB_PULLUPS_OFF);
		INTCON3bits.INT1IP = 1;         // Setting the prior as high here
        
        // Turn on the RB2 interrupt INT2, falling edge
		OpenRB2INT(PORTB_CHANGE_INT_ON & FALLING_EDGE_INT & PORTB_PULLUPS_OFF);
		INTCON3bits.INT2IP = 1;         // Setting the prior as high here
    
        // Turn on the Change on RB4:RB7 interrupt
        OpenPORTB( PORTB_CHANGE_INT_ON & PORTB_PULLUPS_OFF);
        INTCON2bits.RBIP = 0;				// Make it low priority (for example)

// Timers
        // Open Timer 0 with high priority interrupts 128 prescaler
        OpenTimer0(TIMER_INT_ON & T0_16BIT & T0_SOURCE_INT & T0_PS_1_128);
        INTCON2bits.TMR0IP = 1;				// Confirm high priority (the default)

        OpenTimer1( TIMER_INT_ON & T1_16BIT_RW & T1_SOURCE_INT & T1_PS_1_8 & T1_OSC1EN_OFF);
        IPR1bits.TMR1IP = 0;                // Set to use low priority mode here.
		
		// Note, decided not to do interrupts with Timer 2 since it is mainly used with the PWM

        // Open Timer 3 with low priority interrupts, 16 bit mode, 1:4 Prescaler
        OpenTimer3(TIMER_INT_ON & T3_16BIT_RW & T3_SOURCE_INT & T3_PS_1_4);
        IPR2bits.TMR3IP = 0;                // Set to use low priority mode here.


        INTCONbits.GIE = 1;					// Turn on Global interrupts (core interrupts only) (Compatibility mode)
        INTCONbits.PEIE = 1;				// Turn on Global interrupts (peripheral interrupts) (Compatibility mode)
        // - OR -
        INTCONbits.GIEH = 1;				// Turn on High Priority interrupts (Priority mode)
        INTCONbits.GIEL = 1;				// Turn on Low Priority interrupts (Priority mode)

        // For more info see the PDF document "Where is that interrupt bit"
 */

}

/*****************************************************************
 * Additional Helper Functions if using the UART
 ******************************************************************/

/*****************************************************************
 * Function:		void clearBuffer(char bufferToClear[])
 * Input Variables:	bufferToClear - The buffer to clear
 * Output Return:	none
 * Overview:		Clears the contents of a buffer
 ******************************************************************/
void clearBuffer(char bufferToClear[]) {
    char i;
    for (i = 0; i < MAX_RX_MESSAGE_SIZE; i++) {
        bufferToClear[i] = 0;
	}
}

/*****************************************************************
 * Function:		void loadNewMessage(void)
 * Input Variables:	none
 * Output Return:	none
 * Overview:		Copies the contents of tempRxBuffer into newRxBuffer
 ******************************************************************/
void loadNewMessage(void) {
    char i;
    for (i = 0; i < MAX_RX_MESSAGE_SIZE; i++) {
        newRxMessage[i] = tempRxBuffer[i];
	}
}

/*****************************************************************
 * Function:        void high_isr(void)
 ******************************************************************/
#pragma interrupt high_isr

void high_isr(void) {
    // Add code here for the high priority Interrupt Service Routine (ISR)
    // All eight possible interrupts used in this class

    if (INTCONbits.INT0IF) { // Interrupt 0, RB0
        INTCONbits.INT0IF = 0;
    }
    if (INTCONbits.INT0IF) { // Interrupt 0, RB0 with debounce
        INTCONbits.INT0IF = 0;
        Delay1KTCYx(30); // Assumes a 4 MHz clock
        if (PORTBbits.RB0 == PRESSED) {
		  // do stuff
        }

    }
	
    if (INTCON3bits.INT1IF) { // Interrupt 1, RB1
        INTCON3bits.INT1IF = 0;
    }
    if (INTCON3bits.INT1IF) { // Interrupt 1, RB1 with debounce
        INTCON3bits.INT1IF = 0;
        Delay1KTCYx(30); // Assumes a 4 MHz clock
        if (PORTBbits.RB1 == PRESSED) {
		  // do stuff
        }
    }

    if (INTCON3bits.INT2IF) { // Interrupt 2, RB2
        INTCON3bits.INT2IF = 0;
    }
    if (INTCON3bits.INT2IF) { // Interrupt 2, RB2 with debounce
        INTCON3bits.INT2IF = 0;
        Delay1KTCYx(30); // Assumes a 4 MHz clock
        if (PORTBbits.RB2 == PRESSED) {
		  // do stuff
        }
    }

    if (INTCONbits.RBIF) { // Change on RB4:RB7 interrupt
	    // Rarely used.
        char currentPORTBvalue;
        // Weird note: Can only clear this flag AFTER doing a read or write to PORTB
        currentPORTBvalue = PORTB;
        INTCONbits.RBIF = 0;
    }

    if (INTCONbits.TMR0IF) { // Timer 0 interrupt
        INTCONbits.TMR0IF = 0;
        WriteTimer0(65535 - 7812);
    }

    if (PIR1bits.TMR1IF) { // Timer 1 interrupt
        PIR1bits.TMR1IF = 0;
    }

    if (PIR1bits.TMR2IF) { // Timer 2 interrupt (uncommon)
        PIR1bits.TMR2IF = 0;
    }

    if (PIR2bits.TMR3IF) { // Timer 3 interrupt
        PIR2bits.TMR3IF = 0;
    }

	// Advanced UART interrupt for String messages
    if (PIR1bits.RCIF) {
        static unsigned char txCounter = 0; // Makes the variable remember the prior state
        char newByte;

        newByte = RCREG;
        PIR1bits.RCIF = 0; // Clear the interrupt flag
        if (newByte == MESSAGE_TERMINATOR) {
            if (newMessageAvailable == 0) {
                clearBuffer(newRxMessage);
                loadNewMessage();
                newMessageAvailable = 1;
            }
            clearBuffer(tempRxBuffer);
            txCounter = 0;
        } else {
            if (txCounter < MAX_RX_MESSAGE_SIZE && newByte > 31 && newByte < 127) {
                tempRxBuffer[txCounter] = newByte;
                txCounter++;
            }
        }
    }
}

/******************************************************************
 * Function:        void low_isr(void)
 * Possible sources of interrupt - none
 * Overview:
 ********************************************************************/
#pragma interruptlow low_isr

void low_isr(void) {
    // Add code here for the low priority Interrupt Service Routine (ISR)
}