Using the internal oscillator (not ASF)

Go To Last Post
32 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I found this post regarding the clocks (for the D21, but probably applicable to the D09/D10/D11):

 

   http://borkedlabs.com/2014/08/21...

 

Quite clear and close to my needs. But it is ASF. Does someone have the equivalent code using registers? Executive summary: use the internal oscillator, multiply it into GCLK0 and make it the mcu main clock.

 

To document this for future users, I intend to transform the code into a function which takes the desired CPU main clock as a parameter and publish it here (I remember having seen something similar somewhere, probably in AtmelStart code). Similar functions could exist for other oscillators.

 

 

For those that need to have an understanding of the clock circuitry, this is mine (please correct me if I'm wrong):

 

There are 2 PLLs (=clock multipliers that take a clock reference and multiply it into something much higher):

- the DFLL uses a reference of about 32kHz and generates anything up to 48MHz

- the DPLL uses a reference up to 2MHz and generates anything above 48MHz (up to 96Mhz)

 

To configure and use a PLL one must:

- oscillator options:

  - 8MHz from the internal oscillator (OSC8M) <- use this unless you have special needs

  - 400kHz to 32MHz, normally 12Mhz, from a crystal (XOSC)

  - 32768Hz from a crystal (XOSC32K) or internal oscillator (OSC32K)

  - 32kHz from the "low power" RTC clock (OSCULP32K)

- make a (prescaled) oscillator available as a GCLK (here OSC8M divided by 8 as GCLK1)

  - must be 32kHz for the DFLL and 1MHz for the DPLL
- connect the input of a PLL to this GCLK (here the DPLL to GCLK1)

- make the PLL output available as a second GCLK, normally GCLK0 for the main clock

- divide any GCLK down by 2/4/8/etc to create secondary GCLKs, say GCLK2, GCLK3, etc (optional)

- connect the CPU main clock to GCLK0 (don't forget to also use a wait state for high clocks)

- connect peripherals to the desired GCLK

This topic has a solution.

Last Edited: Fri. Nov 27, 2015 - 04:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

atmellian wrote:

- the DFLL uses a reference of about 32kHz and generates anything up to 48MHz

No this is not correct. The DFLL needs a ~32KHz reference clock however the output frequency must be 48MHz.

- the DPLL uses a reference up to 2MHz and generates anything above 48MHz (up to 96Mhz)

Correct.

 

Here is some sample code to set up the DFLL to run at 48MHz using and OSC8M clock (for GCLK0 for the CPU and buses).

  CLOCKS_OSC8M_Init(SYSCTRL_OSC8M_PRESC_0_Val);
  CLOCKS_DFLL_Init();

Here are the routines

/**************************************************************************
Object: Change the OSC8M oscillator divider
Parameters: none
Return: Nothing
**************************************************************************/
void CLOCKS_OSC8M_Init(uint8_t divider)
{
#define MY_SYSCTRL_OSC8M_MASK ((uint32_t)0xCFFF0002ul)
  /* change the clock frequency from 1MHz to 8MHz */
  SYSCTRL->OSC8M.reg = (MY_SYSCTRL_OSC8M_MASK & SYSCTRL->OSC8M.reg) |
                       SYSCTRL_OSC8M_ONDEMAND |
                       SYSCTRL_OSC8M_RUNSTDBY |
                       divider;
}
/**************************************************************************
Object: Change the flash wait states to 1, 
Initialize the DFLL to run at 48MHz. Using the OSC8M clock which must 
have its divider set to 0 (running at 8MHz). GCLK1 is used to create the 32KHz
clock to be the reference for the DFLL. Then configure and enable the DFLL.
Choose GCLK0 source to be DFLL for the CPU and its buses 
Parameters: none
Return: Nothing
**************************************************************************/
void CLOCKS_DFLL_Init(void)
{
  /* configuration of the NVM CTRLB register and set the flash wait states */
  NVMCTRL->CTRLB.bit.RWS = 1;

  /* use the OSC8M as the reference clock for the DFLL, the reference input needs
   to be less than or equal to 32KHz. 8MHz/250 = 32KHz */
  GCLK_GENDIV_Type gendiv =
  {
    .bit.DIV = 250,
    .bit.ID = GCLK_GENERATOR_1,
  };
  GCLK->GENDIV.reg = gendiv.reg;

  GCLK_GENCTRL_Type genctrl =
  {
    .bit.RUNSTDBY = false,  /* Run in Standby */
    .bit.DIVSEL = false,    /* Divide Selection */
    .bit.OE = false,        /* Output Enable to observe on a port pin */
    .bit.OOV = false,       /* Output Off Value */
    .bit.IDC = true,        /* Improve Duty Cycle */
    .bit.GENEN = enable,
    /* Select which generic clock source */
    .bit.SRC = GCLK_SOURCE_OSC8M,
    /* Select which generic clock generator to configure */
    .bit.ID = GCLK_GENERATOR_1,
  };
  GCLK->GENCTRL.reg = genctrl.reg;
  /* wait for synchronization to complete */
  while (GCLK->STATUS.bit.SYNCBUSY);

  /* */
  GCLK_CLKCTRL_Type clkctrl =
  {
    /* The generic clock and the associated generic clock generator and division factor are locked */
    .bit.WRTLOCK = false,
    /* The generic clock is enabled */
    .bit.CLKEN = true,
    /* Select Generic Clock Generator */
    .bit.GEN = GCLK_GENERATOR_1,
    /* Select the Generic Clock */
    .bit.ID = SYSCTRL_GCLK_ID_DFLL48,
  };
  GCLK->CLKCTRL.reg = clkctrl.reg;

  /* Workaround for errata 9905 */
  SYSCTRL->DFLLCTRL.bit.ONDEMAND = 0;
  /* wait for the DFLL clock to be ready */
  while (SYSCTRL->PCLKSR.bit.DFLLRDY == false);

  SYSCTRL_DFLLCTRL_Type dfllctrl =
  {
    .bit.WAITLOCK = false,  /* Output clock when DFLL is locked */
    .bit.BPLCKC = false,    /* Bypass coarse lock is enabled */
    .bit.QLDIS = true,      /* Quick Lock is disabled */
    .bit.CCDIS = false,     /* Chill Cycle is disabled */
    .bit.ONDEMAND = false,  /* The oscillator is enabled when a peripheral is requesting the oscillator to be used as a clock source */
    .bit.RUNSTDBY = false,  /* The oscillator is not stopped in standby sleep mode */
    .bit.USBCRM = false,    /* USB Clock Recovery Mode is enabled. */
    .bit.LLAW = false,      /* Locks will be lost after waking up from sleep modes if the DFLL clock has been stopped */
    .bit.STABLE = false,    /* FINE calibration register value will be fixed after a fine lock */
    .bit.MODE = true,       /* The DFLL operates in closed-loop operation. */
    .bit.ENABLE = false,    /* The DFLL oscillator is enabled. */
  };
  SYSCTRL->DFLLCTRL.reg = dfllctrl.reg;
  /* wait for the DFLL clock to be ready */
  while (SYSCTRL->PCLKSR.bit.DFLLRDY == false);

  /* get the coarse and fine values stored in NVM */
  uint32_t coarse = (*(uint32_t *)(0x806024) >> 26);
  uint32_t fine = (*(uint32_t *)(0x806028) & 0x3FF);

  SYSCTRL_DFLLVAL_Type dfllval =
  {
    .bit.COARSE = coarse,
    .bit.FINE = fine,
  };
  SYSCTRL->DFLLVAL.reg = dfllval.reg;

  SYSCTRL_DFLLMUL_Type dfllmul =
  {
    .bit.MUL = 1500, /* 32KHz * 1500 = 48MHz */
    .bit.CSTEP = (coarse >> 1), /* must be 50% or less of coarse value */
    .bit.FSTEP = (fine >> 1), /* must be 50% or less of fine value */
  };
  SYSCTRL->DFLLMUL.reg = dfllmul.reg;

  /* enable DFLL */
  SYSCTRL->DFLLCTRL.bit.ENABLE = true;
  /* wait for DFLL closed loop lock */
  while (SYSCTRL->PCLKSR.bit.DFLLLCKF == false);

  /* Configure GCLK 0 (CPU clock) to run from the DFLL */
  gendiv.bit.DIV = 1; /* no division */
  gendiv.bit.ID = GCLK_GENERATOR_0;
  GCLK->GENDIV.reg = gendiv.reg;

  genctrl.bit.RUNSTDBY = false; /* Run in Standby */
  genctrl.bit.DIVSEL = false;   /* Divide Selection */
  genctrl.bit.OE = false;       /* Output Enable */
  genctrl.bit.OOV = false;      /* Output Off Value */
  genctrl.bit.IDC = true;       /* Improve Duty Cycle */
  genctrl.bit.GENEN = enable;
  /* Generic clock source */
  genctrl.bit.SRC = GCLK_SOURCE_DFLL48M;
  genctrl.bit.ID = GCLK_GENERATOR_0;
  GCLK->GENCTRL.reg = genctrl.reg;
	/* wait for synchronization to complete */
  while (GCLK->STATUS.bit.SYNCBUSY);
}

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Chris, thanks for the correction and for making this a worthwhile topic.

 

Your example uses the DFLL, like almost every other example out there, except the one I referenced. Is there some reason to avoid the DPLL?

 

Jan

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

cwunder wrote:

atmellian wrote:

- the DFLL uses a reference of about 32kHz and generates anything up to 48MHz

No this is not correct. The DFLL needs a ~32KHz reference clock however the output frequency must be 48MHz.

 

So if I need to an "off" clock like 20MHz then I *must* use the DPLL, for 48/20 is not an integer?

 

Hum. My application in particular needs a 14.4 or 28.8Mhz clock to satisfy the constraint of a 14kHz PWM in 8-bit PWM mode. That means it needs DPLL only and a frequency of about 57.6Mhz.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

A reason to use the FDPLL is mainly for the TCC in my opinion.

 

You can use this for the CPU but you need to use a divider if above 48MHz. Generally for performance you set the device to operated at its max frequency which the DFLL does (48MHz).

 

If you only want to use the internal clocks and want to run the CPU at 28.8MHz then using the FDPLL makes sense. You need to set the FDPLL output to 57.6MHz then you need to divide it by 2 so the CPU is at 28.8MHz. Note: you can use the FDPLL for the timer peripheral and DFLL for the CPU. It is a matter of selecting and configuring the GCLK's for the desired purpose.

 

Here is some very basic code (no waiting for synchronization) that sets the FDPLL to 96MHz it assumes that input clock is 32KHz. You can modify it so the value for LDR is 1799. This should produce the 57.8MHz and use then add the GCLK0 setting from above additionally using a divide by 2. 

/**************************************************************************
Object: Configures the FDPLL to run at 96MHz using the Clock source of GCLK1
that had previously been configured to output a clock of 32KHz.
Parameters: none
Return: Nothing
**************************************************************************/
void CLOCKS_FDPLL_Init(void)
{
	GCLK_CLKCTRL_Type clkctrl =
	{
		.bit.ID = SYSCTRL_GCLK_ID_FDPLL,
		.bit.GEN = GCLK_GENERATOR_1,
		.bit.CLKEN = true,
		.bit.WRTLOCK = false,
	};
	GCLK->CLKCTRL.reg = clkctrl.reg;

  SYSCTRL_DPLLRATIO_Type dpllratio =
  {
    .bit.LDRFRAC = 0,
    .bit.LDR = 2999,
  };
  SYSCTRL->DPLLRATIO.reg = dpllratio.reg;

  SYSCTRL_DPLLCTRLB_Type dpllctrlb =
  {
    .bit.DIV = 0, 		/* set the CLK_DPLL_REF1 clock division factor */
    .bit.LBYPASS = 0, /* select Lock Bypass mode */
    .bit.LTIME = 0, 	/* select the DPLL lock time-out */
    .bit.REFCLK = 2,	/* select the DPLL clock reference */
    .bit.WUF = 0, 		/* select if output is gated during lock time */
    .bit.LPEN = 0, 		/* Low-Power Enable */
    .bit.FILTER = 0, 	/* select Proportional Integral Filter mode */
  };
  SYSCTRL->DPLLCTRLB.reg = dpllctrlb.reg;

  SYSCTRL_DPLLCTRLA_Type dpllctrla =
  {
    .bit.ONDEMAND = 0,
    .bit.RUNSTDBY = 0,
    .bit.ENABLE = 1,
  };
  SYSCTRL->DPLLCTRLA.reg = dpllctrla.reg;
}
Last Edited: Fri. Nov 27, 2015 - 10:44 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I got both the FLL and the PLL working with your code.

 

The PLL code only omitted the part from the FLL code that sets up GCLK1 (with 32kHz from OSC8M @8MHz), which is the source clock the PLL code needs.

 

This part:

    // setup GENDIV divisor of OSC8M to be 8M/250=32k
    GCLK_GENDIV_Type gendiv =
    {
        .bit.DIV = 250,       // needs DIVSEL=0
        .bit.ID = 1,          // GCLK_GENERATOR_1
    };
    GCLK->GENDIV.reg = gendiv.reg;
    // setup GENCTRL
    GCLK_GENCTRL_Type genctrl =
    {
        .bit.RUNSTDBY = 0,        // Run in Standby
        .bit.DIVSEL = 0,          // DIVGEN.DIV selection: 0=linear, 1=powers of 2
        .bit.OE = 0,              // Output Enable: observe clock on a port pin
        .bit.OOV = 0,             // Output Off Value: clock off pin value
        .bit.IDC = 1,             // Improve Duty Cycle
        .bit.GENEN = 1,           // enable this GCLK
        // select GCLK source
        .bit.SRC = GCLK_SOURCE_OSC8M,
        // select GCLK to output on
        .bit.ID = 1,              // GCLK_GENERATOR_1
    };
    GCLK->GENCTRL.reg = genctrl.reg;
    // wait for synchronization to complete
    while (GCLK->STATUS.bit.SYNCBUSY);

 

Newbies like me: put it before the PLL code.

 

Chris, your code is invaluable because of its style. I hope Atmel is listening.

 

Except for using those pesky 70's style /*comments*/ ;P

 


 

Edit: my FAE assures me it is better to use OSC32K, so the code becomes: 

void make_glck1_32kHz()
{
    // setup GENDIV divisor
    GCLK_GENDIV_Type gendiv =
    {
        .bit.DIV = 1,          // <==== changed
        .bit.ID = 1,          // GCLK_GENERATOR_1
    };
    GCLK->GENDIV.reg = gendiv.reg;
    // setup GENCTRL
    GCLK_GENCTRL_Type genctrl =
    {
        .bit.RUNSTDBY = 0,        // Run in Standby
        .bit.DIVSEL = 0,          // Divide Selection (0=linear, 1=powers of 2)
        .bit.OE = 0,              // Output Enable to observe on a port pin
        .bit.OOV = 0,             // Output Off Value
        .bit.IDC = 1,             // Improve Duty Cycle
        .bit.GENEN = 1,           // enable this GCLK
        // select GCLK source
        .bit.SRC = GCLK_SOURCE_OSC32K,  // <==== changed
        // select GCLK1 to output on
        .bit.ID = 1,              // GCLK_GENERATOR_1
    };
    GCLK->GENCTRL.reg = genctrl.reg;
    // wait for synchronization to complete
    while (GCLK->STATUS.bit.SYNCBUSY);
}

I observed a difference of 3% in the clock (the MCU becomes faster). Quite a lot. 

Last Edited: Fri. Jul 15, 2016 - 05:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sorry for necroing this old post , could you please tell me which MCU you used this code for ?

 

i am trying to understand this part of the code...

 

  /* get the coarse and fine values stored in NVM */
  uint32_t coarse = (*(uint32_t *)(0x806024) >> 26);
  uint32_t fine = (*(uint32_t *)(0x806028) & 0x3FF);

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

All of them? (At least all SAMD's seem to have the Software Calibration Area @ 806020).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am aware that SAMD ones have the calibration area. Their calibration areas are organized differently though. the SAMD10 calibration area is organized differently than the SAMD21 etc... 

 

I believe i have figured out what that piece of code does , i just wanted to verify what i am doing is correct. that is why i am interested to know what MCU that code was written for so i can see what piece of the calibration area was used in that code.

if you look at the code , you will see that the code is reading a OSC fine calibration value. Looking in the SAMD10 or SAMD21 datasheet , the calibration area holds no value for fine calibration , only coarse calibration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I see. So the code must be for D20 or D21 then.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

did you look at the table 10.5 in the samd21 datasheet ?

 

the code says right shift 26 bits .  looking at the datasheet of the samd21/10/20   ADC LINEARITY value starts at bit 27.

 

The code should be right shifting 57 bits and then mask it with 0b01111111.

 

You are right about the samd20 though , it has indeed a fine calibration value in the calibration area.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Coarse seems to be 63:58 everywhere. So, we need to shift by 58. Which is what the above code does. Because 0x806024 is the second uint32_t of the calibration area. 58-32=26.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think you have to read the whole row in one piece which is 32 bits wide and it starts at 0x806024  and then right shift 57 bits.

 

Yesterday i wrote a code where i read the data from 0x806024  and it was the same as as the OTP4_WORD1 value in the fuses window . looking at the bits 63:58 the value extracted was the same as the coarse value in the fuses window.

 

so the code should be like...

 

uint32_t coarse = (*(uint32_t *)(0x806024) >> 57);
coarse = coarse & 0b01111111;

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No. (uint32_t is 32 bits, so shifting it by 57 doesn't make sense).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

i gotta look at my code when im back home , my recollection seems to be playing a trick on me . ill report back.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ok , back .

 

you were right.

 

my lack of knowledge on how to address memory areas was getting me all confused. the datasheet confused me a little as well though.

 

now i understand that reading from an address returns 4 bytes from that offset.

 

so...

 

0x806020  = otp4 word 0

0x806024  = otp4 word 1 (location of DFFL48M coarse cal starting at bit 26)

0x806028  = otp4 word 2 (according to fuse window DFFL48M fine cal , according to datasheet of SAMD10 nothing there)

 

0x806030  = temp log 0

0x806034  = temp log 1

 

now i understand how to read from the memory yes

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Looking at the datasheet of the SAMD20 , it says here that the only REF the DFLL48M can accept is from GCLK0.

In the example of cwunder , OSC8 is sent to GCLK1 , divided down to 32KHz , sent to the DFLL48M and multiplied up , then sent to GCLK0 to feed the CPU.

According to the datasheet that should not be possible , can anyone explain this to me ?

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The DFLL48M REF is not a GCLKn itself, but rather the output of the Generic Clock Multiplexer. The Generic Clock Multiplexer for DFLL48M is GCLK_DFLL48M_REF=0x00.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I know that part , what confuses me is this :  "Generic Clock 0 serves as the clock source for the DFLL48M clock input (when
multiplying another clock source)."

 

If you look at the block diagram , you can see that the output of GCLK0  is pointing to DFLL48M .

 

If what is shown in the datasheet is correct , it would be quiet silly . It would mean one could only run the CPU at 32KHz if one wanted to use the DFLL48M in closed loop mode , because the DFLL48M only takes the reference from GCLK0.

 

 

Update :

 

so i am looking at page 94 of the SAMD20 datasheet and it says : "Generic Clock 0 serves as the clock source for the DFLL48M clock input (when
multiplying another clock source)."

 

On page 164 it says : "1. Enable and select a reference clock (CLK_DFLL48M_REF). CLK_DFLL48M_REF is Generic Clock
Channel 0 (GCLK_DFLL48M_REF)
.
"

 

so what you said in your last comment would be along the lines of what it says on page 164.

 

 

I believe they have a bunch of errors and discrepancies in their datasheets.

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"GCLK0  is pointing to DFLL48M" - I don't see GCLK0 on the diagram. Did you mean "GCLK Generator 0"? The way I see it, "GCLK Generator 0" is pointing to that dark vertical bar, which I understand as any "GCLK Generator" can be selected as a clock source for any "GCLK Multiplexer".

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think you have have a point there. i was thinking that the multiplexers are all hardwired to the generic clocks (GCLK0-->MUX0 , GCLK1-->MUX1 ,...).

I indeed overlooked the thick vertical line.

So if that is the case , then only MUX0 is hardwired to DFLLM48M and GCLK0 is hardwired to the CPU

I had no idea the clock system is so modular.

 

Thanks  ezharkov   , ill check that out.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Those Clock Multiplexors 0-y are basically the same things as Generic Clock Selection IDs. Therefore, MUX0 is not really hardwired to DFLL48M - it is DFLL48M (well, GCLK_DFLL48M_REF, see "Generic Clock Selection ID" table in the datasheet).

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

True .

So , to make sure i absolutely understand it right , it means that DFLL48M only takes a REF signal from MUX0 , but MUX0 can also be used just like the other MUX ?

 

 

example 1:

 

32KHz --> GCLK1 --> MUX0 --> DFLL48M --> GCLK0 --> CPU

 

example 2:

 

32KHz --> GCLK1 --> MUX1 --> DFFL48M --> GCLK0 --> CPU

 

 

Would both examples work or only example 1 ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Only 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

While this post is most popular, I am posting my solution for ATSAML21J18B Xplained Pro B board for DFLL running at 48MHz using XOSC32 in closed-loop mode. There is not much information/post about ATSAML21 on the web and definitely not any on clock configuration without using ASF. I hope not everyone wastes their time like I did to figure out how to configure clock on this part.

 

DPLL configuration is very similar to this one except the following, 1) Use OSCM16M as an input to GCLKGEN[1] 2) use PCHCTRL[1] as a reference channel 3) Change the source for GCLK_GENCTRL[0] to DPLL and 4) replace DFLL configuration below with the DPLL config mentioned in this post above. 

 

