/*
*************************************************************************************************
*	  				SSD1928 262k color Graphical LCD Display Driver in Landscape Mode
*										All Rights Reserved
* File name		: SSD1928L_8BIT.c
* Programmer 	: John Leung, TechToys Co. Hong Kong
* Web presence  : www.TechToys.com.hk
* Note			: 
* Language		: RealView Compiler Demo version from Keil
* Hardware		: LPC2103
* Date			: 16th March 2009
*************************************************************************************************
*										DESCRIPTION
*
* This module provides an interface to LVC75Z779 Eval Board (Version 1E) graphical LCD module of 240*320dots
* Image Processor is Solomon SSD1928
* 8-bit data bus mode 8080
*
* --------- LCD			MCU -----------------
* pin 40	n.c.		- NOT connected
* pin 38 	n.c. 		- NOT connected
* pin 36	SLEEP		- P0.3
* pin 34:22	DB[7:0]		- P0.[15:8]
* pin 18	/RES		- RST of the system (MAX811)
* pin 16	RS			- P0.20
* pin 14	CS			- P0.19
* pin 12	RD			- P0.17
* pin 10	WR			- P0.18
* pin 8		n.c.		- NOT used
* pin 6		Vdd			- 3.3V
* pin 4		GND			- GND
* pin 2		EN			- P0.21
*
***********************************************************************************************
*/

#include "SSD1928L_8BIT.h"
#include "..\..\TMR0\tmr0.h"

INT8U _gpioStatus = 0;

/*
***********************************************************************************************
*									LOCAL FUNCTION PROTOTYPES
***********************************************************************************************
*/
#define CS_STROBE() 	{cDispCLR_bit.CS = 1; cDispSET_bit.CS = 1;}

/**************** LOCAL FUNCTIONS PROTOTYPES (SSD1928 SPECIFIC) ****************/
void GPIO_SPRESET(BOOLEAN state);
void GPIO_SPENA(BOOLEAN state);
void GPIO_SPCLK(BOOLEAN state);
void GPIO_SPDAT(BOOLEAN state);

void SPI_Write(INT8U byte);
void SPI_SetReg(INT8U reg, INT16U cmd);



/*
*********************************************************************************************************
*                         				DISPLAY INITIALIZATION
* Function:  Display initialization
*
* PreCondition: none
*
* Input: none
*
* Output: none
*
* Side Effects: none
*
* Overview: Resets LCD, initializes port direction
*			Initialize low level IO port for mcu,
*			configure LCD via SPI, initialize SSD1928 for PCLK, HYSNC, VSYNC etc
*
*
* Note: none
*
*********************************************************************************************************
*/

