/* Code rewritten from Adafruit Arduino library for the TFT
 *  by Syed Tahmid Mahbub
 * The TFT itself is Adafruit product 1480
 * Included below is the text header from the original Adafruit library
 *  followed by the code
 *
 */

/***************************************************
  This is an Arduino Library for the Adafruit 2.2" SPI display.
  This library works with the Adafruit 2.2" TFT Breakout w/SD card
  ----> http://www.adafruit.com/products/1480

  Check out the links above for our tutorials and wiring diagrams
  These displays use SPI to communicate, 4 or 5 pins are required to
  interface (RST is optional)
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!

  Written by Limor Fried/Ladyada for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 ****************************************************/

#include "plib.h"
#include "tft_master.h"

inline void Mode16(void)
{ // configure SPI1 for 16-bit mode
	SPI1CONSET = 0x400;
}

inline void Mode8(void)
{ // configure SPI1 for 8-bit mode
	SPI1CONCLR = 0x400;
}

void tft_spiwrite(UINT8 c)
{ // Transfer to SPI
	while (TxBufFullSPI1());
	WriteSPI1(c);
	while (SPI1STATbits.SPIBUSY); // wait for it to end of transaction
}

void tft_spiwrite8(UINT8 c)
{ // Transfer one byte c to SPI
	/* The default mode for me is to transfer 16-bits at once
	 * However, it is necessary sometimes to transfer only 8-bits at a time
	 * But this is required less often than 16-bits at once
	 * So, the default mode is 16-bit mode and is switched to 8-bit mode when
	 *     required, and then switched back at the end of the function
	 */
	Mode8(); // switch to 8-bit mode
	WriteSPI1(c);
	while (SPI1STATbits.SPIBUSY); // wait for it to end of transaction
	Mode16(); // switch back to 16-bit mode
}

inline void tft_spiwrite16(UINT16 c)
{ // Transfer two bytes "c" to SPI
	WriteSPI1(c);
	while (SPI1STATbits.SPIBUSY); // wait for it to end of transaction
}

void tft_writecommand(UINT8 c)
{
	_dc_low();
	_cs_low();

	tft_spiwrite8(c);

	_cs_high();

}

void tft_writecommand16(UINT16 c)
{
	_dc_low();
	_cs_low();

	tft_spiwrite16(c);

	_cs_high();

}

void tft_writedata(UINT8 c)
{
	_dc_high();
	_cs_low();

	tft_spiwrite8(c);

	_cs_high();

}

void tft_writedata16(UINT16 c)
{
	_dc_high();
	_cs_low();

	tft_spiwrite16(c);

	_cs_high();

}