Also, it is important to configure the power domain to PD2 when running the CPU clock at more than 12MHz. Please refer to the datasheet for the Frequency limit for each PD (default PD0 is enabled). 

 

void CLOCKS_XOSC32K_Init(uint8_t divider)
{
	/* Configure OSC16M and Enable */
	OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_STARTUP(0x4) | OSC32KCTRL_XOSC32K_EN32K
								| OSC32KCTRL_XOSC32K_XTALEN | OSC32KCTRL_XOSC32K_ENABLE;
	OSC32KCTRL->XOSC32K.reg |= OSC32KCTRL_XOSC32K_WRTLOCK;

	/* Wait for OSC16M to be ready */
	while(OSC32KCTRL->STATUS.bit.XOSC32KRDY == 0);

}

void CLOCKS_DFLL_Init(void)
{

/*
     * DFLL is configured to run at 48MHz. 
     * XOSC32K (32KHz) --> GCLK_GEN[1] (32KHz) --> DFLL (48MHz) --> GCLK_GEN[0] (48MHz) --> CPU_CLK (48MHz)
     * */  

	/* Configuration of the NVM CTRLB register and set the flash wait states */
	NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_RWS(2) ;

/* IMPORTANT: Turn on PD2 if running at more than 12MHz or else the CPU will endup in Hard_Fault() */
if ((PM->PLCFG.reg & PM_PLCFG_PLDIS) == 0)
    {
        /* Clear performance level status */
        PM->INTFLAG.reg = PM_INTFLAG_PLRDY;

        /* Switch performance level */
        PM->PLCFG.reg = PM_PLCFG_PLSEL_PL2;

        /* Waiting performance level ready */
        while (!PM->INTFLAG.reg);
    }

	/* Configure and Enable Clock Generator 1 */
	/* Clock Source should be running before enabling this generator */
	/* GCLK_GENCTRL_OE is to enable GCLK_IO output */
	GCLK->GENCTRL[1].reg = GCLK_GENCTRL_DIV(1) | GCLK_GENCTRL_IDC | GCLK_GENCTRL_SRC_XOSC32K | GCLK_GENCTRL_OE |GCLK_GENCTRL_GENEN;

	/* Wait for Generator 1 to be in sync */
	/* Bit is cleared when in sync */
	while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL1);

	// Peripheral Channel Control
	// - Set Generator 1 as a source for the channel to be used as a source to DFLL
	// - Enable peripheral channel
	// - Write Lock
	GCLK->PCHCTRL[0].reg = GCLK_PCHCTRL_GEN_GCLK1 | GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_WRTLOCK;

    // ------------------------------------------//
    // DFLL Configuration
    // ------------------------------------------//

	/* Workaround for errata 9905 */
	OSCCTRL->DFLLCTRL.reg &= ~OSCCTRL_DFLLCTRL_ONDEMAND;

	/* Wait for DFLL WriteSync before writing to SLFFCTRL, DFLLMUL & DFLLVAL registers */
	/* DFLL48M OSCCTRL->STATUS.DFLLRDY bit has to read 1 for read write operation after DFLL is enabled */
	while((OSCCTRL->STATUS.reg & OSCCTRL_STATUS_DFLLRDY) == 0);				// DFLL48M OSCCTRL->STATUS.DFLLRDY bit has to read 1 for read write operation after DFLL is enabled

	#define NVM_DFLL_COARSE_POS 26

	/* get the coarse and fine values stored in NVM */
	uint32_t coarse = *((uint32_t *)(NVMCTRL_OTP5)) >> NVM_DFLL_COARSE_POS;
	//uint32_t coarse = *((uint32_t *)(NVMCTRL_OTP5)) >> NVM_DFLL_COARSE_POS;
	uint32_t fine = (*(uint32_t *)(0x806028) & 0x3FF);

	// 32kHz * 1500 = 48MHz
	// CSTEP and FSTEP must be 50% or less
	OSCCTRL->DFLLMUL.reg = OSCCTRL_DFLLMUL_MUL(1465) | OSCCTRL_DFLLMUL_CSTEP(1) | OSCCTRL_DFLLMUL_FSTEP(1);

	/* Wait for DFLL WriteSync */
	//while((OSCCTRL->STATUS.reg & OSCCTRL_STATUS_DFLLRDY) == 0);

	/* DFLLVAL is read only when closed-loop mode is on */
	//OSCCTRL->DFLLVAL.reg = OSCCTRL_DFLLVAL_COARSE(coarse) | OSCCTRL_DFLLVAL_FINE(fine);

	/* Wait for DFLL WriteSync before writing to SLFFCTRL, DFLLMUL & DFLLVAL registers */
	/* DFLL48M OSCCTRL->STATUS.DFLLRDY bit has to read 1 for read write operation after DFLL is enabled */
	while((OSCCTRL->STATUS.reg & OSCCTRL_STATUS_DFLLRDY) == 0);

	/* enable DFLL */
	OSCCTRL->DFLLCTRL.reg |= OSCCTRL_DFLLCTRL_MODE | OSCCTRL_DFLLCTRL_QLDIS;		

	/* Wait for DFLL WriteSync before writing to SLFFCTRL, DFLLMUL & DFLLVAL registers */
	/* DFLL48M OSCCTRL->STATUS.DFLLRDY bit has to read 1 for read write operation after DFLL is enabled */
	while((OSCCTRL->STATUS.reg & OSCCTRL_STATUS_DFLLRDY) == 0);

	/* enable DFLL */
	OSCCTRL->DFLLCTRL.reg |= OSCCTRL_DFLLCTRL_ENABLE;

    // ------------------------------------------//
    // Clock Gen 0 Configuration 
    // ------------------------------------------//
    
	/* Configure GCLK 0 (CPU clock) to run from the DFLL */
	/* GCLK_GENCTRL_OE is to enable GCLK_IO output */
	GCLK->GENCTRL[0].reg = GCLK_GENCTRL_DIV(1) | GCLK_GENCTRL_IDC | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_OE | GCLK_GENCTRL_GENEN;

	/* Wait for Generator 0 to be in sync */
	/* Bit is cleared when in sync */
	while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL0);

}

 