void cDispInit(void){

	cDispInitPort();
	
    // set CS pin to disable LCD 
    cDispSET_bit.CS = 1;

	cDispWrReg(REG_POWER_SAVE_CONFIG, 0x00);				// Reg[A0h] disable power save mode to start oscillation here!!!   
	DelayMs(200);
	/*** Setup display for SSD1928 external controller ***/

	/* Set GPIO to configure the Himax controller */
	cDispWrReg(REG_GPIO_CONFIG0, 0x0F);						// REG[A8h] defines GPIO[4:0], in this case, we need to set GPIO[3:0] as output for himax controller
	cDispWrReg(REG_GPIO_CONFIG1, 0x80);						// REG[A9h] turns on GPIO control
	// LL Reset to LCD!!!
	GPIO_SPENA(1);
	GPIO_SPCLK(1);
	GPIO_SPDAT(1);
	GPIO_SPRESET(1);
	GPIO_SPRESET(0);
	DelayMs(1);
	GPIO_SPRESET(1);
	SPI_SetReg(0x01, 0x7300);					// 0x73 gives the best result by trial-an-error
	SPI_SetReg(0x02, 0x0200);
	SPI_SetReg(0x03, 0x6364);
	SPI_SetReg(0x04, 0x040f);					// Set himax for 8-bit serial TFT mode
	SPI_SetReg(0x05, 0xBCC4);
	SPI_SetReg(0x0A, 0x4008);
	SPI_SetReg(0x0B, 0xD400);
	SPI_SetReg(0x0D, 0x3229);
	SPI_SetReg(0x0E, 0x3200);
	SPI_SetReg(0x0F, 0x0000);
	SPI_SetReg(0x16, 0x9F80);
	SPI_SetReg(0x17, 0x2212);
	SPI_SetReg(0x1E, 0x0052);
	SPI_SetReg(0x30, 0x0000);
	SPI_SetReg(0x31, 0x0407);
	SPI_SetReg(0x32, 0x0202);
	SPI_SetReg(0x33, 0x0000);
	SPI_SetReg(0x34, 0x0505);
	SPI_SetReg(0x35, 0x0003);
	SPI_SetReg(0x36, 0x0707);
	SPI_SetReg(0x37, 0x0000);
	SPI_SetReg(0x3A, 0x0904);
	SPI_SetReg(0x3B, 0x0904);

	/*** PLL configuration for 4MHz crystal ***/
	cDispWrReg(REG_PLL_CLK_CONFIG0, 0x0A);			// Reg 126h to N value of 0x0A for input crystal freq 4MHz
	cDispWrReg(REG_PLL_CLK_CONFIG1, 0xB4);			// Reg 127h to 0xB4, PLL output clock = 4MHz(input) * (M/N) = 4*180/10 = 72MHz
	cDispWrReg(REG_PLL_CLK_CONFIG2, 0xAE);			// Reg 12Bh to 0xAE, this is a standard procedure
	cDispWrReg(REG_PLL_CLK_CONFIG0, 0x8A);			// Reg 126h to N value of 0x0A and bit 7 set to 1 to enable PLL
	cDispWrReg(REG_MEMCLK_CONFIG, 0x00);			// Reg 04h defines the MCLK divide ratio to 0, i.e. MCLK = PLL output = 72MHz
	DelayMs(20);
	/*** PLL configuration up to here ***/

	/*** hardware initialization starts here ***/
	//SetRegL(REG_PCLK_FREQ_RATIO_0, 0x011555);

	cDispWrReg(REG_PCLK_FREQ_RATIO_0, 0x55);		// Reg 158h is the PCLK divide ratio register0 PCLK[7:0]
	cDispWrReg(REG_PCLK_FREQ_RATIO_1, 0x15);		// Reg 159h is the PCLK divide ratio register1 PCLK[15:8]
	cDispWrReg(REG_PCLK_FREQ_RATIO_2, 0x01);		// Reg 15Ah  configures PCLK[19:16]
													// Altogether, PCLK freq = MCLK freq.*(PCLK freq ratio +1)/(2^18)
													// i.e. PCLK freq = 72MHz * (0x11555+1) / 2^18 = 19.5MHz. We need to set 3x the speed of 24-bit parallel RGB display
	//Aging mode trial
	//cDispWrReg(REG_POWER_SAVE_CONFIG, 0x00);		// 0xa0 disable power save mode to start oscillation here!!!	

	cDispWrReg(REG_PANEL_TYPE, 0x72);				// Reg 10h : Set panel type to serial TFT, color LCD
	cDispWrReg(REG_MOD_RATE, 0x00);					// Reg 11h to 0 for LDEN toggles every LFRAME


	/* Set Horizontal total by REG[12h] & REG[13h] */
	// The old settings REG_HORIZ_TOTAL_0 0x32, TOTAL_1 0x07
	cDispWrReg(REG_HORIZ_TOTAL_0, 0x32);			// Reg 12h for horizontal total bits[10:3]
	cDispWrReg(REG_HORIZ_TOTAL_1, 0x07);			// Reg 13h for horizontal total bits[2:0], i.e. its range is 0 ~ 7; therefore an input of 0x0A as the argument will be eq. 0b00000010
												// Horizontal total in pixel no. = (REG[12h])*8 + REG[13] + 1
												// RLM779-1 panel has Hcycle=408 in landscape position, therefore (bits[10:3])*8 + bits[2:0] + 1 = 408
												// By observation, "50*8 + 7 + 1" fits; so bits[10:3] = 0x32, bits[2:0] = 0x07 to make the total 408 pixels
												// For serial TFT display, 1 pixel = 3 tDOTCLKs, and we need to bear in mind that, one cycle out of every four 
												// DOTCLKs is off in horizontal display period. In horizontal non-display period, all DOTCLKs are on.
												// So, the total number of DOTCLKs would be like 3x that of 24-bit parallel RGB display
	/* Finally, we need to define the effective horizontal display pixel by REG[14h] */
	cDispWrReg(REG_HDP, 0x27);					// Reg 14h defines the effective display pixel no. Horizontal display period = (bits[6:0]+1)*8
												// therefore, DISP_HOR_RESOLUTION = (320)/8 - 1 for landscape

	/* Set LLINE Pulse Start Position by REG[23h] & REG[22h] */
	cDispWrReg(REG_HSYNC_PULSE_START_POS0, 0x00);			//REG[0x22] not needed
	cDispWrReg(REG_HSYNC_PULSE_START_POS1, 0x00);			//REG[0x23] not needed

	/* Set LLINE Pulse width by REG[20h] */
	cDispWrReg(REG_HSYNC_PULSE_WIDTH, 0x00);					// Reg 20h defines LLINE Pulse width, i.e. it defines the LP polarity and width
															// REG[20h] bit 7 is the pulse polarity, 0 for active low for RLM779
															// bits[6:0] define the LLINE pulse width in PCLK = bits[6:0] + 1
    cDispWrReg(REG_LLINE_PULSE_START_SUBPIXEL_POS, 0x00);		// REG[0x21] defines exactly where display period starts down to sub-pixel accuracy, but it is not needed here  

	/* REG[17h] & REG[16h] define the Horizontal display start position relative to the horizontal sync pulse edge */
	cDispWrReg(REG_HDP_START_POS0, 0x44);			// Reg 16h defines period start position timing in pixel no. bits[7:0], RLM779's tHBP=68
	cDispWrReg(REG_HDP_START_POS1, 0x00);			// Reg 17h defines period start position timing bits[10:8]


	// Set the total number of lines in vertical orentation 
	cDispWrReg(REG_VERT_TOTAL0,0x05);				// Reg 18h defines bits[7:0] of the vertical total period
	cDispWrReg(REG_VERT_TOTAL1,0x01);				// Reg 19h defineds bits[10:8] of the vertical total period
												// Vertical total in number of lines = bits[10:0] + 1 = 0x105+1; for RLM779 it is 262
	// The display vertical is 262 lines 
	cDispWrReg(REG_VDP0, 0xEF);					// Reg 1Ch defines vertical display period bits [7:0]
	cDispWrReg(REG_VDP1, 0x00);					// Reg 1Dh defines vertical display period bits [9:8]
												// Vertical display period = bits [9:0] + 1 = 00ef + 1 = 239 + 1
	// Vertical display starts from tVBP=18
	cDispWrReg(REG_VDP_START_POS0, 0x12);			// Reg 1Eh defines bits[7:0] of the vertical display period starts
	cDispWrReg(REG_VDP_START_POS1, 0x00);			// Reg 1Fh defines bits[10:8] of the vertical display period starts
												// For RLM779, it is 18 (tVBP)

	cDispWrReg(REG_VSYNC_PULSE_WIDTH, 0x01);		//Reg 0x24 defines the vertical sync pulse width & polarity. We need a width of two hsync pulses for the v-sync signal
												//LFRAME Pulse Width in no. of pixels(hsync) = (Bits[2:0]+1)xHorizontal Total + offset
	cDispWrReg(REG_VSYNC_PULSE_START_POS0, 0x00);	//Reg 0x26 sets zero
	cDispWrReg(REG_VSYNC_PULSE_START_POS1, 0x00);	//Reg 0x27 sets zero

	/////////////////////////////////////////////////////////////////////
	// ROTATION MODE
	#define  WIN_START_ADDR		0ul
	
	cDispWrReg(REG_MAIN_WIN_DISP_START_ADDR0,WIN_START_ADDR&0x00FF); 			// Reg[74h] defines bits[7:0] for the Main Window display start address
	cDispWrReg(REG_MAIN_WIN_DISP_START_ADDR1,(WIN_START_ADDR>>8)&0x00FF); 		// Reg[75h]	defines bits[15:0] for the Main Window display start address
	cDispWrReg(REG_MAIN_WIN_DISP_START_ADDR2,(WIN_START_ADDR>>16)&0x00FF); 	// Reg[76h] defines bits[16] for the Main Window display start address
																			// These bits form the 17-bit address for the starting double-word of the LCD image in the display
																			// buffer for the main window. 
	/////////////////////////////////////////////////////////////////////
	// Special Effects Register (reg 71h)
	/////////////////////////////////////////////////////////////////////
	cDispWrReg(REG_SPECIAL_EFFECTS,0x40);

	/////////////////////////////////////////////////////////////////////
	// Main Window Display Offset (regs 78h, 79h)
	/////////////////////////////////////////////////////////////////////
	#define WIN_OFFSET  ( SCREEN_HOR_SIZE/2 )
	cDispWrReg(REG_MAIN_WIN_ADDR_OFFSET0, WIN_OFFSET&0x00FF);
	cDispWrReg(REG_MAIN_WIN_ADDR_OFFSET1, (WIN_OFFSET>>8)&0x00FF);

	/////////////////////////////////////////////////////////////////////
	// Display Mode Register (reg 70h)
	/////////////////////////////////////////////////////////////////////
    cDispWrReg(REG_DISPLAY_MODE,0x04);  // 16 BPP, enable RAM content to screen

	//REG_DV_OFORMAT==0x1a4, only RGB mode support for now	
	//Set Main Window to RGB mode
	cDispWrReg(REG_DV_OFORMAT,0xc0);

	cDispWrReg(REG_POWER_SAVE_CONFIG, 0x00);		//Start oscillation here for PCK etc
	
}

