#include "piccolo_all.h"
#include <stdio.h>
#include <delays.h>

#define RS_HIGH()		LATBbits.LATB5=1;
#define RS_LOW()		LATBbits.LATB5=0;
#define	RW_HIGH()		LATBbits.LATB6=1;
#define RW_LOW()		LATBbits.LATB6=0;
#define E_HIGH()		LATBbits.LATB7=1;
#define E_LOW()			LATBbits.LATB7=0;

#define BUSY_FLAG PORTBbits.RB3;

const rom unsigned char betwk[] = {0x02,0x04,0x0E,0x01,0x0F,0x11,0x0F,0x00,    //á
                                   0x02,0x04,0x0E,0x11,0x1F,0x10,0x0E,0x00,    //é
                                   0x02,0x04,0x0C,0x04,0x04,0x04,0x0E,0x00,    //í
                                   0x02,0x04,0x0E,0x11,0x11,0x11,0x0E,0x00,    //ó
                                   0x02,0x04,0x11,0x11,0x11,0x13,0x0D,0x00,    //ú
                                   0x0A,0x00,0x11,0x11,0x11,0x13,0x0D,0x00,    //ü
                                   0x05,0x0A,0x11,0x11,0x11,0x13,0x0D,0x00,    //ű
                                   0x05,0x0A,0x0E,0x11,0x11,0x11,0x0E,0x00};   //ő
unsigned char	i;
void lcd_write(unsigned char cmd, 
               unsigned char data_flag, 
               unsigned char chk_busy, 
               unsigned char dflag);
               
////////////////////////
//Serial port initialise
////////////////////////

//INTERUPT-OS KOMMUNIKÁCIÓS KISÉRLET
#define RX_BUF_SIZE 64         			//USART vevőoldali buffer mérete (2 hatványa legyen!)
#define TX_BUF_SIZE 64         			//USART  adóoldali buffer mérete (2 hatványa legyen!)

#pragma udata access fast_vars  		// Ezek a globális változók az ACCESS blokkban legyenek!
near unsigned char RX_inp;              // USART vevőoldali buffer bemeneti mutató
near unsigned char RX_outp;             // USART vevőoldali buffer kimeneti mutató
near unsigned char TX_inp;              // USART  adóoldali buffer bemeneti mutató
near unsigned char TX_outp;             // USART  adóoldali buffer kimeneti mutató
near unsigned char usartchar;           // Temporary storage
#pragma udata
unsigned char RX_buffer[RX_BUF_SIZE];   //USART vevőoldali buffer (ebből olvasunk)
unsigned char TX_buffer[TX_BUF_SIZE];   //USART  adóoldali buffer (ebbe írunk)


//////////////////////////////////////////////////////////////
// PARANCS KÉSZLET
//////////////////////////////////////////////////////////////
void USARTDeviceTasks(void);
void USARTString(const rom char* psz_s);
char USART_getc(void);
int USART_putc(char c);
void init_SerialKommunication(unsigned int BaudRate);
void epulse(void);
void LCD_Init(void);
void lcd_write(unsigned char cmd, unsigned char data_flag, unsigned char chk_busy, unsigned char dflag);
void lcd_init_cgram(void);
void user_putc(char cc);
void LCD_String(const rom char* psz_s);
void LCD_int(unsigned int num);
void delays(int del);

//Alprogramok
void Introduction(void);
void SerialKommunication(void);



//////////////////////////////////////////////////////////////
// Főprogram
//////////////////////////////////////////////////////////////
void main(void)
{	
	LCD_Init();
	lcd_init_cgram();						//Ékezetes karakterkészlet feltöltése
	
	Introduction();
	SerialKommunication();
	
	while(1);	
}


//////////////////////////////////////////////////////////////
// Alprogramok
//////////////////////////////////////////////////////////////
void Introduction(void)
{
	long int x;
	
	lcd_write(0x0C,0,1,1);					//Set display on/off and cursor command
	//lcd_write(0x0D,0,1,1);				//Set display on/off and cursor command
	lcd_write(0x80,0,1,1);					//Line1 LCD sor eleje
	LCD_String("Soros komm.");
	lcd_write(0xC0,0,1,1);					//Line2 LCD sor eleje
	LCD_String("Kisérleti prog.");
	delays(5);
	lcd_write(0xC0,0,1,1);					//Line2 LCD sor eleje
	LCD_String("Verzió: V1.0    ");
	delays(10);
}