Regards,

Rishit

 

Last Edited: Mon. Nov 19, 2018 - 06:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
while((GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL1) == 1);

 

while((GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL0) == GCLK_SYNCBUSY_GENCTRL0);

The first loop will never wait since the result of the & is 0 or GCLK_SYNCBUSY_GENCTRL1 (which is not 1), the 2:nd one is ok but you might as well not compare at all when waiting for a bit to clear.

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for pointing that out. Corrected above.

 

Regards,

Rishit

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How to configure FDPLL to output 16MHz from 12MHzOSCM source? Changing divider values not helping.  Heres my code, I am configuring OSC16 at 4MHz and dividing it by 2 in GCLK GEN1. Using that as an input to DPLL and multiplying it by 7+1. But DPLL keep waiting for lock and CLKRDY bits to set. Same thing when OSC16 is configured at 12MHz and dividing it by 6. Both of these configurations bring GCLKGEN1 to 2MHz. Latter configuration works alright when I am multiplying DPLL Int by 23 to run CPU at 48MHZ. 

 

What am I missing here? 

 

Thanks in advance. 

 


static void CLOCKS_OSC16M_12MHz_Init(const uint8_t divider)
{
	/* Various bits in the INTFLAG register can be set to one at startup.
	   This will ensure that these bits are cleared */
	//OSCCTRL->INTFLAG.reg = OSCCTRL_INTFLAG_DFLLRDY;
	//SUPC->INTFLAG.reg = SUPC_INTFLAG_BOD33RDY | SUPC_INTFLAG_BOD33DET;
	
	/* Configure OSC16M and Enable */
	OSCCTRL->OSC16MCTRL.reg =	OSCCTRL_OSC16MCTRL_ONDEMAND | OSCCTRL_OSC16MCTRL_RUNSTDBY
								| OSCCTRL_OSC16MCTRL_FSEL_12| OSCCTRL_OSC16MCTRL_ENABLE;
	
	/* Wait for OSC16M to be ready */
	while(OSCCTRL->STATUS.bit.OSC16MRDY == 0);
	
	/* This shouldnt be enabled when DPLL is running at 48MHz */
	/* The code block is here for testing purpose, enable this whn DPLL is not running and want to configure CPU to run at 12MHz */
	//GCLK->GENCTRL[0].reg = GCLK_GENCTRL_DIV(1) | GCLK_GENCTRL_IDC | GCLK_GENCTRL_SRC_OSC16M /*| GCLK_GENCTRL_OE*/ | GCLK_GENCTRL_GENEN;
	//while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL0);

}

