#include <xc.h>
#include "ILI9341.h"
#include "settings.h"
#include "mcc_generated_files/pin_manager.h"
#include "mcc_generated_files/spi2.h"
//----------------------------------------------------------
/*
Autor: Miroslav Hancar
Ovladanie TFT displeja s radicom ILI9341 a podobnych
Vyuziva SPI rozhranie (SPI.c, SPI.h)
Pouzita je len 16-bitova farebna hlbka
Vykreslovanie na displej je mozne vo vsetkych smeroch - nastavenie MADCTL (displej sa da citat zo vsetkych stran)
*/
//----------------------------------------------------------
#define HEIGHT  128
#define WIDTH   160
//----------------------------------------------------------
#define CSXHigh()   CS_SetHigh()
#define CSXLow()    CS_SetLow()
#define RSTHigh()   RST_SetHigh()
#define RSTLow()    RST_SetLow()
#define DCHigh()    DC_SetHigh()   
#define DCLow()     DC_SetLow()
#define SPI_init8() SPI_ChangeLength(8)
#define SPI_init16() SPI_ChangeLength(16)
//#define SPI_init32() SPI_ChangeLength(32)
#define SPI_rw(data)    SPI2_Exchange8bit(data)
#define SPI_write(data) SPI_writeonly(data)
#define SPI_wait()      SPI_waitwrite()
//----------------------------------------------------------
//vyska a sirka fontu v bitoch
volatile uint16_t char_w;
volatile uint16_t font_h;
volatile uint16_t bytes_per_char;

volatile uint8_t madctl_data = MADCTL_DATA;