/*********************************************************************
* Function: void cDispPutPixel(INT16U x, INT16U y)
*
* PreCondition: none
*
* Input: x,y - pixel coordinates
*		 color - color of the pixel to be written in 16-bit
*
* Output: none
*
* Side Effects: none
*
* Overview: puts pixel
*
* Note: This function modified for 8-bit accessing mode for SSD1928
*		Please note that in byte mode access, the address is auto-incr
*		by a single byte instead, therefore we need to make increment
*		on x for an extra byte for advancing the address by 1 byte
********************************************************************/
void cDispPutPixel(INT16U x, INT16U y, INT16U color)
{
	INT32U address;
	SHORT_VAL Val;
	Val.Val = color;
	
	address =  ((INT32U)(SCREEN_HOR_SIZE*y) + x)<<1;

	cDispCLR_bit.WR = 1;
	cDispSetAddress(address);
    cDispWrDat(Val.v[1],Val.v[0]);
	cDispSET_bit.WR = 1;

}

/*
*********************************************************************************************************
*                         					WRITE ADDRESS POINTER
*
* Description : This function writes address pointer		
* Arguments   : //addr0,addr1,addr2 - address bytes
*				address - in DWORD
* Returns     : none
* Note		  : WR pin should be set low first
*********************************************************************************************************
*/
/*
void cDispSetAddress(INT8U addr2, INT8U addr1, INT8U addr0)
{
	cDispCLR_bit.RS  = 1;		// set RS line low for command
 	// M/R#=1 => memory access, and this bitwise OR with the address content AB18:16
	cDispPIN_bit.DAT = addr2|0x80;	
	CS_STROBE();
	
	cDispPIN_bit.DAT = addr1;
	CS_STROBE();

	cDispPIN_bit.DAT = addr0;
	CS_STROBE();

	cDispSET_bit.RS  = 1;		// set RS line high for data
}
*/
void cDispSetAddress(INT32U address)
{
	DWORD_VAL Val;
	Val.Val = address;

	cDispCLR_bit.RS  = 1;		// set RS line low for command
 	// M/R#=1 => memory access, and this bitwise OR with the address content AB18:16
	cDispPIN_bit.DAT = Val.v[2]|0x80;	
	CS_STROBE();
	
	cDispPIN_bit.DAT = Val.v[1];
	CS_STROBE();

	cDispPIN_bit.DAT = Val.v[0];
	CS_STROBE();

	cDispSET_bit.RS  = 1;		// set RS line high for data
}