void SerialKommunication(void)
{
	unsigned char ch;
	lcd_write(0x00,0,1,1);					//LCD clear - kijelző törlése
	lcd_write(0x80,0,1,1);					//Line1 LCD sor eleje
	LCD_String("Soros komm.init.");
	
	init_SerialKommunication(300);
	lcd_write(0x00,0,1,1);					//LCD clear - kijelző törlése
	lcd_write(0x80,0,1,1);					//Line1 LCD sor eleje
	LCD_String("Vett karakterek:");
	
	lcd_write(0xC0,0,1,1);
	while(1)
	{
		lcd_write(0xC0,0,1,1);				//Line2 LCD sor eleje
		ch=USART_getc();
		LCD_String(ch);
	}
}


//////////////////////////////////////////////////////////////
// PARANCS KÉSZLET DEKLARÁLÁSA
//////////////////////////////////////////////////////////////
void USARTDeviceTasks(void) 
{
  // PIC hardware USART Rx interrupt?
  // 1 = The EUSART receive buffer, RCREG, is full (cleared when RCREG is read)
   if (PIR1bits.RCIF)
   {
     // Reading Rx register clears interrupt
     usartchar = RCREG;
     
     // TODO: Do we need to do more for framing errors?
     if (RCSTAbits.FERR)
	 {
       // printf((rom char *)"USART:Rx Framing Error, ");
       lcd_write(0x80,0,1,1);
       LCD_String("Rx Framing Error");
       delays(5);
     } 		
     else if (RCSTAbits.OERR)
     {
       // printf((rom char *)"USART:Rx Overrun error");
       lcd_write(0x80,0,1,1);
       LCD_String("Rx Overrun error");
       delays(5);
       
       RCSTAbits.CREN = 0;  // Clearing CREN clears any Overrun (OERR) errors
       RCSTAbits.CREN = 1;  // Re-enable continuous USART receive
     }
     else // We have a valid byte :-)
     {
       RX_buffer[RX_inp++] = usartchar;
       // Check for wrap around
       RX_inp &= (RX_BUF_SIZE - 1);
       if (RX_inp == RX_BUF_SIZE)
          RX_inp = 0;
       // Check for buffer overrun
       if (RX_inp == RX_outp)
       {
          // printf((rom char *)"USART:RX buffer overrun!\n")
         lcd_write(0x80,0,1,1);
         LCD_String("RX buff. overrun");
         delays(5);
       }
     }
   }

   // PIC hardware USART Tx interrupt?
   // NB: TXIF is almost always true... so need to look at TXIE
   //       to see if the Tx interrupt should be taken and then
   //       only take it if the Tx buffer is empty
   if ((PIE1bits.TXIE) && (TXSTAbits.TRMT))
   {
     // Do we have something to transmit?
     if (TX_inp != TX_outp)
     {
       TXREG = TX_buffer[TX_outp++];
       // Do we need to wrap around to the start of the buffer?
       TX_outp &= (TX_BUF_SIZE - 1);
       //if (TX_outp == TX_BUF_SIZE)
       //   TX_outp = 0;
     }
     else
     {
       // Disable Tx interrupts until we have something to send
       // NB: re-enable interrupts in buffer-fill routine to
       // restart Tx!
       PIE1bits.TXIE = 0;
     }
   }
} //USARTDeviceTasks()

void USARTString(const rom char* psz_s)
 {
   char c;
   while ((c=*psz_s))
     {
       if (c == '\n')
         {
           USART_putc(0x0D);
         }
       USART_putc(c);
       psz_s++;
     }
 }
 
char USART_getc(void)
{
	char c;
   	// Wait for character input
    while(RX_inp == RX_outp) {
      //#if !defined(USE_INTERRUPT)
         USARTDeviceTasks();         //Process character I/O (polling mode)
      //#endif  
    } 
    //=== Start of critical region - disable Rx interrupts
    PIE1bits.RCIE = 0;
    // Copy the character into the output buffer
    c =RX_buffer[RX_outp];
    // Increment pointer and check for buffer wrap around
    RX_outp = (RX_outp+1) & (RX_BUF_SIZE - 1);
    //=== End of critical region - re-enable Tx interrupts 
    PIE1bits.RCIE = 1;
    return (c);
} /* _usart_getc */
 
