/**************************************************************************//**
 * @file     main.c
 * @version  V1.00
 * $Revision: 9 $
 * $Date: 14/08/04 10:02a $
 * @brief    Display an image on TFT LCD panel via SPI interface.
 *
 * @note
 * Copyright (C) 2013 Nuvoton Technology Corp. All rights reserved.
 *
 ******************************************************************************/
#include <stdio.h>
#include "NUC472_442.h"

#define TEST_COUNT 16

uint32_t g_au32SourceData[TEST_COUNT];
uint32_t g_au32DestinationData[TEST_COUNT];

#define ILI9341_RESET   PI7
#define ILI9341_DC      PA5
#define ILI9341_LED     PI8

extern uint8_t u8Image[153600];

uint8_t LCD_ReadReg(uint8_t u8Comm)
{
    SPI_ClearRxFIFO(SPI3);

    ILI9341_DC = 0;

    SPI_WRITE_TX(SPI3, u8Comm);
    SPI_WRITE_TX(SPI3, 0x00);

    // wait tx finish
    while(SPI_IS_BUSY(SPI3));

    SPI_READ_RX(SPI3);

    return (SPI_READ_RX(SPI3));
}

void LCD_WriteCommand(uint8_t u8Comm)
{
    ILI9341_DC = 0;

    SPI_WRITE_TX(SPI3, u8Comm);

    // wait tx finish
    while(SPI_IS_BUSY(SPI3));
}

void LCD_WriteData(uint8_t u8Data)
{
    ILI9341_DC = 1;

    SPI_WRITE_TX(SPI3, u8Data);

    // wait tx finish
    while(SPI_IS_BUSY(SPI3));
}

void ILI9341_LCD_DisplayOnScreen(void)
{
    uint32_t i,j;
    uint16_t *u16ImgPtr = (uint16_t *) &u8Image[0];

    LCD_WriteCommand(0x2c);

    ILI9341_DC = 1;

    /* Need to disable and then can change data width to 16-bit */
    SPI_DISABLE(SPI3);
    SPI_SET_DATA_WIDTH(SPI3, 16);
    SPI_TRIGGER(SPI3);

    /* Load image */
    for (i=0; i<320; i++)
    {
        for (j=0; j<240; j++)
        {
            while(SPI_GET_TX_FIFO_FULL_FLAG(SPI3));
            SPI_WRITE_TX(SPI3, *(u16ImgPtr++));
        }
    }

    // wait tx finish
    while(SPI_IS_BUSY(SPI3));

    /* Change data width back to 8-bit */
    SPI_DISABLE(SPI3);
    SPI_SET_DATA_WIDTH(SPI3, 8);
    SPI_TRIGGER(SPI3);

    LCD_WriteCommand(0x29);
}

void ILI9341_LCD_SetAddress(uint32_t x1,uint32_t x2,uint32_t y1,uint32_t y2)
{
    LCD_WriteCommand(0x2a);
    LCD_WriteData(x1>>8);
    LCD_WriteData(x1);
    LCD_WriteData(x2>>8);
    LCD_WriteData(x2);

    LCD_WriteCommand(0x2b);
    LCD_WriteData(y1>>8);
    LCD_WriteData(y1);
    LCD_WriteData(y2>>8);
    LCD_WriteData(y2);
}

