/*
 *
 */

#include <stdint.h>
#include <stdbool.h>
#include "main.h"
#include "drivers/pinout.h"
#include "utils/uartstdio.h"
#include "driverlib/sysctl.h"

// TivaWare includes
#include "driverlib/sysctl.h"
#include "driverlib/debug.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/inc/hw_memmap.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/ssi.h"
#include "driverlib/inc/hw_ssi.h"
#include "driverlib/inc/hw_types.h"
#include "driverlib/timer.h"
#include "driverlib/interrupt.h"
#include "driverlib/tm4c129encpdt.h"

// FreeRTOS includes
#include "FreeRTOSConfig.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

#define NUM_SSI_DATA            3

//General Functions declarations
void adc_init(void);
void sys_init(void);
void timer0_init(void);
void adc_collect_data(void);
void adc_conv_start (void);

// FreeRTOS Tasks declarations
void blinkLED0_Task(void *pvParameters);
void SerialTask(void *pvParameters);
void Timer0IntTask(void *pvParameters);


//FreeRTOS Handles
TaskHandle_t Timer0IntTask_Handle;


void spi0_transmit (void *pvParameters);


volatile uint32_t sys_clock;
volatile uint32_t counter;
volatile uint32_t ch0,ch1,ch2,ch3,ch4,ch5,ch6,ch7;

//uint32_t data[16];
union {
    uint32_t ui32value[8];
    uint8_t ui8byte[32];
}data;
// Main function
int main(void)
 {
    // Initialize system clock to 120 MHz
   // uint32_t sys_clock;
    sys_clock = SysCtlClockFreqSet(
                               (SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN |
                                SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480),
                               SYSTEM_CLOCK);
    //ASSERT(sys_clock == SYSTEM_CLOCK);


    // Initialize all peripherals i.e. LEDs, user buttons and UART0 connected to the Launchpad except USB and Ethernet
    PinoutSet(false, false);

    //Initialize System
    sys_init();


    // Create tasks

    xTaskCreate(blinkLED0_Task, (const portCHAR *)"BLNKLED0",
                configMINIMAL_STACK_SIZE, NULL, 1, NULL);

    xTaskCreate(SerialTask, (const portCHAR *)"Serial",
                configMINIMAL_STACK_SIZE, NULL, 1, NULL);

    xTaskCreate(spi0_transmit, (const portCHAR *)"SPI0",
                configMINIMAL_STACK_SIZE, NULL, 2, NULL);

    xTaskCreate(Timer0IntTask, (const portCHAR *)"Timer0IntTask",
                configMINIMAL_STACK_SIZE, NULL, 2, &Timer0IntTask_Handle);

    vTaskStartScheduler();
    return 0;
}


// Blink the LED0 on the launchpad
void blinkLED0_Task(void *pvParameters)
{
    for (;;)
    {
        GPIOPinWrite(LED0_PORT,LED0,LED0);
        vTaskDelay(500);
        GPIOPinWrite(LED0_PORT,LED0,0);
        vTaskDelay(500);

    }
}

// Blink the LED1 on the launchpad
void blinkLED1_Task(void *pvParameters)
{
    for (;;)
    {
        GPIOPinWrite(LED1_PORT,LED1,LED1);
        vTaskDelay(500);
        GPIOPinWrite(LED1_PORT,LED1,0);
        vTaskDelay(500);

    }
}
// Blink the LED2 on the launchpad
void blinkLED2_Task(void *pvParameters)
{
    for (;;)
    {
        GPIOPinWrite(LED2_PORT,LED2,LED2);
        vTaskDelay(500);
        GPIOPinWrite(LED2_PORT,LED2,0);
        vTaskDelay(500);

    }
}