int USART_putc(char c)
{
	unsigned char	x;
	
	// Start of critical region - disable Tx interrupts
	// Kritikus indítási terület - TX megszakítások hatástalanítása
    PIE1bits.TXIE = 0;
    
    // Copy the character into the output buffer
    // Karakter másolása a kimeneti pufferbe
    TX_buffer[TX_inp] = c;
 
    // Check for buffer wrap around
    // Ellenőrzi a puffer körülfordulását
    x = (TX_inp + 1) & (TX_BUF_SIZE - 1);
    
    // End of critical region - re-enable Tx interrupts 
    PIE1bits.TXIE = 1;
    while(x == TX_outp)
	{
       //#if !defined(USE_INTERRUPT)
          USARTDeviceTasks();         //Process character I/O (polling mode)
       //#endif 
    }
    TX_inp = x;
    PIE1bits.TXIE = 1;        
    return (int)c;
}

//Soros aszinkron kommunikáci és átviteli sebesség beállítása
//Interruptal kiegészítve!
void init_SerialKommunication(unsigned int BaudRate)
{
	//Átviteli sebesség kiíratása
    lcd_write(0xC0,0,1,1);					//Line2 LCD sor eleje
	LCD_String("Seb.: ");
	LCD_int(BaudRate);
	LCD_String("bps");
	
	TXSTAbits.BRGH = 1;							// high speed
	BAUDCONbits.BRG16 = 1;						// 
	switch (BaudRate)
	{
		case 300:		SPBRGH  = 0x9C;     	//300bps 20MHz Osc, FOSC=48MHz
    					SPBRG   = 0x3F;			//Error[%]=0%
						break;
		case 1200: 		SPBRGH  = 0x27;     	//1200bps 20MHz Osc, FOSC=48MHz
    					SPBRG   = 0x0F;			//Error[%]=0%
						break;
		case 2400:	 	SPBRGH  = 0x13;     	//2400bps 20MHz Osc, FOSC=48MHz
    					SPBRG   = 0x87;			//Error[%]=0%
						break;
		case 4800:	 	SPBRGH  = 0x09;     	//4800bps 20MHz Osc, FOSC=48MHz
    					SPBRG   = 0xC3;			//Error[%]=0%
						break;
		case 9600:	 	SPBRGH  = 0x04;     	//9600bps 20MHz Osc, FOSC=48MHz
    					SPBRG   = 0xE1;			//Error[%]=0%
						break;
		case 19200:	 	SPBRGH  = 0x02;     	//19200bps 20MHz Osc, FOSC=48MHz
    					SPBRG   = 0x27;			//Error[%]=0%
						break;				
		case 38400:	 	SPBRGH  = 0x01;     	//38400bps 20MHz Osc, FOSC=48MHz
    					SPBRG   = 0x39;			//Error[%]=-0,5%
						break;				
		case 57600:	 	SPBRGH  = 0x00;     	//57600bps 20MHz Osc, FOSC=48MHz
    					SPBRG   = 0xD0;			//Error[%]=0,16%
						break;
		case 115200: 	SPBRGH  = 0x00;     	//115200bps 20MHz Osc, FOSC=48MHz
    					SPBRG   = 0x67;			//Error[%]=0,16%
    					lcd_write(0xC0,0,1,1);	//Line2 LCD sor eleje
    					LCD_String("Seb.: 115200bps");
						break;				
		default:		; 
	}    
	RCSTAbits.RX9 = 0;							// 8-bit receiver
	TXSTAbits.TX9 = 0; 							// 8-bit transmission
	RCSTAbits.CREN = 1;							// Enables receiver
	PIE1bits.RCIE = 1;							// Enables the EUSART receive interrupt
	
	TXSTAbits.TXEN = 1; 						// transmit enabled
	
	TXSTAbits.SYNC=0;							// asynchronous mode
	RCSTAbits.SPEN = 1;							// enable serial port - configures RX/DT and TX/CK pins as serial port pins
	
	///////////////////
	// USART interrupts
	///////////////////
	
 	IPR1bits.RCIP = 0;   						// USART Rx on low priority interrupt
  	PIE1bits.RCIE = 1;   						// Enable Rx interrupts
  	IPR1bits.TXIP = 0;   						// USART Tx on low priority interrupt
  	PIE1bits.TXIE = 0;   						// Disable Tx interrupts until we need to send
  	
  	RCONbits.IPEN = 1;				            //Kétszintű megszakítási mód beállítása
    INTCONbits.GIEH = 1; 			            //A magas prioritású interrupt engedélyezése
    INTCONbits.GIEL = 1;			            //Az alacsony prioritású interrupt engedélyezése
    
    
    // And the USART TX and RX buffer management
    RX_inp = 0;
    RX_outp = 0;
    TX_inp = 0;
    TX_outp = 0;
    
	delays(15);        
}