static void CLOCKS_DPLL_Init()
{
	/*
	 * DPLL96M is configured to run at 48MHz. 
	 * OSC16M (12MHz) --> GCLK_GEN[1] (12MHZ) --> DPLL (48MHz) --> GCLK_GEN[0] (48MHz) --> CPU_CLK (48MHz)
	 * */  
	
	/* Configuration of the NVM CTRLB register and set the flash wait states */
	NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_RWS(2) ;
	
	set_power_domain(2);
	
	// ------------------------------------------//
	// Clock Gen 1 Configuration
	// ------------------------------------------//
	
	/* Configure and Enable Clock Generator 1 */
	/* Clock Source should be running before enabling this generator */
	/* GCLK_GENCTRL_OE is to enable GCLK_IO output */
	GCLK->GENCTRL[1].reg = GCLK_GENCTRL_DIV(6) | GCLK_GENCTRL_IDC | GCLK_GENCTRL_SRC_OSC16M | GCLK_GENCTRL_OE | GCLK_GENCTRL_GENEN;

	/* Wait for Generator 1 to be in sync */
	/* Bit is cleared when in sync */
	while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL1);
	
	// Peripheral Channel Control
	// - Set Generator 1 as a source for the channel to be used as a source to DPLL
	// - Enable DPLL peripheral channel
	// - Write Lock
	GCLK->PCHCTRL[1].reg = GCLK_PCHCTRL_GEN_GCLK1 | GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_WRTLOCK;
	
	while(GCLK->PCHCTRL[1].bit.CHEN != 1);
	
	// ------------------------------------------//
	// DPLL Configuration
	// ------------------------------------------//
	
	while(OSCCTRL->DPLLSYNCBUSY.bit.DPLLRATIO == 1);
	
	OSCCTRL->DPLLRATIO.reg |= OSCCTRL_DPLLRATIO_LDR(7) | OSCCTRL_DPLLRATIO_LDRFRAC(0);
	
	while(OSCCTRL->DPLLSYNCBUSY.bit.DPLLRATIO == 1);
	
	OSCCTRL->DPLLCTRLB.reg |= OSCCTRL_DPLLCTRLB_DIV(0) |  OSCCTRL_DPLLCTRLB_LTIME(0) | OSCCTRL_DPLLCTRLB_REFCLK(2) | OSCCTRL_DPLLCTRLB_FILTER(0);
	
	while(OSCCTRL->DPLLSYNCBUSY.bit.ENABLE == 1);
	
	OSCCTRL->DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;
	
	while(OSCCTRL->DPLLSTATUS.bit.CLKRDY == 0 || OSCCTRL->DPLLSTATUS.bit.LOCK == 0);
	
	// ------------------------------------------//
	// Clock Gen 0 Configuration
	// ------------------------------------------//

	/* Configure GCLK0 (CPU clock) to run from the DPLL96M */
	/* GCLK_GENCTRL_OE is to enable GCLK_IO output */
	GCLK->GENCTRL[0].reg = GCLK_GENCTRL_DIV(1) | GCLK_GENCTRL_IDC | GCLK_GENCTRL_SRC_DPLL96M | GCLK_GENCTRL_OE | GCLK_GENCTRL_GENEN;
	
	while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL0);
	
	// ------------------------------------------//
	// Clock Gen 3 Configuration
	// ------------------------------------------//
	
	/* Configure GCLK3 to run from the OSC16M */
	/* GCLK_GENCTRL_OE is to enable GCLK_IO output */
	//GCLK->GENCTRL[3].reg = GCLK_GENCTRL_DIV(1) | GCLK_GENCTRL_IDC | GCLK_GENCTRL_SRC_OSC16M /*| GCLK_GENCTRL_OE*/ | GCLK_GENCTRL_GENEN;
	
	//while(GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL3);
	
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The range of the output frequency of the DPLL96M is 48-96 MHz (from "Electrical Characteristics").

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the replay Lajon. I do not think it says anywhere about the range being 48-96MHz in the datasheet/user manual unless I missed something. In Table 46.6 under 46.5 Maximum Clock Frequencies, it says FDPLL96M Reference clock frequency MAX is 2MHz for both PL0 and PL2. And as I understand this is input frequency for DPLL. Also, using Atmel Start, it lets me configure DPLL at 16MHz using 2MHz Gen1 clock source (attached screenshot, did not run it on actual board though, Atmel START usually shows error for wrong configurations).

I can run DPLL at 18MHz and higher by changing the Integer factor for DPLL in the above code. It just doesn't work for 16MHz and lower. 

 

Also, would you mind sharing section number where you are reading the frequency range for DPLL?

 

Thanks in advance.

 

I appreciate you taking time to reply.

 

Regards,

Rishit 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's in 

46.12.7 Digital Phase Lock Loop (DPLL) Characteristics
Table 46-58. Fractional Digital Phase Lock Loop Characteristics

for L21 (current version of the datasheet).

/Lars

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I found it. Thanks for sharing.

 

Interestingly I am able to configure DPLL at any frequency from 18MHz to 48MHz and I am only using Integer Multiplier. Just 16MHz is giving troubles and that's what I am looking for.

 

Anyway thanks for the reply. I appreciate it. 

 

Regards,

Rishit