#include <util/delay.h>
#include "defines.h"
#include <avr/io.h>
#include "lcd.h"
#define	NEW_VERSION 1


//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Low level stuff
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void init_spi_and_lcd_ports()
{
	PORTD |= (1 << LCD_nRESET);
	DDRD |= (1 << LCD_nRESET);
	PORTD &= ~(1 << LCD_nRESET);

	PORTB |= (1 << SPI_SCLK) | (1 << SPI_MOSI) | (1 << LCD_D_NC) | (1 << SPI_SS) | (1 << LCD_15V_nEN);
	DDRB  |= (1 << SPI_SCLK) | (1 << SPI_MOSI) | (1 << LCD_D_NC) | (1 << SPI_SS) | (1 << LCD_15V_nEN);
	PORTB |= (1 << SPI_SCLK) | (1 << SPI_MOSI) | (1 << LCD_D_NC) | (1 << SPI_SS) | (1 << LCD_15V_nEN);	

	SPSR = (1 << SPI2X);														// Double SPI Speed >> Freq = Fosc / 2
	SPCR = (1 << SPE) | (1 << MSTR) | (1 << CPOL) | (1 << CPHA);				// Enable SPI in master mode
}

void enable_vcc_lcd()
{
	PORTB &= ~(1 << LCD_15V_nEN);
}

void disable_vcc_lcd()
{
	PORTB |= (1 << LCD_15V_nEN);
}

void force_send_spi_data(unsigned char byte)
{
	SPDR = byte;
}

