SAM L10/L11 Oscillators Controller (OSCCTRL)

Last modified by Microchip on 2023/11/15 17:36

Overview

The Oscillators Controller (OSCCTRL) provides a user interface to the XOSC, OSC16M, DFLLULP, and FDPLL96M. It is possible to enable, disable, calibrate, and monitor the OSCCTRL oscillators through the interface registers. All oscillator statuses are collected in the Status (STATUS) register. They can additionally trigger interrupts upon status changes via the INTENSET, INTENCLR, and INTFLAG registers.

I/O lines are configured by OSCCTRL when XOSC is enabled and need no user configuration.

The OSCCTRL can continue to operate in any Sleep mode where the selected source clock is running. The OSCCTRL interrupts can be used to wake up the device from Sleep modes. The events can trigger other operations in the system without exiting Sleep modes.

Features

  • 0.4-32 MHz Crystal Oscillator (XOSC)
  • Tunable gain control
  • Programmable start-up time
  • Crystal or external input clock on XIN I/O
  • Clock failure detection with safe clock switch
  • Clock failure event output
  • 16 MHz Internal Oscillator (OSC16M)
    • Fast startup
    • 4/8/12/16 MHz output frequencies available
  • Ultra Low-Power Digital Frequency Locked Loop (DFLLULP)
    • Operates as a frequency multiplier against a known frequency in Closed Loop mode
    • Optional frequency dithering
  • Fractional Digital Phase Locked Loop (FDPLL96M)
    • 32 MHz to 96 MHz output frequency
    • 32 kHz to 2 MHz reference clock
    • A selection of sources for the reference clock
    • Adjustable proportional integral controller
    • Fractional part used to achieve 1/16th of reference clock step

Block Diagram

SAM L10 Oscillator Controller

The OSCCTRL gathers controls for all device oscillators and provides clock sources to the Generic Clock Generator Controller (GCLK). The available clock sources are XOSC, OSC16M, DFLLULP, and FDPLL96M.

The 0.4-32 MHz crystal must be connected between the XIN and XOUT pins, along with any required load capacitors.

The OSCCTRL bus clock (CLK_OSCCTRL_APB) can be enabled and disabled in the Main Clock Controller (MCLK). The control logic uses the oscillator output, which is also asynchronous to the user interface clock (CLK_OSCCTRL_APB). Due to this, writes to certain registers will require synchronization between the clock domains.

Back to top

Principle of Operation

XOSC, OSC16M, and FDPLL96M are configured via OSCCTRL registers. Through this interface, the oscillators are enabled, disabled, or have their calibration values updated. The Status register gathers different status signals coming from the oscillators controlled by the OSCCTRL. The status signals can be used to generate system interrupts, and in some cases wake the system from Sleep mode.

​Refer to the "SAM L10/L11 Clock System" and "SAM L10/L11 Generic Clock Controller (GLCK)pages for more details.

​Refer to the "OSCCTRL – Oscillators Controller" chapter from the product datasheet for more details.

Back to top

Code Examples

/***
 *** 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));    
}

Back to top