/*
    9-12-04
    Copyright Spark Fun Electronics© 2004
    
    Example code for the PIC-MT-USB written for the 16F877A @ 20MHz

    Takes serial data from the USB<->RS232 IC and displays it on LCD
    Turns on and off the backlight when the user presses Button 1 and reports back to computer
    Flashes Bi-Color LED when user presses buttons 1 and 2
    Counts and displays the number of times Button 2 is pressed - stores the count onto internal EEPROM

    Thanks to Olimex for providing the original C test code for the PIC-MT-C w/ 16F873
    
*/
#define Clock_20MHz
#define Baud_9600

#include "\Pics\c\16F877A.h"  // device dependent interrupt definitions
#include "\Pics\c\int16CXX.H"

#pragma config|= 0x3F32 //HS Oscilator, Power-Up Timer, Write Protect Off

//Hardware port definitions
//=================================================
#define     Serial_In_BB    PORTA.2
#define     Serial_Out_BB   PORTA.1

#define     RS              PORTD.0
#define     R_W             PORTD.1
#define     E               PORTD.2

#define     BL_Control      PORTD.3

#define     D4              PORTD.4
#define     D5              PORTD.5
#define     D6              PORTD.6
#define     D7              PORTD.7

#define     BUTTON1         PORTB.5
#define     BUTTON2         PORTB.4

#define     LED1            PORTB.2
#define     LED2            PORTB.1
//=================================================

//Constant definitions
//=================================================
#define     FALSE       0
#define     TRUE        1

#define     CLR_DISP        0b.0000.0001 //Clear display

#define     CUR_HOME        0b.0000.0010    //Move cursor home and clear screen memory
#define     CUR_RIGHT       0b.0001.0100    //Move cursor one to right
#define     CUR_LEFT        0b.0001.0000    //Move cursor one to left

#define     SCROLL_RIGHT    0b.0001.1100    //Scroll entire screen right one space
#define     SCROLL_LEFT     0b.0001.1000    //Scroll entire screen left one space

#define     DISP_ON         0b.0000.1100    //Turn visible LCD on
#define     DISP_OFF        0b.0000.1000    //Turn visible LCD off

#define     UNDERLINE_ON    0b.0000.1110    //Turn on underlined cursor
#define     UNDERLINE_OFF   0b.0000.1100    //Turn off underlined cursor

#define     BLINKCUR_ON     0b.0000.1101    //Turn on blinking box cursor
#define     BLINKCUR_OFF    0b.0000.1100    //Turn off blinking box cursor

#define     DUALCUR_ON      0b.0000.1111    //Turn on blinking box and underline cursor
#define     DUALCUR_OFF     0b.0000.1100    //Turn off blinking box and underine cursor

#define     SET_CURSOR      0b.1000.0000    //SET_CURSOR + X : Sets cursor position to X

#define     ENTRY_INC       0b.0000.0110 //
#define     DD_RAM_ADDR     0b.1000.0000 //
#define     DD_RAM_ADDR2    0b.1100.0000 //
#define     CG_RAM_ADDR     0b.0100.0000 //
//=================================================

//Global variable declarations
//=================================================
uns8 data_byte_in;
uns8 cursor_position;

uns8 LCD_Width;
uns8 LCD_Lines;

uns8 EL1, EL2;
uns8 CP1, CP2;

int16 number_of_presses;
//=================================================

//Interrupt Vectors
//=================================================
#pragma origin 4
interrupt serverX( void)
{
    int_save_registers
    //char sv_FSR = FSR;  // save FSR if required

    if(RCIF) //If we have received something from the computer store it in the RX_Array array
    {
        data_byte_in = RCREG;
        
        //There is no clearing of the interrupt flag, it's done automatically
    }

    //FSR = sv_FSR;               // restore FSR if saved
    int_restore_registers 
}
//=================================================

//Function declarations
//=================================================
void boot_up(void);
void delay_ms(uns16);

void init_lcd(void);
void send_char(uns8);
void send_cmd(uns8);
void send_string(const char* incoming_string);
void LCD_wait(void);

