0

I'm trying to configure my SAMD21 clock to go as fast as it can. So I'm using the internal 8 MHz oscillator to feed generic clock generator 1 (with a prescaler of 8) to generate a generic clock to feed the digital phase locked loop, which in turn feeds generic clock generator 0 (my main clock) that should clock the CPU, but the micro is running very slowly, where did I make a mistake? ​I followed this guide http://borkedlabs.com/2014/08/21/asf-samd21-dpll-for-internal-clock-from-internal-8mhz/​ , but it doesn't work. Here's my code:

void system_clock_init(void)
{
    SYSCTRL->INTFLAG.reg = SYSCTRL_INTFLAG_BOD33RDY | SYSCTRL_INTFLAG_BOD33DET | SYSCTRL_INTFLAG_DFLLRDY;

    /* switch off all peripheral clocks to save power */
    //_switch_peripheral_gclk();

    /* configure and enable generic clock generator 1 (GENCTRL and GENDIV registers of GCLK module) */
    struct system_gclk_gen_config gclk_gen_config1;
    system_gclk_gen_get_config_defaults(&gclk_gen_config1);
    gclk_gen_config1.source_clock = SYSTEM_CLOCK_SOURCE_OSC8M;
    gclk_gen_config1.division_factor = 8;
    gclk_gen_config1.output_enable = false;
    system_gclk_gen_set_config(GCLK_GENERATOR_1,&gclk_gen_config1);
    system_gclk_gen_enable(GCLK_GENERATOR_1);

    /* configure and enable generic clock for DPLL (CLKCTRL of GCLK module) */
    struct system_gclk_chan_config gclk_chan_config;
    system_gclk_chan_get_config_defaults(&gclk_chan_config);
    gclk_chan_config.source_generator = GCLK_GENERATOR_1;
    system_gclk_chan_set_config(SYSCTRL_GCLK_ID_FDPLL,&gclk_chan_config);
    system_gclk_chan_enable(SYSCTRL_GCLK_ID_FDPLL);

    /* configure and enable clock source: DPLL (SYSCTRL registers) */
    struct system_clock_source_dpll_config dpll_config;
    system_clock_source_dpll_get_config_defaults(&dpll_config);
    dpll_config.reference_clock = SYSTEM_CLOCK_SOURCE_DPLL_REFERENCE_CLOCK_GCLK;
    dpll_config.reference_divider = 1;
    dpll_config.reference_frequency = 1000000;
    dpll_config.output_frequency = 30000000;
    system_clock_source_dpll_set_config(&dpll_config);
    system_clock_source_enable(SYSTEM_CLOCK_SOURCE_DPLL);

    /* set NVM wait states */
    system_flash_set_waitstates(2);

    /* configure and enable generic clock 0 (GCLK_MAIN) */
    struct system_gclk_gen_config gclk_gen_config0;
    system_gclk_gen_get_config_defaults(&gclk_gen_config0);
    gclk_gen_config0.source_clock = SYSTEM_CLOCK_SOURCE_DPLL;
    gclk_gen_config0.division_factor = 1;
    system_gclk_gen_set_config(GCLK_GENERATOR_0,&gclk_gen_config0);
    system_gclk_gen_enable(GCLK_GENERATOR_0);
}

I updated the conf_clocks.h header to reflect the changes (I don't know if those macros are referenced somewhere else, so just in case) and I changed the system_clock_init() function that's called from system_init().

Luca
  • 1,658
  • 4
  • 20
  • 41

2 Answers2

1

I was never a fan of using the ASF of Atmel since you never really only do what you want. I suggest you looking more into the data sheet since this cost way less time to finish your task then lurking around in the ASF. The Atmel data sheets even have an "Initialization" chapter which is a step by step explaination on what to do.

Enabling the OSC8 are basically 5 code lines:

/* Various bits in the INTFLAG register can be set to one at startup.
This will ensure that these bits are cleared */
SYSCTRL->INTFLAG.reg = SYSCTRL_INTFLAG_BOD33RDY | SYSCTRL_INTFLAG_BOD33DET | SYSCTRL_INTFLAG_DFLLRDY;

/* OSC8M Internal 8MHz Oscillator */
SYSCTRL->OSC8M.bit.PRESC = SYSTEM_OSC8M_DIV_1;
SYSCTRL->OSC8M.bit.ONDEMAND = CONF_CLOCK_OSC8M_ON_DEMAND;
SYSCTRL->OSC8M.bit.RUNSTDBY = CONF_CLOCK_OSC8M_RUN_IN_STANDBY;

/* Enable OSC8M */
SYSCTRL->OSC8M.reg |= SYSCTRL_OSC8M_ENABLE;

The remaining steps are only to configure the dpll register by using the already enabled OSC8M, which is also only a few codes lines. (Writing to the GCLK.GENDIV/GENCTRL and CLKCTRL register as well as writing to the SYSCTRL.DPLL register.

1

To configure the SAMD21 CPU to go at its maximum supported frequency (48 MHz), I don't use the ASF, but I use code taken from the Arduino SAMD core instead; you can find what it does in the SystemInit() function at https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/startup.c. Quoting the comments in that file, the relevant steps done in SystemInit() are:

  • Enable XOSC32K clock (External on-board 32.768Hz oscillator), will be used as DFLL48M reference
  • Put XOSC32K as source of Generic Clock Generator 1
  • Put Generic Clock Generator 1 as source for Generic Clock Multiplexer 0 (DFLL48M reference)
  • Enable DFLL48M clock
  • Switch Generic Clock Generator 0 to DFLL48M. CPU will run at 48MHz

The relevant lines of code (assuming that your board mounts an external 32.768 kHz crystal) are:

/* Set 1 Flash Wait State for 48MHz, cf tables 20.9 and 35.27 in SAMD21 Datasheet */
NVMCTRL->CTRLB.bit.RWS = NVMCTRL_CTRLB_RWS_HALF_Val ;

/* Turn on the digital interface clock */
PM->APBAMASK.reg |= PM_APBAMASK_GCLK ;

/* Enable XOSC32K clock (External on-board 32.768Hz oscillator) */
SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_STARTUP( 0x6u ) | /* cf table 15.10 of product datasheet in chapter 15.8.6 */
                       SYSCTRL_XOSC32K_XTALEN | SYSCTRL_XOSC32K_EN32K ;
SYSCTRL->XOSC32K.bit.ENABLE = 1 ; /* separate call, as described in chapter 15.6.3 */
while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_XOSC32KRDY) == 0 )
{
  /* Wait for oscillator stabilization */
}

