//#DOC Main, program entry and main loop
// From: https://github.com/Billwilliams1952/AD9833-Library-Arduino
#include "main.h"
#include <otSpi.h>
#include <otSerial.h>
#include <otTimer.h>
#include <otPort.h>
#include <otMalloc.h>
#include <otPrintf.h>
#include <otAD9833.h>

void PrintMenu(char ch, bool outputOn);
void IncrementFrequencyTest();
void CycleWaveformsTest();
void SwitchFrequencyRegisterTest();
void PhaseTest();

#define RUNNING       "\tRUNNING\n"
#define NOT_RUNNING   "\n"
#define ON            "ON\n"
#define OFF           "OFF\n"
#define YIELD_ON_CHAR       if(UartCheckChar(UART0)) { UartChar(UART0); return; }
#define DELAY_WITH_YIELD    for(U8 i = 0; i < 10; i++ ) { YIELD_ON_CHAR DelayMs(100); }

otAD9833    dds;

void setup()
{
    UartEnableSignals(UART0);
    UartSetBaud(UART0, 115200);
    _initMalloc();
    setStdout(4096, UART0); // printf output is now directed on UART0
    
    PortConfig(PP_F, 10, P_OUT);
    PortConfig(PP_F, 11, P_OUT);
    PPF_SET = BIT(10) | BIT(11);
    
    dds.init(SA_OFFBOARD);
    
    PPF_CLR = BIT(11);
    
    dds.applySignal(otAD9833::SINE_WAVE, otAD9833::REG0, 1000);
    dds.enableOutput(true);
    
    DelayMs(1000);
    PrintMenu(0, false);
}

void loop()
{
    static bool outputOn = false;
    
    if(UartCheckChar(UART0))
    {
        char ch = UartChar(UART0);
        PrintMenu(ch, outputOn);
        switch(ch)
        {
            case '1':
                IncrementFrequencyTest();
                break;
            case '2':
                CycleWaveformsTest();
                break;  
            case '3':
                SwitchFrequencyRegisterTest();
                break;
            case '4':
                PhaseTest();
                break;
            case '6':
                outputOn = ! outputOn;
                dds.enableOutput(outputOn);    // Turn off output
                break;
            default:
                printf("*** Invalid command ***\n");
                break;                    
        }      
    }
}

/*
 * Setup a manual ramp from a Start frequency to a Stop frequency in some increment
 * over a ramp time.
 */
void IncrementFrequencyTest()
{

    F32     startHz = 1000, stopHz = 5000, incHz = 1, sweepTimeSec = 5.0;
 
    // Calculate the delay between each increment.
    U16     numMsecPerStep = (sweepTimeSec * 1000.0) / ((U16) ((stopHz - startHz) / incHz) + 1);
    if(numMsecPerStep == 0)
        numMsecPerStep = 1;

    // Apply a signal to the output. If phaseReg is not supplied, then
    // a phase of 0.0 is applied to the same register as freqReg
    dds.applySignal(otAD9833::SINE_WAVE, otAD9833::REG1, startHz);

    while(true)
    {
        dds.setFrequency(otAD9833::REG1, startHz - incHz);

        for(F32 i = startHz; i <= stopHz; i += incHz)
        {
            YIELD_ON_CHAR
            dds.incrementFrequency(otAD9833::REG1, incHz);
            DelayMs(numMsecPerStep);
        }
    }
}

/*
 * Cycle through all of the waveform types. Also cycle the
 * frequency registers.
 */
void CycleWaveformsTest()
{
    U16  waveType = otAD9833::SINE_WAVE;
    dds.setFrequency(otAD9833::REG0, 10000.0);   // Load values
    dds.setFrequency(otAD9833::REG1, 1000.0);
    // We don't care about phase for this test
    
    while(true)
    {
        dds.setWaveform(otAD9833::REG1,waveType);   // Next waveform
        dds.setWaveform(otAD9833::REG0,waveType);
        dds.setOutputSource(otAD9833::REG1);        // Output 1000 Hz waveform

        // Hack to allow I'm alive lamp a chance to blink and give a better
        // response to user input
        DELAY_WITH_YIELD
        
        dds.setOutputSource(otAD9833::REG0);        // Output 10000 Hz waveform
        
        DELAY_WITH_YIELD

        switch(waveType)    // Cycle through all the waveform types
        {             
            case otAD9833::SINE_WAVE:
                waveType = otAD9833::TRIANGLE_WAVE;
                break;
            case otAD9833::TRIANGLE_WAVE:
                waveType = otAD9833::SQUARE_WAVE;
                break;
            case otAD9833::SQUARE_WAVE:
                waveType = otAD9833::HALF_SQUARE_WAVE;
                break;
            case otAD9833::HALF_SQUARE_WAVE:
                waveType = otAD9833::SINE_WAVE;
                break;
        }
    }    
}