/*
*********************************************************************************************************
*                         			 CLEAR DISPLAY WITH PURE COLOR IN 16K
*
* Description : This function simply clears the display with a pure color
* Arguments   : INT16U color, in 16k mode. Color definition can be found under ili9320P_8bit.h
*			
* Returns     : note
* Note		  : This function needs low level pin toggle for CS pin; therefore it is hardware specific
*******************************************************************************************************
*/
void cDispClearDevice(INT16U color){

	long counter;
	SHORT_VAL Val;
	Val.Val = color;
 
    cDispCLR_bit.WR = 1;

    cDispSetAddress(0);
    for(counter=0; counter<(long)(SCREEN_HOR_SIZE)*(SCREEN_VER_SIZE); counter++){
        cDispWrDat(Val.v[1],Val.v[0]);
    }
    // set CS pin to DISABLE LCD 
    cDispSET_bit.WR = 1;

}

/*
*********************************************************************************************************
*                         			LOW LEVEL PORT INITIALIZATION
*
* Description : This function performs low level port initialization
* Arguments   : none
* Returns     : none
* Notes		  : Hardware specific
*********************************************************************************************************
*/
void cDispInitPort(void)
{
	cDispDIR_bit.DAT	= 0xFF;
	cDispDIR_bit.RS		= 1;
	cDispDIR_bit.WR		= 1;
	cDispDIR_bit.RD		= 1;
	cDispDIR_bit.CS		= 1;
	cDispDIR_bit.LED	= 1;
	cDispDIR_bit.SLEEP	= 1;


	cDispSET_bit.CS		= 1;	//set CS, WR, RD high to start
	cDispSET_bit.WR		= 1;
	cDispSET_bit.RD		= 1;
	cDispSET_bit.LED	= 1;	//set LED EN pin high to enable backlight
	cDispCLR_bit.SLEEP	= 1;	//SLEEP pin of SSD1928, image processor in sleep mode when SLEEP=high

}

