/******************************************************************************/
/*             Spark Fun Electronics, Owen Osborn 1-22-05                    */
/*   Nokia 6100 LCD with Epson S1D15G10 Cntroller Test Program                */
/******************************************************************************/
/*                    Written for Philips ARM LPC2138                         */
/*                       Change SPI functions to suit your MCU                */            
/*                                                                            */
/*                                                                            */
/******************************************************************************/

#include "LPC21xx.h"   // LPC I/O definitions


// Epson S1D15G10 Command Set

#define DISON       0xaf   
#define DISOFF      0xae   
#define DISNOR      0xa6  
#define DISINV      0xa7  
#define COMSCN      0xbb   
#define DISCTL      0xca   
#define SLPIN       0x95   
#define SLPOUT      0x94   
#define PASET       0x75   
#define CASET       0x15   
#define DATCTL      0xbc   
#define RGBSET8     0xce   
#define RAMWR       0x5c   
#define RAMRD       0x5d   
#define PTLIN       0xa8   
#define PTLOUT      0xa9   
#define RMWIN       0xe0   
#define RMWOUT      0xee   
#define ASCSET      0xaa   
#define SCSTART     0xab   
#define OSCON       0xd1   
#define OSCOFF      0xd2   
#define PWRCTR      0x20   
#define VOLCTR      0x81   
#define VOLUP       0xd6   
#define VOLDOWN     0xd7   
#define TMPGRD      0x82   
#define EPCTIN      0xcd   
#define EPCOUT      0xcc   
#define EPMWR       0xfc   
#define EPMRD       0xfd   
#define EPSRRD1     0x7c   
#define EPSRRD2     0x7d   
#define NOP         0x25    


// LCD Chip Select
#define LCD_CS    0x00200000;
#define LCD_RESET    0x00400000;
#define LCD_SCK     0x00020000;
#define LCD_DI     0x00080000;

// these are for the LPC2138 internal initialization
void Initialize(void);
void feed(void);

// general purpose
void delay_ms(int);

// spi functions use whatever method you want
void spi_command(int);
void spi_data(int);
void spi_init(void);


// this can be the basis for more complex graphics
void LCD_put_pixel(unsigned char color, unsigned char x, unsigned char y);


int main (void) {
  
  unsigned int   i = 0;

  // Initialize the MCU
  Initialize();
  spi_init();

  delay_ms(10);


  IODIR0 |= LCD_SCK;
  IODIR0 |= LCD_DI;
  IOSET0 |= LCD_SCK;
  IOCLR0 |= LCD_DI;
  
  // reset display 
  IODIR0 |= LCD_RESET;

  IOCLR0 |= LCD_RESET;
  delay_ms(1);
  IOSET0 |= LCD_RESET;
  delay_ms(20);

  spi_command(DISCTL);  // display control
  spi_data(0x03);
  spi_data(32);
  spi_data(12);
  spi_data(0x00);

  spi_command(COMSCN);  // comscn
    spi_data(0x01);

  spi_command(OSCON);  // oscon

  spi_command(SLPOUT);  // sleep out

  spi_command(VOLCTR);  // electronic volume, this is kinda contrast/brightness
  spi_data(5);//ff);       //  this might be different for individual LCDs
  spi_data(0x01);//01);     //

  spi_command(PWRCTR);  // power ctrl
  spi_data(0x0f);      //everything on, no external reference resistors
  delay_ms(100);

  spi_command(DISINV);  // display mode


  spi_command(DATCTL);  // datctl
  spi_data(0x00);
  spi_data(0);
  spi_data(0x01);
  spi_data(0x00);

  spi_command(RGBSET8);   // setup color lookup table
    // color table
    //RED
    spi_data(0);
    spi_data(2);
    spi_data(4);
    spi_data(6);
    spi_data(8);
    spi_data(10);
    spi_data(12);
    spi_data(15);
    // GREEN
    spi_data(0);
    spi_data(2);
    spi_data(4);
    spi_data(6);
    spi_data(8);
    spi_data(10);
    spi_data(12);
    spi_data(15);
    //BLUE
    spi_data(0);
    spi_data(4);
    spi_data(9);
    spi_data(15);


  spi_command(NOP);  // nop

  spi_command(PASET);   // page start/end ram
  spi_data(2);            // for some reason starts at 2
  spi_data(131);          
  
  spi_command(CASET);   // column start/end ram
  spi_data(0);          
  spi_data(131);


  spi_command(RAMWR);    // write some stuff (background)
  for (i = 0; i < 18000; i++){
    spi_data(28);  // 28 is green
  }
  

  spi_command(DISON);   // display on

  delay_ms(200);

 for (i = 0; i < 160; i++){   // this loop adjusts the contrast, change the number of iterations to get
   spi_command(VOLUP);                // desired contrast.  This might be different for individual LCDs
   delay_ms(2);
 }


    // draw a multi-colored square in the center of screen
  for (i = 0; i < 4096; i++){
    LCD_put_pixel(i, (i % 64) + 32, (i / 64) + 32);
  }

  for(;;){  
   
    
  
  }

} // main()



