/***
 *** These examples have no copyright and can be used by anyone.
 *** The following examples are based on Device File Package
 *** required to compile the macro definitions used.
 *** The Device File Package is available by downloading Atmel Studio 7.
 ***/

/*** 
 *** EXAMPLE 1:
 *** As the device boots using the OSC16M
 *** oscillator running @4MHz,
 *** Here is an example function configuring the device
 *** to use the OSC16M running @8MHz.
 *** The function performs the following steps:
 *** 1- Configure and Enable Clock Generator 0 (GCLK0), 
 ***    XOSC32K as source to switch back on OSC16M
 *** 2- Disabling the OSC16M to reconfigure it
 *** 3- Reconfigure the OSC16M oscillator frequency to 8MHz
 *** 4- Re-enable the OSC16M
 *** 5- Enable and Connect Generic clock 0 to OSC16M 
 ***/
void switch_OSC16M_freq_8MHz()
{          
/*** 
 *** Configure and Enable Clock Generator 0
 *** (GCLK0), XOSC32K as source to switch back on OSC16M 
 ***/
 GCLK->GENCTRL[0].reg = GCLK_GENCTRL_DIV(0) | GCLK_GENCTRL_SRC_XOSC32K |GCLK_GENCTRL_GENEN;
 /*** (write synchronized) ***/
 while((GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL0));

 /*** Disabling the OSC16M to reconfigure it ***/
 OSCCTRL->OSC16MCTRL.bit.ENABLE = 0;
 /*** Wait for OSC16M is ready ***/
 while((OSCCTRL->STATUS.reg & OSCCTRL_STATUS_OSC16MRDY));

 /*** Reconfigure the OSC16M oscillator frequency to 8MHz ***/
 OSCCTRL->OSC16MCTRL.reg= OSCCTRL_OSC16MCTRL_FSEL(OSCCTRL_OSC16MCTRL_FSEL_8_Val); 

 /*** Re Enable the OSC16M ***/
 OSCCTRL->OSC16MCTRL.bit.ENABLE = 1;
 /*** Wait for OSC16M is ready ***/
 while(!(OSCCTRL->STATUS.reg & OSCCTRL_STATUS_OSC16MRDY));

 /*** Enable and Connect Generic clock 0 to OSC16M ***/
 GCLK->GENCTRL[0].reg = (GCLK_GENCTRL_SRC_OSC16M| GCLK_GENCTRL_DIV(1)|GCLK_GENCTRL_GENEN);
 /*** (write synchronized) ***/
 while((GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL0));
}

/******************************************************************************************/

/*** 
 *** EXAMPLE 2:
 *** As the device boots using the OSC16M
 *** oscillator running @4MHz,
 *** Here is an example function configuring the device
 *** to use the DFLLULP running @fequency passed in argument.
 *** The function performs the following steps:
 *** 1- Configure and Enable Clock Generator 1 (GCLK1), XOSC32K as source not divided 
 *** 2- Enable DFLLULP Peripheral Channel with GCLK1 as source
 *** 3- configure the DFLLULP output frequency 
 *** 4- Get DFLLULP Division Factor in PL0 from NVM Software Calibration Area
 ***    To be written to DFLLULPCTRL register
 *** 5- Configure DFLLULP with PL0 Division Factor
 *** 6- Switch DFLLULP clock on Main Clock (OSC16M = 4MHz by default after reset) +ERRATA
 *** 7- Disable OSC16M and GCLK0 (if not used).
 ***/

void configure_dfllulp(uint32_t freq)
{
 uint8_t divider;

 /*** Configure and Enable Clock Generator 1 (GCLK1), XOSC32K as source not divided ***/
 GCLK->GENCTRL[1].reg = GCLK_GENCTRL_DIV(1) | CURRENT_32KOSC | GCLK_GENCTRL_GENEN;
 /*** (write synchronized) ***/
 while((GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL1));

 /*** Enable DFLLULP Peripheral Channel with GCLK1 as source ***/
 GCLK->PCHCTRL[OSCCTRL_GCLK_ID_DFLLULP].reg = (GCLK_PCHCTRL_GEN_GCLK1 | GCLK_PCHCTRL_CHEN);

 /*** configure the DFLLULP output frequency ***/
 OSCCTRL->DFLLULPRATIO.reg = OSCCTRL_DFLLULPRATIO_RATIO(freq/CURRENT_32KOSC_FREQ);

 /*** Get DFLLULP Division Factor in PL0 from NVM Software Calibration Area
  *** To be written to DFLLULPCTRL register
  ***/
 divider = ((*((uint32_t *)FUSES_DFLLULP_DIV_PL0_ADDR) & FUSES_DFLLULP_DIV_PL0_Msk) >> FUSES_DFLLULP_DIV_PL0_Pos);

 /*** Configure DFLLULP with PL0 Division Factor ***/ 
 OSCCTRL->DFLLULPCTRL.reg = OSCCTRL_DFLLULPCTRL_DIV(divider)|  
                            OSCCTRL_DFLLULPCTRL_BINSE| 
                            OSCCTRL_DFLLULPCTRL_SAFE|
                            OSCCTRL_DFLLULPCTRL_ENABLE|
                            OSCCTRL_DFLLULPCTRL_RUNSTDBY|
                            OSCCTRL_DFLLULPCTRL_ONDEMAND;
 while(OSCCTRL->DFLLULPSYNCBUSY.bit.ENABLE==1);     /* (write synchronized) */

 /*** Switch DFLLULP clock on Main Clock (OSC16M = 4MHz by default after reset) ***/
 MCLK->CTRLA.bit.CKSEL= 1;

 /*** Applying Workaround ERRATA DFLLULP Clock
  *** Add 6 NOP instructions after writing the CTRAL.CKSEL bit.
  ***/
 __NOP();
 __NOP();
 __NOP();
 __NOP();
 __NOP();
 __NOP();
 while(MCLK->INTFLAG.bit.CKRDY == 0);

 /*** Wait for DFLLULP lock flag ***/
 while((OSCCTRL->STATUS.reg & OSCCTRL_STATUS_DFLLULPLOCK) == 0);

 /*** Disable OSC16M and GCLK0 ***/
 GCLK->GENCTRL[0].bit.GENEN = 0;
 while (GCLK->SYNCBUSY.bit.GENCTRL0==1);    /* (write synchronized) */
 OSCCTRL->OSC16MCTRL.bit.ENABLE = 0;
 while(OSCCTRL->STATUS.bit.OSC16MRDY==1);   
}