/*
*********************************************************************************************************
*                         				LOW LEVEL REGISTER WRITE TO LCD
*
* Function:  void  cDispWrReg(INT16U reg, INT8U value)
*
* PreCondition: 
*
* Input: reg - register number in WORD format
*        value - byte value to be set
*
* Output: none
*
* Side Effects: none
*
* Overview: sets graphics controller register(INT16U index)
*	    	with a particular value(BYTE)
*
* Note: The min. CS# pulse low is 82ns, and min. pulse high is 82ns,
*		therefore it may be necessary to pause between CS strobes.
*********************************************************************************************************
*/
void cDispWrReg(INT16U reg, INT8U value)	
{
	SHORT_VAL Val;
	Val.Val = reg;

	cDispCLR_bit.RS  = 1;	//RS low for command
	cDispCLR_bit.WR	 = 1;	//WR pin low for write action

	cDispPIN_bit.DAT = 0x00;//register access
	CS_STROBE();
	
	cDispPIN_bit.DAT = Val.v[1];
	CS_STROBE();

	cDispPIN_bit.DAT = Val.v[0];
	CS_STROBE();

	cDispSET_bit.RS	 = 1;	//Set RS line to high for data
	cDispPIN_bit.DAT = value;
	CS_STROBE();

	cDispSET_bit.WR  = 1;   //Write disable SSD1928
}