uns8 onboard_eeread(uns8 e_address);
void onboard_eewrite(uns8 e_data, uns8 e_address);

void putc(uns8 nate);
uns8 getc(void);
uns8 bin2Hex(char x);
void printf(const char *nate, int16 my_byte);

//=================================================

void main(void)
{
    boot_up(); //Initialize PIC ports and LCD interface
 
    while(1)
    {
        //Check to see if anything new has happened
        if (data_byte_in != 0)
        {
            send_char(data_byte_in);
            putc(data_byte_in);
            
            data_byte_in = 0;

            cursor_position++;

            //When the cursor gets to the end of one line, it must
            //advance to the next visual line
            //===============================================================
            if (cursor_position == EL1) //End of line 1
            {
                cursor_position = 64; //Beginning of line 2
                send_cmd(CP2);
            }
            else if (cursor_position == EL2) //End of line 2
            {
                cursor_position = 0; //Return to line 1
                send_cmd(CP1);
            }
            //===============================================================
            
        }
        
        if(BUTTON1 == 0) //If user presses button one, turn on or off the back light
        {
            LED1 = 1;
            delay_ms(250); //Wait for user to get their finger off the button!
            LED1 = 0;

            send_cmd(CLR_DISP);
            send_string("Button 1"); //Sends a string to the LCD

            BL_Control ^= 1;
            
            printf("\n\rYou pressed button number 1", 0); //Sends a string to the computer
        }
        
        if(BUTTON2 == 0)
        {
            LED2 = 1;
            delay_ms(250); //Wait for user to get their finger off the button!
            LED2 = 0;

            send_cmd(CLR_DISP);
            send_string("Button 2: "); //Sends a string to the LCD

            //Reading and writing to the PIC's onboard EEPROM
            //===============================================================
            
            number_of_presses.high8 = onboard_eeread(0); //Read data from EEPROM position 0
            number_of_presses.low8 = onboard_eeread(1);

            number_of_presses++;

            onboard_eewrite(number_of_presses.high8, 0); //Write data to EEPROM position 0
            onboard_eewrite(number_of_presses.low8, 1); 
            
            //===============================================================

            //This prints 16-bit decimal numbers to the LCD
            //===============================================================
            
            int16 number_to_print;
            number_to_print = number_of_presses;
            
            uns8 m, temp;
            uns8 decimal_output[5];

            //Divide number by a series of 10s
            for(m = 4 ; number_to_print > 0 ; m--)
            {
                temp = number_to_print % (uns16)10;
                decimal_output[m] = temp;
                number_to_print = number_to_print / (uns16)10;               
            }
        
            printf("\n\rYou pressed button number 2 ", 0); //Sends a string to the computer

            for(m++ ; m < 5 ; m++)
            {
                putc(bin2Hex(decimal_output[m]));
                send_char(bin2Hex(decimal_output[m]));
            }

            printf(" times", 0);
        
            //===============================================================

        
        }
        
   }//End infinite loop


}//End Main

//Initializes the various ports and interrupts
//Also inits the LCD
void boot_up(void)
{
    //Setup Ports
    PORTA = 0b.0000.0000;
    TRISA = 0b.0000.0000;   //0 = Output, 1 = Input

    PORTB = 0b.0000.0000;
    TRISB = 0b.0011.0000;   //0 = Output, 1 = Input (RB5 = Button1) (RB4 = Button2)

    PORTC = 0b.0000.0000;
    TRISC = 0b.1000.0000;   //0 = Output, 1 = Input (RC7 = UART RX)

    PORTD = 0b.0000.0000;
    TRISD = 0b.0000.0000;   //0 = Output, 1 = Input

    //Sets up the UART for serial comm
    //=============================================================
    SPBRG = 129; //20MHz for 9600 inital communication baud rate
    //SPBRG = 25; //4MHz for 9600 inital communication baud rate - Not yet tested
    TXSTA = 0b.0010.0100; //8-bit asych mode, high speed uart enabled
    RCSTA = 0b.1001.0000; //Serial port enable, 8-bit asych continous receive mode
    //=============================================================

    //Init LCD
    init_lcd();

    //Setup interrupts for incoming RX
    data_byte_in = 0;    
    RCIE = 1; //Enable the UART RX interrupt
    PEIE = 1; //Enable Peripheral Ints
    GIE = 1;
}