/*
 * Fast switching example.
 * I use the FFT display capability on my scope
 */
void SwitchFrequencyRegisterTest()
{
    dds.applySignal(otAD9833::SINE_WAVE, otAD9833::REG0, 500000);
    dds.applySignal(otAD9833::SINE_WAVE, otAD9833::REG1, 100000);
    dds.setPhase(otAD9833::REG1, 180);           // Offset second freq by 180 deg
    dds.reset();

    while(true) // This takes time
    {                  
        YIELD_ON_CHAR                 // This takes more time

        dds.setOutputSource(otAD9833::REG0);    // This takes about 18 usec
        dds.setOutputSource(otAD9833::REG1);    // This takes about 18 usec  
        
        // What ends up is REG0 frequency is active a shorter amount of time
        // then REG1 frequency. In the sepctrum, the duty cycle differences will
        // show up (power is lower by 10log(DC))
    }  
}

/*
 * Phase shift between REG0 and REG1. Use a oscilloscope set to Normal
 * triggering, AC coupling, 500usec/div, 100 mV/div. This will display
 * about two cycles for register 0, 4 cycle for register 1, plus dead
 * time for the Reset.
 * Use Normal triggering so the display remains even when triggering is
 * lost. Can use any waveform for this test. Remember that the square
 * wave is about 5v-pp while sine and triangle are about 600 mv-pp
 */
void PhaseTest()
{
    dds.applySignal(otAD9833::TRIANGLE_WAVE, otAD9833::REG0, 1000);
    dds.applySignal(otAD9833::SINE_WAVE, otAD9833::REG1, 2000);

    bool reverse = true;

    while(true)
    {
        reverse = ! reverse;

        for(S16 i = 0; i <= 360; i += 1 )
        {
            if(!reverse )
                dds.incrementPhase(otAD9833::REG1, -1);
            else
                dds.incrementPhase(otAD9833::REG1, 1);
            YIELD_ON_CHAR
            /*
             * Display ~ 2 cycles using REG0 phase. If no REG is supplied for phase,
             * defaults to REG specified for frequency. RESET is removed during this
             * function call.
             */
            dds.setOutputSource(otAD9833::REG0);
            /*
             * This is just a wag to try to get exactly 2 cycles of the waveform.
             * It makes the phase alignments easier to verify.
             */
            DelayUs(1900);
                
            YIELD_ON_CHAR
            
            /* This also works if you keep using REG1 for frequency
             * Now display ~ 4 cycles using REG1
             */
            dds.setOutputSource(otAD9833::REG1);
            DelayUs(1950);
            /*
             * Turn off for remaining trace. Reset the registers so triggering occurs
             * on the start of REG0 signal. Reset() includes 15 msec delay which is good  
             * to ensure sweep is completed.
             * I tried using EnableOutput(true) then EnableOutput(false) in this
             * loop but could not get reliable triggering on the scope.
             *
             * The difference between Reset() and EnableOutput(false) is that EnableOutput(false)
             * keeps the AD9833 in RESET until you specifically remove the RESET using
             * EnableOutput(true). However, after a call to Reset(), calls to ANY function
             * EXCEPT Set/Increment Phase will also remove the RESET.
             *
             */
            dds.reset();

            if(i % 90 == 0)
                DelayMs(1000);    // Stop and show phase alignment between REG0 REG1
        }
    }
}

/*
 * Display the command menu
 */
void PrintMenu(char ch, bool outputOn)
{
    printf("\n\n");
    printf("****** AD9833 Test Menu ******\n\n");
    printf("'1' IncrementFrequencyTest");
    if(ch == '1') printf(RUNNING); else printf(NOT_RUNNING);
    
    printf("'2' CycleWaveformsTest\t");
    if(ch == '2') printf(RUNNING); else printf(NOT_RUNNING);
    
    printf("'3' SwitchFrequencyRegisterTest");
    if(ch == '3') printf(RUNNING); else printf(NOT_RUNNING);
    
    printf("'4' PhaseTest\t\t");
    if(ch == '4') printf(RUNNING); else printf(NOT_RUNNING);
        
    printf("'5' RequestedvsProgrammedValues");
    if(ch == '5') printf(RUNNING); else printf(NOT_RUNNING);
        
    printf("'6' Output ");  
    if(ch == '6')
    {  
        if(outputOn) printf(OFF);
        else         printf(ON);
    }
    else
    {
        if(outputOn) printf(ON);
        else         printf(OFF);      
    }
    printf("Enter a number 1 to 6 >");
}