samd/clock_config: Add HW_DFLL_USB_SYNC and HW_MCU_OSC32KULP extensions.
Two new compile flags are: MICROPY_HW_DFLL_USB_SYNC: Effective only if DFLL48 does not run from the crystal. It will synchronize the DFLL48M clock with the USB's SOF pulse. If no USB is connected, it will fall back to open loop mode. The DFLL48M clock is then pretty precise, but with a higher clock jitter at SAMD51 devices. MICROPY_HW_MCU_OSC32KULP: Effective only if the devics uses a crystal as clock source. Run the MCU clock from the ULP 32kHz oszillator instead of the crystal. This flag was added to cater for a interference problem of the crystal and Neopixel/Debug pins at Adafruit FEATHER Mx boards, which causes the board to crash. Drawback: ticks_ms() and time.time() vs. than ticks_us() and the peripherals like PWM run at not synchronous clocks.
This commit is contained in:
parent
f00356a486
commit
20e7313453
|
@ -1,4 +1,5 @@
|
|||
#define MICROPY_HW_BOARD_NAME "Feather M4 Express"
|
||||
#define MICROPY_HW_MCU_NAME "SAMD51J19A"
|
||||
|
||||
#define MICROPY_HW_XOSC32K (1)
|
||||
#define MICROPY_HW_XOSC32K (1)
|
||||
#define MICROPY_HW_MCU_OSC32KULP (1)
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
#define MICROPY_HW_BOARD_NAME "ItsyBitsy M0 Express"
|
||||
#define MICROPY_HW_MCU_NAME "SAMD21G18A"
|
||||
|
||||
#define MICROPY_HW_DFLL_USB_SYNC (1)
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
#define MICROPY_HW_BOARD_NAME "ItsyBitsy M4 Express"
|
||||
#define MICROPY_HW_MCU_NAME "SAMD51G19A"
|
||||
|
||||
#define MICROPY_HW_DFLL_USB_SYNC (1)
|
||||
|
|
|
@ -30,4 +30,5 @@ void init_clocks(uint32_t cpu_freq);
|
|||
void set_cpu_freq(uint32_t cpu_freq);
|
||||
uint32_t get_cpu_freq(void);
|
||||
uint32_t get_apb_freq(void);
|
||||
void check_usb_recovery_mode(void);
|
||||
void enable_sercom_clock(int id);
|
||||
|
|
|
@ -29,10 +29,12 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "samd_soc.h"
|
||||
|
||||
static uint32_t cpu_freq = CPU_FREQ;
|
||||
static uint32_t apb_freq = APB_FREQ;
|
||||
static uint32_t dfll48m_calibration;
|
||||
|
||||
int sercom_gclk_id[] = {
|
||||
GCLK_CLKCTRL_ID_SERCOM0_CORE, GCLK_CLKCTRL_ID_SERCOM1_CORE,
|
||||
|
@ -52,14 +54,28 @@ void set_cpu_freq(uint32_t cpu_freq_arg) {
|
|||
cpu_freq = cpu_freq_arg;
|
||||
}
|
||||
|
||||
void check_usb_recovery_mode(void) {
|
||||
#if !MICROPY_HW_XOSC32K
|
||||
mp_hal_delay_ms(500);
|
||||
// Check USB status. If not connected, switch DFLL48M back to open loop
|
||||
if (USB->DEVICE.DeviceEndpoint[0].EPCFG.reg == 0) {
|
||||
// Set/keep the open loop mode of the device.
|
||||
SYSCTRL->DFLLVAL.reg = dfll48m_calibration;
|
||||
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_CCDIS | SYSCTRL_DFLLCTRL_ENABLE;
|
||||
}
|
||||
#endif // MICROPY_HW_XOSC32K
|
||||
}
|
||||
|
||||
void init_clocks(uint32_t cpu_freq) {
|
||||
|
||||
dfll48m_calibration = 0; // please the compiler
|
||||
|
||||
// SAMD21 Clock settings
|
||||
// GCLK0: 48MHz from DFLL open loop mode or closed loop mode from 32k Crystal
|
||||
// GCLK1: 32768 Hz from 32K ULP or 32k Crystal
|
||||
// GCLK1: 32768 Hz from 32K ULP or DFLL48M
|
||||
// GCLK2: 48MHz from DFLL for Peripherals
|
||||
// GCLK3: 1Mhz for the us-counter (TC4/TC5)
|
||||
// GCLK4: 32kHz from crystal, if present
|
||||
// GCLK8: 1kHz clock for WDT
|
||||
|
||||
NVMCTRL->CTRLB.bit.MANW = 1; // errata "Spurious Writes"
|
||||
|
@ -74,19 +90,34 @@ void init_clocks(uint32_t cpu_freq) {
|
|||
}
|
||||
// Set up the DFLL48 according to the data sheet 17.6.7.1.2
|
||||
// Step 1: Set up the reference clock
|
||||
// Connect the OSC32K via GCLK1 to the DFLL input and for further use.
|
||||
|
||||
#if MICROPY_HW_MCU_OSC32KULP
|
||||
// Connect the GCLK1 to the XOSC32KULP
|
||||
GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(1);
|
||||
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(1);
|
||||
#else
|
||||
// Connect the GCLK1 to OSC32K via GCLK1 to the DFLL input and for further use.
|
||||
GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(1);
|
||||
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_XOSC32K | GCLK_GENCTRL_ID(1);
|
||||
#endif
|
||||
|
||||
while (GCLK->STATUS.bit.SYNCBUSY) {
|
||||
}
|
||||
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_DFLL48 | GCLK_CLKCTRL_GEN_GCLK1 | GCLK_CLKCTRL_CLKEN;
|
||||
|
||||
// Connect the GCLK4 to OSC32K via GCLK1 to the DFLL input and for further use.
|
||||
GCLK->GENDIV.reg = GCLK_GENDIV_ID(4) | GCLK_GENDIV_DIV(1);
|
||||
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_XOSC32K | GCLK_GENCTRL_ID(4);
|
||||
while (GCLK->STATUS.bit.SYNCBUSY) {
|
||||
}
|
||||
|
||||
// Connect GCLK4 to the DFLL input and for further use.
|
||||
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_DFLL48 | GCLK_CLKCTRL_GEN_GCLK4 | GCLK_CLKCTRL_CLKEN;
|
||||
// Enable access to the DFLLCTRL reg acc. to Errata 1.2.1
|
||||
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE;
|
||||
while (SYSCTRL->PCLKSR.bit.DFLLRDY == 0) {
|
||||
}
|
||||
// Step 2: Set the coarse and fine values.
|
||||
// The coarse setting will be taken from the calibration data. So the value used here
|
||||
// does not matter. Get the coarse value from the calib data. In case it is not set,
|
||||
// Get the coarse value from the calib data. In case it is not set,
|
||||
// set a midrange value.
|
||||
uint32_t coarse = (*((uint32_t *)FUSES_DFLL48M_COARSE_CAL_ADDR) & FUSES_DFLL48M_COARSE_CAL_Msk)
|
||||
>> FUSES_DFLL48M_COARSE_CAL_Pos;
|
||||
|
@ -103,7 +134,7 @@ void init_clocks(uint32_t cpu_freq) {
|
|||
}
|
||||
// Step 4: Start the DFLL and wait for the PLL lock. We just wait for the fine lock, since
|
||||
// coarse adjusting is bypassed.
|
||||
SYSCTRL->DFLLCTRL.reg |= SYSCTRL_DFLLCTRL_MODE | SYSCTRL_DFLLCTRL_WAITLOCK |
|
||||
SYSCTRL->DFLLCTRL.reg |= SYSCTRL_DFLLCTRL_MODE | SYSCTRL_DFLLCTRL_WAITLOCK | SYSCTRL_DFLLCTRL_STABLE |
|
||||
SYSCTRL_DFLLCTRL_BPLCKC | SYSCTRL_DFLLCTRL_ENABLE;
|
||||
while (SYSCTRL->PCLKSR.bit.DFLLLCKF == 0) {
|
||||
}
|
||||
|
@ -114,18 +145,33 @@ void init_clocks(uint32_t cpu_freq) {
|
|||
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE;
|
||||
while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {
|
||||
}
|
||||
SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(1) | SYSCTRL_DFLLMUL_FSTEP(1)
|
||||
| SYSCTRL_DFLLMUL_MUL(48000);
|
||||
|
||||
uint32_t coarse = (*((uint32_t *)FUSES_DFLL48M_COARSE_CAL_ADDR) & FUSES_DFLL48M_COARSE_CAL_Msk)
|
||||
>> FUSES_DFLL48M_COARSE_CAL_Pos;
|
||||
if (coarse == 0x3f) {
|
||||
coarse = 0x1f;
|
||||
}
|
||||
SYSCTRL->DFLLVAL.reg = SYSCTRL_DFLLVAL_COARSE(coarse) | SYSCTRL_DFLLVAL_FINE(512);
|
||||
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_CCDIS | SYSCTRL_DFLLCTRL_USBCRM
|
||||
SYSCTRL->DFLLVAL.reg = SYSCTRL_DFLLVAL_COARSE(coarse) | SYSCTRL_DFLLVAL_FINE(511);
|
||||
|
||||
#if MICROPY_HW_DFLL_USB_SYNC
|
||||
// Configure the DFLL48M for USB clock recovery.
|
||||
// Will have to switch back if no USB
|
||||
SYSCTRL->DFLLSYNC.bit.READREQ = 1;
|
||||
dfll48m_calibration = SYSCTRL->DFLLVAL.reg;
|
||||
// Set the Multiplication factor.
|
||||
SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(1) | SYSCTRL_DFLLMUL_FSTEP(1)
|
||||
| SYSCTRL_DFLLMUL_MUL(48000);
|
||||
// Set the mode to closed loop USB Recovery mode
|
||||
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_USBCRM | SYSCTRL_DFLLCTRL_CCDIS
|
||||
| SYSCTRL_DFLLCTRL_MODE | SYSCTRL_DFLLCTRL_ENABLE;
|
||||
#else
|
||||
// Set/keep the open loop mode of the device.
|
||||
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_CCDIS | SYSCTRL_DFLLCTRL_ENABLE;
|
||||
#endif
|
||||
|
||||
while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {
|
||||
}
|
||||
|
||||
// Enable 32768 Hz on GCLK1 for consistency
|
||||
GCLK->GENDIV.reg = GCLK_GENDIV_ID(1) | GCLK_GENDIV_DIV(48016384 / 32768);
|
||||
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL48M | GCLK_GENCTRL_ID(1);
|
||||
|
@ -154,10 +200,10 @@ void init_clocks(uint32_t cpu_freq) {
|
|||
GCLK->GENCTRL.reg = GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(8);
|
||||
while (GCLK->STATUS.bit.SYNCBUSY) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void enable_sercom_clock(int id) {
|
||||
// Next: Set up the clocks
|
||||
// Enable synchronous clock. The bits are nicely arranged
|
||||
PM->APBCMASK.reg |= 0x04 << id;
|
||||
// Select multiplexer generic clock source and enable.
|
||||
|
|
|
@ -29,10 +29,12 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/mphal.h"
|
||||
#include "samd_soc.h"
|
||||
|
||||
static uint32_t cpu_freq = CPU_FREQ;
|
||||
static uint32_t apb_freq = APB_FREQ;
|
||||
static uint32_t dfll48m_calibration;
|
||||
|
||||
int sercom_gclk_id[] = {
|
||||
SERCOM0_GCLK_ID_CORE, SERCOM1_GCLK_ID_CORE,
|
||||
|
@ -84,12 +86,43 @@ void set_cpu_freq(uint32_t cpu_freq_arg) {
|
|||
}
|
||||
}
|
||||
|
||||
void check_usb_recovery_mode(void) {
|
||||
#if !MICROPY_HW_XOSC32K
|
||||
mp_hal_delay_ms(500);
|
||||
// Check USB status. If not connected, switch DFLL48M back to open loop
|
||||
if (USB->DEVICE.DeviceEndpoint[0].EPCFG.reg == 0) {
|
||||
// as per Errata 2.8.3
|
||||
OSCCTRL->DFLLMUL.reg = 0;
|
||||
while (OSCCTRL->DFLLSYNC.bit.DFLLMUL == 1) {
|
||||
}
|
||||
// Set the mode to open loop mode
|
||||
OSCCTRL->DFLLCTRLB.reg = 0;
|
||||
while (OSCCTRL->DFLLSYNC.bit.DFLLCTRLB == 1) {
|
||||
}
|
||||
OSCCTRL->DFLLCTRLA.reg = OSCCTRL_DFLLCTRLA_RUNSTDBY | OSCCTRL_DFLLCTRLA_ENABLE;
|
||||
while (OSCCTRL->DFLLSYNC.bit.ENABLE == 1) {
|
||||
}
|
||||
OSCCTRL->DFLLVAL.reg = dfll48m_calibration; // Reload DFLLVAL register
|
||||
while (OSCCTRL->DFLLSYNC.bit.DFLLVAL == 1) {
|
||||
}
|
||||
// Set the mode to open loop mode
|
||||
OSCCTRL->DFLLCTRLB.reg = 0;
|
||||
while (OSCCTRL->DFLLSYNC.bit.DFLLCTRLB == 1) {
|
||||
}
|
||||
}
|
||||
#endif // MICROPY_HW_XOSC32K
|
||||
}
|
||||
|
||||
void init_clocks(uint32_t cpu_freq) {
|
||||
|
||||
dfll48m_calibration = 0; // please the compiler
|
||||
|
||||
// SAMD51 clock settings
|
||||
// GCLK0: 48MHz from DFLL48M or 48 - 200 MHz from DPLL0 (SAMD51)
|
||||
// GCLK1: DPLLx_REF_FREQ 32768 Hz from 32KULP or 32k Crystal
|
||||
// GCLK1: 32768 Hz from 32KULP or DFLL48M
|
||||
// GCLK2: 48MHz from DFLL48M for Peripheral devices
|
||||
// GCLK3: 16Mhz for the us-counter (TC0/TC1)
|
||||
// GCLK4: 32kHz from crystal, if present
|
||||
// DPLL0: 48 - 200 MHz
|
||||
|
||||
// Steps to set up clocks:
|
||||
|
@ -102,6 +135,7 @@ void init_clocks(uint32_t cpu_freq) {
|
|||
// Setup GCLK0 to 120MHz
|
||||
// Setup GCLK2 to 48MHz for Peripherals
|
||||
// Setup GCLK3 to 8MHz for TC0/TC1
|
||||
// Setup GCLK4 to 32kHz crystal, if present
|
||||
|
||||
// Setup GCLK0 for 48MHz as default state to keep the MCU running during config change.
|
||||
GCLK->GENCTRL[0].reg = GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL;
|
||||
|
@ -124,15 +158,26 @@ void init_clocks(uint32_t cpu_freq) {
|
|||
while (OSC32KCTRL->STATUS.bit.XOSC32KRDY == 0) {
|
||||
}
|
||||
|
||||
#if MICROPY_HW_MCU_OSC32KULP
|
||||
// Setup GCLK1 for 32kHz ULP
|
||||
GCLK->GENCTRL[1].reg = GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K;
|
||||
#else
|
||||
// Setup GCLK1 for 32kHz crystal
|
||||
GCLK->GENCTRL[1].reg = GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_XOSC32K;
|
||||
#endif
|
||||
|
||||
while (GCLK->SYNCBUSY.bit.GENCTRL1) {
|
||||
}
|
||||
|
||||
// Setup GCLK4 for 32kHz crystal
|
||||
GCLK->GENCTRL[4].reg = GCLK_GENCTRL_RUNSTDBY | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_XOSC32K;
|
||||
while (GCLK->SYNCBUSY.bit.GENCTRL4) {
|
||||
}
|
||||
|
||||
// Set-up the DFLL48M in closed loop mode with input from the 32kHz crystal
|
||||
|
||||
// Step 1: Peripheral channel 0 is driven by GCLK1 and it feeds DFLL48M
|
||||
GCLK->PCHCTRL[0].reg = GCLK_PCHCTRL_GEN_GCLK1 | GCLK_PCHCTRL_CHEN;
|
||||
// Step 1: Peripheral channel 0 is driven by GCLK4 and it feeds DFLL48M
|
||||
GCLK->PCHCTRL[0].reg = GCLK_PCHCTRL_GEN_GCLK4 | GCLK_PCHCTRL_CHEN;
|
||||
while (GCLK->PCHCTRL[0].bit.CHEN == 0) {
|
||||
}
|
||||
// Step 2: Set the multiplication values. The offset of 16384 to the freq is for rounding.
|
||||
|
@ -141,7 +186,7 @@ void init_clocks(uint32_t cpu_freq) {
|
|||
while (OSCCTRL->DFLLSYNC.bit.DFLLMUL == 1) {
|
||||
}
|
||||
// Step 3: Set the mode to closed loop
|
||||
OSCCTRL->DFLLCTRLB.reg = OSCCTRL_DFLLCTRLB_BPLCKC | OSCCTRL_DFLLCTRLB_MODE;
|
||||
OSCCTRL->DFLLCTRLB.reg = OSCCTRL_DFLLCTRLB_BPLCKC | OSCCTRL_DFLLCTRLB_STABLE | OSCCTRL_DFLLCTRLB_MODE;
|
||||
while (OSCCTRL->DFLLSYNC.bit.DFLLCTRLB == 1) {
|
||||
}
|
||||
// Wait for lock fine
|
||||
|
@ -154,12 +199,34 @@ void init_clocks(uint32_t cpu_freq) {
|
|||
|
||||
#else // MICROPY_HW_XOSC32K
|
||||
|
||||
// Set GCLK1 to DPLL0_REF_FREQ as defined in mpconfigboard.h (e.g. 32768 Hz)
|
||||
// Derive GCLK1 from DFLL48M at DPLL0_REF_FREQ as defined in mpconfigboard.h (e.g. 32768 Hz)
|
||||
GCLK->GENCTRL[1].reg = ((APB_FREQ + DPLLx_REF_FREQ / 2) / DPLLx_REF_FREQ) << GCLK_GENCTRL_DIV_Pos
|
||||
| GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_DFLL;
|
||||
while (GCLK->SYNCBUSY.bit.GENCTRL1) {
|
||||
}
|
||||
|
||||
OSCCTRL->DFLLCTRLA.bit.RUNSTDBY = 1;
|
||||
OSCCTRL->DFLLCTRLA.bit.ONDEMAND = 0;
|
||||
|
||||
OSCCTRL->DFLLCTRLA.bit.ENABLE = 1;
|
||||
while (OSCCTRL->DFLLSYNC.bit.ENABLE == 1) {
|
||||
}
|
||||
|
||||
#if MICROPY_HW_DFLL_USB_SYNC
|
||||
// Configure the DFLL48M for USB clock recovery.
|
||||
// Will have to switch back if no USB
|
||||
dfll48m_calibration = OSCCTRL->DFLLVAL.reg;
|
||||
// Set the Multiplication factor.
|
||||
OSCCTRL->DFLLMUL.reg = OSCCTRL_DFLLMUL_MUL(48000) |
|
||||
OSCCTRL_DFLLMUL_FSTEP(1) | OSCCTRL_DFLLMUL_CSTEP(1);
|
||||
while (OSCCTRL->DFLLSYNC.bit.DFLLMUL == 1) {
|
||||
}
|
||||
// Set the mode to closed loop USB Recovery
|
||||
OSCCTRL->DFLLCTRLB.reg = OSCCTRL_DFLLCTRLB_USBCRM | OSCCTRL_DFLLCTRLB_CCDIS | OSCCTRL_DFLLCTRLB_MODE;
|
||||
while (OSCCTRL->DFLLSYNC.bit.DFLLCTRLB == 1) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_HW_XOSC32K
|
||||
|
||||
// Peripheral channel 1 is driven by GCLK1 and it feeds DPLL0
|
||||
|
@ -183,7 +250,6 @@ void init_clocks(uint32_t cpu_freq) {
|
|||
}
|
||||
|
||||
void enable_sercom_clock(int id) {
|
||||
// Next: Set up the clocks
|
||||
GCLK->PCHCTRL[sercom_gclk_id[id]].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK2;
|
||||
// no easy way to set the clocks, except enabling all of them
|
||||
switch (id) {
|
||||
|
|
|
@ -111,6 +111,7 @@ void samd_init(void) {
|
|||
SysTick_Config(get_cpu_freq() / 1000);
|
||||
init_us_counter();
|
||||
usb_init();
|
||||
check_usb_recovery_mode();
|
||||
#if defined(MCU_SAMD51)
|
||||
mp_hal_ticks_cpu_enable();
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue