Use the external crystal on SAMD21 again.
Also, re-enable calibration storage for CircuitPlayground Express. Tested with a 500hz PWMOut on Metro M0 with Saleae: * with crystal 500hz * with usb 500hz +- 0.1hz * without either 487hz += 0.1hz SAMD51 is skipped due to DFLL errata and the fact it defaults to a factory calibrated 48mhz that works fine for USB. Fixes #648
This commit is contained in:
parent
de61bd0d05
commit
e580d22f4a
@ -70,5 +70,6 @@ bool clock_get_parent(uint8_t type, uint8_t index, uint8_t *p_type, uint8_t *p_i
|
||||
uint32_t clock_get_frequency(uint8_t type, uint8_t index);
|
||||
uint32_t clock_get_calibration(uint8_t type, uint8_t index);
|
||||
int clock_set_calibration(uint8_t type, uint8_t index, uint32_t val);
|
||||
void save_usb_clock_calibration(void);
|
||||
|
||||
#endif // MICROPY_INCLUDED_ATMEL_SAMD_CLOCKS_H
|
||||
|
@ -24,15 +24,24 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "peripherals/clocks.h"
|
||||
|
||||
#include "hpl_gclk_config.h"
|
||||
#include "hal/include/hal_flash.h"
|
||||
|
||||
#include "bindings/samd/Clock.h"
|
||||
#include "shared-bindings/microcontroller/__init__.h"
|
||||
|
||||
#include "py/runtime.h"
|
||||
|
||||
#ifdef EXPRESS_BOARD
|
||||
#define INTERNAL_CIRCUITPY_CONFIG_START_ADDR (0x00040000 - NVMCTRL_ROW_SIZE - CIRCUITPY_INTERNAL_NVM_SIZE)
|
||||
#else
|
||||
#define INTERNAL_CIRCUITPY_CONFIG_START_ADDR (0x00040000 - 0x010000 - NVMCTRL_ROW_SIZE - CIRCUITPY_INTERNAL_NVM_SIZE)
|
||||
#endif
|
||||
|
||||
bool gclk_enabled(uint8_t gclk) {
|
||||
common_hal_mcu_disable_interrupts();
|
||||
// Explicitly do a byte write so the peripheral knows we're just wanting to read the channel
|
||||
@ -102,17 +111,48 @@ static void init_clock_source_xosc32k(void) {
|
||||
while (!SYSCTRL->PCLKSR.bit.XOSC32KRDY) {}
|
||||
}
|
||||
|
||||
static void init_clock_source_dfll48m(void) {
|
||||
static void init_clock_source_dfll48m_xosc(void) {
|
||||
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_ENABLE;
|
||||
while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {}
|
||||
SYSCTRL->DFLLMUL.reg = SYSCTRL_DFLLMUL_CSTEP(0x1f / 4) |
|
||||
SYSCTRL_DFLLMUL_FSTEP(0xff / 4) |
|
||||
SYSCTRL_DFLLMUL_MUL(48000000 / 32768);
|
||||
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 = 0;
|
||||
while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {}
|
||||
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_MODE |
|
||||
SYSCTRL_DFLLCTRL_ENABLE;
|
||||
while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {}
|
||||
while (GCLK->STATUS.bit.SYNCBUSY) {}
|
||||
}
|
||||
|
||||
static void init_clock_source_dfll48m_usb(void) {
|
||||
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)
|
||||
if (coarse == 0x3f) {
|
||||
coarse = 0x1f;
|
||||
}
|
||||
uint32_t fine = 512;
|
||||
#ifdef CALIBRATE_CRYSTALLESS
|
||||
// This is stored in an NVM page after the text and data storage but before
|
||||
// the optional file system. The first 16 bytes are the identifier for the
|
||||
// section.
|
||||
if (strcmp((char*) INTERNAL_CIRCUITPY_CONFIG_START_ADDR, "CIRCUITPYTHON1") == 0) {
|
||||
fine = ((uint16_t *) INTERNAL_CIRCUITPY_CONFIG_START_ADDR)[8];
|
||||
}
|
||||
#endif
|
||||
SYSCTRL->DFLLVAL.reg = SYSCTRL_DFLLVAL_COARSE(coarse) |
|
||||
SYSCTRL_DFLLVAL_FINE(512);
|
||||
SYSCTRL_DFLLVAL_FINE(fine);
|
||||
SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_CCDIS |
|
||||
SYSCTRL_DFLLCTRL_USBCRM |
|
||||
SYSCTRL_DFLLCTRL_MODE |
|
||||
@ -130,9 +170,16 @@ void clock_init(void)
|
||||
init_clock_source_osc32k();
|
||||
}
|
||||
|
||||
if (board_has_crystal()) {
|
||||
enable_clock_generator(3, GCLK_GENCTRL_SRC_XOSC32K_Val, 1);
|
||||
connect_gclk_to_peripheral(3, GCLK_CLKCTRL_ID_DFLL48_Val);
|
||||
init_clock_source_dfll48m_xosc();
|
||||
} else {
|
||||
init_clock_source_dfll48m_usb();
|
||||
}
|
||||
|
||||
enable_clock_generator(0, GCLK_GENCTRL_SRC_DFLL48M_Val, 1);
|
||||
enable_clock_generator(1, GCLK_GENCTRL_SRC_DFLL48M_Val, 150);
|
||||
init_clock_source_dfll48m();
|
||||
if (board_has_crystal()) {
|
||||
enable_clock_generator(2, GCLK_GENCTRL_SRC_XOSC32K_Val, 32);
|
||||
} else {
|
||||
@ -316,6 +363,41 @@ int clock_set_calibration(uint8_t type, uint8_t index, uint32_t val) {
|
||||
return -2; // calibration is read only
|
||||
}
|
||||
|
||||
void save_usb_clock_calibration(void) {
|
||||
#ifndef CALIBRATE_CRYSTALLESS
|
||||
return;
|
||||
#endif
|
||||
// If we are on USB lets double check our fine calibration for the clock and
|
||||
// save the new value if its different enough.
|
||||
SYSCTRL->DFLLSYNC.bit.READREQ = 1;
|
||||
uint16_t saved_calibration = 0x1ff;
|
||||
if (strcmp((char*) INTERNAL_CIRCUITPY_CONFIG_START_ADDR, "CIRCUITPYTHON1") == 0) {
|
||||
saved_calibration = ((uint16_t *) INTERNAL_CIRCUITPY_CONFIG_START_ADDR)[8];
|
||||
}
|
||||
while (SYSCTRL->PCLKSR.bit.DFLLRDY == 0) {
|
||||
// TODO(tannewt): Run the mass storage stuff if this takes a while.
|
||||
}
|
||||
int16_t current_calibration = SYSCTRL->DFLLVAL.bit.FINE;
|
||||
if (abs(current_calibration - saved_calibration) > 10) {
|
||||
// Copy the full internal config page to memory.
|
||||
uint8_t page_buffer[NVMCTRL_ROW_SIZE];
|
||||
memcpy(page_buffer, (uint8_t*) INTERNAL_CIRCUITPY_CONFIG_START_ADDR, NVMCTRL_ROW_SIZE);
|
||||
|
||||
// Modify it.
|
||||
memcpy(page_buffer, "CIRCUITPYTHON1", 15);
|
||||
// First 16 bytes (0-15) are ID. Little endian!
|
||||
page_buffer[16] = current_calibration & 0xff;
|
||||
page_buffer[17] = current_calibration >> 8;
|
||||
|
||||
// Write it back.
|
||||
// We don't use features that use any advanced NVMCTRL features so we can fake the descriptor
|
||||
// whenever we need it instead of storing it long term.
|
||||
struct flash_descriptor desc;
|
||||
desc.dev.hw = NVMCTRL;
|
||||
flash_write(&desc, (uint32_t) INTERNAL_CIRCUITPY_CONFIG_START_ADDR, page_buffer, NVMCTRL_ROW_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SAMD21_EXPOSE_ALL_CLOCKS
|
||||
CLOCK_SOURCE(XOSC);
|
||||
CLOCK_SOURCE(GCLKIN);
|
||||
|
@ -123,6 +123,9 @@ void clock_init(void) {
|
||||
enable_clock_generator_sync(5, GCLK_GENCTRL_SRC_DFLL_Val, 24, false);
|
||||
|
||||
init_clock_source_dpll0();
|
||||
|
||||
// Do this after all static clock init so that they aren't used dynamically.
|
||||
init_dynamic_clocks();
|
||||
}
|
||||
|
||||
static bool clk_enabled(uint8_t clk) {
|
||||
@ -203,6 +206,7 @@ static uint32_t dpll_get_frequency(uint8_t index) {
|
||||
break;
|
||||
case 0x2: // XOSC0
|
||||
case 0x3: // XOSC1
|
||||
default:
|
||||
return 0; // unknown
|
||||
}
|
||||
|
||||
@ -335,6 +339,10 @@ int clock_set_calibration(uint8_t type, uint8_t index, uint32_t val) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
void save_usb_clock_calibration(void) {
|
||||
}
|
||||
|
||||
#include <instance/can0.h>
|
||||
#include <instance/can1.h>
|
||||
#include <instance/i2s.h>
|
||||
|
@ -32,13 +32,6 @@
|
||||
|
||||
#include "flash_api.h"
|
||||
|
||||
#ifdef EXPRESS_BOARD
|
||||
// #include "common-hal/touchio/TouchIn.h"
|
||||
#define INTERNAL_CIRCUITPY_CONFIG_START_ADDR (0x00040000 - 0x100 - CIRCUITPY_INTERNAL_NVM_SIZE)
|
||||
#else
|
||||
#define INTERNAL_CIRCUITPY_CONFIG_START_ADDR (0x00040000 - 0x010000 - 0x100 - CIRCUITPY_INTERNAL_NVM_SIZE)
|
||||
#endif
|
||||
|
||||
fs_user_mount_t fs_user_mount_flash;
|
||||
mp_vfs_mount_t mp_vfs_mount_flash;
|
||||
|
||||
|
@ -62,6 +62,7 @@
|
||||
#include "peripherals/dma.h"
|
||||
#include "shared-bindings/rtc/__init__.h"
|
||||
#include "tick.h"
|
||||
#include "usb.h"
|
||||
|
||||
#ifdef CIRCUITPY_GAMEPAD_TICKS
|
||||
#include "shared-module/gamepad/__init__.h"
|
||||
@ -184,7 +185,6 @@ safe_mode_t port_init(void) {
|
||||
_pm_init();
|
||||
#endif
|
||||
clock_init();
|
||||
init_dynamic_clocks();
|
||||
|
||||
board_init();
|
||||
|
||||
@ -270,16 +270,6 @@ void reset_port(void) {
|
||||
|
||||
reset_all_pins();
|
||||
|
||||
// Set up debugging pins after reset_all_pins().
|
||||
|
||||
// Uncomment to init PIN_PA17 for debugging.
|
||||
// struct port_config pin_conf;
|
||||
// port_get_config_defaults(&pin_conf);
|
||||
//
|
||||
// pin_conf.direction = PORT_PIN_DIR_OUTPUT;
|
||||
// port_pin_set_config(MICROPY_HW_LED1, &pin_conf);
|
||||
// port_pin_set_output_level(MICROPY_HW_LED1, false);
|
||||
|
||||
// Output clocks for debugging.
|
||||
// not supported by SAMD51G; uncomment for SAMD51J or update for 51G
|
||||
// #ifdef SAMD51
|
||||
@ -291,52 +281,9 @@ void reset_port(void) {
|
||||
|
||||
usb_hid_reset();
|
||||
|
||||
// #ifdef CALIBRATE_CRYSTALLESS
|
||||
// // If we are on USB lets double check our fine calibration for the clock and
|
||||
// // save the new value if its different enough.
|
||||
// if (mp_msc_enabled) {
|
||||
// SYSCTRL->DFLLSYNC.bit.READREQ = 1;
|
||||
// uint16_t saved_calibration = 0x1ff;
|
||||
// if (strcmp((char*) INTERNAL_CIRCUITPY_CONFIG_START_ADDR, "CIRCUITPYTHON1") == 0) {
|
||||
// saved_calibration = ((uint16_t *) INTERNAL_CIRCUITPY_CONFIG_START_ADDR)[8];
|
||||
// }
|
||||
// while (SYSCTRL->PCLKSR.bit.DFLLRDY == 0) {
|
||||
// // TODO(tannewt): Run the mass storage stuff if this takes a while.
|
||||
// }
|
||||
// int16_t current_calibration = SYSCTRL->DFLLVAL.bit.FINE;
|
||||
// if (abs(current_calibration - saved_calibration) > 10) {
|
||||
// enum status_code error_code;
|
||||
// uint8_t page_buffer[NVMCTRL_ROW_SIZE];
|
||||
// for (int i = 0; i < NVMCTRL_ROW_PAGES; i++) {
|
||||
// do
|
||||
// {
|
||||
// error_code = nvm_read_buffer(INTERNAL_CIRCUITPY_CONFIG_START_ADDR + i * NVMCTRL_PAGE_SIZE,
|
||||
// page_buffer + i * NVMCTRL_PAGE_SIZE,
|
||||
// NVMCTRL_PAGE_SIZE);
|
||||
// } while (error_code == STATUS_BUSY);
|
||||
// }
|
||||
// // If this is the first write, include the header.
|
||||
// if (strcmp((char*) page_buffer, "CIRCUITPYTHON1") != 0) {
|
||||
// memcpy(page_buffer, "CIRCUITPYTHON1", 15);
|
||||
// }
|
||||
// // First 16 bytes (0-15) are ID. Little endian!
|
||||
// page_buffer[16] = current_calibration & 0xff;
|
||||
// page_buffer[17] = current_calibration >> 8;
|
||||
// do
|
||||
// {
|
||||
// error_code = nvm_erase_row(INTERNAL_CIRCUITPY_CONFIG_START_ADDR);
|
||||
// } while (error_code == STATUS_BUSY);
|
||||
// for (int i = 0; i < NVMCTRL_ROW_PAGES; i++) {
|
||||
// do
|
||||
// {
|
||||
// error_code = nvm_write_buffer(INTERNAL_CIRCUITPY_CONFIG_START_ADDR + i * NVMCTRL_PAGE_SIZE,
|
||||
// page_buffer + i * NVMCTRL_PAGE_SIZE,
|
||||
// NVMCTRL_PAGE_SIZE);
|
||||
// } while (error_code == STATUS_BUSY);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// #endif
|
||||
if (usb_connected()) {
|
||||
save_usb_clock_calibration();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user