/*
*********************************************************************************************************
*                         				LOW LEVEL DATA WRITE TO LCD
*
* Description : This function performs low level display data write to LCD
* Arguments   : (INT8U) 'byte1' 	is the high byte written to the LCD module
*				(INT8U) 'byte0'		is the lower byte
* Overview: 	writes 16-bit word into video buffer at current address.
*           	The address pointer is incremented after the operation.
*
* Returns     : none
* Notes		  : WR pin should be set low first.
*				Hardware specific.
*********************************************************************************************************
*/
 void cDispWrDat(INT8U byte1, INT8U byte0)	
{
	//cDispSET_bit.RS  = 1;	//RS high for data
 	cDispPIN_bit.DAT = byte1;
	CS_STROBE();

  	cDispPIN_bit.DAT = byte0;
	CS_STROBE();
}

/*
*************************************************************************************
* Function: 
*
* PreCondition: none
*
* Arguments:
*
* Return: none
*
* Note:
*
*************************************************************************************
*/
void GPIO_SPRESET(BOOLEAN state)
{
	if(state==TRUE)
		_gpioStatus = _gpioStatus|LCD_RESET;
	else
		_gpioStatus = _gpioStatus&(~LCD_RESET);
	
	cDispWrReg(REG_GPIO_STATUS_CONTROL0, _gpioStatus);
}

/*
*************************************************************************************
* Function: 
*
* PreCondition: none
*
* Arguments:
*
* Return: none
*
* Note:
*
*************************************************************************************
*/
void GPIO_SPENA(BOOLEAN state)
{
	if(state==TRUE)
		_gpioStatus = _gpioStatus|LCD_SPENA;
	else
		_gpioStatus = _gpioStatus&(~LCD_SPENA);
	
	cDispWrReg(REG_GPIO_STATUS_CONTROL0, _gpioStatus);

}

/*
*************************************************************************************
* Function: 
*
* PreCondition: none
*
* Arguments:
*
* Return: none
*
* Note:
*
*************************************************************************************
*/
void GPIO_SPCLK(BOOLEAN state)
{
	if(state==TRUE)
		_gpioStatus = _gpioStatus|LCD_SPCLK;
	else
		_gpioStatus = _gpioStatus&(~LCD_SPCLK);
	
	cDispWrReg(REG_GPIO_STATUS_CONTROL0, _gpioStatus);	
}

/*
*************************************************************************************
* Function: 
*
* PreCondition: none
*
* Arguments:
*
* Return: none
*
* Note:
*
*************************************************************************************
*/
void GPIO_SPDAT(BOOLEAN state)
{
	if(state==TRUE)
		_gpioStatus = _gpioStatus|LCD_SPDAT;
	else
		_gpioStatus = _gpioStatus&(~LCD_SPDAT);
	
	cDispWrReg(REG_GPIO_STATUS_CONTROL0, _gpioStatus);	
}

/*
*************************************************************************************
* Function: 
*
* PreCondition: none
*
* Arguments:
*
* Return: none
*
* Note:
*
*************************************************************************************
*/
void SPI_Write(INT8U byte)
{
	INT8U bit_ctr;

	for(bit_ctr=0; bit_ctr<8; bit_ctr++)
	{
		if(byte&0x80)
			GPIO_SPDAT(1);
		else
			GPIO_SPDAT(0);

		GPIO_SPCLK(0);
		GPIO_SPCLK(1);
		byte=(byte<<1);
	}
}

/*
*************************************************************************************
* Function: 
*
* PreCondition: none
*
* Arguments:
*
* Return: none
*
* Note:
*
*************************************************************************************
*/
void SPI_SetReg(INT8U reg, INT16U cmd)
{
	SHORT_VAL Val;
	Val.Val = cmd;
	
	GPIO_SPENA(0);		// RS=0 for index read/write, RS=1 for instruction read/write
	SPI_Write(0x70);	// This is a bitwise for (Device ID|RS|RW), therefore 0b011100|RS|RW, with RS=0, RW=0
	SPI_Write(0x00);	// register in 0x00[reg]
	SPI_Write(reg);
	GPIO_SPENA(1);

	GPIO_SPENA(0);	
	SPI_Write(0x72);	// Now RS=1 & RW=0 for command set
	SPI_Write(Val.v[1]);
	SPI_Write(Val.v[0]);
	GPIO_SPENA(1);
}