void lcd_write_command(unsigned char byte)
{
	PORTB &= ~(1 << LCD_D_NC);
	PORTB &= ~(1 << SPI_SS);
	SPDR = byte;
	while(!(SPSR & (1 << SPIF)));					// Wait for transmit to complete
	PORTB |= (1 << SPI_SS);
	PORTB |= (1 << LCD_D_NC);
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Instruction Setting
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void Set_Column_Address(unsigned char start_addr, unsigned char end_addr)
{
	lcd_write_command(0x15);						// Set Column Address
	lcd_write_data(start_addr);						//   Default => 0x00
	lcd_write_data(end_addr);						//   Default => 0x77
}


void Set_Row_Address(unsigned char start_addr, unsigned char end_addr)
{
	lcd_write_command(0x75);						// Set Row Address
	lcd_write_data(start_addr);						//   Default => 0x00
	lcd_write_data(end_addr);						//   Default => 0x7F
}

void Set_Write_RAM()
{
	lcd_write_command(0x5C);						// Enable MCU to Write into RAM
}

void Set_Read_RAM()
{
	lcd_write_command(0x5D);						// Enable MCU to Read from RAM
}

void Set_Remap_Format(unsigned char format)
{
	lcd_write_command(0xA0);						// Set Re-Map / Dual COM Line Mode
	lcd_write_data(format);							// Default => 0x40, Horizontal Address Increment, Column Address 0 Mapped to SEG0, Disable Nibble Remap
													// Scan from COM0 to COM[N-1], Disable COM Split Odd Even
	lcd_write_data(0x11);							// Default => 0x01 (Disable Dual COM Mode)
}

void Set_Start_Line(unsigned char line)
{
	lcd_write_command(0xA1);						// Set Vertical Scroll by RAM
	lcd_write_data(line);							// Default => 0x00
}

void Set_Display_Offset(unsigned char offset)
{
	lcd_write_command(0xA2);						// Set Vertical Scroll by Row
	lcd_write_data(offset);							// Default => 0x00
}

void Set_Display_Mode(unsigned char mode)
{
	lcd_write_command(0xA4|mode);					// Set Display Mode
													//   Default => 0xA4
													//     0xA4 (0x00) => Entire Display Off, All Pixels Turn Off
													//     0xA5 (0x01) => Entire Display On, All Pixels Turn On at GS Level 15
													//     0xA6 (0x02) => Normal Display
													//     0xA7 (0x03) => Inverse Display
}

void Set_Partial_Display_On(unsigned char start_row, unsigned char end_row)
{
	lcd_write_command(0xA8);
	lcd_write_data(start_row);
	lcd_write_data(end_row);
}

void Set_Partial_Display_Off()
{
	lcd_write_command(0xA9);
}

void Set_Function_Selection(unsigned char function)
{
	lcd_write_command(0xAB);						// Function Selection
	lcd_write_data(function);						//   Default => 0x01, Enable Internal VDD Regulator
}

void Set_Display_On()
{
	lcd_write_command(0xAF);
}

void Set_Display_Off()
{
	lcd_write_command(0xAE);
}

void Set_Phase_Length(unsigned char length)
{
	lcd_write_command(0xB1);						// Phase 1 (Reset) & Phase 2 (Pre-Charge) Period Adjustment
	lcd_write_data(length);							//   Default => 0x74 (7 Display Clocks [Phase 2] / 9 Display Clocks [Phase 1])
													//     D[3:0] => Phase 1 Period in 5~31 Display Clocks
													//     D[7:4] => Phase 2 Period in 3~15 Display Clocks
}

void Set_Display_Clock(unsigned char divider)
{
	lcd_write_command(0xB3);						// Set Display Clock Divider / Oscillator Frequency
	lcd_write_data(divider);						//   Default => 0xD0, A[3:0] => Display Clock Divider, A[7:4] => Oscillator Frequency
}

void Set_Display_Enhancement_A(unsigned char vsl, unsigned char quality)
{
	lcd_write_command(0xB4);						// Display Enhancement
	lcd_write_data(0xA0|vsl);						//   Default => 0xA2, 0xA0 (0x00) => Enable External VSL, 0xA2 (0x02) => Enable Internal VSL (Kept VSL Pin N.C.)
	lcd_write_data(0x05|quality);					//   Default => 0xB5, 0xB5 (0xB0) => Normal, 0xFD (0xF8) => Enhance Low Gray Scale Display Quality
}

void Set_GPIO(unsigned char gpio)
{
	lcd_write_command(0xB5);						// General Purpose IO
	lcd_write_data(gpio);							//   Default => 0x0A (GPIO Pins output Low Level.)
}

void Set_Precharge_Period(unsigned char period)
{
	lcd_write_command(0xB6);						// Set Second Pre-Charge Period
	lcd_write_data(period);							//   Default => 0x08 (8 Display Clocks)
}

void Set_Precharge_Voltage(unsigned char voltage)
{
	lcd_write_command(0xBB);						// Set Pre-Charge Voltage Level
	lcd_write_data(voltage);						//   Default => 0x17 (0.50*VCC)
}

void Set_VCOMH(unsigned char voltage_level)
{
	lcd_write_command(0xBE);						// Set COM Deselect Voltage Level
	lcd_write_data(voltage_level);					//   Default => 0x04 (0.80*VCC)
}

void Set_Contrast_Current(unsigned char contrast)
{
	lcd_write_command(0xC1);						// Set Contrast Current
	lcd_write_data(contrast);						//   Default => 0x7F
}

void Set_Master_Current(unsigned char master_current)
{
	lcd_write_command(0xC7);						// Master Contrast Current Control
	lcd_write_data(master_current);					//   Default => 0x0f (Maximum)
}

void Set_Multiplex_Ratio(unsigned char ratio)
{
	lcd_write_command(0xCA);						// Set Multiplex Ratio
	lcd_write_data(ratio);							//   Default => 0x7F (1/128 Duty)
}

void Set_Display_Enhancement_B(unsigned char enhancement)
{
	lcd_write_command(0xD1);						// Display Enhancement
	lcd_write_data(0x82|enhancement);				//   Default => 0xA2, 0x82 (0x00) => Reserved, 0xA2 (0x20) => Normal
	lcd_write_data(0x20);
}

void Set_Command_Lock(unsigned char lock)
{
	lcd_write_command(0xFD);						// Set Command Lock
	lcd_write_data(0x12|lock);						//   Default => 0x12
													//     0x12 => Driver IC interface is unlocked from entering command.
													//     0x16 => All Commands are locked except 0xFD.
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Gray Scale Table Setting (Full Screen)
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void Set_Gray_Scale_Table()
{
	lcd_write_command(0xB8);						// Set Gray Scale Table
	#ifdef NEW_VERSION
		lcd_write_data(0x00);						//   Gray Scale Level 1
		lcd_write_data(0x28);						//   Gray Scale Level 2
		lcd_write_data(0x37);						//   Gray Scale Level 3
		lcd_write_data(0x43);						//   Gray Scale Level 4
		lcd_write_data(0x4D);						//   Gray Scale Level 5
		lcd_write_data(0x56);						//   Gray Scale Level 6
		lcd_write_data(0x60);						//   Gray Scale Level 7
		lcd_write_data(0x68);						//   Gray Scale Level 8
		lcd_write_data(0x72);						//   Gray Scale Level 9
		lcd_write_data(0x7C);						//   Gray Scale Level 10
		lcd_write_data(0x86);						//   Gray Scale Level 11
		lcd_write_data(0x91);						//   Gray Scale Level 12
		lcd_write_data(0x9B);						//   Gray Scale Level 13
		lcd_write_data(0xA6);						//   Gray Scale Level 14
		lcd_write_data(0xB4);						//   Gray Scale Level 15
	#else
		lcd_write_data(0);							//   Gray Scale Level 1
		lcd_write_data(25);							//   Gray Scale Level 2
		lcd_write_data(35);							//   Gray Scale Level 3
		lcd_write_data(45);							//   Gray Scale Level 4
		lcd_write_data(55);							//   Gray Scale Level 5
		lcd_write_data(73);							//   Gray Scale Level 6
		lcd_write_data(82);							//   Gray Scale Level 7
		lcd_write_data(90);							//   Gray Scale Level 8
		lcd_write_data(100);						//   Gray Scale Level 9
		lcd_write_data(110);						//   Gray Scale Level 10
		lcd_write_data(122);						//   Gray Scale Level 11
		lcd_write_data(135);						//   Gray Scale Level 12
		lcd_write_data(150);						//   Gray Scale Level 13
		lcd_write_data(165);						//   Gray Scale Level 14
		lcd_write_data(180);						//   Gray Scale Level 15
	#endif
	lcd_write_command(0x00);						// Enable Gray Scale Table
}

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Show Regular Pattern (Full Screen)
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void lcd_fill_ram(unsigned char data)
{
	unsigned char i,j;

	Set_Column_Address(0x00,0x77);
	Set_Row_Address(0x00,0x7F);
	Set_Write_RAM();

	for(i=0;i<0x80;i++)
	{
		for(j=0;j<0x78;j++)
		{
			lcd_write_data(data);
			lcd_write_data(data);
		}
	}
}

void lcd_init()
{
	force_send_spi_data(0x00);						// Send dummy data to have a flag
	_delay_ms(10);									// Wait for Internal voltages to be stable
	PORTD &= ~(1 << LCD_nRESET);					// Reset system
	_delay_ms(10);
	PORTD |= (1 << LCD_nRESET);						// Releave reset
	Set_Command_Lock(0x12);							// Unlock Basic Commands (0x12/0x16)
	Set_Display_Off();								// Display Off (0x00/0x01)
	Set_Display_Clock(0x91);						// Set Clock as 80 Frames/Sec
	Set_Multiplex_Ratio(0x3F);						// 1/64 Duty (0x0F~0x3F)
	Set_Display_Offset(0x00);						// Shift Mapping RAM Counter (0x00~0x3F)
	Set_Start_Line(0x00);							// Set Mapping RAM Display Start Line (0x00~0x7F)
	Set_Remap_Format(0x14);							// Set Horizontal Address Increment
													//     Column Address 0 Mapped to SEG0
													//     Disable Nibble Remap
													//     Scan from COM[N-1] to COM0
													//     Disable COM Split Odd Even
													//     Enable Dual COM Line Mode
	Set_GPIO(0x00);									// Disable GPIO Pins Input
	Set_Function_Selection(0x01);					// Enable Internal VDD Regulator
	Set_Display_Enhancement_A(0xA0,0xFD);			// Enable External VSL, Low Gray Scale Enhancement
	Set_Contrast_Current(Contrast);					// Set Segment Output Current
	Set_Master_Current(Brightness);					// Set Scale Factor of Segment Output Current Control
	Set_Gray_Scale_Table();							// Set Pulse Width for Gray Scale Table
	Set_Phase_Length(0xE8);							// Set Phase 1 as 17 Clocks & Phase 2 as 14 Clocks
	Set_Display_Enhancement_B(0x20);				// Enhance Driving Scheme Capability (0x00/0x20)
	if(NEW_VERSION)
		Set_Precharge_Voltage(0x08);				// Set Pre-Charge Voltage Level as 0.30*VCC
	else
		Set_Precharge_Voltage(0x1F);				// Set Pre-Charge Voltage Level as 0.60*VCC
	if(NEW_VERSION)
		Set_Precharge_Period(0x0F);					// Set Second Pre-Charge Period as 15 Clocks
	else
		Set_Precharge_Period(0x08);					// Set Second Pre-Charge Period as 8 Clocks
	Set_VCOMH(0x07);								// Set Common Pins Deselect Voltage Level as 0.86*VCC
	Set_Display_Mode(0x02);							// Normal Display Mode (0x00/0x01/0x02/0x03)
	Set_Partial_Display_Off();						// Disable Partial Display
	lcd_fill_ram(0x00);								// Clear Screen
	enable_vcc_lcd();								// Enable 15V
	_delay_ms(1000);								// Wait for Vcc to be stable
	Set_Display_On();								// Display On (0x00/0x01)															
}

void lcd_off()
{
	Set_Display_Off();
	disable_vcc_lcd();
	_delay_ms(200);
}

void lcd_on()
{
	enable_vcc_lcd();								// Enable 15V
	_delay_ms(1000);								// Wait for Vcc to be stable
	Set_Display_On();								// Display On (0x00/0x01)
	_delay_ms(1000);								// Wait for Vcc to be stable
}