/******************************************************************************************/

/*** 
 *** EXAMPLE 3:
 *** Here is an example function configuring the device
 *** to use the DPLL running to run at Full speed (32 MHz)
 *** The function performs the following steps:
 *** 1- Initialize the XOSC32K Oscillator (32 kHz On-board Crystal)
 *** 2- Switch Performance level from PL0 (Default after reset) to PL2
 *** 3- Configure Flash Wait States based on next Main Clock frequency (32 MHz <-> 2 WS)
 *** 4- Initialize the DPLL to generate 64 MHz Set Main clock to DPLL/2
 ***/

void Device_Fullspeed(void)
{
 /***
  *** Set XOSC32K Startup time 
  *** (see "Start-Up Time for 32KHz External Crystal Oscillator" 
  *** table from datasheet)
  *** Enable 32.768kHz output
  *** Enable Crystal Oscillator mode
  *** Enable XOSC32K
  ***/

 OSC32KCTRL->XOSC32K.reg =(OSC32KCTRL_XOSC32K_STARTUP(0x3)| 
                           OSC32KCTRL_XOSC32K_EN32K       |
                           OSC32KCTRL_XOSC32K_XTALEN      | 
                           OSC32KCTRL_XOSC32K_ENABLE);

 /*** Write Synchronized ***/
 while(!(OSC32KCTRL->STATUS.bit.XOSC32KRDY));     

 /*** 
  *** Switch Performance level 
  *** from PL0 (Default after reset) to PL2 
  ***/
 PM->PLCFG.bit.PLSEL = 2;
 while(PM->INTFLAG.bit.PLRDY == 0);

 /*** 
  *** Configure Flash Wait States based 
  *** on next Main Clock frequency (32 MHz <-> 2 WS) 
  ***/
 NVMCTRL->CTRLB.bit.RWS = 2;

 /*** 
  *** Set DPLL Ratio to generate 64MHz clock 
  *** (64MHz/32.768kHz=1953.125 
  *** => LDR=1952/LDRFRAC=2 <=> 
  *** 64MHz=32.768*(LDR+1+LDRFRAC/16) 
  ***/
 OSCCTRL->DPLLRATIO.reg = (OSCCTRL_DPLLRATIO_LDR(1952)| 
 OSCCTRL_DPLLRATIO_LDRFRAC(2));

 /*** Write Synchronized ***/
 while((OSCCTRL->DPLLSYNCBUSY.bit.DPLLRATIO));

 /*** Disable On-demand ***/
 OSCCTRL->DPLLCTRLA.bit.ONDEMAND = 0;

 /*** 
  *** Set Lock time as automatic 
  ***and reference clock to XOSC32KHz 
  ***/
 OSCCTRL->DPLLCTRLB.reg = (OSCCTRL_DPLLCTRLB_LTIME(0)|
                           OSCCTRL_DPLLCTRLB_REFCLK_XOSC32K);

 /*** Enable DPLL ***/
 OSCCTRL->DPLLCTRLA.bit.ENABLE = 1;
 while(OSCCTRL->DPLLSYNCBUSY.bit.ENABLE);

 /*** Wait for DPLL lock flag ***/
 while(!OSCCTRL->DPLLSTATUS.bit.LOCK);

 /*** 
  *** Set Generic clock 0 
  ***(MAIN clock for CPU and synchronous clock) 
  ***/
 GCLK->GENCTRL[0].reg = (GCLK_GENCTRL_SRC(0x07)|
                         GCLK_GENCTRL_DIV(2)|
                         GCLK_GENCTRL_GENEN);

 /*** Write Synchronized ***/
 while((GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL0));

 /***
  *** Set Generic clock 1 (GCLK1), 
  *** XOSC32K as source not divided 
  ***/
 GCLK->GENCTRL[1].reg = (GCLK_GENCTRL_SRC_XOSC32K|
                         GCLK_GENCTRL_DIV(1)|
                         GCLK_GENCTRL_GENEN);

 /*** Write Synchronized ***/
 while((GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL0));    
}