// Blink the LED3 on the launchpad
void blinkLED3_Task(void *pvParameters)
{
    for (;;)
    {
        GPIOPinWrite(LED3_PORT,LED3,LED3);
        vTaskDelay(500);
        GPIOPinWrite(LED3_PORT,LED3,0);
        vTaskDelay(500);

    }
}
// Write text over the Stellaris debug interface UART port
void SerialTask(void *pvParameters)
{


    for (;;)
    {
        UARTprintf("\r\nSystem Clock:\t\t%dMHz",sys_clock/1000000);
        //UARTprintf("\r\ndata.ui32value[0] = 0x%X, data.ui8byte[3] = 0x%X, data.ui8byte[2] = 0x%X, data.ui8byte[1] = 0x%X, data.ui8byte[0] = 0x%X",data.ui32value[0],data.ui8byte[3],data.ui8byte[2],data.ui8byte[1],data.ui8byte[0]);

        vTaskDelay(1000 / portTICK_PERIOD_MS);

    }
}

void sys_init(void)
{
    uint32_t i;

        //Set up the UART which is connected to the virtual COM port
        UARTStdioConfig(UART0, UART0_BAUD_RATE, SYSTEM_CLOCK);
        //
        // The SSI0 peripheral must be enabled for use.
        //
        SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOM);
        SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);

        //
        // Configure the pin muxing for SSI0 functions on port A2, A3, A4, and A5.
        // This step is not necessary if your part does not support pin muxing.
        // TODO: change this to select the port/pin you are using.
        //
        GPIOPinConfigure(GPIO_PA2_SSI0CLK);
        GPIOPinConfigure(GPIO_PA3_SSI0FSS);
        GPIOPinConfigure(GPIO_PA4_SSI0RX);
        GPIOPinConfigure(GPIO_PA5_SSI0TX);

        //
        // Configure the GPIO settings for the SSI pins.  This function also gives
        // control of these pins to the SSI hardware.  Consult the data sheet to
        // see which functions are allocated per pin.
        // The pins are assigned as follows:
        //      PA5 - SSI0Tx
        //      PA4 - SSI0Rx
        //      PA3 - SSI0Fss
        //      PA2 - SSI0CLK
        // TODO: change this to select the port/pin you are using.
        //
        GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_5 | GPIO_PIN_4 | GPIO_PIN_3 |
                       GPIO_PIN_2);

#if defined(TARGET_IS_TM4C129_RA0) ||                                         \
    defined(TARGET_IS_TM4C129_RA1) ||                                         \
    defined(TARGET_IS_TM4C129_RA2)
    SSIConfigSetExpClk(SSI0_BASE, sys_clock, SSI_FRF_MOTO_MODE_2,
                       SSI_MODE_MASTER, SPI0_CLK_SPEED, SPI0_DATA_WIDTH);
#else
    SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,
                       SSI_MODE_MASTER, 1000000, 8);
#endif
    // Enable the SSI0 module.
    //
    SSIEnable(SSI0_BASE);
    adc_init();
    timer0_init();


    for (i=0;i<8;i++){data.ui32value[i] = 0x3FFFF;} //Fill the array with some data for simulation purpose only





}