#define SPACE   0
//----------------------------------------------------------
//inicializacia displeja
void ILI9341_init (void)
{	
    volatile uint32_t i;
    //reset
    RSTHigh();
	CSXHigh();
	RSTLow();
	for(i=0; i<300; i++);
	RSTHigh();
	for(i=0; i<100000; i++);
    SPI_init8();
	//Power Control B
    ILI9341_write_COM(PWCTRLB);  
    ILI9341_write_DATA(0x00); 
    ILI9341_write_DATA(0XC1); 
    ILI9341_write_DATA(0X30); 

	//Power On Sequence Control
    ILI9341_write_COM(PWRONSEQ);
    ILI9341_write_DATA(0x64); 
    ILI9341_write_DATA(0x03); 
    ILI9341_write_DATA(0X12); 
    ILI9341_write_DATA(0X81); 
 
	//Driver timing control A
    ILI9341_write_COM(DRTCA);  
    ILI9341_write_DATA(0x85); 
    ILI9341_write_DATA(0x00); 
    ILI9341_write_DATA(0x78); 

	//Power control A
    ILI9341_write_COM(PWCTRLA);  
    ILI9341_write_DATA(0x39); 
    ILI9341_write_DATA(0x2C); 
    ILI9341_write_DATA(0x00); 
    ILI9341_write_DATA(0x34); 
    ILI9341_write_DATA(0x02); 
 
	//Pump Ratio Control
	//2x VCI
    ILI9341_write_COM(PRC);  
    ILI9341_write_DATA(0x20); 

	//Driver timing control B
    ILI9341_write_COM(DRTCB);  
    ILI9341_write_DATA(0x00); 
    ILI9341_write_DATA(0x00); 
 
	//Power control 1
	//VRH[5:0] - 4,60V
    ILI9341_write_COM(PWCTRL1);    
    ILI9341_write_DATA(0x23);   
 
	//Power control 2
    ILI9341_write_COM(PWCTRL2);    
    ILI9341_write_DATA(0x10);    
  
	//VCM control 1
	//VMH = 4,250V
	//VML = -1,500V
    ILI9341_write_COM(VMCTRL1);    
    ILI9341_write_DATA(0x3E); 
    ILI9341_write_DATA(0x28); 
  
	//VCM control 2
	//VMF = VMH-58, VML-58
    ILI9341_write_COM(VMCTRL2);    
    ILI9341_write_DATA(0x86);  
 
	// Memory Access Control 
    ILI9341_write_COM(MADCTL);   
    ILI9341_write_DATA(madctl_data); 

	//Pixel Format Set
	//16-bit color
    ILI9341_write_COM(PIXSET);    
    ILI9341_write_DATA(0x55); 
   
	//Frame Rate Control (In Normal Mode/Full Colors)
    ILI9341_write_COM(FRMCTR1);    
    ILI9341_write_DATA(0x00);  
    ILI9341_write_DATA(0x18); 
 
	//Display Function Control 
    ILI9341_write_COM(DISCTRL);    
    ILI9341_write_DATA(0x08); 
    ILI9341_write_DATA(0x82);
    ILI9341_write_DATA(0x27);  
  
	//3Gamma Function Enable
	//Disable
    ILI9341_write_COM(EN3G);     
    ILI9341_write_DATA(0x02); 
  
	//Gamma curve selected
   	ILI9341_write_COM(GAMSET);     
   	ILI9341_write_DATA(0x02); 
	
	//Positive Gamma Correction 
   	ILI9341_write_COM(PGAMCTRL);    
   	ILI9341_write_DATA(0x0F); 
   	ILI9341_write_DATA(0x31); 
   	ILI9341_write_DATA(0x2B); 
   	ILI9341_write_DATA(0x0C); 
   	ILI9341_write_DATA(0x0E); 
   	ILI9341_write_DATA(0x08); 
   	ILI9341_write_DATA(0x4E); 
   	ILI9341_write_DATA(0xF1); 
   	ILI9341_write_DATA(0x37); 
   	ILI9341_write_DATA(0x07); 
   	ILI9341_write_DATA(0x10); 
   	ILI9341_write_DATA(0x03); 
   	ILI9341_write_DATA(0x0E); 
   	ILI9341_write_DATA(0x09); 
 	ILI9341_write_DATA(0x00); 
  
	//Negative Gamma Correction
    ILI9341_write_COM(NGAMCTRL);    
    ILI9341_write_DATA(0x00); 
    ILI9341_write_DATA(0x0E); 
    ILI9341_write_DATA(0x14); 
    ILI9341_write_DATA(0x03); 
    ILI9341_write_DATA(0x11); 
    ILI9341_write_DATA(0x07); 
    ILI9341_write_DATA(0x31); 
    ILI9341_write_DATA(0xC1); 
    ILI9341_write_DATA(0x48); 
    ILI9341_write_DATA(0x08); 
    ILI9341_write_DATA(0x0F); 
    ILI9341_write_DATA(0x0C); 
    ILI9341_write_DATA(0x31); 
    ILI9341_write_DATA(0x36); 
    ILI9341_write_DATA(0x0F); 

	//Sleep Out
    ILI9341_write_COM(SLPOUT);    
    for(i=0; i<200000; i++);
	//Display ON		
    ILI9341_write_COM(DISPON);    	
}
//---------------------------------------------------------
//skontroluje ID modulu prikazom RDID2 ktore je pri tomto displeji 0
//skontroluje obsah registru MADCTL ktory bol zapisany pri inicializacii (symbolicky madctl_data)
//life-sign funkcia -> vrati 1 ak su obidva vratene parametre spravne a 0 ak su ine
uint8_t ILI9341_check (void)
{
	volatile uint8_t temp1;
	volatile uint8_t temp2;

	temp1 = ILI9341_read_COM(RDID2);
	temp2 = ILI9341_read_COM(RDDMADCTL);

	if((temp1 == 0) && (temp2 == madctl_data))
		return 1;
	else
		return 0;

}
//---------------------------------------------------------
//na poziciu x,y nakresli pixel s farbou color
void ILI9341_pixel (uint16_t color, uint16_t x, uint16_t y)
{
	volatile uint16_t xlimit;
	volatile uint16_t ylimit;

	//testuje adresovaci mod (MADCTL) 
	if((madctl_data & 0x20) == 0)
	{
		xlimit = HEIGHT;
		ylimit = WIDTH;
	}
	else
	{
		xlimit = WIDTH;
		ylimit = HEIGHT;
	}

	//ak zadana suradnica prekroci sirku/vysku pixel sa nenakresli
	if((x >= xlimit) || (y >= ylimit))
		return;
    ILI9341_frameset(x,y,x,y);
    
    SPI_init8();
	ILI9341_write_COM(RAMWR);
    SPI_init16();
	DCHigh();
    CSXLow();
    SPI_write(color);
    SPI_wait();
    CSXHigh();   
}
//---------------------------------------------------------
//vyplni cely displej 
void ILI9341_fill (unsigned int color)
{
	volatile uint32_t x;
    volatile uint32_t total_pixels = WIDTH*HEIGHT;
	volatile uint16_t cdata;
	volatile uint16_t pdata;

	//testuje adresovaci mod displeja (ci sa kresli na sirku alebo vysku)
	if((madctl_data & 0x20) == 0)
	{
		cdata = HEIGHT;
		pdata = WIDTH;
	}
	else
	{
		cdata = WIDTH;
		pdata = HEIGHT;
	}
    
    ILI9341_frameset(0,0,cdata,pdata);

    SPI_init8();
	ILI9341_write_COM(RAMWR);
	DCHigh();
	CSXLow();
	//SPI sa prepne na 16bit
	SPI_init16();
	for(x=0; x<total_pixels; x++)
	{
		SPI_write(color);		//16bit
	}
    //pocka na dokoncenie zapisu velkeho mnozstva dat
    SPI_wait();
    CSXHigh();
}
//---------------------------------------------------------
//"vymaluje" urcenu plochu (obldznik) na dispelji
//xs,ys - lavy horny okraj obdlzniku (zaciatok), xe,ye - pravy dolny okraj 
void ILI9341_fillrect (unsigned int xs, unsigned int ys, unsigned int xe, unsigned int ye, unsigned int color)
{
	volatile uint16_t xlimit;
	volatile uint16_t ylimit;
	
	volatile uint16_t temp;
	volatile uint16_t i;
	volatile uint16_t j;
    volatile uint16_t total;

	volatile int16_t xdif = 0;
	volatile int16_t ydif = 0;

	//skontroluje ci plocha nepresahuje plochu displeja 
	if((madctl_data & 0x20) == 0)
	{
		xlimit = HEIGHT;
		ylimit = WIDTH;
	}
	else
	{
		xlimit = WIDTH;
		ylimit = HEIGHT;
	}

	xdif = xe - xs;
	if(xdif < 0)
	{
		xdif = xdif * (-1);
		temp = xs;
		xs = xe;
		xe = temp;
	}	
	ydif = ye - ys;
	if(ydif < 0)
	{
		ydif = ydif * (-1);
		temp = ys;
		ys = ye;
		ye = temp;
	}
	
	if((xs > xlimit) || (ys > ylimit) || (xdif > xlimit) || (ydif > ylimit))
		return;
    total = (xdif+1) * (ydif+1);
    
    ILI9341_frameset(xs,ys,xe,ye);

	ILI9341_write_COM(RAMWR);
	DCHigh();
	CSXLow();
	//SPI sa prepne na 16bit
	SPI_init16();
	for(i=0; i < total; i++)
	{
        SPI_write(color);
	}
    SPI_wait();
    CSXHigh();
}
//---------------------------------------------------------
//nakresli znak z ASCII na poziciu (x,y)
//funkcii sa zadava ASCII znak, pozicia, farba znaku (16b), farba pozadia (16b)
void ILI9341_char (uint8_t character, uint16_t x, uint16_t y, uint16_t col, uint16_t bg_col, const uint8_t *fonts, const uint16_t font_descriptor[][2])
{
    uint16_t i;
    uint8_t j;
    uint16_t k;
    uint8_t m = 0;
    uint8_t n;
    uint16_t add;
    uint8_t line;
    uint8_t char_w2;
    uint8_t char_w3;
    volatile uint8_t ch;
    
//    if(character>92)
//        character -= 1;
    ch = character - 0x20;
    char_w = font_descriptor[ch][0];
    char_w2 = ((char_w-1)>>3)+1;
    font_h = (font_descriptor[1][1]-font_descriptor[0][1])/(((font_descriptor[0][0]-1)>>3)+1);
    bytes_per_char = char_w2*font_h;
    char_w3 = char_w2<<3;
    
    //OUTPUT_SetHigh();
    ILI9341_frameset(x,y,x+(char_w+(SPACE-1)),y+(font_h-1));
    SPI_init8();
   	ILI9341_write_COM(RAMWR);
    
    add = font_descriptor[ch][1];
            
    k = 0;
    SPI_init16();
    DCHigh();
    CSXLow();
    for(i=0;i<font_h;i++)
    {
        for(j=0;j<char_w+SPACE;j++,m++)
        {
            if(j<char_w)
            {
                if(m==8)
                {
                    m=0;
                    k+=1;
                }
                line = fonts[add+k];
                if((line<<m) & 0x80)
                    SPI_write(col);
                else
                    SPI_write(bg_col);                
            }
            else
            {
                SPI_write(bg_col);
            }
        }
        m=0;
        k+=1;
    }
    SPI_wait();
    CSXHigh();
}
//---------------------------------------------------------
//vypise string
//pri presahu riadku sa zapisuje na novy riadok
//pri presahu dolnej hranice sa zapisuje odznova
//maximalna velkost 5
void ILI9341_string (uint8_t str[], uint16_t x, uint16_t y, uint16_t col, uint16_t bg_col, const uint8_t *fonts, const uint16_t font_descriptor[][2])
{
    volatile uint16_t xlimit,ylimit;
	volatile uint8_t i;
    volatile uint16_t x_pos = x;

	if((madctl_data & 0x20) == 0)
	{
		xlimit = HEIGHT;
		ylimit = WIDTH;
	}
	else
	{
		xlimit = WIDTH;
		ylimit = HEIGHT;
	}

    font_h = (font_descriptor[1][1]-font_descriptor[0][1])/(((font_descriptor[0][0]-1)>>3)+1);
	 
	for(i=0; str[i] != '\0'; i++)
	{
        char_w = font_descriptor[str[i]-32][0];
		if((x_pos+char_w) > 489)
		{
			y = y+(font_h);
			x = 0;
			x_pos = 0;
		} 
		if((y+(font_h)) > 319)
			y = 0;
		ILI9341_char(str[i], x_pos, y, col, bg_col, fonts, font_descriptor);
        x_pos += char_w+SPACE;
	}
}
//------------------------------------------------------------------------------
void ILI9341_center_string (uint8_t str[], uint16_t x, uint16_t y, uint16_t color, uint16_t bg_color, const uint8_t *fonts, const uint16_t font_descriptor[][2])
{
    uint8_t i;
    uint16_t w = 0;
    uint16_t h = 0;
    
    for(i=0; str[i] != '\0'; i++)
    {
        w += font_descriptor[str[i]-32][0]+SPACE;
    }
    h = (font_descriptor[1][1]-font_descriptor[0][1])/(((font_descriptor[0][0]-1)>>3)+1);
    ILI9341_string(str,x-(w>>1),y-(h>>1),color,bg_color,fonts,font_descriptor);
}
//---------------------------------------------------------
//nakresli horizontalnu ciaru s pociatocnymi suradnicami xs a ys a dlzkou lenght a farbou color
void ILI9341_Hline (unsigned int xs, unsigned int ys, unsigned int lenght, unsigned int color)
{
	static unsigned int xlimit;
	static unsigned int ylimit;
	
	if((madctl_data & 0x20) == 0)
	{
		xlimit = 0x00EF;
		ylimit = 0x013F;
	}
	else
	{
		xlimit = 0x013F;
		ylimit = 0x00EF;
	}

	if((ys > ylimit) || (xs+lenght) > xlimit)
		return;


	ILI9341_fillrect(xs, ys, (xs+lenght), ys, color);
}
//---------------------------------------------------------
//nakresli vertikalnu ciaru 
void ILI9341_Vline (unsigned int xs, unsigned int ys, unsigned int lenght, unsigned int color)
{
	static unsigned int xlimit;
	static unsigned int ylimit;
	
	if((madctl_data & 0x20) == 0)
	{
		xlimit = 0x00EF;
		ylimit = 0x013F;
	}
	else
	{
		xlimit = 0x013F;
		ylimit = 0x00EF;
	}

	if(((ys+lenght) > ylimit) || (xs > xlimit))
		return;

	ILI9341_fillrect(xs, ys, xs, (ys+lenght), color);
}
//---------------------------------------------------------
//nakresli ciaru od xs,ys do xe,ye
void ILI9341_line (unsigned int xs, unsigned int ys, unsigned int xe, unsigned int ye, unsigned color)
{
	int dx = abs(xe-xs);	//abs - funkcia XC16
	int sx = xs<xe ? 1 : -1;
	int dy = abs(ye-ys);
	int sy = ys<ye ? 1 : -1; 
	int err = (dx>dy ? dx : -dy)/2;
	int e2;
	static unsigned int xlimit;
	static unsigned int ylimit;

	if((madctl_data & 0x20) == 0)
	{
		xlimit = 0x00EF;
		ylimit = 0x013F;
	}
	else
	{
		xlimit = 0x013F;
		ylimit = 0x00EF;
	}

	if((dx > xlimit) || (dy > ylimit))
		return;
	
	for(;;)
	{
		ILI9341_pixel(color,xs,ys);
		if (xs==xe && ys==ye) 
			break;
		e2 = err;
		if (e2 >-dx) 
		{ 
			err -= dy; 
			xs += sx; 
		}
		if (e2 < dy) 	
		{ 
			err += dx; 
			ys += sy;
		}
	}

}
//---------------------------------------------------------
//nakresli bitmapu na zadane suradnice (x,y) so zadanou sirkou a vyskou 
//bitmapa musi byt 16-bitova (RGB 5-6-5) a musi to byt jednorozmerne pole
void ILI9341_bitmap (unsigned int x, unsigned int y, unsigned int s, unsigned int v, unsigned int bmp[])
{

	static unsigned int xlimit;
	static unsigned int ylimit;
	static unsigned long pixels;
	static unsigned long i;

	if((madctl_data & 0x20) == 0)
	{
		xlimit = 0x00EF;
		ylimit = 0x013F;
	}
	else
	{
		xlimit = 0x013F;
		ylimit = 0x00EF;
	}


	if((x+s-1)>xlimit || (y+v-1)>ylimit)
		return;

	pixels = v;
	pixels *= s;
	
	ILI9341_write_COM(CASET);
	ILI9341_write_DATA(x>>8);
	ILI9341_write_DATA(x);
	ILI9341_write_DATA((x+s)>>8);
	ILI9341_write_DATA(x+s);

	ILI9341_write_COM(PASET);
	ILI9341_write_DATA(y>>8);
	ILI9341_write_DATA(y);
	ILI9341_write_DATA((y+v)>>8);
	ILI9341_write_DATA(y+v);

	ILI9341_write_COM(RAMWR);
	DCHigh();
	CSXLow();
	SPI_init16();

	for(i=0; i<pixels; i++)
	{
		SPI_write(bmp[i]);
	}

	DCLow();
	CSXHigh();

	SPI_init8();
 
} 
//------------------------------------------------------------------------------
//nakresli kruznicu s polomerom r okolo bodu (x,y)
void ILI9341_circle (unsigned int x0, unsigned int y0, unsigned int r, unsigned int color)
{
    static int x = 0;
    static int dx = 1;
    int dy = r*2;
    int p = -(r>>1);

    ILI9341_pixel(color, x0 + r, y0);
    ILI9341_pixel(color, x0 - r, y0);
    ILI9341_pixel(color, x0, y0 - r);
    ILI9341_pixel(color, x0, y0 + r);
    
    while(x<r)
    {
        if(p>=0) 
        {
          dy-=2;
          p-=dy;
          r--;
        }

        dx+=2;
        p+=dx;

        x++;
        
        ILI9341_pixel(color, x0 + x, y0 + r);
        ILI9341_pixel(color, x0 - x, y0 + r);
        ILI9341_pixel(color, x0 - x, y0 - r);
        ILI9341_pixel(color, x0 + x, y0 - r);

        ILI9341_pixel(color, x0 + r, y0 + x);
        ILI9341_pixel(color, x0 - r, y0 + x);
        ILI9341_pixel(color, x0 - r, y0 - x);
        ILI9341_pixel(color, x0 + r, y0 - x);       
    }
    
}
//---------------------------------------------------------
//zapis dat do displeja 
void ILI9341_write_DATA (unsigned char data)
{
	DCHigh();
	CSXLow();
	SPI_rw(data);
	CSXHigh();
}
//----------------------------------------------------------
//zapis instrukcie
void ILI9341_write_COM (unsigned char data)
{
	DCLow();
	CSXLow();
	SPI_rw(data);
    SPI_wait();
	CSXHigh();
}
//----------------------------------------------------------
//citanie cez instrukciu (8 bitov)
unsigned char ILI9341_read_COM (unsigned char data)
{
	static unsigned char ret_data;
	DCLow();
	CSXLow();
	ret_data = SPI_rw(data);
	ret_data = 0;
	DCHigh();
	ret_data = SPI_rw(DUMMY);
	CSXHigh();

	return ret_data;
}
//----------------------------------------------------------
//nastavi Frame (CASET,PASET)
void ILI9341_frameset (unsigned int xs, unsigned int ys, unsigned int xe, unsigned int ye)
{
    SPI_init8();
	ILI9341_write_COM(CASET);
    SPI_init16();
    CSXLow();
    DCHigh();
    SPI_write(xs);
    SPI_write(xe);
    SPI_wait();
    CSXHigh();
    
    SPI_init8();
	ILI9341_write_COM(PASET);
    SPI_init16();
    CSXLow();
    DCHigh();
    SPI_write(ys);
    SPI_write(ye);
    SPI_wait();
    CSXHigh();
}
//-----------------------------------------------------------
//madctl
void ILI9341_madctl (uint8_t data)
{
    SPI_init8();
    ILI9341_write_COM(MADCTL);   
    ILI9341_write_DATA(data);
    madctl_data = data;
}
