/*
 * TCD0_PLL_fuer_Landolt.cpp
 *
 * DDS Code von S.Landolt
 *
 * Created: 12.03.2021 
 * Author : Devil-Elec
 * Code angepasst für AVR128DB48 mit TCD0 und PLL
 * Board: AVR128DB48 Curiosity Nano
 *
 * Ausgangspin: PA5
 */ 

// #define TIMERTAKT_PLL        // für 48MHz PLL Takt "einkommentieren"

#include <avr/io.h>
#include <avr/pgmspace.h>

void initPins (void);
void initTCD0 (void);
void initOnboard16MHzOscillator (void);
void initInternal16MHzOscillator48MHzPLL (void);

const uint16_t RESOLUTION = 256;

#if defined (TIMERTAKT_PLL)
    const uint32_t PWM = 48000000UL/RESOLUTION;  // = 187'500  (48MHz Timertakt)
#else
    const uint16_t PWM = F_CPU/RESOLUTION;       // =  62'500  (16MHz CPU und Timertakt)
#endif        

const uint16_t TonA4 = 440;
const uint16_t A4_Phasendelta = (uint16_t)(65536UL*TonA4/PWM);
uint16_t Phase;

const uint8_t Sinus[256] PROGMEM = {
    127,  130,  133,  136,  139,  143,  146,  149,  152,  155,  158,  161,  164,  167,  170,  173,
    176,  178,  181,  184,  187,  190,  192,  195,  198,  200,  203,  205,  208,  210,  212,  215,
    217,  219,  221,  223,  225,  227,  229,  231,  233,  234,  236,  238,  239,  240,  242,  243,
    244,  245,  247,  248,  249,  249,  250,  251,  252,  252,  253,  253,  253,  254,  254,  254,
    254,  254,  254,  254,  253,  253,  253,  252,  252,  251,  250,  249,  249,  248,  247,  245,
    244,  243,  242,  240,  239,  238,  236,  234,  233,  231,  229,  227,  225,  223,  221,  219,
    217,  215,  212,  210,  208,  205,  203,  200,  198,  195,  192,  190,  187,  184,  181,  178,
    176,  173,  170,  167,  164,  161,  158,  155,  152,  149,  146,  143,  139,  136,  133,  130,
    127,  124,  121,  118,  115,  111,  108,  105,  102,   99,   96,   93,   90,   87,   84,   81,
    78,   76,   73,   70,   67,   64,   62,   59,   56,   54,   51,   49,   46,   44,   42,   39,
    37,   35,   33,   31,   29,   27,   25,   23,   21,   20,   18,   16,   15,   14,   12,   11,
    10,    9,    7,    6,    5,    5,    4,    3,    2,    2,    1,    1,    1,    0,    0,    0,
     0,    0,    0,    0,    1,    1,    1,    2,    2,    3,    4,    5,    5,    6,    7,    9,
    10,   11,   12,   14,   15,   16,   18,   20,   21,   23,   25,   27,   29,   31,   33,   35,
    37,   39,   42,   44,   46,   49,   51,   54,   56,   59,   62,   64,   67,   70,   73,   76,
    78,   81,   84,   87,   90,   93,   96,   99,  102,  105,  108,  111,  115,  118,  121,  124
};

int main(void)
{  
    #if defined (TIMERTAKT_PLL)
        initInternal16MHzOscillator48MHzPLL();
    #else 
        initOnboard16MHzOscillator();    // external 16MHz Oscillator - PLL not working - Errata
    #endif
    
    initPins();
    initTCD0();
           
    while (1) 
    {
        Phase += A4_Phasendelta;
                                        
        TCD0.CMPBSET = pgm_read_byte(&(Sinus[Phase >> 8]));  // Pulsweite 
                
        TCD0.CTRLE = TCD_SYNC_bm;                       // aktualisiert die double buffered register
            
        while (!(TCD0.INTFLAGS & (TCD_OVF_bm))) { ; }   // ohne jitter: sleep plus interrupt
            
        TCD0.INTFLAGS = TCD_OVF_bm;                     // delete interrupt flag
    }
}

void initPins (void)
{
    VPORTA_DIR = _BV(PIN5_bm);  // PA5 Output
}

void initTCD0(void) // 12 Bit Timer -->> max 4095
{                   
    /*
    Errata:
    The PLL Will Not Run when Using XOSCHF with an External Crystal.
    When the PLL is configured to run from an external source (SOURCE in CLKCTRL.PLLCTRLA is ‘1’), the PLL will
    only run if XOSCHF is configured to use an external clock (SELHF in CLKCTRL.XOSCHFCTRLA is ‘1’).
    It will not work with an external crystal.
    */
    CPU_CCP = CCP_IOREG_gc;
    #if defined (TIMERTAKT_PLL)
        TCD0.CTRLA = TCD_CLKSEL_PLL_gc;          // TCD0 wird von PLL getaktet
    #else
        TCD0.CTRLA = TCD_CLKSEL_EXTCLK_gc;      // TCD0 wird von externen Takt oder crystal oscillator getaktet
    #endif
    
    TCD0.CTRLB = TCD_WGMODE_ONERAMP_gc;        
    TCD0.CMPBCLR = RESOLUTION-1;                // Timer TOP Einstellung, Periodendauer
        
    /* Enable the PWM channels */
    CPU_CCP = CCP_IOREG_gc;
    TCD0.FAULTCTRL = (TCD_CMPBEN_bm | TCD_CMPB_bm); // only WOB
    
    
    while (!(TCD0.STATUS & TCD_ENRDY_bm))
    { ; }                                           // Wait for TCD to be ready for enabling
    
    TCD0.CTRLA |= TCD_ENABLE_bm;                    // enable TCD0
}

void initOnboard16MHzOscillator (void)
{    					
    CPU_CCP = CCP_IOREG_gc;
    CLKCTRL.XOSCHFCTRLA = (
              CLKCTRL_RUNSTDBY_bm       // Run standby enabled
            | CLKCTRL_CSUTHF_4K_gc      // 4k XOSCHF cycles
            | CLKCTRL_FRQRANGE_16M_gc   // Max 16 MHz XTAL Frequency
            | CLKCTRL_SELHF_CRYSTAL_gc  // Typ: External Crystal
            | CLKCTRL_ENABLE_bm         // External high-frequency Oscillator enabled
    );
        
    // Confirm crystal oscillator start-up
    while(!(CLKCTRL.MCLKSTATUS & CLKCTRL_EXTS_bm))
    { ; }

    // Select External Clock Source and enable Pin Clock Out
    CPU_CCP = CCP_IOREG_gc;
    CLKCTRL.MCLKCTRLA = (CLKCTRL_CLKSEL_EXTCLK_gc | CLKCTRL_CLKOUT_bm);        

    // wait for system oscillator changing to finish
    while (CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm)
    { ; }
}

void initInternal16MHzOscillator48MHzPLL (void)
{
    CPU_CCP = CCP_IOREG_gc;
    CLKCTRL.OSCHFCTRLA = ( CLKCTRL_FREQSEL_16M_gc );    // Internal OSCHF 16MHz
        
    // wait internal oscillator is stable
    while(!(CLKCTRL.MCLKSTATUS & CLKCTRL_OSCHFS_bm)) { ; }
        
    // Set the PLL to use OSCHF as source, and select 3x multiplication factor
    CPU_CCP = CCP_IOREG_gc;
    CLKCTRL.PLLCTRLA = CLKCTRL_MULFAC_3x_gc;        // 48MHz PLL
    
    // wait for system oscillator changing to finish
    while (CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm) { ; }
}