void ILI9341_LCD_Init(void)
{
    /* Configure DC/RESET/LED pins */
    SYS->GPA_MFPL &= ~SYS_GPA_MFPL_PA5MFP_Msk;
    SYS->GPI_MFPL &= ~SYS_GPI_MFPL_PI7MFP_Msk;
    SYS->GPI_MFPH &= ~SYS_GPI_MFPH_PI8MFP_Msk;

    ILI9341_DC =0;
    ILI9341_RESET=0;
    ILI9341_LED=0;

    GPIO_SetMode(PA, BIT5, GPIO_MODE_OUTPUT);
    GPIO_SetMode(GPI, BIT7, GPIO_MODE_OUTPUT);
    GPIO_SetMode(GPI, BIT8, GPIO_MODE_OUTPUT);

    /* Configure LCD */
    ILI9341_DC = 1;

    ILI9341_RESET = 0;
    TIMER_Delay(TIMER0, 20000);

    ILI9341_RESET = 1;
    TIMER_Delay(TIMER0, 40000);

    LCD_WriteCommand(0xCB);
    LCD_WriteData(0x39);
    LCD_WriteData(0x2C);
    LCD_WriteData(0x00);
    LCD_WriteData(0x34);
    LCD_WriteData(0x02);

    LCD_WriteCommand(0xCF);
    LCD_WriteData(0x00);
    LCD_WriteData(0xC1);
    LCD_WriteData(0x30);

    LCD_WriteCommand(0xE8);
    LCD_WriteData(0x85);
    LCD_WriteData(0x00);
    LCD_WriteData(0x78);

    LCD_WriteCommand(0xEA);
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);

    LCD_WriteCommand(0xED);
    LCD_WriteData(0x64);
    LCD_WriteData(0x03);
    LCD_WriteData(0x12);
    LCD_WriteData(0x81);

    LCD_WriteCommand(0xF7);
    LCD_WriteData(0x20);

    LCD_WriteCommand(0xC0);
    LCD_WriteData(0x23);

    LCD_WriteCommand(0xC1);
    LCD_WriteData(0x10);

    LCD_WriteCommand(0xC5);
    LCD_WriteData(0x3e);
    LCD_WriteData(0x28);

    LCD_WriteCommand(0xC7);
    LCD_WriteData(0x86);

    LCD_WriteCommand(0x36);
    LCD_WriteData(0x48);

    LCD_WriteCommand(0x3A);
    LCD_WriteData(0x55);

    LCD_WriteCommand(0xB1);
    LCD_WriteData(0x00);
    LCD_WriteData(0x18);

    LCD_WriteCommand(0xB6);
    LCD_WriteData(0x08);
    LCD_WriteData(0x82);
    LCD_WriteData(0x27);

    LCD_WriteCommand(0xF2);
    LCD_WriteData(0x00);

    LCD_WriteCommand(0x26);
    LCD_WriteData(0x01);

    LCD_WriteCommand(0xE0);
    LCD_WriteData(0x0F);
    LCD_WriteData(0x31);
    LCD_WriteData(0x2B);
    LCD_WriteData(0x0C);
    LCD_WriteData(0x0E);
    LCD_WriteData(0x08);
    LCD_WriteData(0x4E);
    LCD_WriteData(0xF1);
    LCD_WriteData(0x37);
    LCD_WriteData(0x07);
    LCD_WriteData(0x10);
    LCD_WriteData(0x03);
    LCD_WriteData(0x0E);
    LCD_WriteData(0x09);
    LCD_WriteData(0x00);

    LCD_WriteCommand(0xE1);
    LCD_WriteData(0x00);
    LCD_WriteData(0x0E);
    LCD_WriteData(0x14);
    LCD_WriteData(0x03);
    LCD_WriteData(0x11);
    LCD_WriteData(0x07);
    LCD_WriteData(0x31);
    LCD_WriteData(0xC1);
    LCD_WriteData(0x48);
    LCD_WriteData(0x08);
    LCD_WriteData(0x0F);
    LCD_WriteData(0x0C);
    LCD_WriteData(0x31);
    LCD_WriteData(0x36);
    LCD_WriteData(0x0F);

    LCD_WriteCommand(0x11);
    TIMER_Delay(TIMER0, 60000);

    LCD_WriteCommand(0x29);    //Display on

    ILI9341_LED = 1;
}

