diff --git a/boards/samd21-xpro/include/periph_conf.h b/boards/samd21-xpro/include/periph_conf.h index 836bbd3f82..33a080b404 100644 --- a/boards/samd21-xpro/include/periph_conf.h +++ b/boards/samd21-xpro/include/periph_conf.h @@ -34,8 +34,9 @@ extern "C" { /** * @name External oscillator and clock configuration * - * There are two choices for selection of CORECLOCK: + * There are three choices for selection of CORECLOCK: * + * - usage of the 48 MHz DFLL fed by external oscillator running at 32 kHz * - usage of the PLL fed by the internal 8MHz oscillator divided by 8 * - usage of the internal 8MHz oscillator directly, divided by N if needed * @@ -70,6 +71,12 @@ extern "C" { #define CLOCK_PLL_DIV (1U) /* adjust to your needs */ /* generate the actual used core clock frequency */ #define CLOCK_CORECLOCK (((CLOCK_PLL_MUL + 1) * 1000000U) / CLOCK_PLL_DIV) +#elif CLOCK_USE_XOSC32_DFLL +/* Settings for 32 kHz external oscillator and 48 MHz DFLL */ +#define CLOCK_CORECLOCK (48000000U) +#define CLOCK_XOSC32K (32768UL) +#define CLOCK_8MHZ (1) +#define GEN2_ULP32K (1) #else /* edit this value to your needs */ #define CLOCK_DIV (1U) diff --git a/boards/samr21-xpro/include/periph_conf.h b/boards/samr21-xpro/include/periph_conf.h index f4910d0d0a..aae2a47786 100644 --- a/boards/samr21-xpro/include/periph_conf.h +++ b/boards/samr21-xpro/include/periph_conf.h @@ -32,16 +32,17 @@ extern "C" { #endif /** - * @name External oscillator and clock configuration + * @name External oscillator and clock configuration * - * For selection of the used CORECLOCK, we have implemented two choices: + * There are three choices for selection of CORECLOCK: * + * - usage of the 48 MHz DFLL fed by external oscillator running at 32 kHz * - usage of the PLL fed by the internal 8MHz oscillator divided by 8 * - usage of the internal 8MHz oscillator directly, divided by N if needed * * * The PLL option allows for the usage of a wider frequency range and a more - * stable clock with less jitter. This is why we use this option as default. + * stable clock with less jitter. This is why this option is default. * * The target frequency is computed from the PLL multiplier and the PLL divisor. * Use the following formula to compute your values: @@ -70,6 +71,12 @@ extern "C" { #define CLOCK_PLL_DIV (1U) /* adjust to your needs */ /* generate the actual used core clock frequency */ #define CLOCK_CORECLOCK (((CLOCK_PLL_MUL + 1) * 1000000U) / CLOCK_PLL_DIV) +#elif CLOCK_USE_XOSC32_DFLL + /* Settings for 32 kHz external oscillator and 48 MHz DFLL */ +#define CLOCK_CORECLOCK (48000000U) +#define CLOCK_XOSC32K (32768UL) +#define CLOCK_8MHZ (1) +#define GEN2_ULP32K (1) #else /* edit this value to your needs */ #define CLOCK_DIV (1U) diff --git a/cpu/samd21/cpu.c b/cpu/samd21/cpu.c index b4f2d379a9..c5579147e2 100644 --- a/cpu/samd21/cpu.c +++ b/cpu/samd21/cpu.c @@ -22,6 +22,14 @@ #include "periph_conf.h" #include "periph/init.h" +#ifndef CLOCK_8MHZ +#define CLOCK_8MHZ 1 +#endif + +#ifndef GEN2_ULP32K +#define GEN2_ULP32K 1 +#endif + #ifndef VDD /** * @brief Set system voltage level in mV (determines flash wait states) @@ -54,12 +62,14 @@ static void clk_init(void) NVMCTRL->CTRLB.reg |= NVMCTRL_CTRLB_RWS(WAITSTATES); PM->APBBMASK.reg &= ~PM_APBBMASK_NVMCTRL; +#if CLOCK_8MHZ /* configure internal 8MHz oscillator to run without prescaler */ SYSCTRL->OSC8M.bit.PRESC = 0; SYSCTRL->OSC8M.bit.ONDEMAND = 1; SYSCTRL->OSC8M.bit.RUNSTDBY = 0; SYSCTRL->OSC8M.bit.ENABLE = 1; while (!(SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_OSC8MRDY)) {} +#endif #if CLOCK_USE_PLL /* reset the GCLK module so it is in a known state */ @@ -90,6 +100,83 @@ static void clk_init(void) GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_FDPLL | GCLK_GENCTRL_ID(0)); +#elif CLOCK_USE_XOSC32_DFLL + /* Use External 32.768KHz Oscillator */ + SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_ONDEMAND | + SYSCTRL_XOSC32K_EN32K | + SYSCTRL_XOSC32K_XTALEN | + SYSCTRL_XOSC32K_STARTUP(6) | + SYSCTRL_XOSC32K_RUNSTDBY; + + /* Enable with Seperate Call */ + SYSCTRL->XOSC32K.bit.ENABLE = 1; + + /* reset the GCLK module so it is in a known state */ + GCLK->CTRL.reg = GCLK_CTRL_SWRST; + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {} + + /* setup generic clock 1 as 1MHz for timer.c */ + GCLK->GENDIV.reg = (GCLK_GENDIV_DIV(8) | + GCLK_GENDIV_ID(1)); + GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | + GCLK_GENCTRL_SRC_OSC8M | + GCLK_GENCTRL_ID(1)); + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {} + + /* Setup clock GCLK3 with divider 1 */ + GCLK->GENDIV.reg = GCLK_GENDIV_ID(3) | GCLK_GENDIV_DIV(1); + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {} + + /* Enable GCLK3 with XOSC32K as source */ + GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(3) | + GCLK_GENCTRL_GENEN | + GCLK_GENCTRL_RUNSTDBY | + GCLK_GENCTRL_SRC_XOSC32K; + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {} + + /* set GCLK3 as source for DFLL */ + GCLK->CLKCTRL.reg = (GCLK_CLKCTRL_GEN_GCLK3 | + GCLK_CLKCTRL_ID_DFLL48 | + GCLK_CLKCTRL_CLKEN); + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {} + + /* Disable ONDEMAND mode while writing configurations */ + SYSCTRL->DFLLCTRL.bit.ONDEMAND = 0; + while ((SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0) { + /* Wait for DFLL sync */ + } + + /* get the coarse and fine values stored in NVM (Section 9.3) */ + uint32_t coarse = (*(uint32_t *)(0x806024) >> 26); /* Bits 63:58 */ + uint32_t fine = (*(uint32_t *)(0x806028) & 0x3FF); /* Bits 73:64 */ + + SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(coarse >> 1) | + SYSCTRL_DFLLMUL_FSTEP(fine >> 1) | + SYSCTRL_DFLLMUL_MUL(CLOCK_CORECLOCK / CLOCK_XOSC32K); + SYSCTRL->DFLLVAL.reg = SYSCTRL_DFLLVAL_COARSE(coarse) | + SYSCTRL_DFLLVAL_FINE(fine); + SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_MODE; + while ((SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0) { + /* Wait for DFLL sync */ + } + + SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE; + while ((SYSCTRL->PCLKSR.reg & (SYSCTRL_PCLKSR_DFLLRDY | + SYSCTRL_PCLKSR_DFLLLCKF | + SYSCTRL_PCLKSR_DFLLLCKC)) == 0) { + /* Wait for DFLLLXXX sync */ + } + + /* select the DFLL as source for clock generator 0 (CPU core clock) */ + GCLK->GENDIV.reg = (GCLK_GENDIV_DIV(1U) | GCLK_GENDIV_ID(0)); + GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(0)); + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0; + while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {} + + SYSCTRL->DFLLCTRL.bit.ONDEMAND = 1; + while ((SYSCTRL->PCLKSR.reg & SYSCTRL_PCLKSR_DFLLRDY) == 0) { + /* Wait for DFLL sync */ + } #else /* do not use PLL, use internal 8MHz oscillator directly */ GCLK->GENDIV.reg = (GCLK_GENDIV_DIV(CLOCK_DIV) | GCLK_GENDIV_ID(0)); @@ -101,6 +188,7 @@ static void clk_init(void) /* make sure we synchronize clock generator 0 before we go on */ while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY) {} +#if GEN2_ULP32K /* Setup Clock generator 2 with divider 1 (32.768kHz) */ GCLK->GENDIV.reg = (GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(0)); GCLK->GENCTRL.reg = (GCLK_GENCTRL_ID(2) | GCLK_GENCTRL_GENEN | @@ -108,6 +196,7 @@ static void clk_init(void) GCLK_GENCTRL_SRC_OSCULP32K); while (GCLK->STATUS.bit.SYNCBUSY) {} +#endif /* redirect all peripherals to a disabled clock generator (7) by default */ for (int i = 0x3; i <= 0x22; i++) { diff --git a/cpu/samd21/periph/timer.c b/cpu/samd21/periph/timer.c index 1f2c78bbde..a50ba97314 100644 --- a/cpu/samd21/periph/timer.c +++ b/cpu/samd21/periph/timer.c @@ -52,8 +52,9 @@ int timer_init(tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) /* select the clock generator depending on the main clock source: * GCLK0 (1MHz) if we use the internal 8MHz oscillator * GCLK1 (8MHz) if we use the PLL */ -#if CLOCK_USE_PLL +#if CLOCK_USE_PLL || CLOCK_USE_XOSC32_DFLL /* configure GCLK1 (configured to 1MHz) to feed TC3, TC4 and TC5 */; + /* configure GCLK1 to feed TC3, TC4 and TC5 */; GCLK->CLKCTRL.reg = (uint16_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK1 | (TC3_GCLK_ID << GCLK_CLKCTRL_ID_Pos))); while (GCLK->STATUS.bit.SYNCBUSY) {} /* TC4 and TC5 share the same channel */ @@ -78,8 +79,8 @@ int timer_init(tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) while (TIMER_0_DEV.CTRLA.bit.SWRST) {} /* choosing 16 bit mode */ TIMER_0_DEV.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT16_Val; -#if CLOCK_USE_PLL - /* sourced by 1MHz with prescaler 1 results in... you know it :-) */ +#if CLOCK_USE_PLL || CLOCK_USE_XOSC32_DFLL + /* PLL/DFLL: sourced by 1MHz and prescaler 1 to reach 1MHz */ TIMER_0_DEV.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV1_Val; #else /* sourced by 8MHz with Presc 8 results in 1MHz clk */ @@ -102,8 +103,8 @@ int timer_init(tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) TIMER_1_DEV.CTRLA.bit.MODE = TC_CTRLA_MODE_COUNT32_Val; -#if CLOCK_USE_PLL - /* sourced by 1MHz and prescaler 1 to reach 1MHz */ +#if CLOCK_USE_PLL || CLOCK_USE_XOSC32_DFLL + /* PLL/DFLL: sourced by 1MHz and prescaler 1 to reach 1MHz */ TIMER_1_DEV.CTRLA.bit.PRESCALER = TC_CTRLA_PRESCALER_DIV1_Val; #else /* sourced by 8MHz with Presc 8 results in 1Mhz clk */