/**
 * @email support.ele@noritake.com
 * @website www.noritake-elec.com
 * @version v2.3
 * @ide Code Composer Studio
 * @brief GT-CP command library for MSP430 and Noritake GT-CP modules.
 *
 * @verbatim
 * Provide GT-CP command functionality for Noritake GT-CP modules.
 * @endverbatim
 */

#include "GTCP.h"

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

/**
 * Helper function to reduce code and use the correct interface.
 * This function will send the passed-in data through the selected interface.
 *
 * @param  data The data byte to be sent.
 * @return none
 */
void GTCP_print(unsigned char data)
{
	switch(interface)
	{
	case UART: //Asynchronous Serial
		writeUART(data);
		break;
	case SPI: //SPI
		beginWriteSPI();
		writeSPI(data);
		endSPI();
		break;
	case I2C: //I2C
		writeI2C(data);
		break;
	default:
		break;
	}
}

/**
 * Read data from the selected interface.
 *
 * @return none
 */
uint8_t GTCP_read()
{
    uint8_t result = 0;
	switch(interface)
	{
	case UART: //Asynchronous Serial
		result = readUART();
		break;
	case SPI: //SPI
		readSPIStatus();
		readSPI(RXDataArray);
		break;
	case I2C: //I2C
		readI2C(1);
		break;
	default:
		break;
	}

	return result;
}

/**
 * Read multiple bytes of data from the display module.
 *
 * @param length Number of bytes to read.
 * @return none
 */
void GTCP_readMulti(uint8_t length)
{
	switch(interface)
	{
	case UART: //Asynchronous Serial
	    readUARTMultiple(length);
		break;
	case SPI: //SPI
		while(readSPIStatus() == 0);
		readSPI(RXDataArray);
		break;
	case I2C: //I2C
		while(TRDYFlag == 0)
		{
#ifdef __MSP430F5529__
		    if(P2IN & BIT7) // Check if TRDY is HIGH
#elif defined __MSP430FR6989
		    if(P1IN & BIT5) // Check if TRDY is HIGH
#endif
		    {
		        TRDYFlag = 1;
		    }
		}
		readI2C(length);
		break;
	default:
		break;
	}
}

/**
 * New GTCP Initialize function
 *
 * - Establish interface
 * - Initialize module
 * - Collect module information
 * - Module information is stored in display struct.
 *
 * @param interface Selected serial interface. (UART, I2C, or SPI)
 * @return none
 */
void GTCP_start(enum interfaceType selectedInterface)
{
    interface = selectedInterface;

	//Establish interface
	switch(interface)
	{
	case UART: //Asynchronous Serial
		initUART();
		break;
	case SPI: //SPI
		initSPI();
		break;
	case I2C: //I2C
		initI2C();
		break;
	default:
		break;
	}

	// Initialize  module and read display info
	GTCP_initialize();
	GTCP_readDisplayInfo();

	DispMemSize = (display.HEIGHT * display.WIDTH) * 8;
	Max_DispMemAddr = DispMemSize - 1;
}

/**
 * Reads all applicable display information
 * and stores info in the global TFT struct "display".
 *
 * @return none
 */
void GTCP_readDisplayInfo()
{
    // Collect module information
    GTCP_getHorizontalResolution();
    GTCP_getVerticalResolution();
    GTCP_getProductType();
    GTCP_getFirmwareVersion();
    GTCP_getBootVersion();
    GTCP_getTSPName();
    GTCP_getTSPID();
    GTCP_getTouchGain();
    GTCP_getTouchThreshold();
}

/**
 * General print string function for all interfaces.
 *
 * @param  string The string to print to the display.
 * @return none
 */
void GTCP_printString(unsigned char* string)
{
	switch(interface)
	{
	case UART:
		writeStringUART(string);
		break;
	case SPI:
		beginWriteSPI();
		writeStringSPI(string);
		endSPI();
		break;
	case I2C:
		writeMultipleI2C(string);
		break;
	default:
		break;
	}
}

/**
 * Send a packet of data to the connected display.
 *
 * @param data Data to send.
 * @param size Size of data.
 * @return none
 */
void GTCP_sendCommand(uint8_t* data, int size)
{
	switch(interface)
	{
	case UART:
		writeArrayUART(data, size);
		break;
	case SPI:
		beginWriteSPI();
		writeArraySPI(data, size);
		endSPI();
		break;
	case I2C:
		writeArrayI2C(data, size);
		break;
	default:
		break;
	}
}

/**
 * Send the initialize command to the GTCP display.
 *
 * @return none
 */
void GTCP_initialize()
{
    unsigned char command[] = {0x1B, 0x40};

    GTCP_sendCommand(command, ARRAY_SIZE(command));
    __delay_cycles(DISPLAY_INIT_DELAY);
}

/**
 * Performs a backspace on the GTCP display.
 *
 * @return none
 */
void GTCP_back()
{
	GTCP_print(0x08);
}

/**
 * Moves the cursor forward one position on the GTCP display.
 *
 * @return none
 */
void GTCP_forward()
{
	GTCP_print(0x09);
}

/**
 * Performs a line feed on the GTCP display.
 *
 * @return none
 */
void GTCP_lineFeed()
{
    GTCP_print(0x0A);
}

/**
 * Brings the cursor to its home position (top left) on the GTCP display.
 *
 * @return none
 */
void GTCP_home()
{
	GTCP_print(0x0B);
}

/**
 * Performs a carriage return on the GTCP display.
 *
 * @return none
 */
void GTCP_carriageReturn()
{
	GTCP_print(0x0D);
}

/**
 * Performs a carriage return and line feed on the GTCP display.
 *
 * @return none
 */
void GTCP_crlf()
{
	GTCP_carriageReturn();
	GTCP_lineFeed();
}

/**
 * Moves the cursor to a specific location on the GTCP module.
 *
 * @param  x       The desired x coordinate.
 * @param  y       The desired y coordinate.
 * @return none
 */