void SYS_Init(void)
{
    /*---------------------------------------------------------------------------------------------------------*/
    /* Init System Clock                                                                                       */
    /*---------------------------------------------------------------------------------------------------------*/
    /* Unlock protected registers */
    SYS_UnlockReg();

    /* Enable External XTAL (4~24 MHz) */
    CLK_EnableXtalRC(CLK_PWRCTL_HXTEN_Msk);

    /* Waiting for 12MHz clock ready */
    CLK_WaitClockReady( CLK_STATUS_HXTSTB_Msk);

    /* Switch HCLK clock source to HXT */
    CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HXT,CLK_CLKDIV0_HCLK(1));

    /* Set PLL to power down mode and PLL_STB bit in CLKSTATUS register will be cleared by hardware.*/
    CLK->PLLCTL |= CLK_PLLCTL_PD_Msk;

    /* Set PLL frequency */
    CLK->PLLCTL = CLK_PLLCTL_84MHz_HXT;

    /* Waiting for clock ready */
    CLK_WaitClockReady(CLK_STATUS_PLLSTB_Msk);

    /* Switch HCLK clock source to PLL */
    CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_PLL,CLK_CLKDIV0_HCLK(1));

    /* Enable IP clock */
    CLK_EnableModuleClock(UART0_MODULE);
    CLK_EnableModuleClock(TMR0_MODULE);
    CLK_EnableModuleClock(SPI3_MODULE);

    /* Select IP clock source */
    CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UARTSEL_HXT, CLK_CLKDIV0_UART(1));
    CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0SEL_HXT, 0);
    CLK_SetModuleClock(SPI3_MODULE, CLK_CLKSEL1_SPI3SEL_PLL, 0);

    /* Update System Core Clock */
    /* User can use SystemCoreClockUpdate() to calculate SystemCoreClock. */
    SystemCoreClockUpdate();

    /*---------------------------------------------------------------------------------------------------------*/
    /* Init I/O Multi-function                                                                                 */
    /*---------------------------------------------------------------------------------------------------------*/
    /* Set GPG multi-function pins for UART0 RXD and TXD */
    SYS->GPG_MFPL |= SYS_GPG_MFPL_PG1MFP_UART0_RXD | SYS_GPG_MFPL_PG2MFP_UART0_TXD ;

    /* SPI3: GPI3=SS0, GPI4=CLK, GPI5=MISO0, GPI6=MOSI0 */
    SYS->GPI_MFPL = (SYS->GPI_MFPL & ~(SYS_GPI_MFPL_PI3MFP_Msk | SYS_GPI_MFPL_PI4MFP_Msk | SYS_GPI_MFPL_PI5MFP_Msk | SYS_GPI_MFPL_PI6MFP_Msk)) |
                    (SYS_GPI_MFPL_PI3MFP_SPI3_SS0 | SYS_GPI_MFPL_PI4MFP_SPI3_CLK | SYS_GPI_MFPL_PI5MFP_SPI3_MISO0 | SYS_GPI_MFPL_PI6MFP_SPI3_MOSI0);

    /* Lock protected registers */
    SYS_LockReg();
}

int main(void)
{
    /* Init System, IP clock and multi-function I/O */
    SYS_Init();

    /* Init UART to 115200-8n1 for print message */
    UART_Open(UART0, 115200);

    /* Configure SPI3 as a master, MSB first, 8-bit transaction, SPI Mode-0 timing, clock is 4MHz */
    SPI_Open(SPI3, SPI_MASTER, SPI_MODE_0, 8, 4000000);

    /* Configure SPI1 as a low level active device. */
    SPI_EnableAutoSS(SPI3, SPI_SS0, SPI_SS_ACTIVE_LOW);

    /* Start SPI */
    SPI_TRIGGER(SPI3);

    /* Init LCD */
    ILI9341_LCD_Init();

    /* Setup display address */
    ILI9341_LCD_SetAddress(0, 239, 0, 319);

    /* Show the image on the screen */
    ILI9341_LCD_DisplayOnScreen();

    while(1);
}