/* Software reset the module to ensure it is re-initialized correctly */
GCLK->CTRL.reg = GCLK_CTRL_SWRST ;
while ( (GCLK->CTRL.reg & GCLK_CTRL_SWRST) && (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) )
{
  /* Wait for reset to complete */
}

/* Put XOSC32K as source of Generic Clock Generator 1 */
GCLK->GENDIV.reg = GCLK_GENDIV_ID( GENERIC_CLOCK_GENERATOR_XOSC32K ) ; // Generic Clock Generator 1
while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY )
{
  /* Wait for synchronization */
}

/* Write Generic Clock Generator 1 configuration */
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID( GENERIC_CLOCK_GENERATOR_OSC32K ) | // Generic Clock Generator 1
                    GCLK_GENCTRL_SRC_XOSC32K | // Selected source is External 32KHz Oscillator
//                  GCLK_GENCTRL_OE | // Output clock to a pin for tests
                    GCLK_GENCTRL_GENEN ;
while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY )
{
  /* Wait for synchronization */
}

/* Put Generic Clock Generator 1 as source for Generic Clock Multiplexer 0 (DFLL48M reference) */
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID( GENERIC_CLOCK_MULTIPLEXER_DFLL48M ) | // Generic Clock Multiplexer 0
                  GCLK_CLKCTRL_GEN_GCLK1 | // Generic Clock Generator 1 is source
                  GCLK_CLKCTRL_CLKEN ;
while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY )
{
  /* Wait for synchronization */
}

/* Enable DFLL48M clock */  
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE;
while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0 )
{
  /* Wait for synchronization */
}
SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP( 31 ) | // Coarse step is 31, half of the max value
                       SYSCTRL_DFLLMUL_FSTEP( 511 ) | // Fine step is 511, half of the max value
                       SYSCTRL_DFLLMUL_MUL( (VARIANT_MCK + VARIANT_MAINOSC/2) / VARIANT_MAINOSC ) ; // External 32KHz is the reference
while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0 )
{
  /* Wait for synchronization */
}

/* Write full configuration to DFLL control register */
SYSCTRL->DFLLCTRL.reg |= SYSCTRL_DFLLCTRL_MODE | /* Enable the closed loop mode */
                         SYSCTRL_DFLLCTRL_WAITLOCK |
                         SYSCTRL_DFLLCTRL_QLDIS ; /* Disable Quick lock */
while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0 )
{
  /* Wait for synchronization */
}

/* Enable the DFLL */
SYSCTRL->DFLLCTRL.reg |= SYSCTRL_DFLLCTRL_ENABLE ;
while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKC) == 0 ||
        (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLLCKF) == 0 )
{
  /* Wait for locks flags */
}
while ( (SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0 )
{
  /* Wait for synchronization */
}

/* Switch Generic Clock Generator 0 to DFLL48M. CPU will run at 48MHz. */
GCLK->GENDIV.reg = GCLK_GENDIV_ID( GENERIC_CLOCK_GENERATOR_MAIN ) ; // Generic Clock Generator 0
while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY )
{
  /* Wait for synchronization */
}

/* Write Generic Clock Generator 0 configuration */
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID( GENERIC_CLOCK_GENERATOR_MAIN ) | // Generic Clock Generator 0
                    GCLK_GENCTRL_SRC_DFLL48M | // Selected source is DFLL 48MHz
//                  GCLK_GENCTRL_OE | // Output clock to a pin for tests
                    GCLK_GENCTRL_IDC | // Set 50/50 duty cycle
                    GCLK_GENCTRL_GENEN ;
while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY )
{
  /* Wait for synchronization */
}
Francesco Lavra
  • 809
  • 6
  • 16
  • Note that the XOSC32K can take a long time to stabilise after power-on - up to one second. The DFLL48M takes time to lock as well, but nowhere near as long as the 32K. (See oscillator timing characteristics section near the end of the device data sheet.) So it might make sense to start the XOSC32K, then go off and do other initialisation tasks (e.g. C runtime memory initialisation) before checking SYSCTRL_PCLKSR_XOSC32KRDY. The CPU is running from OSC8M out of reset, so you can get a lot done in that otherwise (potentially) dead time. – Jeremy Jan 14 '19 at 11:47