void spi0_transmit (void *pvParameters)
{
    //uint32_t data[8], temp1;
    uint8_t i;

    for (;;)
    {
        //
        // Display indication that the SSI is transmitting data.
        //
        //UARTprintf("'\r\n0x%X sent to MOSI' ", counter);



        for (i = 0;i<1;i++)
        {

            //data ui32value 0
            SSIDataPut(SSI0_BASE, data.ui8byte[2]); //MSB
            SSIDataPut(SSI0_BASE, data.ui8byte[1]);
            SSIDataPut(SSI0_BASE, data.ui8byte[0]); //LSB

            //data ui32value 1
            SSIDataPut(SSI0_BASE, data.ui8byte[6]);
            SSIDataPut(SSI0_BASE, data.ui8byte[5]);
            SSIDataPut(SSI0_BASE, data.ui8byte[4]);

            //data ui32value 2
            SSIDataPut(SSI0_BASE, data.ui8byte[10]);
            SSIDataPut(SSI0_BASE, data.ui8byte[9]);
            SSIDataPut(SSI0_BASE, data.ui8byte[8]);

            //data ui32value 3
            SSIDataPut(SSI0_BASE, data.ui8byte[10]);
            SSIDataPut(SSI0_BASE, data.ui8byte[9]);
            SSIDataPut(SSI0_BASE, data.ui8byte[8]);

            //data ui32value 4
            SSIDataPut(SSI0_BASE, data.ui8byte[2]); //MSB
            SSIDataPut(SSI0_BASE, data.ui8byte[1]);
            SSIDataPut(SSI0_BASE, data.ui8byte[0]); //LSB

            //data ui32value 5
            SSIDataPut(SSI0_BASE, data.ui8byte[6]);
            SSIDataPut(SSI0_BASE, data.ui8byte[5]);
            SSIDataPut(SSI0_BASE, data.ui8byte[4]);

            //data ui32value 6
            SSIDataPut(SSI0_BASE, data.ui8byte[10]);
            SSIDataPut(SSI0_BASE, data.ui8byte[9]);
            SSIDataPut(SSI0_BASE, data.ui8byte[8]);

            //data ui32value 7
            SSIDataPut(SSI0_BASE, data.ui8byte[10]);
            SSIDataPut(SSI0_BASE, data.ui8byte[9]);
            SSIDataPut(SSI0_BASE, data.ui8byte[8]);



            //while(SSIBusy(SSI0_BASE)){}
            //SSIDataGetNonBlocking(SSI0_BASE, &data[i]); //(0b01000101  << 8) = (0b100010100000000)

        }
                //while(SSIBusy(SSI0_BASE)){}

        vTaskDelay(1000);

    }


}

void adc_init(void)
{
    ROM_GPIOPinTypeGPIOOutput(ADC_CTRL1_PORT, ADC_OS0 | ADC_OS1 | ADC_OS2 | ADC_CNVSTARTA | ADC_CNVSTARTB | ADC_RANGE | ADC_RESET);
    ROM_GPIOPinTypeGPIOOutput(ADC_CTRL2_PORT, ADC_CS | ADC_READ);

    ROM_GPIOPinTypeGPIOInput(ADC_CTRL1_PORT, ADC_BUSY); //Configured ADC_BUSY pin as input
    ROM_GPIOPinTypeGPIOInput(ADC_MSB_PORT, 0xFF); //Configured ADC MSB Data Pins as input
    ROM_GPIOPinTypeGPIOInput(ADC_LSB_PORT, 0xFF); //Configured ADC LSB Data Pins as input

   GPIOPadConfigSet(ADC_CTRL1_PORT, ADC_CNVSTARTA | ADC_CNVSTARTB,GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);

   GPIOPinWrite(ADC_CTRL2_PORT,ADC_CS | ADC_READ, ADC_CS | ADC_READ); //Pull ADC_CS and ADC_READ High
   GPIOPinWrite(ADC_CTRL1_PORT, ADC_CNVSTARTA | ADC_CNVSTARTB, ADC_CNVSTARTA | ADC_CNVSTARTB);


}

void adc_conv_start (void)
{
        GPIOPinWrite(ADC_CTRL1_PORT,ADC_CNVSTARTA |ADC_CNVSTARTB ,0);
        //for(i=0;i<1;i++){}
        //SysCtlDelay(1);
        GPIOPinWrite(ADC_CTRL1_PORT, ADC_CNVSTARTA | ADC_CNVSTARTB, ADC_CNVSTARTA | ADC_CNVSTARTB);
}