//Initializes the 4-bit parallel interface to the HD44780
void init_lcd(void)
{
    
    RS = 0;               
    R_W = 0;

    //Tell the LCD we are using 4bit data communication
    delay_ms(750);
    PORTD = 0b.0011.0000;
    E = 1; E = 0;

    delay_ms(200);
    PORTD = 0b.0011.0000;
    E = 1; E = 0;

    delay_ms(200);
    PORTD = 0b.0011.0000;
    E = 1; E = 0;

    delay_ms(20);
    PORTD = 0b.0010.0000;
    E = 1; E = 0;

    send_cmd(DISP_ON);
    send_cmd(CLR_DISP);
    //LCD Init Complete
    
    send_string("  SparkFun.com");
    send_cmd(SET_CURSOR + 64);
    send_string(" MT-USB Example");

    //Retrieve last Backlight state
    /*
    Backlight_Setting = onboard_eeread(0);
    if (Backlight_Setting > 1) Backlight_Setting = 1; //Default
    onboard_eewrite(Backlight_Setting, 0); //Record to spot 0
    BL_Control = Backlight_Setting; //Turn on/off the backlight
    */
    BL_Control = 1; //Turn on Backlight
    
    LCD_Lines = 2;
    LCD_Width = 16;

    //Set end lines
    EL1 = LCD_Width;
    EL2 = 64 + LCD_Width;

    //Set cursor positions
    CP1 = 0b.1000.0000 | 0;
    CP2 = 0b.1000.0000 | 64;

    delay_ms(750);
    send_cmd(CLR_DISP);
    
    cursor_position = 0;
}    

//Checks the busy flag and waits until LCD is ready for next command
void LCD_wait(void)
{
    bit i = 1;
    
    TRISD = 0b.1111.0000;   //0 = Output, 1 = Input

    R_W = 1; //Tell LCD to output status
    RS = 0;               

    while(i == 1)
    {
        E = 1; 
        i = D7; //Read data bit 7 - Busy Flag
        E = 0;
    
        E = 1; E = 0; //Toggle E to get the second four bits of the status byte - but we don't care
    }
    
    TRISD = 0b.0000.0000;   //0 = Output, 1 = Input
}

//Sends an ASCII character to the LCD
void send_char(uns8 c)
{
    LCD_wait();
    
    R_W = 0; //set LCD to write
    RS = 1; //set LCD to data mode
    
    PORTD.7 = c.7;
    PORTD.6 = c.6;
    PORTD.5 = c.5;
    PORTD.4 = c.4;
    E = 1; E = 0; //Toggle the Enable Pin

    PORTD.7 = c.3;
    PORTD.6 = c.2;
    PORTD.5 = c.1;
    PORTD.4 = c.0;
    E = 1; E = 0;
}

//Sends an LCD command
void send_cmd(uns8 d)
{
    LCD_wait();

    R_W = 0; //set LCD to write

    PORTD.7 = d.7;
    PORTD.6 = d.6;
    PORTD.5 = d.5;
    PORTD.4 = d.4;
    E = 1; E = 0;

    PORTD.7 = d.3;
    PORTD.6 = d.2;
    PORTD.5 = d.1;
    PORTD.4 = d.0;
    E = 1; E = 0;
}

//Sends a given string to the LCD. Will start printing from
//current cursor position.
void send_string(const char *incoming_string)
{
    uns8 x;
    
    for(x = 0 ; incoming_string[x] != '\0' ; x++)
        send_char(incoming_string[x]);
}

//General short delay
void delay_ms(uns16 x)
{
    uns8 y, z;
    //Clocks out to 1.00ms per 1ms
    //9.99 ms per 10ms
    for ( ; x > 0 ; x--)
        for ( y = 0 ; y < 4 ; y++)
            for ( z = 0 ; z < 176 ; z++);
}

