#include <16F690.h>
#include <math.h>
#fuses INTRC,EC_IO,FCMEN,IESO,NOWDT,NOPROTECT,BROWNOUT,NOMCLR,BROWNOUT,PUT
#use delay(clock=4000000)
#use spi(FORCE_HW, BITS=8)

//### LCD vez.változók ###
#define LCD_DB4   PIN_C5
#define LCD_DB5   PIN_C4
#define LCD_DB6   PIN_C3
#define LCD_DB7   PIN_C6
#define LCD_E     PIN_C2
#define LCD_RS    PIN_C0
#define LCD_RW    PIN_C1
#define USE_LCD_RW	1;
#define lcd_type	2;			// 0=5x7, 1=5x10, 2=2 lines
#define lcd_line_two 0x40			// LCD RAM address for the 2nd line

//### Digitális jeladó + Gomb ###
#define	POTI_CLK	PIN_A2			// Órajel
#define POTI_D		PIN_A4			// Le/Fel
#define GOMB		PIN_B4			// Gomb

//### Digitális poti ###
#define DIGIPOT_CS	PIN_A3			// Engedélyezés
#define DIGIPOT_CS1	PIN_C6			// Ki1 durva szabályzó
#define DIGIPOT_CS2	PIN_C4			// Ki2 durva szabályzó
#define DIGIPOT_CS3	PIN_C5			// Ki1 és Ki2 finom szabályzó

//### Müveletek, fügvények ###
void lcd_init(void);				// LCD inicializálás
void lcd_putc(char c);				// LCD-re szöveg írás
void DigiPot(int DPValue1, DPValue2, DPCS);	// Digipotik értékének beállítása
float Voltmeter(int channel);			// Feszültség mérő
void toDisplay(float SetVolt, SetAmper, MeasureVolt, MeasureAmper);

//### GLÓBÁLIS VÁLTOZÓK ###
long	Digip_value1;				// 512 lépés durva szabályzó Ki1
long	Digip_value2;				// 512 lépés durva szabályzó Ki2
int	Digip_value3a;				// 256 lépés finom szabályzó Ki2
int	Digip_value3b;				// 256 lépés finom szabályzó Ki1
float	AnalogVolt0;				// Analog bemenet 1
float	AnalogVolt1;				// Analog bemenet 2
float	AnalogVolt2;				// Analog kimenet 1 számított felsz
float	AnalogVolt3;				// Analog kimenet 2 számított fesz
int	Menu = 1;				// Menu választó, def-> főmenü
//#########################