void tft_begin(void)
{

	_width = ILI9340_TFTWIDTH;
	_height = ILI9340_TFTHEIGHT;
	RPA1R = 3; // SDO pin for SPI - goes to MOSI on TFT

	TRIS_rst = 0;
	_rst_low();
	TRIS_dc = 0;
	TRIS_cs = 0;

	_dc_low();
	_cs_high();

	SpiChnOpen(1, SPI_OPEN_MSTEN | SPI_OPEN_MODE8 | SPI_OPEN_ON |
			SPI_OPEN_DISSDI | SPI_OPEN_CKE_REV, 2);// PBCLK / SPI_freq);

	// Start with 8-bit mode for initialization - move to 16-bit mode once
	// that's done

	_rst_high();
	delay_ms(5);
	_rst_low();
	delay_ms(20);
	_rst_high();
	delay_ms(150);

	tft_writecommand(0xEF);
	tft_writedata(0x03);
	tft_writedata(0x80);
	tft_writedata(0x02);

	tft_writecommand(0xCF);
	tft_writedata(0x00);
	tft_writedata(0xC1);
	tft_writedata(0x30);

	tft_writecommand(0xED);
	tft_writedata(0x64);
	tft_writedata(0x03);
	tft_writedata(0x12);
	tft_writedata(0x81);

	tft_writecommand(0xE8);
	tft_writedata(0x85);
	tft_writedata(0x00);
	tft_writedata(0x78);

	tft_writecommand(0xCB);
	tft_writedata(0x39);
	tft_writedata(0x2C);
	tft_writedata(0x00);
	tft_writedata(0x34);
	tft_writedata(0x02);

	tft_writecommand(0xF7);
	tft_writedata(0x20);

	tft_writecommand(0xEA);
	tft_writedata(0x00);
	tft_writedata(0x00);

	tft_writecommand(ILI9340_PWCTR1); //Power control
	tft_writedata(0x23); //VRH[5:0]

	tft_writecommand(ILI9340_PWCTR2); //Power control
	tft_writedata(0x10); //SAP[2:0];BT[3:0]

	tft_writecommand(ILI9340_VMCTR1); //VCM control
	tft_writedata(0x3e);
	tft_writedata(0x28);

	tft_writecommand(ILI9340_VMCTR2); //VCM control2
	tft_writedata(0x86);

	tft_writecommand(ILI9340_MADCTL); // Memory Access Control
	tft_writedata(ILI9340_MADCTL_MX | ILI9340_MADCTL_BGR);

	tft_writecommand(ILI9340_PIXFMT);
	tft_writedata(0x55);

	tft_writecommand(ILI9340_FRMCTR1);
	tft_writedata(0x00);
	tft_writedata(0x18);

	tft_writecommand(ILI9340_DFUNCTR); // Display Function Control
	tft_writedata(0x08);
	tft_writedata(0x82);
	tft_writedata(0x27);

	tft_writecommand(0xF2); // 3Gamma Function Disable
	tft_writedata(0x00);

	tft_writecommand(ILI9340_GAMMASET); //Gamma curve selected
	tft_writedata(0x01);

	tft_writecommand(ILI9340_GMCTRP1); //Set Gamma
	tft_writedata(0x0F);
	tft_writedata(0x31);
	tft_writedata(0x2B);
	tft_writedata(0x0C);
	tft_writedata(0x0E);
	tft_writedata(0x08);
	tft_writedata(0x4E);
	tft_writedata(0xF1);
	tft_writedata(0x37);
	tft_writedata(0x07);
	tft_writedata(0x10);
	tft_writedata(0x03);
	tft_writedata(0x0E);
	tft_writedata(0x09);
	tft_writedata(0x00);

	tft_writecommand(ILI9340_GMCTRN1); //Set Gamma
	tft_writedata(0x00);
	tft_writedata(0x0E);
	tft_writedata(0x14);
	tft_writedata(0x03);
	tft_writedata(0x11);
	tft_writedata(0x07);
	tft_writedata(0x31);
	tft_writedata(0xC1);
	tft_writedata(0x48);
	tft_writedata(0x08);
	tft_writedata(0x0F);
	tft_writedata(0x0C);
	tft_writedata(0x31);
	tft_writedata(0x36);
	tft_writedata(0x0F);

	tft_writecommand(ILI9340_SLPOUT); //Exit Sleep
	delay_ms(120);
	tft_writecommand(ILI9340_DISPON); //Display on

	// Now move to 16-bit mode to speed things up for display
	Mode16();
}

void tft_setAddrWindow(UINT16 x0, UINT16 y0, UINT16 x1, UINT16 y1)
{
// old code:
//	tft_writecommand(ILI9340_CASET); // Column addr set
// replacement:
	_dc_low();
	_cs_low();
	tft_spiwrite8(ILI9340_CASET);
//	_cs_high();

	//old code:
	//  tft_writedata16(x0);
	//  tft_writedata16(x1);
	// replacement:
	_dc_high();
//	_cs_low();
	tft_spiwrite16(x0);
	tft_spiwrite16(x1);
//	_cs_high();

// old code:
//	tft_writecommand(ILI9340_PASET); // Row addr set
// replacement:
	_dc_low();
//	_cs_low();
	tft_spiwrite8(ILI9340_PASET);
//	_cs_high();

	//old code:
	//  tft_writedata16(y0);
	//  tft_writedata16(y1);
	// replacement:

	_dc_high();
//	_cs_low();
	tft_spiwrite16(y0);
	tft_spiwrite16(y1);
	_cs_high();

	tft_writecommand(ILI9340_RAMWR); // write to RAM
}

inline void tft_setAddrWindow_inline(UINT16 x0, UINT16 y0, UINT16 x1, UINT16 y1)
{
	_dc_low();
	_cs_low();
	Mode8();
	WriteSPI1(ILI9340_CASET);
	while (SPI1STATbits.SPIBUSY);
	Mode16();
	_dc_high();
	WriteSPI1(x0);
	while (TxBufFullSPI1());
	WriteSPI1(x1);
	while (SPI1STATbits.SPIBUSY);

	_dc_low();
	tft_spiwrite8(ILI9340_PASET);
	_dc_high();
	WriteSPI1(y0);
	while (TxBufFullSPI1());
	WriteSPI1(y1);
	while (SPI1STATbits.SPIBUSY);

	_dc_low();
	Mode8(); // switch to 8-bit mode
	WriteSPI1(ILI9340_RAMWR); // write to RAM
	while (SPI1STATbits.SPIBUSY);
	Mode16(); // switch back to 16-bit mode

	_cs_high();
}