////////////////////////////////////////////////////////////////////////
// LCD kezelő parancsok
//
////////////////////////////////////////////////////////////////////////
               
void epulse(void)
{
	Delay10TCYx(2);
	E_HIGH();
	Delay10TCYx(2);
	E_LOW();
	Delay10TCYx(1);
}

void LCD_Init(void)
{
	TRISB=0b00000000;						//Vezérlő lábak írásra állítása
											//ADAT vonal írásra állítása
	Delay10KTCYx(60);						//első várakozás min. 15ms
	
	RS_LOW();								//első utasítás
	RW_LOW();
		
	PORTB=0b00000011;
	Delay10KTCYx(6);						//5ms várakozás
	PORTB=0b00000011;
	Delay10KTCYx(1);
	PORTB=0b00000011;						//0x20 - 4 bites mód
	PORTB=0b00000010;
	
	lcd_write(0x28,0,1,1);
	lcd_write(0x06,0,1,1);
	lcd_write(0x0D,0,1,1);
	lcd_write(0x01,0,1,1);
	
}

//-- Egy bájt (parancs vagy adat) kiírása
void lcd_write(unsigned char cmd, 
               unsigned char data_flag, 
               unsigned char chk_busy, 
               unsigned char dflag){
char bflag;
unsigned char ch_upper;

  if (chk_busy)
  {
    RS_LOW();                 				//RS = 0 a foglaltság figyeléshez
    //DATA_DIR_RD();           				//adatvonalak vételre állítása
    TRISB=0b00001111;
    RW_HIGH();                				//R/W = 1 olvasáshoz
    
	do {
  	    		E_HIGH(); 
   		   		Delay10TCYx(2);
      			bflag = BUSY_FLAG;     		//felső 4 bit olvasása
      			E_LOW();
      			Delay10TCYx(2);
      			epulse();              		//alsó félbájt kiléptetése
    	} while(bflag);
    
  }
  else {
    	 Delay10KTCYx(12);        			//Foglaltság figyelés helyett késleltetés
	   }
 
  	TRISB=0b00000000;
  
  	if (data_flag) 
	{ 
    	RS_HIGH();                			//RS=1, ha adatküldés következik
	}
	else RS_LOW();              			//RS=0, ha parancsot küldünk
	
	RW_LOW();                   			//R/W = 0, íráshoz
  
	ch_upper=cmd;
	cmd=(cmd&0xF0)>>4;
	PORTB=(PORTB&0xF0)|cmd;	
	epulse();
	if (dflag)
	{
		ch_upper=ch_upper&0x0F;
		PORTB=(PORTB&0xF0)|ch_upper;
		epulse();	
	}
	Delay10KTCYx(6);
}

void lcd_init_cgram(void) 
{
    lcd_write(0x40,0,1,1);  				// kurzor a CGRAM elejére
    for(i=0; i<64; i++) 
	{
    	lcd_write(betwk[i],1,1,1);    		// definiálható karakterek feltöltése
    }                               		// ékezetes karakterekkel
    lcd_write(0x80,0,1,1);          		// kurzor vissza, a DDRAM elejére	
}

//-- LCD-re egy karaktert kiíró függvény a _H_USER stream számára
void user_putc(char cc) 
{
    switch(cc) 
	{
	    case 'á':  cc = 0x00; break;
	    case 'é':  cc = 0x01; break;
	    case 'í':  cc = 0x02; break;
	    case 'ó':  cc = 0x03; break;
	    case 'ú':  cc = 0x04; break;
	    case 'ü':  cc = 0x05; break;
	    case 'ű':  cc = 0x06; break;
	    case 'ő':  cc = 0x07; break;
	    case 'ö':  cc = 0xEF; break;
    } 
    lcd_write(cc,1,1,1);
}

void LCD_String(const rom char* psz_s)
 {
   char c;
   while ((c=*psz_s))
     {
       user_putc(c);
       psz_s++;
     }     
 }
 
void LCD_int(unsigned int num)
{
	unsigned int oszto=10000;
	unsigned int e;
	unsigned int i;
	
	for(i=0; i<=4; i++)
	{
		e=num/oszto;
		user_putc(e+0x30);
		num=num-(e*oszto);
		oszto=oszto/10;
	}
}

void delays(int del)
{
	int d;
	
	for(d=0; d<=del; d++) Delay10KTCYx(500);
}