//#DOC Main, program entry and main loop
#include "main.h"
#include <otFPGA.h>
#include <otPort.h>
#include <otSpi.h>
#include <otSerial.h>
#include <otTimer.h>
#include <otPrintf.h>
#include <otPga.h>
#include <otADS868x.h>
#include <otILI9341.h>
#include <otStatistics.h>

//#define DLY DelayUs(1);
#define DLY NOP;

#define SPEC_ST          BIT(15)
#define SPEC_CLK         BIT(14)

#define SPEC_CHANNELS    288    // New Spec Channel

S16     data[SPEC_CHANNELS];

otPga           pga;
otADS868x       adc;
otILI9341       tft;
otStatistics    stat;

// Draw range of sampled data
void plotRange(S16 mn, S16 mx)
{
    char    buf[16];
    U16     col = RGB565_GREEN;
    U32     l = (U32) mx;
    // Blank previous draw
    tft.fillRect(288, 0, 319, 239, RGB565_BLACK);
    l = 240 * (mx - mn) / 25000;
    // 15%
    if(l < 36) col = RGB565_BLUE;
    // 80%
    if(l > 192) col = RGB565_RED;
    tft.fillRect(288, 240 - l, 319, 239, col);
    
    sprintf(buf, "%5d", mn);
    tft.setCursor(0, 0);
    tft.writeString(buf);
    
    sprintf(buf, "%5d", mx);
    tft.setCursor(260, 0);
    tft.writeString(buf);
}

FAST_CODE_2 void plotSpectral(U16 col)
{
    U16     y;
    SpiSet(SA_LCD);
    
    stat.evaluate();
    S16 rng = stat.max - stat.min;
    S16 mn = stat.min;
    S16 mx = stat.max;
    plotRange(mn, mx);
    
    // Force fixed range
    //rng = 25000;
    
    //tft.clear();
    for(int i = 0; i < SPEC_CHANNELS; i++)
    {
        y = 240 - (data[i] - mn) * 240 / rng;
        //tft.setPixel(i, y, RGB565_YELLOW);
        tft.drawLine(i, y, i, 239, col);
    }
}

// Read spectrometer data
FAST_CODE_2 void readSpectrometer()
{
    int     delayTime = 1; // delay time
    SpiSet(SA_ADC);
    // Start clock cycle and set start pulse to signal start
    PPF_CLR = SPEC_CLK;
    DLY
    PPF_SET = SPEC_CLK;
    DLY
    PPF_CLR = SPEC_CLK;
    PPF_SET = SPEC_ST;
    DLY

    // Sample for a period of time
    for(int i = 0; i < 15; i++)
    {
        PPF_SET = SPEC_CLK;
        DLY
        PPF_CLR = SPEC_CLK;
        DLY
    }

    // Set SPEC_ST to low
    PPF_CLR = SPEC_ST;
    //Sample for a period of time
    for(int i = 0; i < 85; i++)
    {
        PPF_SET = SPEC_CLK;
        DLY
        PPF_CLR = SPEC_CLK;
        DLY
    }
    // One more clock pulse before the actual read
    PPF_SET = SPEC_CLK;
    DLY
    PPF_CLR = SPEC_CLK;
    DLY

    // Read from SPEC_VIDEO
    for(int i = 0; i < SPEC_CHANNELS; i++)
    {
        data[i] = adc.read();
        PPF_SET = SPEC_CLK;
        DLY
        PPF_CLR = SPEC_CLK;
        DLY
    }
    
    // Set SPEC_ST to high
    PPF_SET = SPEC_ST;

    // Sample for a small amount of time
    for(int i = 0; i < 7; i++)
    {
        PPF_SET = SPEC_CLK;
        DLY
        PPF_CLR = SPEC_CLK;
        DLY
    }
    PPF_SET = SPEC_CLK;
    DLY
}

// Used for debug, print data values
void printData()
{
    for(int i = 0; i < SPEC_CHANNELS; i++)
    {
        printf("%5d ", data[i]);
        if(((i + 1) % 16) == 0)
            printf("\n");
    }
    printf("\n\n");
}

void setup()
{
    // Setup the UART0 used by printf
    UartEnableSignals(UART0);
    UartSetBaud(UART0, 115200);
    // Init printf
    setStdout(1024, UART0); // printf output is now directed on UART0
    
    TimerSetAsCounter(TIMER0);
    TimerEnable(TIMER0, true);
   
    // Init multiplexor and amplifier
    pga.init();
    pga.channel(0);
    pga.gain(otPga::GAIN_1);

    // Used port and bits for the spectrometer controls
    PortConfig(PP_F, 14, P_OUT);
    PortConfig(PP_F, 15, P_OUT);
    PPF_CLR = SPEC_ST | SPEC_CLK;
    PPF_SET = SPEC_CLK;
    PPF_CLR = SPEC_CLK;
    
    // Init graphics for spectral plot
    tft.init();
    tft.setRotation(otILI9341::IR_270);
    tft.clear();
    tft.setTextColor(RGB565_YELLOW, RGB565_BLACK);
    tft.setTextSize(1);
    
    // Init 16 bit ADC
    adc.init();
    // Set ADC range
    adc.setRange(otADS868x::UNI_5V);
    
    // Arm statistics engine ...
    stat.setData(otStatistics::T_S16, (void *) data, SPEC_CHANNELS);
}

void loop()
{
    U32     st, et;
    // Blank previous plot
    plotSpectral(RGB565_BLACK);
    // Grab new spectral
    st = TimerGetCounts(TIMER0);    // Grab start time
    readSpectrometer();
    et = TimerGetCounts(TIMER0);    // Grab end time
    et = ElapsedTimeUs(st, et);
    printf("Grab time: %4u uS, Benchmark: %.1lf Spectrals/s\n", et, 1E6 / (double) et);
    // Plot grabbed spectral
    plotSpectral(RGB565_YELLOW);
    //printData();
}