void tft_pushColor(UINT16 color)
{
	_dc_high();
	_cs_low();

	tft_spiwrite16(color);

	_cs_high();
}

void tft_drawPixel(INT16 x, INT16 y, UINT16 color)
{
	if ((x >= _width) || (y >= _height)) return;

	tft_setAddrWindow_inline(x, y, x + 1, y + 1);

	_dc_high();
	_cs_low();
	tft_spiwrite16(color);
	_cs_high();
}

void tft_drawFastVLine(INT16 x, INT16 y, INT16 h, UINT16 color)
{
	// Rudimentary clipping
	if ((x >= _width) || (y >= _height)) return;

	if ((y + h - 1) >= _height)
		h = _height - y;

	tft_setAddrWindow_inline(x, y, x, y + h - 1);

	_dc_high();
	_cs_low();

	while (h--) {
		tft_spiwrite16(color);
	}

	_cs_high();
}

void tft_drawFastHLine(INT16 x, INT16 y, INT16 w, UINT16 color)
{

	// Rudimentary clipping
	if ((x >= _width) || (y >= _height)) return;
	if ((x + w - 1) >= _width) w = _width - x;
	tft_setAddrWindow_inline(x, y, x + w - 1, y);

	_dc_high();
	_cs_low();

	while (w--) {
		tft_spiwrite16(color);
	}

	_cs_high();
}

void tft_fillScreen(UINT16 color)
{
	tft_fillRect(0, 0, _width, _height, color);
}

// fill a rectangle
void tft_fillRect(INT16 x, INT16 y, INT16 w, INT16 h,
		UINT16 color)
{

	// rudimentary clipping (drawChar w/big text requires this)
	if ((x >= _width) || (y >= _height)) return;
	if ((x + w - 1) >= _width) w = _width - x;
	if ((y + h - 1) >= _height) h = _height - y;

	tft_setAddrWindow_inline(x, y, x + w - 1, y + h - 1);

	_dc_high();
	_cs_low();

	UINT32 prod = w*h;
	while (prod--){
		tft_spiwrite16(color);
	}
//	for (y = h; y > 0; y--) {
//		for (x = w; x > 0; x--) {
//			tft_spiwrite16(color);
//		}
//	}

	_cs_high();
}

UINT16 tft_Color565(UINT8 r, UINT8 g, UINT8 b)
{
	return((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}

void tft_setRotation(UINT8 m)
{
	UINT8 rotation;
	tft_writecommand(ILI9340_MADCTL);
	rotation = m % 4; // can't be higher than 3
	switch (rotation) {
	case 0:
		tft_writedata(ILI9340_MADCTL_MX | ILI9340_MADCTL_BGR);
		_width = ILI9340_TFTWIDTH;
		_height = ILI9340_TFTHEIGHT;
		break;
	case 1:
		tft_writedata(ILI9340_MADCTL_MV | ILI9340_MADCTL_BGR);
		_width = ILI9340_TFTHEIGHT;
		_height = ILI9340_TFTWIDTH;
		break;
	case 2:
		tft_writedata(ILI9340_MADCTL_MY | ILI9340_MADCTL_BGR);
		_width = ILI9340_TFTWIDTH;
		_height = ILI9340_TFTHEIGHT;
		break;
	case 3:
		tft_writedata(ILI9340_MADCTL_MV | ILI9340_MADCTL_MY | ILI9340_MADCTL_MX | ILI9340_MADCTL_BGR);
		_width = ILI9340_TFTHEIGHT;
		_height = ILI9340_TFTWIDTH;
		break;
	}
}

void delay_ms(unsigned long i)
{
	/* Create a software delay about i ms long
	 * Parameters:
	 *      i:  equal to number of milliseconds for delay
	 * Returns: Nothing
	 * Note: Uses Core Timer. Core Timer is cleared at the initialiazion of
	 *      this function. So, applications sensitive to the Core Timer are going
	 *      to be affected
	 */
	UINT32 j;
	j = dTime_ms * i;
	WriteCoreTimer(0);
	while (ReadCoreTimer() < j);
}