//Reads e_data from the onboard eeprom at e_address
uns8 onboard_eeread(uns8 e_address)
{
    EEPGD = 0; //Read from EEPROM memory and not program memory
    EEADR = e_address; //Set the address to read
    RD = 1; //Read it

    return(EEDATA); //Read that EEPROM value
}    

//Write e_data to the onboard eeprom at e_address
void onboard_eewrite(uns8 e_data, uns8 e_address)
{
    bit temp_GIE = GIE;
    
    EEPGD = 0; //Read from EEPROM memory and not program memory
    
    EEIF = 0; //Clear the write completion intr flag
    EEADR = e_address; //Set the address
    EEDATA = e_data; //Give it the data
    WREN = 1; //Enable EE Writes
    GIE = 0; //Disable Intrs
    
    //Specific EEPROM write steps
    EECON2 = 0x55;
    EECON2 = 0xAA;
    WR = 1;
    //Specific EEPROM write steps

    while(WR == 1); //Wait for write to complete
    WREN = 0; //Disable EEPROM Writes

    GIE = temp_GIE; //Set GIE to its original state
}

//The following are printf routines and UART control for sending serial characters and strings to the computer
//=============================================================

//Sends nate to the Transmit Register
void putc(uns8 nate)
{
    while(TXIF == 0);
    TXREG = nate;
}

uns8 getc(void)
{
    while(RCIF == 0);
    return (RCREG);
}    

//Returns ASCII Decimal and Hex values
uns8 bin2Hex(char x)
{
   skip(x);
   #pragma return[16] = "0123456789ABCDEF"
}

//Prints a string including variables
void printf(const char *nate, int16 my_byte)
{
  
    uns8 i, k, m, temp;
    uns8 high_byte = 0, low_byte = 0;
    uns8 y, z;
    
    uns8 decimal_output[5];
    
    for(i = 0 ; ; i++)
    {
        k = nate[i];

        if (k == '\0') 
            break;

        else if (k == '%') //Print var
        {
            i++;
            k = nate[i];

            if (k == '\0') 
                break;
            else if (k == '\\') //Print special characters
            {
                i++;
                k = nate[i];
                
                putc(k);
                

            } //End Special Characters
            else if (k == 'b') //Print Binary
            {
                for( m = 0 ; m < 8 ; m++ )
                {
                    if (my_byte.7 == 1) putc('1');
                    if (my_byte.7 == 0) putc('0');
                    if (m == 3) putc(' ');
                    
                    my_byte = my_byte << 1;
                }
            } //End Binary               
            else if (k == 'd') //Print Decimal
            {
                //Print negative sign and take 2's compliment
                /*
                if(my_byte < 0)
                {
                    putc('-');
                    my_byte ^= 0xFFFF;
                    my_byte++;
                }
                */
                
                if (my_byte == 0)
                    putc('0');
                else
                {
                    //Divide number by a series of 10s
                    for(m = 4 ; my_byte > 0 ; m--)
                    {
                        temp = my_byte % (uns16)10;
                        decimal_output[m] = temp;
                        my_byte = my_byte / (uns16)10;               
                    }
                
                    for(m++ ; m < 5 ; m++)
                        putc(bin2Hex(decimal_output[m]));
                }
    
            } //End Decimal
            else if (k == 'h') //Print Hex
            {
                //New trick 3-15-04
                putc('0');
                putc('x');
                
                if(my_byte > 0x00FF)
                {
                    putc(bin2Hex(my_byte.high8 >> 4));
                    putc(bin2Hex(my_byte.high8 & 0b.0000.1111));
                }

                putc(bin2Hex(my_byte.low8 >> 4));
                putc(bin2Hex(my_byte.low8 & 0b.0000.1111));

            } //End Hex
            else if (k == 'f') //Print Float
            {
                putc('!');
            } //End Float
            else if (k == 'u') //Print Direct Character
            {
                //All ascii characters below 20 are special and screwy characters
                //if(my_byte > 20) 
                    putc(my_byte);
            } //End Direct
                        
        } //End Special Chars           

        else
            putc(k);
    }    
}