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:
Scott Shawcroft 2018-06-01 18:01:42 -07:00
parent de61bd0d05
commit e580d22f4a
5 changed files with 100 additions and 69 deletions

View File

@ -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

View File

@ -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);

View File

@ -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>

View File

@ -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;

View File

@ -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();
}
}
/**