//############
//### Main ###
//############
void main(void) 
{
	int1 a;
	int1 b;
	int Pot1, Pot2;
	setup_oscillator(OSC_4MHZ | OSC_NORMAL);
	setup_adc_ports(sAN0 | sAN1);
	setup_adc(ADC_CLOCK_DIV_4);	
	SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_64);
	set_tris_a(0b00010100);
	output_a(0x00);
	set_tris_b(0b00010000);
	output_b(0x00);

	lcd_init();

	Digip_value1 = 0;
	Digip_value2 = 0;
	Digip_value3a = 0;
	Digip_value3b = 0;
	AnalogVolt0 = 0;
	AnalogVolt1 = 0;
	AnalogVolt2 = 0;
	AnalogVolt3 = 0;
	a = 0;
	DigiPot(0,0,1);
	DigiPot(0,0,2);
	DigiPot(0,0,3);

	lcd_putc("\f*LABORTAPEGYSEG*\n");
	lcd_putc("*   GaryLaci   *");
	delay_ms(1000);

	for(;;){
		if(!input(GOMB) && b == 0){
			b = 1;
			if(Menu == 2) Menu = 1;
			if(Menu < 2) Menu++;
		}
		if(input(GOMB) && b == 1) b = 0;

		switch(Menu){
			case 1 :
				if(input(POTI_CLK) && a == 0){
					a = 1;
					delay_ms(20);
					if(input(POTI_CLK){
						if(input(POTI_D)){
							if(Digip_value3b < 511) Digip_value3b++;
						} else {
							if(Digip_value3b > 0) Digip_value3b--;
						}
					} else {
						if(input(POTI_D)){
							if(Digip_value1 < 511) Digip_value1++;
						} else {
							if(Digip_value1 > 0) Digip_value1--;
						}
					}
				}
				if(!input(POTI_CLK) && a == 1){
					a = 0;
					if(Digip_value1 > 255){
						Pot1 = 255;
						Pot2 = Digip_value1 - 256;
					} else {
						Pot1 = Digip_value1;
						Pot2 = 0;
					}
					DigiPot(Pot1,Pot2,1)
					DigiPot(Digip_value3a,Digip_value3b,3);
				}
				break;
			case 2 :
				if(input(POTI_CLK) && a == 0){
					a = 1;
					delay_ms(20);
					if(input(POTI_CLK){
						if(input(POTI_D)){
							if(Digip_value3a < 511) Digip_value3a++;
						} else {
							if(Digip_value3a > 0) Digip_value3a--;
						}
					} else {
						if(input(POTI_D)){
							if(Digip_value2 < 511) Digip_value2++;
						} else {
							if(Digip_value2 > 0) Digip_value2--;
						}
					}
				}
				if(!input(POTI_CLK) && a == 1){
					a = 0;
					if(Digip_value2 > 255){
						Pot1 = 255;
						Pot2 = Digip_value2 - 256;
					} else {
						Pot1 = Digip_value2;
						Pot2 = 0;
					}
					DigiPot(Pot1,Pot2,2)
					DigiPot(Digip_value3a,Digip_value3b,3);
				}
				break;
		}

		AnalogVolt0 = Voltmeter(0);
		AnalogVolt1 = Voltmeter(1);
		AnalogVolt2 = (10/512)*Digip_value1 + (1/256)*Digip_value3b;
		AnalogVolt3 = (10/512)*Digip_value2 + (1/256)*Digip_value3a;
		toDisplay(AnalogVolt2, AnalogVolt3, AnalogVolt0, AnalogVolt1);
	}
}


//################
//### Kijelzés ###
//################
void toDisplay(float SetVolt, SetAmper, MeasureVolt, MeasureAmper)
{
	switch(Menu){
		case 1:
			printf(lcd_putc,"\f>%4.2fV| %4.3Af\n", SetVolt, SetAmper);
			break;
		case 2:
			printf(lcd_putc,"\f %4.2fV|>%4.3Af\n", SetVolt, SetAmper);
			break;
	}
	printf(lcd_putc, "V%4.2fV|A%4.3Af", MeasureVolt, MeasureAmper);

}

//######################
//### Digitális poti ###
//######################
void DigiPot(int DPValue1, DPValue2, DPCS)
{
	output_high(DIGIPOT_CS);
	switch(DPCS){
		case 1: output_low(DIGIPOT_CS1);
			break;
		case 2: output_low(DIGIPOT_CS2);
			break;
		case 3: output_low(DIGIPOT_CS3);
			break;
	}
	delay_ms(1);
	spi_write(0x11);		// write pot. 1
	spi_write(DPValue1); 		// data
	switch(DPCS){
		case 1:
			output_high(DIGIPOT_CS1);
			delay_us(1);
			output_low(DIGIPOT_CS1);
			break;
		case 2:
			output_high(DIGIPOT_CS2);
			delay_us(1);
			output_low(DIGIPOT_CS2);
			break;
		case 3:
			output_high(DIGIPOT_CS3);
			delay_us(1);
			output_low(DIGIPOT_CS3);
			break;
	}
	spi_write(0x12);		// write pot. 2
	spi_write(DPValue2);		// data
	switch(DPCS){
		case 1: output_high(DIGIPOT_CS1);
			break;
		case 2: output_high(DIGIPOT_CS2);
			break;
		case 3: output_high(DIGIPOT_CS3);
			break;
	}	output_low(DIGIPOT_CS);
}

//#################
//### Voltmeter ###
//#################
float Voltmeter(int Channel)
{
	set_adc_channel(Channel);
	f = 0;
	for(i=1; i<=10; i++){
		read_adc(ADC_START_ONLY);
		done = adc_done();
		while(!done) {   
			done = adc_done();
		}
		value = read_adc();
		f = f + value;
	}
	f = f * 0.019607 / 10;
	f = ceil(f * 100) / 100;
	return(f);
}

//####################################
//### LCD Driver modify by GryLaci ###
//####################################

void lcd_send_nibble(int8 nibble) 
{ 
	output_bit(LCD_DB4, !!(nibble & 1)); 
	output_bit(LCD_DB5, !!(nibble & 2));  
	output_bit(LCD_DB6, !!(nibble & 4));    
	output_bit(LCD_DB7, !!(nibble & 8));    
	delay_cycles(1); 
	output_high(LCD_E); 
	delay_us(2); 
	output_low(LCD_E); 
} 

#ifdef USE_LCD_RW 
	int8 lcd_read_nibble(void){ 
		int8 retval; 
		#bit retval_0 = retval.0 
		#bit retval_1 = retval.1 
		#bit retval_2 = retval.2 
		#bit retval_3 = retval.3 
		retval = 0; 
		output_high(LCD_E); 
		delay_cycles(1); 
		retval_0 = input(LCD_DB4); 
		retval_1 = input(LCD_DB5); 
		retval_2 = input(LCD_DB6); 
		retval_3 = input(LCD_DB7); 
		output_low(LCD_E); 
		return(retval);    
	}    
#endif 

#ifdef USE_LCD_RW 
	int8 lcd_read_byte(void){ 
		int8 low; 
		int8 high; 
		output_high(LCD_RW); 
		delay_cycles(1); 
		high = lcd_read_nibble(); 
		low = lcd_read_nibble(); 
		return( (high<<4) | low); 
	} 
#endif 

void lcd_send_byte(int8 address, int8 n){ 
	output_low(LCD_RS); 
	#ifdef USE_LCD_RW 
		while(bit_test(lcd_read_byte(),7)) ; 
	#else 
		delay_us(60);  
	#endif 
	if(address) 
		output_high(LCD_RS); 
	else 
		output_low(LCD_RS); 
	delay_cycles(1); 
	#ifdef USE_LCD_RW 
		output_low(LCD_RW); 
		delay_cycles(1); 
	#endif 
	output_low(LCD_E); 
	lcd_send_nibble(n >> 4); 
	lcd_send_nibble(n & 0xf); 
} 

void lcd_init(void){ 
	int8 i;
	int LCD_INIT_STRING[4];
	LCD_INIT_STRING[0] = 0xa0;	// Func set: 4-bit, 2 lines, 5x8 dots 
	LCD_INIT_STRING[1] = 0xc;	// Display on
	LCD_INIT_STRING[2] = 1;		// Clear display 
	LCD_INIT_STRING[3] = 6;		// Increment cursor
	output_low(LCD_RS); 
	#ifdef USE_LCD_RW 
		output_low(LCD_RW); 
	#endif 
	output_low(LCD_E); 
	delay_ms(15); 
	for(i=0 ;i < 3; i++){ 
	    lcd_send_nibble(0x03); 
    	delay_ms(5); 
	} 
	lcd_send_nibble(0x02); 
	for(i=0; i < sizeof(LCD_INIT_STRING); i++){ 
		lcd_send_byte(0, LCD_INIT_STRING[i]); 
    	#ifndef USE_LCD_RW 
    		delay_ms(5); 
    	#endif 
	} 
} 

void lcd_gotoxy(int8 x, int8 y){ 
	int8 address;
	if(y != 1) 
		address = lcd_line_two; 
	else 
		address=0; 
	address += x-1; 
	lcd_send_byte(0, 0x80 | address); 
}

void lcd_putc(char c){ 
switch(c){
	case '\f':
		lcd_send_byte(0,1);
		delay_ms(2);
		break;
	case '\n':
		lcd_gotoxy(1,2);
		break;
	case '\b':
		lcd_send_byte(0,0x10);
		break;
	default:
		lcd_send_byte(1,c);
		break; 
	} 
} 

#ifdef USE_LCD_RW 
	char lcd_getc(int8 x, int8 y){ 
		char value; 
		lcd_gotoxy(x,y); 
		while(bit_test(lcd_read_byte(),7));  
		output_high(LCD_RS); 
		value = lcd_read_byte(); 
		output_low(lcd_RS); 
		return(value); 
	} 
#endif
