samd/clock_config: Support changing machine.freq() for SAMD21.

The range is 1MHz - 48 MHz.  Note that below 8 MHz there is no USB support.
The frequency will be set to an integer fraction of 48 MHz.  And after
changing the frequency, the peripherals like PWM, UART, I2C, SPI have to be
reconfigured.

Current consumption e.g. of the Seeed Xiao board at 1 MHz is about 1.5 mA,
mostly caused by the on-board LED (green LED with 1k resistor at 3.3V).
This commit is contained in:
robert-hh 2022-06-29 17:22:20 +02:00 committed by Damien George
parent edc3f3d0d3
commit 1c32cec7f1
5 changed files with 36 additions and 15 deletions

View File

@ -51,7 +51,36 @@ uint32_t get_peripheral_freq(void) {
} }
void set_cpu_freq(uint32_t cpu_freq_arg) { void set_cpu_freq(uint32_t cpu_freq_arg) {
cpu_freq = cpu_freq_arg;
// Set 1 waitstate to be safe
NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_MANW | NVMCTRL_CTRLB_RWS(1);
int div = DFLL48M_FREQ / cpu_freq_arg;
peripheral_freq = cpu_freq = DFLL48M_FREQ / div;
// Enable GCLK output: 48M on both CCLK0 and GCLK2
GCLK->GENDIV.reg = GCLK_GENDIV_ID(0) | GCLK_GENDIV_DIV(div);
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(0);
while (GCLK->STATUS.bit.SYNCBUSY) {
}
GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(div);
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(2);
while (GCLK->STATUS.bit.SYNCBUSY) {
}
if (cpu_freq >= 8000000) {
// Enable GCLK output: 48MHz on GCLK5 for USB
GCLK->GENDIV.reg = GCLK_GENDIV_ID(5) | GCLK_GENDIV_DIV(1);
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(5);
while (GCLK->STATUS.bit.SYNCBUSY) {
}
} else {
// Disable GCLK output on GCLK5 for USB, since USB is not reliable below 8 Mhz.
GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(5);
while (GCLK->STATUS.bit.SYNCBUSY) {
}
}
// Set 0 waitstates for slower CPU clock
NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_MANW | NVMCTRL_CTRLB_RWS(cpu_freq > 24000000 ? 1 : 0);
} }
void check_usb_recovery_mode(void) { void check_usb_recovery_mode(void) {
@ -76,6 +105,7 @@ void init_clocks(uint32_t cpu_freq) {
// GCLK2: 48MHz from DFLL for Peripherals // GCLK2: 48MHz from DFLL for Peripherals
// GCLK3: 1Mhz for the us-counter (TC4/TC5) // GCLK3: 1Mhz for the us-counter (TC4/TC5)
// GCLK4: 32kHz from crystal, if present // GCLK4: 32kHz from crystal, if present
// GCLK5: 48MHz from DFLL for USB
// GCLK8: 1kHz clock for WDT // GCLK8: 1kHz clock for WDT
NVMCTRL->CTRLB.bit.MANW = 1; // errata "Spurious Writes" NVMCTRL->CTRLB.bit.MANW = 1; // errata "Spurious Writes"
@ -180,15 +210,7 @@ void init_clocks(uint32_t cpu_freq) {
#endif // MICROPY_HW_XOSC32K #endif // MICROPY_HW_XOSC32K
// Enable GCLK output: 48M on both CCLK0 and GCLK2 set_cpu_freq(cpu_freq);
GCLK->GENDIV.reg = GCLK_GENDIV_ID(0) | GCLK_GENDIV_DIV(1);
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(0);
while (GCLK->STATUS.bit.SYNCBUSY) {
}
GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(1);
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(2);
while (GCLK->STATUS.bit.SYNCBUSY) {
}
// Enable GCLK output: 1MHz on GCLK3 for TC4 // Enable GCLK output: 1MHz on GCLK3 for TC4
GCLK->GENDIV.reg = GCLK_GENDIV_ID(3) | GCLK_GENDIV_DIV(48); GCLK->GENDIV.reg = GCLK_GENDIV_ID(3) | GCLK_GENDIV_DIV(48);
@ -200,7 +222,6 @@ void init_clocks(uint32_t cpu_freq) {
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(8); GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(8);
while (GCLK->STATUS.bit.SYNCBUSY) { while (GCLK->STATUS.bit.SYNCBUSY) {
} }
} }
void enable_sercom_clock(int id) { void enable_sercom_clock(int id) {

View File

@ -19,6 +19,7 @@
#define CPU_FREQ (48000000) #define CPU_FREQ (48000000)
#define DFLL48M_FREQ (48000000) #define DFLL48M_FREQ (48000000)
#define MAX_CPU_FREQ (48000000)
#define IRQ_PRI_PENDSV ((1 << __NVIC_PRIO_BITS) - 1) #define IRQ_PRI_PENDSV ((1 << __NVIC_PRIO_BITS) - 1)

View File

@ -26,6 +26,7 @@ unsigned long trng_random_u32(void);
#define CPU_FREQ (120000000) #define CPU_FREQ (120000000)
#define DFLL48M_FREQ (48000000) #define DFLL48M_FREQ (48000000)
#define MAX_CPU_FREQ (200000000)
#define DPLLx_REF_FREQ (32768) #define DPLLx_REF_FREQ (32768)
#define NVIC_PRIORITYGROUP_4 ((uint32_t)0x00000003) #define NVIC_PRIORITYGROUP_4 ((uint32_t)0x00000003)

View File

@ -67,13 +67,11 @@ STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) {
if (n_args == 0) { if (n_args == 0) {
return MP_OBJ_NEW_SMALL_INT(get_cpu_freq()); return MP_OBJ_NEW_SMALL_INT(get_cpu_freq());
} else { } else {
#if defined(MCU_SAMD51)
uint32_t freq = mp_obj_get_int(args[0]); uint32_t freq = mp_obj_get_int(args[0]);
if (freq >= 1000000 && freq <= 200000000) { if (freq >= 1000000 && freq <= MAX_CPU_FREQ) {
set_cpu_freq(freq); set_cpu_freq(freq);
SysTick_Config(get_cpu_freq() / 1000); SysTick_Config(get_cpu_freq() / 1000);
} }
#endif
return mp_const_none; return mp_const_none;
} }
} }

View File

@ -41,7 +41,7 @@
static void usb_init(void) { static void usb_init(void) {
// Init USB clock // Init USB clock
#if defined(MCU_SAMD21) #if defined(MCU_SAMD21)
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_USB; GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK5 | GCLK_CLKCTRL_ID_USB;
PM->AHBMASK.bit.USB_ = 1; PM->AHBMASK.bit.USB_ = 1;
PM->APBBMASK.bit.USB_ = 1; PM->APBBMASK.bit.USB_ = 1;
uint8_t alt = 6; // alt G, USB uint8_t alt = 6; // alt G, USB