void GTCP_setCursor(uint16_t x, uint16_t y)
{
	if(x < Xdots && y < Ydots)
	{
	    uint8_t command[] = {0x1F, '$', x, (x >> 8), y, (y >> 8)};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Clears the screen of the GTCP module.
 *
 * @return none
 */
void GTCP_clearScreen()
{
	GTCP_print(0x0C);
}

/**
 * Clear the current line. Cursor will return to left end.
 *
 * @return none
 */
void GTCP_lineClear()
{
	GTCP_print(0x18);
}

/**
 * Clear the current line. Cursor will move to right end.
 *
 * @return none
 */
void GTCP_lineEndClear()
{
	GTCP_print(0x19);
}

/**
 * Turns the cursor on.
 *
 * @return none
 */
void GTCP_cursorOn()
{
    uint8_t command[] = {0x1f, 'C', 0x01};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Turns the cursor off.
 *
 * @return none
 */
void GTCP_cursorOff()
{
    uint8_t command[] = {0x1F, 'C', 0x00};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Perform a hardware reset
 *
 * @return none
 */
void GTCP_reset()
{
    RESET();
}

/**
 * Set display screen mode.
 * During display screen mode, display actions are valid within the display or hidden area.
 *
 * @return none
 */
void GTCP_setDisplayScreenMode()
{
    uint8_t command[] = {0x1F, 0x28, 0x77, 0x10, 0x00};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set all screen mode.
 * During all screen mode, display actions are valid over the entire display memory.
 *
 * @return none
 */
void GTCP_setAllScreenMode()
{
    uint8_t command[] = {0x1F, 0x28, 0x77, 0x10, 0x01};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set display to normal write mode.
 * Background pixels are written to display memory.
 *
 * This is the default write mode.
 *
 * @return none
 */
void GTCP_setNormalWriteDisplayMode()
{
    uint8_t command[] = {0x1F, 0x77, 0x10};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set display to thru write mode.
 * Background pixels are not written to display memory.
 *
 * @return none
 */
void GTCP_setThruWriteDisplayMode()
{
    uint8_t command[] = {0x1F, 0x77, 0x11};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Enables the use of multiple-byte characters.
 *
 * @return none
 */
void GTCP_enableMultiByteChars()
{
    uint8_t command[] = {0x1F, 0x28, 'g', 0x02, 0x01};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Disables the use of multiple-byte characters.
 *
 * @return none
 */
void GTCP_disableMultiByteChars()
{
    uint8_t command[] = {0x1F, 0x28, 'g', 0x02, 0x00};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Sets the multiple byte character set to be used.
 *
 * Code for each character set:
 * <ul>
 * <li> 0x00 - Japanese
 * <li> 0x01 - Korean
 * <li> 0x02 - Simplified Chinese
 * <li> 0x03 - Traditional Chinese
 * </ul>
 *
 * @param  code The character set code to be used.
 * @return none
 */
void GTCP_setMultiByteCharSet(uint8_t code)
{
	if(code <= 0x03)
	{
	    uint8_t command[] = {0x1F, 0x28, 'g', 0x0f, code};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Enables the use of custom characters.
 *
 * @return none
 */
void GTCP_enableCustomChars()
{
    uint8_t command[] = {0x1B, '%', 0x01};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Disables the use of custom characters.
 *
 * @return none
 */
void GTCP_disableCustomChars()
{
    uint8_t command[] = {0x1B, '%', 0x00};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * code starts at 0x20 and ends at 0x27
 * <ul>
 * <li> format = 0x01: 6x8 pixel character
 * <li> format = 0x02: 8x16 pixel character
 * <li> format = 0x03: 12x24 pixel character
 * <li> format = 0x04: 16x32 pixel character
 * </ul>
 *
 * To print custom character, write the code of the character
 * to the display after using this function.
 *
 * Data format for custom characters:
 * 6x8 character
 * <ul>
 * <li> 0b00000000
 * <li> 0b00000000
 * <li> 0b00000000
 * <li> 0b00000000
 * <li> 0b00000000
 * <li> 0b00000000
 * </ul>
 *
 * 8x16 character
 * <ul>
 * <li> 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000
 * </ul>
 *
 * 12x24 character
 * <ul>
 * <li> 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000
 * </ul>
 *
 * 16x32 character
 * <ul>
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * <li> 0b00000000, 0b00000000, 0b00000000, 0b00000000
 * </ul>
 *
 * <ul>
 * <li> X = don't care
 * <li> LSB is the bottom of the character.
 * <li> MSB is the top of the character.
 * <li> First byte of data is left most column of the character.
 * <li> Last byte of data is the right most column of the character.
 * </ul>
 *
 * @param   codeStart The code for the character being defined.
 * @param   codeEnd The ending character code value.
 * @param   format The format of the character being defined.
 * @param   *data  The data for the character being defined.
 * @returns none
 */
void GTCP_defineCustomChar(uint8_t codeStart, uint8_t codeEnd, uint8_t format, uint8_t pixelNumX, uint8_t *data)
{
	bool validParam = false;

	if((format >= 0x01) && (format <= 0x04) && (codeStart < codeEnd) && (codeStart >= 0x20) && (codeEnd <= 0xFF))
	{
	    switch(format)
	    {
	    case 0x01: //6x8 Pixel Character
	        if(pixelNumX <= 0x06)
	        {
	            validParam = true;
	        }
	        break;
	    case 0x02: //8x16 Pixel Character
	        if(pixelNumX <= 0x08)
	        {
	            pixelNumX *= 2;
	            validParam = true;
	        }
	        break;
	    case 0x03: //12x24 Pixel Character
	        if(pixelNumX <= 0x0C)
	        {
	            pixelNumX *= 3;
	            validParam = true;
	        }
	        break;
	    case 0x04: //16x32 Pixel Character
	        if((pixelNumX <= 0x10))
	        {
	            pixelNumX *= 4;
	            validParam = true;
	        }
	        break;
	    default:
	        break;
	    }
	}

	if(validParam)
	{
	    uint8_t command[] = {0x1B, '&', format, codeStart, codeEnd, pixelNumX};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));

	    for(unsigned int i = 0; i < pixelNumX; i++)
	    {
	        GTCP_print(data[i]);
	    }
	}
}

/**
 * Deletes a previously defined custom character.
 * <ul>
 * <li> type = 0x01: Select 6x8 pixel character.
 * <li> type = 0x02: Select 8x16 pixel character.
 * <li> type = 0x03: Select 12x24 pixel character.
 * <li> type = 0x04: Select 16x32 pixel character.
 * <li> 0x20 <= code <= 0xff
 * </ul>
 *
 * @param  type    The custom character type to be deleted.
 * @param  code    Address of the custom character to be deleted.
 * @return none
 */
void GTCP_deleteCustomChar(uint8_t type, uint8_t code)
{
	if((type <= 0x04) && (code >= 0x20) && (code <= 0xFF))
	{
	    uint8_t command[] = {0x1B, '?', type, code};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Sets the desired ASCII variant to be used.
 * Also called "International font select" in the GTCP software manual.
 * Font codes:
 * <ul>
 * <li> 0x00 - America
 * <li> 0x01 - France
 * <li> 0x02 - Germany
 * <li> 0x03 - England
 * <li> 0x04 - Denmark 1
 * <li> 0x05 - Sweden
 * <li> 0x06 - Italy
 * <li> 0x07 - Spain 1
 * <li> 0x08 - Japan
 * <li> 0x09 - Norway
 * <li> 0x0A - Denmark 2
 * <li> 0x0B - Spain 2
 * <li> 0x0C - Latin America
 * <li> 0x0D - Korea
 * </ul>
 *
 * @param  code   The code of the ASCII variant to be used.
 * @return none
 */
void GTCP_setAsciiVariant(uint8_t code)
{
	if(code <= 0x0d)
	{
	    uint8_t command[] = {0x1B, 'R', code};
		GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Sets the desired character set.
 * Character set codes:
 * <ul>
 * <li> 0x00 - PC437(USA-Euro std)
 * <li> 0x01 - Katakana - Japanese
 * <li> 0x02 - PC850 (Multilingual)
 * <li> 0x03 - PC860 (Portuguese)
 * <li> 0x04 - PC863 (Canadian-French)
 * <li> 0x05 - PC865 (Nordic)
 * <li> 0x10 - WPC1252 (Latin)
 * <li> 0x11 - PC866 (Cryllic #2)
 * <li> 0x12 - PC852 (Latin 2)
 * <li> 0x13 - PC858 (Eastern European)
 * <li> 0xFE - UTF-8 input
 * <li> 0xFF - User table
 * </ul>
 *
 * @param  code   The code for the desired character set.
 * @return none
 */
void GTCP_setCharSet(uint8_t code)
{
	if(code <= 0x05 || (0x10 <= code && code <= 0x13) || (code == 0xFE) || (code == 0xFF))
	{
	    uint8_t command[] = {0x1B, 't', code};
		GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Set the display mode to overwrite mode.
 *
 * @return  none
 */
void GTCP_setOverwriteMode()
{
    uint8_t command[] = {0x1F, 0x01};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set the display mode to vertical scroll mode.
 *
 * @return none
 */
void GTCP_setVerticalScrollMode()
{
    uint8_t command[] = {0x1F, 0x02};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set the display mode to horizontal scroll mode.
 *
 * @return none
 */
void GTCP_setHorizontalScrollMode()
{
    uint8_t command[] = {0x1F, 0x03};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set the display mode to horizontal scroll mode
 * with the scroll ON state.
 *
 * @return none
 */
void GTCP_setHorizontalScrollONMode()
{
    uint8_t command[] = {0x1F, 0x05};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Sets the horizontal scroll speed on the GTCP series module.
 * Speed parameter specifics:
 * <ul>
 * <li> 0x00 <= speed <= 0x1f
 * <li> speed = 0x00 : Instantaneous Speed
 * <li> speed = 0x01 : IntTime / 2 pixels
 * <li> speed = 0x02 - 0x1f : (n-1) * T ms / pixel
 * </ul>
 *
 * @param  speed  The desired horizontal scroll speed.
 * @return none
 */
void GTCP_setHorizScrollSpeed(uint8_t speed)
{
	if(speed <= 0x1F)
	{
	    uint8_t command[] = {0x1F, 's', speed};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Turns the display inversion OFF.
 *
 * @return none
 */
void GTCP_invertOff()
{
    uint8_t command[] = {0x1F, 'r', 0x00};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Turns the display inversion ON.
 *
 * @return none
 */
void GTCP_invertOn()
{
    uint8_t command[] = {0x1f, 'r', 0x01};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Suspends the program and waits for a user defined amount of time.
 * Wait time = t * approximately 0.47s
 *
 * @param  wait    The amount of time for the MCU to wait.
 * @return none
 */
void GTCP_wait(uint8_t wait)
{
    uint8_t command[] = {0x1F, 0x28, 'a', 0x01, wait};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Suspends the program and waits for a user defined amount of time.
 * Wait time = t * IntTime
 *
 * @param  time The amount of time to wait.
 * @return none
 */
void GTCP_shortWait(uint8_t time)
{
    uint8_t command[] = {0x1F, 0x28, 'a', 0x02, time};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Resets the blink settings on the module to all zeros.
 *
 * @return none
 */
void GTCP_blinkScreenOff()
{
    uint8_t command[] = {0x1F, 0x28, 'a', 0x11, 0x00, 0x01, 0x01, 0x00};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Blinks the GTCP series module screen based on user input parameters.
 *
 * <ul>
 * <li> 0x01 <= onTime <= 0xff
 * <li> 0x01 <= offTime <= 0xff
 * <li> 0x00 <= cycles <= 0xff
 * </ul>
 *
 * @param  pattern   Enables or disables screen blinking.
 *                  <ul>
 *                  <li> 0x00 - Normal display
 *                  <li> 0x01 - Blink display (alternate between normal and blank display)
 *                  <li> 0x02 - Reserved.
 *                  </ul>
 * @param  onTime   The time that the display stays ON during blinking
 * @param  offTtime The time that the displays stays OFF during blinking.
 * @return none
 */
void GTCP_blinkScreenOn(uint8_t pattern, uint8_t onTime, uint8_t offTime, uint8_t cycles)
{
    if((pattern <= 0x02) && (onTime) && (offTime))
    {
        uint8_t command[] = {0x1F, 0x28, 'a', 0x11, pattern, onTime, offTime, cycles};
        GTCP_sendCommand(command, ARRAY_SIZE(command));
    }
}

/**
 * Sets the scroll parameters and scrolls the screen.
 *
 * @param  x       The amount of pixels for the screen to scroll horizontally.
 * @param  y       The amount of pixels for the screen to scroll vertically.
 * @param times    The number of times the screen will scroll?
 * @param speed    The speed at which scrolling occurs.
 * @return none
 */
void GTCP_scrollScreen(uint16_t x, uint16_t y, uint16_t times, uint8_t speed)
{
	if(times)
	{
	    uint8_t command[] = {0x1F, 0x28, 'a', 0xA0, x, (x >> 8), y, (y >> 8), times, (times >> 8), speed};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Start a curtain display action on-screen.
 * <ul>
 * <li> direction = 0x00: To the right from the left edge.
 * <li> direction = 0x01: To the left from the right edge.
 * <li> direction = 0x02: To the left and right separately from the center.
 * <li> direction = 0x03: To the center from the left and right edge.
 * </ul>
 *
 * <ul>
 * <li> Curtain action speed = 60 * speed * IntTime
 * <li> 0x00 <= speed <= 0xFF
 * </ul>
 *
 * <ul>
 * <li> 0x00 <= red <= 0xFF
 * <li> 0x00 <= green <= 0xFF
 * <li> 0x00 <= blue <= 0xFF
 * </ul>
 *
 * @param  direction Direction of curtain action.
 * @param  speed     Curtain action speed.
 * @param  red       Red brightness
 * @param  green     Green brightness
 * @param  blue      Blue brightness
 * @return none
 */
void GTCP_curtainAction(uint8_t direction, uint8_t speed, uint8_t red, uint8_t green, uint8_t blue)
{
	if(direction <= 0x03)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x61, 0xA2, direction, speed, red, green, blue};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Start a curtain display action on-screen.
 * <ul>
 * <li> direction = 0x00: To the right from the left edge.
 * <li> direction = 0x01: To the left from the right edge.
 * <li> direction = 0x02: To the left and right separately from the center.
 * <li> direction = 0x03: To the center from the left and right edge.
 * </ul>
 *
 * <ul>
 * <li> Curtain action speed = 60 * speed * IntTime
 * <li> 0x00 <= speed <= 0xFF
 * </ul>
 *
 * @param  direction Direction of curtain action.
 * @param  speed     Spring action speed.
 * @param  x         Display memory X position.
 * @param  y         Display memory y position.
 * @return none
 */
void GTCP_springAction(uint8_t direction, uint8_t speed, uint16_t x, uint16_t y)
{
	if((direction <= 0x03) && (x <= display.WIDTH) && (y <= display.HEIGHT))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x61, 0xA3, direction, speed, x, (x >> 8), y, (y >> 8)};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Start a checkerboard display action.
 * Command name on software datasheet is "Random Display Action XY".
 *
 * <ul>
 * <li> 0x00 <= actionType <= 0x02
 * <li> actionType = 0x00: Transition pattern 1
 * <li> actionType = 0x01: Transition pattern 2
 * <li> actionType = 0x02: Transition pattern 3
 * <li> 0x00 <= speed <= 0xff
 * </ul>
 *
 * @param  direction Direction of curtain action.
 * @param  speed     Checkerboard action speed.
 * @param  x         Display memory X position.
 * @param  y         Display memory y position.
 * @return none
 */
void GTCP_randomAction(uint8_t actionType, uint8_t speed, uint16_t x, uint16_t y)
{
	if((actionType <= 0x02) && (x <= display.WIDTH) && (y <= display.HEIGHT))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x61, 0xA4, actionType, speed, x, (x >> 8), y, (y >> 8)};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Start a fade-in display action.
 * <ul>
 * <li> Fade-in time = speed * approximately 0.5s
 * </ul>
 *
 * @param  speed Fade-in action speed.
 * @param  x     Display memory X position.
 * @param  y     Display memory Y position.
 * @return none
 */
void GTCP_fadeInAction(uint8_t speed, uint16_t x, uint16_t y)
{
	if((x <= display.WIDTH) && (y <= display.HEIGHT))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x61, 0xA5, speed, x, (x >> 8), y, (y >> 8)};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Start a fade-out display action.
 * <ul>
 * <li> Fade-out time = speed * approximately 0.5s
 * </ul>
 *
 * @param  speed Fade-out action speed.
 * @return none
 */
void GTCP_fadeOutAction(uint8_t speed)
{
    uint8_t command[] = {0x1F, 0x28, 0x61, 0xA6, speed};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Turns the display OFF.
 *
 * @return none
 */
void GTCP_displayOff()
{
    uint8_t command[] = {0x1F, 0x28, 'a', 0x40, 0x00};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Turns the display ON.
 *
 * @return none
 */
void GTCP_displayOn()
{
    uint8_t command[] = {0x1F, 0x28, 'a', 0x40, 0x01};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Turn the display OFF and wait for touch input
 * to turn the display back ON.
 *
 * @return none
 */
void GTCP_displayOffWaitTouch()
{
    uint8_t command[] = {0x1F, 0x28, 'a', 0x40, 0x80};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Sets the screen brightness of the GTCP module.
 *
 * Brightness level = (level / 255) * 100%
 *
 * @param  level The desired brightness level.
 * @return none
 */
void GTCP_setScreenBrightness(uint8_t level)
{
    uint8_t command[] = {0x1F, 'X', level};
	GTCP_displayOn();
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set the desired character width.
 *
 * <ul>
 * <li> widthSetting = 0x00 | Fixed-width
 * <li> widthSetting = 0x02 | Proportional 1
 * <li> widthSetting = 0x03 | Proportional 2
 * <li> widthSetting = 0x04 | Proportional 3
 * </ul>
 *
 * @param widthSetting Desired character width setting.
 * @return none
 */
void GTCP_setFontWidth(uint8_t widthSetting)
{
    uint8_t command[] = {0x1F, 0x28, 0x67, 0x04, widthSetting};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Sets the 2-byte character type for the GTCP series module.
 *
 * <ul>
 * <li> type = 0x00 | Japanese (JIS X0208 (Shift-JIS))
 * <li> type = 0x01 | Korean (KSC5601-87)
 * <li> type = 0x02 | Simplified Chinese (GB2312-80)
 * <li> type = 0x03 | Traditional Chinese (Big-5)
 * </ul>
 *
 * @param  proportional Enable or disable a proportional font style.
 * @param  evenSpacing  Enable or disable even spacing between characters.
 * @return none
 */
void GTCP_setCharacterType(uint8_t type)
{
	if(type <= 0x03)
	{
	    uint8_t command[] = {0x1F, 0x28, 'g', 0x03, type};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Set the desired 1-byte character font size.
 * <ul>
 * <li> size = 0x00: Outline font
 * <li> size = 0x01: 6x8 font
 * <li> size = 0x02: 8x16 font
 * <li> size = 0x03: 12x24 font
 * <li> size = 0x04: 16x32 font
 * </ul>
 *
 * @param  size The desired character size.
 * @return none
 */
void GTCP_setFontSize(uint8_t size)
{
	if(size <= 0x04)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x67, 0x01, size};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Set the desired outline font size.
 * <ul>
 * <li> 0x0001 <= charLineHeight <= Ydots
 * <li> 0x0001 <= nominalCharY <= 0x03ff
 * <li> 0x0001 <= nominalCharX <= 0x03ff
 * <li> 0x0001 <= baseOffsetY <= 0x03ff | if 0x8000: zero offset
 * </ul>
 *
 * @param  charLineHeight Y-size (height) of character line (line spacing), in pixels.
 * @param  nominalCharY   Nominal character Y-size (height), in pixels.
 * @param  nominalCharX   Nominal character X-size (width), in pixels.
 * @param  baseOffsetY    Baseline Y-offset from cursor position, in pixels.
 * @return none
 */
void GTCP_setOutlineFontSize(uint16_t charLineHeight, uint16_t nominalCharY, uint16_t nominalCharX, int16_t baseOffsetY)
{
	if((charLineHeight <= display.HEIGHT) && (nominalCharY <= 0x03FF) && (nominalCharX <= 0x03FF) &&
	   ((baseOffsetY >= 0xFC00) || (baseOffsetY <= 0x03FF) || (baseOffsetY == 0x8000)))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x67, 0x06,
	                     charLineHeight, (charLineHeight >> 8),
	                     nominalCharY, (nominalCharY >> 8),
	                     nominalCharX, (nominalCharX >> 8),
	                     baseOffsetY, (baseOffsetY >> 8)};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Set the address and size for a user-supplied font file in FROM2.
 *
 * <ul>
 * <li> 0x0000 <= address <= 0x0FAFFFFF
 * <li> 0x0001 <= size <= 0x0FB00000 - address
 * </ul>
 *
 * @param address Font file start address in general-purpose FROM2.
 * @param size Font file size in general-purpose FROM2.
 * @return none
 */
void GTCP_setUserFontAddressSize(uint32_t address, uint32_t size)
{
    if((address <= 0x0FAFFFFF) && (size >= 1) && (size <= (0x0FB00000 - address)))
    {
        uint8_t command[] = {0x1F, 0x28, 0x67, 0x07,
                             address, (address >> 8), (address >> 16), (address >> 24),
                             size, (size >> 8), (size >> 16), (size >> 24)};
        GTCP_sendCommand(command, ARRAY_SIZE(command));
    }
}

/**
 * Set the outline font type.
 * This needs to be set in order to use outline fonts.
 * <ul>
 * <li> fontType = 0x00: Japanese
 * <li> fontType = 0x01: Korean
 * <li> fontType = 0x02: Simplified Chinese
 * <li> fontType = 0x03: Traditional Chinese
 * <li> fontType = 0x80: None (no outline font selected)
 * <li> fontType = 0xFF: User-supplied font file
 * <li> Default fontType = 0x80
 * </ul>
 *
 * @param  fontType The desired outline font type.
 * @return none
 */
void GTCP_setOutlineFontType(uint8_t fontType)
{
	if((fontType <= 0x03) || (fontType == 0x80) || (fontType == 0xFF))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x67, 0x08, fontType};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Sets the font size for the GTCP series module.
 *
 * @param  x    X magnification factor.
 * @param  y    Y magnification factor.
 * @return none
 */
void GTCP_setFontMagnification(uint8_t x, uint8_t y)
{
	//Check if x and y are within their definable area (seen in software spec)
	if((x <= 0x04) && (y <= 0x04))
	{
	    uint8_t command[] = {0x1F, 0x28, 'g', 0x40, x, y};
		GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Set the character style.
 * <ul>
 * <li> style = 0x00: Normal
 * <li> style = 0x01: Bold
 * <li> style = 0x02: Shadow
 * <li> style = 0x03: Bordering
 * <li> Default: style = 0x00
 * </ul>
 *
 * @param  style The desired character style.
 * @return none
 */
void GTCP_setCharacterStyle(uint8_t style)
{
	if(style <= 0x03)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x67, 0x41, style};
		GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Set the character color.
 * <ul>
 * <li> 0x00 = No brightness
 * <li> 0x7F = 50% brightness
 * <li> 0xFF = Maximum brightness
 * </ul>
 *
 * @param  red   Red brightness
 * @param  green Green brightness
 * @param  blue  Blue brightness
 * @return none
 */
void GTCP_setCharacterColor(uint8_t red, uint8_t green, uint8_t blue)
{
    uint8_t command[] = {0x1F, 0x28, 0x67, 0x50, red, green, blue};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set the character background (highlight) color.
 * <ul>
 * <li> 0x00 = No brightness
 * <li> 0x7F = 50% brightness
 * <li> 0xFF = Maximum brightness
 * </ul>
 *
 * @param  red   Red brightness
 * @param  green Green brightness
 * @param  blue  Blue brightness
 * @return none
 */
void GTCP_setBackgroundColor(uint8_t red, uint8_t green, uint8_t blue)
{
    uint8_t command[] = {0x1F, 0x28, 0x67, 0x51, red, green, blue};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set the character shadow and bordering color.
 * <ul>
 * <li> 0x00 = No brightness
 * <li> 0x7F = 50% brightness
 * <li> 0xFF = Maximum brightness
 * </ul>
 *
 * @param  red   Red brightness
 * @param  green Green brightness
 * @param  blue  Blue brightness
 * @return none
 */
void GTCP_setShadowBorderingColor(uint8_t red, uint8_t green, uint8_t blue)
{
    uint8_t command[] = {0x1F, 0x28, 0x67, 0x52, red, green, blue};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Turns the background OFF.
 * When the background is OFF, its color is always black.
 *
 * @return none
 */
void GTCP_turnBackgroundOff()
{
    uint8_t command[] = {0x1F, 0x28, 0x67, 0x58, 0x00};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Turns the background ON.
 * When the background is ON, its color is determined by GTCP_setBackgroundColor.
 *
 * @param none
 */
void GTCP_turnBackgroundOn()
{
    uint8_t command[] = {0x1F, 0x28, 0x67, 0x58, 0x01};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Selects the current window.
 *
 * @param  window  The desired window to select. (0-4)
 * @return none
 */
void GTCP_selectWindow(uint8_t window)
{
	if(window <= 4)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x77, 0x01, window};
		GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Defines a specific window for the GTCP series module.
 *
 * @param  window  The number of the window to be created. (1-4)
 * @param  x       The x coordinate of the new window.
 * @param  y       The y coordinate of the new window.
 * @param  width   The width of the new window.
 * @param  height  The height of the new window.
 * @return none
 */
void GTCP_defineWindow(uint8_t window, uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
	// NOTE: Total display area is display width or height multiplied by 2.
	if((window <= 0x04) &&
	   (x <= display.WIDTH) && (y <= display.HEIGHT) &&
	   (width) && (width <= (display.WIDTH * 2)) &&
	   (height) && (height <= display.HEIGHT * 2))
	{
	    uint8_t command[] = {0x1F, 0x28, 'w', 0x02, window, 0x01,
	                         x, (x >> 8), y, (y >> 8),
	                         width, (width >> 8), height, (height >> 8)};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Deletes a previously defined window.
 *
 * @param  window  The number of the window to be deleted. (1-4)
 * @return none
 */
void GTCP_deleteWindow(uint8_t window)
{
    uint8_t command[] = {0x1F, 0x28, 'w', 0x02, window, 0x00};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Real-time bit image display.
 *
 * <ul>
 * <li> format = 0x81: Monochrome (1-bit) format
 * <li> format = 0x86: Color 6-bit format
 * <li> format = 0x8c: Color 12-bit format
 * <li> format = 0x90: Color 16-bit format
 * <li> format = 0x98: Color 24-bit format
 * <li> format = 0xf0: BMP
 * </ul>
 *
 * @param  width  The width of the image to display.
 * @param  height The height of the image to display.
 * @param  format The desired image format.
 * @param  data   The raw data of the image to display.
 * @return none
 */
void GTCP_drawImage(uint16_t width, uint16_t height, uint8_t format, unsigned char *data)
{
	if((width <= Xdots) && (height <= Ydots) &&
	   ((format == 0x81) || (format == 0x86) || (format == 0x8C) || (format == 0x90) || (format == 0x98) || (format == 0xF0)))
	{
	    uint8_t command[] = {0x1F, 0x28, 'f', 0x11, width, (width >> 8), height, (height >> 8), format};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));

	    switch(format)
        {
        case 0x81: //Monochrome (1 bit per pixel)
            //Deal with widths that are not a multiple of 8
            while(width % 8)
            {
                width++;
            }
            GTCP_sendCommand(data, (height * (width / 8)));
            break;
        case 0x86: //6-bit (1 byte per pixel)
            GTCP_sendCommand(data, (height * width));
            break;
        case 0x8c: //12-bit (2 bytes per pixel)
            GTCP_sendCommand(data, (height * width) * 2);
            break;
        case 0x90: //16-bit (2 bytes per pixel)
            GTCP_sendCommand(data, ((height * width) * 2));
            break;
        case 0x98: //24-bit (3 bytes per pixel)
            GTCP_sendCommand(data, ((height * width) * 3));
            break;
        case 0xf0: //BMP (entire file)
            unsigned long int filesize = (unsigned long int)data[2] | ((unsigned long int)data[3] << 8) |
                                         ((unsigned long int)data[4] << 16) | ((unsigned long int)data[5] << 24);
            GTCP_sendCommand(data, filesize);
            break;
        }
	}
}

/**
 * Software datasheet function: Downloaded bit image display
 * Display an image stored in system memory.
 * <ul>
 * <li> memory = 0x00: RAM bit image
 * <li> memory = 0x01: FROM1 bit image
 * <li> memory = 0x02: Display memory
 * <li> memory = 0x10: FROM2 base address 0x0000.0000
 * <li> memory = 0x11: FROM2 base address 0x0100.0000
 * <li> memory = 0x12: FROM2 base address 0x0200.0000
 * <li> memory = 0x13: FROM2 base address 0x0300.0000
 * <li> memory = 0x14: FROM2 base address 0x0400.0000
 * <li> memory = 0x15: FROM2 base address 0x0500.0000
 * <li> memory = 0x16: FROM2 base address 0x0600.0000
 * <li> memory = 0x17: FROM2 base address 0x0700.0000
 * <li> memory = 0x18: FROM2 base address 0x0800.0000
 * <li> memory = 0x19: FROM2 base address 0x0900.0000
 * <li> memory = 0x1A: FROM2 base address 0x0a00.0000
 * <li> memory = 0x1B: FROM2 base address 0x0b00.0000
 * <li> memory = 0x1C: FROM2 base address 0x0c00.0000
 * <li> memory = 0x1D: FROM2 base address 0x0d00.0000
 * <li> memory = 0x1E: FROM2 base address 0x0e00.0000
 * <li> memory = 0x1F: FROM2 base address 0x0f00.0000
 * </ul>
 *
 * <ul>
 * <li> format = 0x81: Monochrome (1-bit) format
 * <li> format = 0x86: Color 6-bit format
 * <li> format = 0x8C: Color 12-bit format
 * <li> format = 0x90: Color 16-bit format
 * <li> format = 0x91: Color 16-bit high-speed format
 * <li> format = 0x98: Color 24-bit format
 * <li> format = 0xF0: BMP file format
 * </ul>
 *
 * @param  memory    The type of memory where the image is stored.
 * @param  address   The memory address of the image.
 * @param  xDefine   The defined width to be displayed of the image.
 * @param  xSize     The width of the image.
 * @param  ySize     The height of the image.
 * @param  format    The desired image format.
 * @return none
 */
void GTCP_storedBitImageDraw(uint8_t memory, uint32_t address, uint16_t xDefine,
                             uint16_t xSize, uint16_t ySize, uint8_t format)
{
	bool isValid = false;

    if((memory == 0x00) && (address <= 0x000FFF))
    {
        isValid = true;
    } else if((memory == 0x01) && (address <= 0x1FFFFF))
    {
        isValid = true;
    } else if((memory == 0x02) && (address <= 0x176FFF))
    {
        isValid = true;
    } else if((memory >= 0x10) && (((memory <= 0x1E) && (address <= 0xFFFFFF)) ||
            ((memory == 0x1F) && (address <= 0xAFFFFF))))
    {
        if((xDefine <= 0xFFFF) && (xSize <= Xdots) && (ySize <= Ydots) &&
           ((format == IMAGE_FORMAT_MONOCHROME) || (format == IMAGE_FORMAT_6BIT) ||
            (format == IMAGE_FORMAT_12BIT) || (format == IMAGE_FORMAT_16BIT) ||
            (format == IMAGE_FORMAT_16BIT_HS) || (format == IMAGE_FORMAT_24BIT) ||
            (format == IMAGE_FORMAT_BMP)))
        {
            isValid = true;
        }
    }

	if(isValid)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x66, 0x10, memory,
	                         address, (address >> 8), (address >> 16),
	                         xDefine, (xDefine >> 8),
	                         xSize, (xSize >> 8), ySize, (ySize >> 8), format};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Draw a pixel on the display.
 * <ul>
 * <li> pen = 0x00: Pen color is set to the background color.
 * <li> pen = 0x01: Pen color is set to the character color.
 * </ul>
 *
 * @param  pen Set pen color to character or background color.
 * @param  x   Pixel position X.
 * @param  y   Pixel position Y.
 * @return none
 */
void GTCP_pixelDrawing(uint8_t pen, uint16_t x, uint16_t y)
{
    if((pen <= 0x01) && (x <= (Xdots - 1)) && (y <= (Ydots - 1)))
	{
        uint8_t command[] = {0x1F, 0x28, 0x64, 0x10, pen, x, (x >> 8), y, (y >> 8)};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Draw a line or box on the display.
 *
 * <ul>
 * <li> mode = 0x00: Line
 * <li> mode = 0x01: Box
 * <li> mode = 0x02: Box Fill
 * </ul>
 *
 * <ul>
 * <li> pen = 0x00: Pen color is set to the background color.
 * <li> pen = 0x01: Pen color is set to the character color.
 * </ul>
 *
 * @param  mode   The desired drawing mode.
 * @param  pen    Set pen color to character or background color.
 * @param  xStart Line/Box drawing start position.
 * @param  yStart Line/Box drawing start position.
 * @param  xEnd   Line/Box drawing end position.
 * @param  yEnd   Line/Box drawing end position.
 * @return none
 */
void GTCP_drawLineBox(uint8_t mode, uint8_t pen, uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd)
{
    if((mode <= BOXFILL_DRAW) && (pen <= PEN_CHARACTER_COLOR) &&
       (xStart <= Max_Xdot_CurtWin) && (yStart <= Max_Ydot_CurtWin) &&
       (xEnd <= Max_Xdot_CurtWin) && (yEnd <= Max_Ydot_CurtWin))
    {
        uint8_t command[] = {0x1F, 0x28, 0x64, 0x11, mode, pen,
							 xStart, (xStart >> 8), yStart, (yStart >> 8),
							 xEnd, (xEnd >> 8), yEnd, (yEnd >> 8)};
        GTCP_sendCommand(command, ARRAY_SIZE(command));
    }
}

/**
 * Enable touch data transmission.
 *
 * @return none
 */
void GTCP_enableTouchTransmission()
{
    uint8_t command[] = {0x1F, 0x50, 0x20, 0x01};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Disable touch data transmission.
 *
 * @return none
 */
void GTCP_disableTouchTransmission()
{
    uint8_t command[] = {0x1F, 0x50, 0x20, 0x00};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Selects the currently-active touch panel control channel.
 * <ul>
 * <li> 0x00 <= channel <= 0x03
 * </ul>
 *
 * @param  channel The desired touch control channel.
 * @return none
 */
void GTCP_setTouchChannel(uint8_t channel)
{
    uint8_t command[] = {0x1F, 0x50, 0x21, channel};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set single touch mode.
 *
 * @return none
 */
void GTCP_setSingleTouchMode()
{
    uint8_t command[] = {0x1F, 0x50, 0x01, 0x00};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set multi-touch mode and configure maximum number of
 * simultaneous touches.
 *
 * @param maxTouches Maximum number of simultaneous touches.
 * @return none
 */
void GTCP_setMultiTouchMode(uint8_t maxTouches)
{
    uint8_t command[] = {0x1F, 0x50, 0x01, maxTouches};
    GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set the coordinate touch mode.
 * <ul>
 * <li> 0x00 <= channel <= 0x03
 * </ul>
 *
 * @param  channel The desired touch data channel.
 * @return none
 */
void GTCP_coordinatesMode(uint8_t channel)
{
    uint8_t command[] = {0x1F, 0x50, 0x10, channel, 0x00};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set the switch matrix mode.
 * <ul>
 * <li> 0x00 <= channel <= 0x03
 * <li> 0x01 <= switchesX <= 0x10
 * <li> 0x01 <= switchesY <= 0x10
 * <li> 0x01 <= clearanceX <= 0x10
 * <li> 0x01 <= clearanceY <= 0x10
 * </ul>
 *
 * @param  channel    The desired touch data channel.
 * @param  switchesX  The number of switches on the x-axis.
 * @param  switchesY  The number of switches on the y-axis.
 * @param  clearanceX The size of the non-responsive area in-between switches on the x-axis.
 * @param  clearanceY The size of the non-responsive area in-between switches on the y-axis.
 * @return none
 */
void GTCP_switchMatrixMode(uint8_t channel, uint8_t switchesX, uint8_t switchesY, uint8_t clearanceX, uint8_t clearanceY)
{
    uint8_t command[] = {0x1F, 0x50, 0x10, channel, 0x01, switchesX, clearanceX, switchesY, clearanceY};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Creates 1 custom switch.
 *
 * @param channel The desired touch data channel.
 * @param switchX The switch's X coordinate.
 * @param switchY The switch's Y coordinate.
 * @param sizeX Switch width.
 * @param sizeY Switch height.
 * @return none
 */
void GTCP_customSwitchModeSingle(uint8_t channel, uint16_t switchX, uint16_t switchY, uint16_t sizeX, uint16_t sizeY)
{
    uint8_t command[] = {0x1F, 0x50, 0x10, channel, 0x02, 0x01,
						 switchX, (switchX >> 8), switchY, (switchY >> 8),
						 sizeX, (sizeX >> 8), sizeY, (sizeY >> 8)};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Starts the creation of multiple custom switches.
 * This should be followed by loop of customSwitchParam() that runs switchNum times.
 *
 * @param channel The desired touch data channel.
 * @param switchNum Number of switches to define.
 * @return none
 */
void GTCP_customSwitchModeMultiple(uint8_t channel, uint8_t switchNum)
{
    uint8_t command[] = {0x1F, 0x50, 0x10, channel, 0x02, switchNum};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * This defines a custom switch.
 * This should be used after customSwitchModeMultiple()
 *
 * @param switchX The switch's X coordinate.
 * @param switchY The switch's Y coordinate.
 * @param sizeX Switch width.
 * @param sizeY Switch height.
 * @return none
 */
void GTCP_customSwitchParam(uint16_t switchX, uint16_t switchY, uint16_t sizeX, uint16_t sizeY)
{
    uint8_t command[] = {switchX, (switchX >> 8), switchY, (switchY >> 8),
						 sizeX, (sizeX >> 8), sizeY, (sizeY >> 8)};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Function to set the GPIO ports on the GTCP unit to be inputs or outputs.
 * Settings:
 * <ul>
 * <li> portNumber = 0x00: Port 0 (GPIO 0-7)
 * <li> portNumber = 0x01: Port 1 (GPIO 8-15)
 * <li> portNumber = 0x02: Port 2 (GPIO 16-32)
 * <li> portNumber = 0x03: Port 3 (GPIO 24, 25)
 * <li> portSetting = 0x00 (All input) to 0x0f (all output)
 * <li> 0x00 <= portSetting <= 0xff
 * <li> The portSetting parameter is bit-wise to the ports present on the module.
 * </ul>
 *
 * @param portNumber The desired port number to set.
 * @param portSetting The passed in port setting.
 * @return none
 */
void GTCP_IOPortSetting(uint8_t portNumber, uint8_t portSetting)
{
	if(portNumber <= 0x03)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x70, 0x01, portNumber, portSetting};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Function to set the output value on the GPIO ports.
 * Settings:
 * <ul>
 * <li> portNumber = 0x00: Port 0 (GPIO 0-7)
 * <li> portNumber = 0x01: Port 1 (GPIO 8-15)
 * <li> portNumber = 0x02: Port 2 (GPIO 16-32)
 * <li> portNumber = 0x03: Port 3 (GPIO 24, 25)
 * <li> 0x00 <= portValue <= 0xff
 * </ul>
 *
 * @param portNumber The desired port number to set.
 * @param portValue The value to put on the port.
 * @return none
 */
void GTCP_IOPortOutput(uint8_t portNumber, uint8_t portValue)
{
	if(portNumber <= 0x03)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x70, 0x10, portNumber, portValue};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Function to request the GTCP module to read the values present on the GPIO ports.
 *
 * <ul>
 * <li> portNumber = 0x00: Port 0 (GPIO 0-7)
 * <li> portNumber = 0x01: Port 1 (GPIO 8-15)
 * <li> portNumber = 0x02: Port 2 (GPIO 16-32)
 * <li> portNumber = 0x03: Port 3 (GPIO 24, 25)
 * </ul>
 *
 * @param portNumber The port number to read.
 * @return none
 */
void GTCP_IOPortInput(uint8_t portNumber)
{
	if(portNumber <= 0x03)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x70, 0x20, portNumber};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	    GTCP_readMulti(4);
	}
}

/**
 * Sets a memory switch to the passed in value.
 *
 * This command can only be executed while in "user setup mode".
 * So, use this function after GTCP_enterUserSetupMode().
 *
 * @param  memorySW The memory SW to be changed.
 * @param  data     The data to be written to the memory SW.
 * @return none
 */
void GTCP_setSingleMemorySW(uint8_t memorySW, uint8_t data)
{
	if((memorySW <= 13) || ((memorySW >= 16) && (memorySW <= 19)) || (memorySW == 48) || (memorySW == 49) ||
	   ((memorySW >= 52) && (memorySW <= 54)) || ((memorySW >= 58) && (memorySW <= 63)))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x65, 0x03, memorySW, data};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Function to start modifying multiple memory switch values in a row.
 * This should be followed by GTCP_setMemorySwitch() used numSettings times.
 *
 * This command can only be executed while in "user setup mode".
 * So, use this function after GTCP_enterUserSetupMode().
 *
 * @param numSettings Number of memory switch settings to write.
 * @return none
 */
void GTCP_setMultipleMemorySwitchHeader(uint8_t numSettings)
{
    uint8_t command[] = {0x1F, 0x28, 0x65, 0x03, 0xFF, numSettings};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Modify a memory switch.
 * This function is to be used numSettings times after
 * GTCP_setMultipleMemorySwitchHeader().
 *
 * @param switchNumber The memory switch to modify.
 * @param data The data to write to memory switch.
 * @return none
 */
void GTCP_setMemorySwitch(uint8_t switchNumber, uint8_t data)
{
	if((switchNumber <= 13) || ((switchNumber >= 16) && (switchNumber <= 19)) || (switchNumber == 48) || (switchNumber == 49) ||
	   ((switchNumber >= 52) && (switchNumber <= 54)) || ((switchNumber >= 58) && (switchNumber <= 63)))
	{
	    uint8_t switchSetting[] = {switchNumber, data};
	    GTCP_sendCommand(switchSetting, ARRAY_SIZE(switchSetting));
	}
}

/**
 * Modify a single memory switch.
 *
 * The following data is transmitted:
 * 1. Header | 0x28 | 1 byte
 * 2. Identifier 1 | 0x65 | 1 byte
 * 3. Identifier 2 | 0x04 | 1 byte
 * 4. Data | 0x00-0xff | 1 byte / n bytes
 *
 * @param  switchNum The memory SW to be read.
 * @return none
 */
void GTCP_getSingleMemorySWData(uint8_t switchNum)
{
	if((switchNum <= 13) || ((switchNum >= 16) && (switchNum <= 19)) || (switchNum == 48) || (switchNum == 49) ||
	  ((switchNum >= 52) && (switchNum <= 54)) || ((switchNum >= 58) && (switchNum <= 63)))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x65, 0x04, switchNum};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}

}

/**
 * Send the contents of memory Sw data.
 *
 * <ul>
 * <li> 0x01 <= numReads <= 0xff
 * </ul>
 *
 * @param  numReads The number of reads to perform.
 * @param  data The memory SW numbers to be read.
 * @return none
 */
void GTCP_getMultipleMemorySWData(uint8_t numReads, uint8_t *data)
{
	if(numReads)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x65, 0x04, 0xFF, numReads};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	    GTCP_sendCommand(data, numReads);
	    GTCP_readMulti(3 + numReads);
	}
}

/**
 * Defines a 16x16 pixel downloaded character (2-byte character) in the code
 * specified by codeUpper and codeLower.
 *
 * codeUpper, codeLower: Depends on currently selected language.
 *
 * <ul>
 * <li> codeUpper = 0xEC && 0x40 <= codeLower <= 0x4F : Japanese - JIS X0208 (SHIFT-JIS)
 * <li> codeUpper = 0xFE && 0xa1 <= codeLower <= 0xB0 : Korean - KSC5601-87
 * <li> codeUpper = 0xFE && 0xa1 <= codeLower <= 0xB0 : Simplified Chinese - GB2312-80
 * <li> codeUpper = 0xFE && 0xa1 <= codeLower <= 0xB0 : Traditional Chinese - Big-5
 * </ul>
 *
 * @param  codeUpper Character code, upper byte
 * @param  codeLower Character code, lower byte
 * @param  data      Character definition data
 * @return none
 */
void GTCP_16x16CharacterDefinition(uint8_t codeUpper, uint8_t codeLower, uint8_t *data)
{
	if(((codeUpper == 0xEC) && (codeLower >= 0x40) && (codeLower <= 0x4F)) ||
	   ((codeUpper == 0xFE) && (codeLower >= 0xA1) && (codeLower <= 0xB0)))
	{
	   uint8_t command[] = {0x1F, 0x28, 0x67, 0x10, codeUpper, codeLower};
	   GTCP_sendCommand(command, ARRAY_SIZE(command));
	   GTCP_sendCommand(data, 32);
	}
}

/**
 * Deletes a defined 16x16 download character in code specified by codeUpper and
 * codeLower.
 *
 * codeUpper, codeLower: Depends on currently selected language.
 *
 * <ul>
 * <li> codeUpper = 0xec && 0x40 <= codeLower <= 0x4f : Japanese - JIS X0208 (SHIFT-JIS)
 * <li> codeUpper = 0xfe && 0xa1 <= codeLower <= 0xb0 : Korean - KSC5601-87
 * <li> codeUpper = 0xfe && 0xa1 <= codeLower <= 0xb0 : Simplified Chinese - GB2312-80
 * <li> codeUpper = 0xfe && 0xa1 <= codeLower <= 0xb0 : Traditional Chinese - Big-5
 * </ul>
 *
 * @param  codeUpper Character code, upper byte
 * @param  codeLower Character code, lower byte
 * @return none
 */
void GTCP_16x16CharacterDelete(uint8_t codeUpper, uint8_t codeLower)
{
	if(((codeUpper == 0xEC) && (codeLower >= 0x40) && (codeLower <= 0x4F)) ||
	   ((codeUpper == 0xFE) && (codeLower >= 0xA1) && (codeLower <= 0xB0)))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x67, 0x11, codeUpper, codeLower};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Defines a 32x32 pixel downloaded character (2-byte character) in character code
 * specified by codeUpper and codeLower.
 *
 * <ul>
 * <li> codeUpper, codeLower: Depends on currently selected language.
 * <li> codeUpper = 0xEC && 0x40 <= codeLower <= 0x4F : Japanese - JIS X0208 (SHIFT-JIS)
 * </ul>
 *
 * @param  codeUpper Character code, upper byte
 * @param  codeLower Character code, lower byte
 * @param  data      Character definition data.
 * @return none
 */
void GTCP_32x32CharacterDefinition(uint8_t codeUpper, uint8_t codeLower, uint8_t *data)
{
	if((codeUpper == 0xEC) && (codeLower >= 0x40) && (codeLower <= 0x4F))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x67, 0x14, codeUpper, codeLower};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	    GTCP_sendCommand(data, 128);
	}
}

/**
 * Delete a defined 32x32 download character in code specified by codeUpper and
 * codeLower.
 * This command is invalid if Japanese is not the selected language.
 *
 * <ul>
 * <li> codeUpper, codeLower: Depends on currently selected language.
 * <li> codeUpper = 0xec && 0x40 <= codeLower <= 0x4f : Japanese - JIS X0208 (SHIFT-JIS)
 * </ul>
 *
 * @param  codeUpper Character code, upper byte
 * @param  codeLower Character code, lower byte
 * @return none
 */
void GTCP_32x32CharacterDelete(uint8_t codeUpper, uint8_t codeLower)
{
	if((codeUpper == 0xEC) && (codeLower >= 0x40) && (codeLower <= 0x4F))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x67, 0x15, codeUpper, codeLower};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Save the download characters defined in RAM to FROM (RAM -> FROM)
 *
 * <ul>
 * <li> fontSize = 0x01: 6x8 pixels
 * <li> fontSize = 0x02: 8x16 pixels
 * <li> fontSize = 0x03: 16x16 pixels
 * <li> fontSize = 0x04: 16x32 pixels
 * <li> fontSize = 0x05: 32x32 pixels
 * <li> fontSize = 0x06: 12x24 pixels
 * </ul>
 *
 * @param  fontSize The desired character font size to be saved.
 * @return none
 */
void GTCP_downloadCharacterSave(uint8_t fontSize)
{
	if((fontSize >= 0x01) && (fontSize <= 0x06))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x65, 0x11, fontSize};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Transfer the downloaded characters saved in FROM to RAM.
 *
 * <ul>
 * <li> fontSize = 0x01: 6x8 pixels
 * <li> fontSize = 0x02: 8x16 pixels
 * <li> fontSize = 0x03: 16x16 pixels
 * <li> fontSize = 0x04: 16x32 pixels
 * <li> fontSize = 0x05: 32x32 pixels
 * <li> fontSize = 0x06: 12x24 pixels
 * </ul>
 *
 * @param  fontSize The desired character font to be restored.
 * @return none
 */
void GTCP_downloadCharacterRestore(uint8_t fontSize)
{
	if((fontSize >= 0x01) && (fontSize <= 0x06))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x65, 0x21, fontSize};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Start user setup mode.
 *
 * The following data is transmitted when entering user setup mode:
 * 1. Header | 0x28 | 1 byte
 * 2. Identifier 1 | 0x65 | 1 byte
 * 3. Identifier 2 | 0x01 | 1 byte
 * 4. NUL | 0x00 | 1 byte
 *
 * @return none
 */
void GTCP_enterUserSetupMode()
{
    uint8_t command[] = {0x1F, 0x28, 0x65, 0x01, 0x49, 0x4E};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * End (exit) user setup mode and initiate a software reset.
 *
 * @return none
 */
void GTCP_endUserSetupMode()
{
    uint8_t command[] = {0x1F, 0x28, 0x65, 0x02, 0x4F, 0x55, 0x54};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Define the user font table for a specified character size.
 *
 * <ul>
 * <li> table = 0x01: 6x8 pixel user table
 *   <li> P(0x80 - 1) ... P(0x80 - 6) ... P(0xff - 6)
 *   <li> 6 Bytes / font * 128 characters (768 bytes)
 * <li> table = 0x02: 8x16 pixel user table
 *   <li> P(0x80 - 1) ... P(0x80 - 16) ... P(0xff - 16)
 *   <li> 16 Bytes / font * 128 characters (2048 bytes)
 * <li> table = 0x03: 12x24 pixel user table
 *   <li> P(0x80 - 1) ... P(0x80 - 36) ... P(0xff - 36)
 *   <li> 36 Bytes / font * 128 characters (4608 bytes)
 * <li> table = 0x04: 16x32 pixel user table
 *   <li> P(0x80 - 1) ... P(0x80 - 64) ... P(0xff - 64)
 *   <li> 64 Bytes / font * 128 characters (8192 bytes)
 * </ul>
 *
 * It is not possible to only define a part of the character code space.
 * THIS COMMAND IS ONLY VALID IN USER SETUP MODE.
 *
 * @param  table The desired user table font size.
 * @param  data  The user table data.
 * @return none
 */
void GTCP_FROMUserFontDefinition(uint8_t table, uint8_t *data)
{
    uint8_t command[] = {0x1F, 0x28, 0x65, 0x13, table};
	GTCP_sendCommand(command, ARRAY_SIZE(command));

	switch(table)
	{
	case CHARACTER_TABLE_6X8: //6x8 character table
		GTCP_sendCommand(data, CHARACTER_TABLE_6X8_SIZE);
		break;
	case CHARACTER_TABLE_8X16: //8x16 character table
	    GTCP_sendCommand(data, CHARACTER_TABLE_8X16_SIZE);
		break;
	case CHARACTER_TABLE_12X24: //12x24 character table
	    GTCP_sendCommand(data, CHARACTER_TABLE_12X24_SIZE);
		break;
	case CHARACTER_TABLE_16X32: //16x32 character table
	    GTCP_sendCommand(data, CHARACTER_TABLE_16X32_SIZE);
		break;
	default:
	    break;
	}
}

/**
 * Define or delete RAM Macro or RAM Program Macro.
 *
 * <ul>
 * <li> 0x0000 <= length <= 0x0400
 * <li> length > 0x0000: Supplied data is stored as Macro
 * <li> length = 0x0000: Macro is deleted
 * </ul>
 *
 * @param  length RAM Macro data length
 * @param  data        RAM Macro data
 * @return none
 */
void GTCP_RAMMacroDefineDelete(unsigned int length, uint8_t *data)
{
	if(length <= 0x0400)
	{
	    uint8_t command[] = {0x1F, 0x3A, length, (length >> 8)};
	    GTCP_sendCommand(command, ARRAY_SIZE(command)); //Send command
	    GTCP_sendCommand(data, length); //Send macro data
	}
}


/**
 * Define or delete FROM Macro or FROM Program Macro.
 *
 * <ul>
 * <li> 0x01 <= registrationNum <= 0x04: FROM Macro number 1-4
 * <li> 0x0000 <= (lengthLower + lengthUpper * 0x0100) <= 0x2000 (if using 4 Macros), 0x8000 (if using 1 Macro)
 *   <li> (lengthLower + lengthUpper * 0x0100) > 0: Supplied data is stored as a Macro
 *   <li> (lengthLower + lengthUpper * 0x0100) = 0: Macro is deleted.
 * <li> 0x00 <= interval <= 0xff
 * <li> 0x00 <= idleTime <= 0xff
 * <li> 0x00 <= data <= 0xff
 * </ul>
 *
 * @param  registrationNum FROM Macro registration number
 * @param  lengthUpper     FROM Macro data length, upper byte
 * @param  lengthLower     FROM Macro data length, lower byte
 * @param  interval        Display time interval (interval * IntTime)
 * @param  idleTime        Idle time for macro repetition (idleTime * IntTime)
 * @param  data            FROM Macro data
 * @return none
 */
void GTCP_FROMMacroDefineDelete(uint8_t registrationNum, unsigned int length,
		uint8_t interval, uint8_t idleTime, uint8_t *data)
{
    bool isValid = false;

	if(display.firmwareVer >= 2.20f)
	{
	    if(((registrationNum > 1) && (length <= 0xE000)) || (registrationNum == 1))
	    {
	        isValid = true;
	    }
	}else if((length <= 0x2000) || (length == 0x8000))
	{
	    isValid = true;
	}

	if(isValid)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x65, 0x12, registrationNum,
	                         length, (length >> 8), interval, idleTime};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	    GTCP_sendCommand(data, length);
	}
}

/**
 * Continuously execute contents of defined Macro 'definitionNum'.
 *
 * <ul>
 * <li> 0x00 <= definitionNum <= 0x04, 0x80 <= definitionNum <= 0x84
 * <li> definitionNum = 0x00: RAM Macro 0
 * <li> 0x01 <= definitionNum <= 0x04: FROM Macro 1-4
 * <li> definitionNum = 0x80: RAM Program Macro 0
 * <li> 0x81 <= definitionNum <= 0x84: FROM Program Macro 1-4
 * <li> 0x00 <= interval <= 0xff
 * <li> 0x00 <= idleTime <= 0xff
 * </ul>
 *
 * @param  definitionNum Macro processing definition number
 * @param  interval      Display time interval (interval * IntTime)
 * @param  idleTime      Idle time for Macro repetition (idleTime * IntTime)
 * @return none
 */
void GTCP_macroExecution(uint8_t definitionNum, uint8_t interval, uint8_t idleTime)
{
    bool isValid = false;

	if((display.firmwareVer >= 2.20f) && ((definitionNum <= 0x08) || ((definitionNum >= 0x80) && (definitionNum <= 0x88))))
	{
	    isValid = true;
	} else if((display.firmwareVer < 2.20f) && ((definitionNum <= 0x04) || ((definitionNum >= 0x80) && (definitionNum <= 0x84))))
	{
	    isValid = true;
	}

	if(isValid)
	{
	    uint8_t command[] = {0x1F, 0x5E, definitionNum, interval, idleTime};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Macro end condition set.
 *
 * <ul>
 * <li> endCodeEnable = 0x00: Macro end code disabled
 * <li> endCodeEnable = 0x01: Macro end code enabled
 * <li> 0x00 <= endCode <= 0xff
 * <li> endScreenSetting = 0x00: Clear screen at Macro end
 * <li> endScreenSetting = 0x01: Do not clear screen at Macro end
 * </ul>
 *
 * @param  endCodeEnable    Macro end code Enable/Disable
 * @param  endCode          Macro end code
 * @param  endScreenSetting Macro end clear screen setting
 * @return none
 */
void GTCP_macroEndCondition(uint8_t endCodeEnable, uint8_t endCode, uint8_t endScreenSetting)
{
	if((endCodeEnable <= 1) && (endScreenSetting <= 1))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x69, 0x20, endCodeEnable, endCode, endScreenSetting};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Store the supplied data into general-purpose memory / FROM2.
 *
 * <ul>
 * <li> memorySelect = 0x10: FROM2 base address 0x0000.0000
 * <li> memorySelect = 0x11: FROM2 base address 0x0100.0000
 * <li> memorySelect = 0x12: FROM2 base address 0x0200.0000
 * <li> memorySelect = 0x13: FROM2 base address 0x0300.0000
 * <li> memorySelect = 0x14: FROM2 base address 0x0400.0000
 * <li> memorySelect = 0x15: FROM2 base address 0x0500.0000
 * <li> memorySelect = 0x16: FROM2 base address 0x0600.0000
 * <li> memorySelect = 0x17: FROM2 base address 0x0700.0000
 * <li> memorySelect = 0x18: FROM2 base address 0x0800.0000
 * <li> memorySelect = 0x19: FROM2 base address 0x0900.0000
 * <li> memorySelect = 0x1A: FROM2 base address 0x0A00.0000
 * <li> memorySelect = 0x1B: FROM2 base address 0x0B00.0000
 * <li> memorySelect = 0x1C: FROM2 base address 0x0C00.0000
 * <li> memorySelect = 0x1D: FROM2 base address 0x0D00.0000
 * <li> memorySelect = 0x1E: FROM2 base address 0x0E00.0000
 * <li> memorySelect = 0x1F: FROM2 base address 0x0F00.0000
 * <li> 0x000001 <= (sizeLower + sizeUpper * 0x0100 + sizeExtension * 0x00010000) <= 0x00FFFFFF
 * <li> 0x000000 <= (addressLower + addressUpper * 0x0100 + addressExtension * 0x00010000) <= 0x00FFFFFF
 * <li> memorySelect = 0x30 (General-purpose RAM):
 * <li> 0x000001 <= (sizeLower + sizeUpper * 0x0100 + sizeExtension * 0x00010000) <= 0x00000400
 * <li> 0x000000 <= (addressLower + addressUpper * 0x0100 + addressExtension * 0x00010000) <= 0x000003FF
 * <li> memorySelect = 0x31 (General-purpose FROM):
 * <li> 0x000001 <= (sizeLower + sizeUpper * 0x0100 + sizeExtension * 0x00010000) <= 0x00001000
 * <li> 0x000000 <= (addressLower + addressUpper * 0x0100 + addressExtension * 0x00010000) <= 0x0000FFFF
 * <li> 0x00 <= data <= 0xFF
 * </ul>
 *
 * @param  sizeUpper        Data size, upper byte
 * @param  sizeLower        Data size, lower byte
 * @param  sizeExtension    Data size, extension byte
 * @param  memorySelect     Desired memory to use.
 * @param  addressUpper     Memory address, upper byte
 * @param  addressLower     Memory address, lower byte
 * @param  addressExtension Memory address, extension byte
 * @param  data             Data to store
 * @return none
 */
void GTCP_memoryStore(uint32_t size, uint8_t memorySelect, uint32_t address, uint8_t *data)
{
    bool isValid = false;

	if((memorySelect >= 0x10) && (memorySelect <= 0x1F) && (size) && (size <= 0xFFFFFF) && (address <= 0xFFFFFF))
	{
	    isValid = true;
	} else if((memorySelect == 0x30) && (size) && (size <= 0x000400) && (address <= 0x0003FF))
	{
	    isValid = true;
	} else if((memorySelect == 0x31) && (size) && (size <= 0x001000) && (address <= 0x00FFFF))
	{
	    isValid = true;
	}

	if(isValid)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x65, 0x18, size, (size >> 8), (size >> 16),
							 memorySelect, address, (address >> 8), (address >> 16)};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	    GTCP_sendCommand(data, size);
	}
}

/**
 * Transfer data between general-purpose memory / FROM2 areas.
 *
 * <ul>
 * <li> 0x10 <= destMemory, srcMemory <= 0x17, destMemory, srcMemory = 0x30, 0x31
 * <li> destMemory, srcMemory = 0x10: FROM2 base address 0x0000.0000
 * <li> destMemory, srcMemory = 0x11: FROM2 base address 0x0100.0000
 * <li> destMemory, srcMemory = 0x12: FROM2 base address 0x0200.0000
 * <li> destMemory, srcMemory = 0x13: FROM2 base address 0x0300.0000
 * <li> destMemory, srcMemory = 0x14: FROM2 base address 0x0400.0000
 * <li> destMemory, srcMemory = 0x15: FROM2 base address 0x0500.0000
 * <li> destMemory, srcMemory = 0x16: FROM2 base address 0x0600.0000
 * <li> destMemory, srcMemory = 0x17: FROM2 base address 0x0700.0000
 * <li> destMemory, srcMemory = 0x18: FROM2 base address 0x0800.0000
 * <li> destMemory, srcMemory = 0x19: FROM2 base address 0x0900.0000
 * <li> destMemory, srcMemory = 0x1A: FROM2 base address 0x0A00.0000
 * <li> destMemory, srcMemory = 0x1B: FROM2 base address 0x0B00.0000
 * <li> destMemory, srcMemory = 0x1C: FROM2 base address 0x0C00.0000
 * <li> destMemory, srcMemory = 0x1D: FROM2 base address 0x0D00.0000
 * <li> destMemory, srcMemory = 0x1E: FROM2 base address 0x0E00.0000
 * <li> destMemory, srcMemory = 0x1F: FROM2 base address 0x0F00.0000
 * <li> 0x000000 <= (sizeLower + sizeUpper * 0x100 + sizeExtension * 0x10000) <= 0xFFFFFF
 * <li> 0x000000 <= (destAddressLower + destAddressUpper * 0x100 + destAddressExtension * 0x10000) <= 0xFFFFFF
 * <li> 0x000000 <= (srcAddressLower + srcAddressUpper * 0x100 + srcAddressExtension * 0x10000) <= 0xFFFFFF
 * <li> destMemory, srcMemory = 0x30 (General-purpose RAM):
 * <li> 0x000001 <= (sizeLower + sizeUpper * 0x100 + sizeExtension * 0x10000) <= 0x000400
 * <li> 0x000000 <= (destAddressLower + destAddressUpper * 0x100 + destAddressExtension * 0x10000) <= 0x0003FF
 * <li> 0x000000 <= (srcAddressLower + srcAddressUpper * 0x100 + srcAddressExtension * 0x10000) <= 0x0003FF
 * <li> destMemory, srcMemory = 0x31 (General-purpose FROM):
 * <li> 0x000001 <= (sizeLower + sizeUpper * 0x100 + sizeExtension * 0x10000) <= 0x001000
 * <li> 0x000000 <= (destAddressLower + destAddressUpper * 0x100 + destAddressExtension * 0x10000) <= 0x00FFFF
 * <li> 0x000000 <= (srcAddressLower + srcAddressUpper * 0x100 + srcAddressExtension * 0x10000) <= 0x00FFFF
 * <li> 0x00 <= data <= 0xFF
 * </ul>
 *
 * @param  sizeUpper            Transfer data size, upper byte
 * @param  sizeLower            Transfer data size, lower byte
 * @param  sizeExtension        Transfer data size, extension byte
 * @param  destMemory           Destination memory select
 * @param  destAddressUpper     Destination address, upper byte
 * @param  destAddressLower     Destination address, lower byte
 * @param  destAddressExtension Destination address, extension byte
 * @param  srcMemory            Source memory select
 * @param  srcAddressUpper      Source address, upper byte
 * @param  srcAddressLower      Source address, lower byte
 * @param  srcAddressExtension  Source address, extension byte
 * @return none
 */
void GTCP_memoryTransfer(uint32_t size, uint8_t destMemory, uint32_t destAddress,
		uint8_t srcMemory, uint32_t srcAddress)
{
    bool isValid = false;

	if((destMemory >= 0x10) && (destMemory <= 0x1F) && (size) && (size <= 0xFFFFFF) && (destAddress <= 0xFFFFFF))
	{
	    isValid = true;
	} else if((destMemory == 0x30) && (size) && (size <= 0x000400) && (destAddress <= 0x0003FF))
	{
	    isValid = true;
	} else if((destMemory == 0x31) && (size) && (size <= 0x001000) && (destAddress <= 0x00FFFF))
	{
	    isValid = true;
	} else {
	    isValid = false;
	}

	if(isValid)
	{
	    if((srcMemory >= 0x10) && (srcMemory <= 0x1F) && (size) && (size <= 0xFFFFFF) && (srcAddress <= 0xFFFFFF))
	    {
	        isValid = true;
	    } else if((srcMemory == 0x30) && (size) && (size <= 0x000400) && (srcAddress <= 0x0003FF))
	    {
	        isValid = true;
	    } else if((srcMemory == 0x31) && (size) && (size <= 0x001000) && (srcAddress <= 0x00FFFF))
	    {
	        isValid = true;
	    } else {
	        isValid = false;
	    }
	}

	if(isValid)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x65, 0x19, size, (size >> 8), (size >> 16),
							 destMemory, destAddress, (destAddress >> 8), (destAddress >> 16),
							 srcMemory, srcAddress, (srcAddress >> 8), (srcAddress >> 16)};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Send data stored in general-purpose memory.
 *
 * <ul>
 * <li> 0x10 <= memorySelect <= 0x17, memorySelect = 0x30, 0x31
 * <li> memorySelect = 0x10: FROM2 base address 0x0000.0000
 * <li> memorySelect = 0x11: FROM2 base address 0x0100.0000
 * <li> memorySelect = 0x12: FROM2 base address 0x0200.0000
 * <li> memorySelect = 0x13: FROM2 base address 0x0300.0000
 * <li> memorySelect = 0x14: FROM2 base address 0x0400.0000
 * <li> memorySelect = 0x15: FROM2 base address 0x0500.0000
 * <li> memorySelect = 0x16: FROM2 base address 0x0600.0000
 * <li> memorySelect = 0x17: FROM2 base address 0x0700.0000
 * <li> memorySelect = 0x18: FROM2 base address 0x0800.0000
 * <li> memorySelect = 0x19: FROM2 base address 0x0900.0000
 * <li> memorySelect = 0x1A: FROM2 base address 0x0A00.0000
 * <li> memorySelect = 0x1B: FROM2 base address 0x0B00.0000
 * <li> memorySelect = 0x1C: FROM2 base address 0x0C00.0000
 * <li> memorySelect = 0x1D: FROM2 base address 0x0D00.0000
 * <li> memorySelect = 0x1E: FROM2 base address 0x0E00.0000
 * <li> memorySelect = 0x1F: FROM2 base address 0x0F00.0000
 * <li> 0x000000 <= address <= 0xFFFFFF
 * <li> 0x000000 <= size <= 0xFF
 * <li> memorySelect = 0x30 (General-purpose RAM):
 * <li> 0x000000 <= address <= 0xFF
 * <li> 0x000001 <= size <= 0x000400
 * <li> memorySelect = 0x31 (General-purpose FROM):
 * <li> 0x000000 <= address <= 0x00FFFF
 * <li> 0x000001 <= size <= 0xFF
 * </ul>
 *
 * The following data is transmitted:
 * 1. Header | 0x28 | 1 byte
 * 2. Identifier | 0x65 | 1 byte
 * 3. Identifier | 0x28 | 1 byte
 * 4. Data | 0x00-0xff | <size> bytes
 *
 * @param  size 		 Data size, restricted to 255 bytes
 * @param  memorySelect 	 Desired memory select
 * @param  address   Memory address, upper byte
 * @return none
 */
void GTCP_readMemory(uint8_t size, uint8_t memorySelect, uint32_t address)
{
    bool isValid = false;

	if((memorySelect >= 0x10) && (memorySelect <= 0x1F) && (size) && (size <= 0xFFFFFF) && (address <= 0xFFFFFF))
	{
	    isValid = true;
	} else if((memorySelect == 0x30) && (size) && (size <= 0x000400) && (address <= 0x0003FF))
	{
	    isValid = true;
	} else if((memorySelect == 0x31) && (size) && (size <= 0x001000) && (address <= 0x00FFFF))
	{
	    isValid = true;
	}

	if(isValid)
	{
	    uint8_t command[] = {0x1F, 0x28, 0x65, 0x28, size, 0, 0,
							 memorySelect, address, (address >> 8), (address >> 16)};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	    GTCP_readMulti(size);
	}
}

/**
 * Request the connected GT-CP module's boot version.
 * After this command is sent, the module will send
 * 7 response bytes.
 * Example response: "0.00"
 *
 * @return none
 */
float GTCP_getBootVersion()
{
	char boot[4];

	uint8_t command[] = {0x1F, 0x28, 0x65, 0x40, 0x01};
	GTCP_sendCommand(command, ARRAY_SIZE(command));

	GTCP_readMulti(7);
	boot[0] = RXDataArray[3];
	boot[1] = RXDataArray[4];
	boot[2] = RXDataArray[5];
	boot[3] = RXDataArray[6];

	display.bootVer = atof(boot);
	return display.bootVer;
}

/**
 * Request the connected GT-CP module's firmware version.
 * After this command is sent, the module will send
 * 7 response bytes.
 * Example response: "1.62"
 *
 * @return none
 */
float GTCP_getFirmwareVersion()
{
	char firmware[4];

	uint8_t command[] = {0x1F, 0x28, 0x65, 0x40, 0x02};
	GTCP_sendCommand(command, ARRAY_SIZE(command));

	GTCP_readMulti(7);
	firmware[0] = RXDataArray[3];
	firmware[1] = RXDataArray[4];
	firmware[2] = RXDataArray[5];
	firmware[3] = RXDataArray[6];

	display.firmwareVer = atof(firmware);
	return display.firmwareVer;
}

/**
 * Request the connected GT-CP module's character code information.
 * After this command is sent, the module will send
 * 18 response bytes.
 * Example response: "JIS X208 L 100"
 *
 * @return none
 */
void GTCP_getCharacterCodeInfo()
{
    uint8_t command[] = {0x1F, 0x28, 0x65, 0x40, 0x10};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
	GTCP_readMulti(18);
}

/**
 * Request the connected GT-CP module's language type.
 * After this command is sent, the module will send
 * 18 response bytes.
 * Example response: "MULTI LANGUAGE"
 *
 * @return none
 */
void GTCP_getLanguageType()
{
    uint8_t command[] = {0x1F, 0x28, 0x65, 0x40, 0x11};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
	GTCP_readMulti(18);
}

/**
 * Request the connected GT-CP module's memory checksum.
 * After this command is sent, the module will send
 * 7 response bytes.
 *
 * @return none
 */
void GTCP_getMemoryChecksum(uint8_t address, uint8_t length)
{
    uint8_t command[] = {0x1F, 0x28, 0x65, 0x40, 0x20, address, length};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
	GTCP_readMulti(7);
}

/**
 * Request the connected GT-CP module's FROM2 memory checksum.
 * After this command is sent, the module will send
 * 7 response bytes.
 *
 * @param  address FROM2 address for checksum.
 * @param  size    Size of checksum calculation.
 * @return none
 */
uint32_t GTCP_getFROM2MemoryChecksum(uint32_t address, uint32_t size)
{
	uint32_t checksum;
	uint8_t command[] = {0x1F, 0x28, 0x65, 0x40, 0x21, 0x00,
								(address >> 24), (address >> 16), (address >> 8), address,
								(size >> 24), (size >> 16), (size >> 8), size};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
	GTCP_readMulti(7);

	checksum = (((uint32_t)RXDataArray[3] << 24) | ((uint32_t)RXDataArray[4] << 16) |
				((uint32_t)RXDataArray[5] << 8) | (uint32_t)RXDataArray[6]);
	return checksum;
}

/**
 * Request the connected GT-CP module's product type.
 * After this command is sent, the module will send
 * 18 response bytes.
 * Example response: "GT-C900PA"
 *
 * @return none
 */
void GTCP_getProductType()
{
	char productType[16];

	uint8_t command[] = {0x1F, 0x28, 0x65, 0x40, 0x30};
	GTCP_sendCommand(command, ARRAY_SIZE(command));

	GTCP_readMulti(18);
	for(unsigned int i = 0; i < 15; i++)
	{
		productType[i] = RXDataArray[i + 3];
	}
	productType[15] = '\0';
	strcpy(display.productType, productType);
}

/**
 * Request the connected GT-CP module's horizontal resolution.
 * After this command is sent, the module will send
 * 6 response bytes.
 * Example response: "800"
 *
 * @return none
 */
void GTCP_getHorizontalResolution()
{
	char res[3];

	uint8_t command[5] = {0x1f, 0x28, 0x65, 0x40, 0x40};
	GTCP_sendCommand(command, ARRAY_SIZE(command));

	GTCP_readMulti(6);
	res[0] = RXDataArray[3];
	res[1] = RXDataArray[4];
	res[2] = RXDataArray[5];
	display.WIDTH = atoi(res);

	// Update display memory configuration
	Xdots = display.WIDTH * 2;
	Max_Xdot = Xdots - 1;
	Max_Xdot_CurtWin = Max_Xdot;
}

/**
 * Request the connected GT-CP module's vertical resolution.
 * After this command is sent, the module will send
 * 6 response bytes.
 * Example response: "480"
 *
 * @return none
 */
void GTCP_getVerticalResolution()
{
	char res[3];

	uint8_t command[] = {0x1F, 0x28, 0x65, 0x40, 0x41};
	GTCP_sendCommand(command, ARRAY_SIZE(command));

	GTCP_readMulti(6);
	res[0] = RXDataArray[3];
	res[1] = RXDataArray[4];
	res[2] = RXDataArray[5];

	display.HEIGHT = atoi(res);
	display.LINES = display.HEIGHT / 8;
	Ydots = display.HEIGHT * 2;
	Max_Ydot = Ydots - 1;
	Max_Ydot_CurtWin = Max_Ydot;
}

/**
 * Request the connected GT-CP module's active touch setting package name.
 * After this command is sent, the module will send
 * 18 response bytes.
 * Example response: "Noritake"
 *
 * @return none
 */
void GTCP_getTSPName()
{
	char tspName[16];

	uint8_t command[] = {0x1F, 0x28, 0x65, 0x40, 0x70};
	GTCP_sendCommand(command, ARRAY_SIZE(command));

	GTCP_readMulti(18);
	for(unsigned int i = 0; i < 15; i++)
	{
		tspName[i] = RXDataArray[i + 3];
	}
	tspName[15] = '\0';
	strcpy(display.tspName, tspName);
}

/**
 * Request the connected GT-CP module's active touch setting package ID.
 * After this command is sent, the module will send
 * 7 response bytes.
 * Example response: "0003"
 *
 * @return none
 */
void GTCP_getTSPID()
{
	char tspIDArray[4];

	uint8_t command[] = {0x1F, 0x28, 0x65, 0x40, 0x71};
	GTCP_sendCommand(command, ARRAY_SIZE(command));

	GTCP_readMulti(7);
	for(unsigned int i = 0; i < 4; i++)
	{
		tspIDArray[i] = RXDataArray[i + 3];
	}
	display.tspID = atoi(tspIDArray);
}

/**
 * Request the connected GT-CP module's current touch gain value.
 * After this command is sent, the module will send
 * 4 response bytes.
 *
 * @return none
 */
void GTCP_getTouchGain()
{
    uint8_t command[] = {0x1F, 0x28, 0x65, 0x40, 0x72};
	GTCP_sendCommand(command, ARRAY_SIZE(command));

	GTCP_readMulti(4);
	display.gain = (uint8_t) RXDataArray[3];
}

/**
 * Request the connected GT-CP module's current touch threshold value.
 * After this command is sent, the module will send
 * 4 response bytes.
 *
 * @return none
 */
void GTCP_getTouchThreshold()
{
    uint8_t command[] = {0x1F, 0x28, 0x65, 0x40, 0x73};
	GTCP_sendCommand(command, ARRAY_SIZE(command));

	GTCP_readMulti(4);
	display.threshold = (uint8_t) RXDataArray[3];
}

/**
 * Store touch setting package data.
 * This function is not intended for common use.
 * Please contact a Noritake sales consultant for further information.
 *
 * packageNum: 0x01 <= packageNum <= 0x04
 *
 * @param  packageNum  The desired package number.
 * @param  data        Touch setting package data.
 * @return none
 */
void GTCP_touchSettingPackageDataStore(uint8_t packageNum, uint8_t *data)
{
	if(packageNum <= 4)
	{
	    const int fileSize = 1152;
	    uint8_t command[] = {0x1F, 0x28, 0x65, 0x1C, packageNum};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	    GTCP_sendCommand(data, fileSize);
	}
}

/**
 * Select the desired touch setting data package to use.
 * After this command is executed, touch control will start with the selected
 * touch setting package number.
 *
 * packageNum: 0x00 <= packageNum <= 0x04
 * default: packageNum = 0x00
 *
 * @param  packageNum  The desired package number.
 * @return none
 */
void GTCP_touchSettingPackageSelection(uint8_t packageNum)
{
	if(packageNum <= 4)
	{
	    uint8_t command[] = {0x1F, 0x4B, 0x70, 0x10, packageNum};
		GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Set the desired power saving mode.
 *
 * The wakeup parameter determines many sleep mode options
 * related to different bits.
 * b0 = 1: Wakeup on touch
 * b0 = 0: No wakeup on touch
 *
 * b4 - b6:
 * -------------------------------------------------
 * | b4 | b5 | b6 |   GPIO15 wakeup condition      |
 * -------------------------------------------------
 * |  0 |  - |  - | No wakeup on GPIO15 input      |
 * |  1 |  0 |  0 | Wakeup on GPIO15 LOW level     |
 * |  1 |  0 |  1 | Wakeup on GPIO15 falling edge  |
 * |  1 |  1 |  0 | Wakeup on GPIO15 HIGH level    |
 * |  1 |  1 |  1 | Wakeup on GPIO15 rising edge   |
 * -------------------------------------------------
 *
 * @param mode Desired power saving mode.
 * @param wakeup Desired wake up method.
 * @return none
 */
void setPowerSavingMode(uint8_t wakeup)
{
    uint8_t command[] = {0x1F, 0x28, 0x61, 0x48, 0x01, wakeup};
	GTCP_sendCommand(command, ARRAY_SIZE(command));
}

/**
 * Set the amount of time in-between each touch scan.
 * This parameter is used if the "wakeup on touch"
 * power saving mode is selected.
 *
 * 0x05 (5ms) <= period <= 0xFE (254ms)
 * Default: memory switch 61
 *
 * @param period The amount of time between each touch scan.
 * @return none
 */
void setTouchScanPeriod(uint8_t period)
{
	if((period >= 0x05) && (period <= 0xFE))
	{
	    uint8_t command[] = {0x1F, 0x28, 0x61, 0x49, period};
	    GTCP_sendCommand(command, ARRAY_SIZE(command));
	}
}

/**
 * Set the module's display orientation.
 * This function will change the module's memory switch
 * orientation value and then reset the module to use
 * the new orientation.
 * User setup mode is used while changing memory switch.
 *
 * @param degrees The desired display orientation.
 * @return none
 */
void GTCP_setDisplayOrientation(int degrees)
{
	GTCP_enterUserSetupMode();
	GTCP_readMulti(4);

	switch(degrees)
	{
	case 0: // 0 Degree rotation (Default)
	    GTCP_setSingleMemorySW(DISPLAY_ORIENTATION_MSW, 0x00);
		break;
	case 90: // 90 Degree rotation
	    GTCP_setSingleMemorySW(DISPLAY_ORIENTATION_MSW, 0x01);
		break;
	case 180: // 180 Degree rotation
	    GTCP_setSingleMemorySW(DISPLAY_ORIENTATION_MSW, 0x02);
		break;
	case 270: // 270 Degree rotation
	    GTCP_setSingleMemorySW(DISPLAY_ORIENTATION_MSW, 0x03);
		break;
	default:
		// Input is invalid, do not change rotation
		break;
	}
	GTCP_endUserSetupMode();
}