void adc_collect_data(void)
{
    uint32_t i = 0, ch = 0;
    adc_conv_start();
    if ((GPIOPinRead(ADC_CTRL1_PORT,ADC_BUSY) != 0) & (i != 600)) {i++;}

    //Read ADC Channel 0
    for(ch=0;ch<8;ch++)
    {
        GPIOPinWrite(ADC_CTRL2_PORT,ADC_CS | ADC_READ, 0); //Pull ADC_CS and ADC_READ Low to read MSB 16bits
        //data.ui32value[ch] = (((GPIOPinRead(ADC_MSB_PORT,0xFF)) & 0xFF) | (0xFF)); //Read all 8 Pins (((GPIOPinRead(ADC_MSB_PORT,0xFF)) & 0xFF) | (0xFF));
        //UARTprintf("\r\nvalue before shift left: 0x%X",data.ui32value[ch]);
        //data.ui32value[ch] = data.ui32value[ch] << 8;
        //UARTprintf("\r\nvalue after shift left: 0x%X",data.ui32value[ch]);
        //data.ui32value[ch] |=(((GPIOPinRead(ADC_LSB_PORT,0xFF)) & 0xFF) | (0xFF)); //Read all 8 Pins (((GPIOPinRead(ADC_LSB_PORT,0xFF)) & 0xFF) | (0xFF));
        //UARTprintf("\r\nvalue before second shift left: 0x%X",data.ui32value[ch]);
        GPIOPinWrite(ADC_CTRL2_PORT,ADC_CS | ADC_READ, ADC_CS | ADC_READ); //Pull ADC_CS and ADC_READ High
        //data.ui32value[ch] = data.ui32value[ch] << 2;
        //UARTprintf("\r\nvalue after second shift left: 0X%x",data.ui32value[ch]);
        GPIOPinWrite(ADC_CTRL2_PORT,ADC_CS | ADC_READ, 0); //Pull ADC_CS and ADC_READ Low to read remaining 2 LSB bits
        //data.ui32value[ch] |= (((GPIOPinRead(ADC_LSB_PORT,0x3)) & 0x3) | (0x3)); //Read 2 LSBs the 17th and 18th bits of the ADC
        //UARTprintf("\r\nvalue after full adjustment: 0X%x",data.ui32value[ch]);
        GPIOPinWrite(ADC_CTRL2_PORT,ADC_CS | ADC_READ, ADC_CS | ADC_READ); //Pull ADC_CS and ADC_READ High
    }

}



void timer0_init(void)
{
    uint32_t ui32Period;
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);

    //ui32Period = (sys_clock / 10) / 2;
    ui32Period = (sys_clock / 100);
    UARTprintf("\r\nPeriod = %d",ui32Period);

    TimerLoadSet(TIMER0_BASE, TIMER_A, ui32Period -1);

    IntEnable(INT_TIMER0A);

    TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);

    IntMasterEnable();

    TimerEnable(TIMER0_BASE, TIMER_A);

}

void Timer0IntHandler(void)
{
// Clear the timer interrupt
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
// Read the current state of the GPIO pin and
// write back the opposite state
adc_collect_data();
//BaseType_t checkIfYieldRequired;
//checkIfYieldRequired = xTaskResumeFromISR(Timer0IntTask_Handle);
//portYIELD_FROM_ISR(checkIfYieldRequired);
//GPIOPinWrite(LED2_PORT,LED2,((~LED2) & 0x10));
}

void Timer0IntTask(void *pvParameters)
{
    uint32_t i = 0;
    while(1)
    {
        vTaskSuspend(NULL); //suspend itself
        //adc_collect_data();


        //UARTprintf("\r\n%d Timer0IntTask\n\rdata.ui32value[0] = 0x%X, data.ui8byte[3] = 0x%X, data.ui8byte[2] = "
                //"0x%X, data.ui8byte[1] = 0x%X, data.ui8byte[0] = 0x%X",\
                i++,data.ui32value[0],data.ui8byte[3],data.ui8byte[2],data.ui8byte[1],data.ui8byte[0]);


        //UARTprintf("\r\n%d This is printing form Timer0IntTask",i++);

    }
}








/*  ASSERT() Error function
 *
 *  failed ASSERTS() from driverlib/debug.h are executed in this function
 */
void __error__(char *pcFilename, uint32_t ui32Line)
{
    // Place a breakpoint here to capture errors until logging routine is finished
    while (1)
    {
    }
}