// a stupid delay
void delay_ms(int count){
  int i;
  count *= 2600;
  for (i = 0; i < count; i++){
  asm volatile ("nop");
  }
}

void LCD_put_pixel(unsigned char color, unsigned char x, unsigned char y){
  x += 2;                  // for some reason starts at 2
  spi_command(0x75);   // page start/end ram
  spi_data(x);            
  spi_data(132);          
  spi_command(0x15);   // column start/end ram
  spi_data(y);            // for some reason starts at 2
  spi_data(131); 
  spi_command(0x5C);    // write some shit
  spi_data(color); 
}

void spi_init(void){
  PINSEL1 |= 0x00000088;
  IODIR0 |= LCD_CS;
  IOSET0 |= LCD_CS;
  SSPCR0 = 0x08C8;          // cpol = 1, cpha = 1
  SSPCPSR = 32;            // prescale
  SSPCR1 = 2;           // enable
}

void spi_command(int dat){
  int temp;
  IOCLR0 |= LCD_CS;
  SSPDR  = dat & ~0x0100;// & ~0x00000100;   // send next SPI channel 0 data
  while ((SSPSR & 0x10)) ;     // wait for transfer completed
  IOSET0 |= LCD_CS;
}

void spi_data(int dat){
  IOCLR0 |= LCD_CS;
  SSPDR  = dat | 0x0100;// & ~0x00000100;   // send next SPI channel 0 data
  while ((SSPSR & 0x10)) ;     // wait for transfer completed
  IOSET0 |= LCD_CS;
}


/**********************************************************
                      Initialize
**********************************************************/

#define PLOCK 0x400

void Initialize(void)  {
	
 
	// 				Setting the Phased Lock Loop (PLL)
	//               ----------------------------------
	//
	// Olimex LPC-P2106 has a 14.7456 mhz crystal
	//
	// We'd like the LPC2106 to run at 53.2368 mhz (has to be an even multiple of crystal)
	// 
	// According to the Philips LPC2106 manual:   M = cclk / Fosc	where:	M    = PLL multiplier (bits 0-4 of PLLCFG)
	//																		cclk = 53236800 hz
	//																		Fosc = 14745600 hz
	//
	// Solving:	M = 53236800 / 14745600 = 3.6103515625
	//			M = 4 (round up)
	//
	//			Note: M - 1 must be entered into bits 0-4 of PLLCFG (assign 3 to these bits)
	//
	//
	// The Current Controlled Oscilator (CCO) must operate in the range 156 mhz to 320 mhz
	//
	// According to the Philips LPC2106 manual:	Fcco = cclk * 2 * P    where:	Fcco = CCO frequency 
	//																			cclk = 53236800 hz
	//																			P = PLL divisor (bits 5-6 of PLLCFG)
	//
	// Solving:	Fcco = 53236800 * 2 * P
	//			P = 2  (trial value)
	//			Fcco = 53236800 * 2 * 2
	//			Fcc0 = 212947200 hz    (good choice for P since it's within the 156 mhz to 320 mhz range
	//
	// From Table 19 (page 48) of Philips LPC2106 manual    P = 2, PLLCFG bits 5-6 = 1  (assign 1 to these bits)
	//
	// Finally:      PLLCFG = 0  01  00011  =  0x23
	//
	// Final note: to load PLLCFG register, we must use the 0xAA followed 0x55 write sequence to the PLLFEED register
	//             this is done in the short function feed() below
	//
   
	// Setting Multiplier and Divider values
  	PLLCFG=0x23;
  	feed();
  
	// Enabling the PLL */	
	PLLCON=0x1;
	feed();
  
	// Wait for the PLL to lock to set frequency
	while(!(PLLSTAT & PLOCK)) ;
  
	// Connect the PLL as the clock source
	PLLCON=0x3;
	feed();
  
	// Enabling MAM and setting number of clocks used for Flash memory fetch (4 cclks in this case)
	MAMCR=0x2;
	MAMTIM=0x4;
  
	// Setting peripheral Clock (pclk) to System Clock (cclk)
	VPBDIV=0x1;

}


void feed(void)
{
  PLLFEED=0xAA;
  PLLFEED=0x55;
}















