diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index bb2d92b4a5..d6ed0707a9 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -225,6 +225,7 @@ SRC_C = \ audio_dma.c \ background.c \ clocks.c \ + $(CHIP_FAMILY)_clocks.c \ events.c \ fatfs_port.c \ flash_api.c \ @@ -238,6 +239,8 @@ SRC_C = \ timers.c \ usb.c \ usb_mass_storage.c \ + bindings/samd/__init__.c \ + bindings/samd/Clock.c \ boards/$(BOARD)/board.c \ boards/$(BOARD)/pins.c \ lib/oofatfs/ff.c \ diff --git a/ports/atmel-samd/README.rst b/ports/atmel-samd/README.rst index d475e3816d..0c1026644d 100644 --- a/ports/atmel-samd/README.rst +++ b/ports/atmel-samd/README.rst @@ -231,3 +231,9 @@ Mass storage All boards will also show up as a mass storage device. Make sure to eject it before resetting or disconnecting the board. + +Port Specific modules +--------------------- + +.. toctree:: + bindings/samd/__init__ diff --git a/ports/atmel-samd/asf4_conf/samd21/hpl_sysctrl_config.h b/ports/atmel-samd/asf4_conf/samd21/hpl_sysctrl_config.h index 5b9ec6ed6c..74ec2bee95 100644 --- a/ports/atmel-samd/asf4_conf/samd21/hpl_sysctrl_config.h +++ b/ports/atmel-samd/asf4_conf/samd21/hpl_sysctrl_config.h @@ -109,7 +109,7 @@ // Indicates whether configuration for OSC32K is enabled or not // enable_osc32k #ifndef CONF_OSC32K_CONFIG -#define CONF_OSC32K_CONFIG 0 +#define CONF_OSC32K_CONFIG 1 #endif // 32kHz Internal Oscillator (OSC32K) Control @@ -117,7 +117,7 @@ // Indicates whether Internal 32K Oscillator is enabled or not // osc32k_arch_enable #ifndef CONF_OSC32K_ENABLE -#define CONF_OSC32K_ENABLE 0 +#define CONF_OSC32K_ENABLE 1 #endif // On Demand Control @@ -142,7 +142,7 @@ // Enable 32 Khz Output // osc32k_arch_en32k #ifndef CONF_OSC32K_EN32K -#define CONF_OSC32K_EN32K 0 +#define CONF_OSC32K_EN32K 1 #endif // Enable 1K diff --git a/ports/atmel-samd/bindings/samd/Clock.c b/ports/atmel-samd/bindings/samd/Clock.c new file mode 100644 index 0000000000..81e8fb1cec --- /dev/null +++ b/ports/atmel-samd/bindings/samd/Clock.c @@ -0,0 +1,174 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Noralf Trønnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "clocks.h" +#include "bindings/samd/Clock.h" + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +//| .. currentmodule:: samd +//| +//| :class:`Clock` --- Clock reference +//| ------------------------------------------ +//| +//| Identifies a clock on the microcontroller. +//| +//| .. class:: Clock +//| +//| Identifies a clock on the microcontroller. They are fixed by the +//| hardware so they cannot be constructed on demand. Instead, use +//| `samd.clock` to reference the desired clock. +//| + +STATIC void samd_clock_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + samd_clock_obj_t *self = MP_OBJ_TO_PTR(self_in); + + mp_printf(print, "%q.%q.%s(", MP_QSTR_samd, MP_QSTR_clock, self->name); + if (clock_get_enabled(self->type, self->index)) { + mp_printf(print, "frequency=%u", clock_get_frequency(self->type, self->index)); + uint32_t calibration = clock_get_calibration(self->type, self->index); + if (calibration) { + mp_printf(print, ", calibration=%u", calibration); + } + } + mp_printf(print, ")"); +} + +//| .. attribute:: enabled +//| +//| Is the clock enabled? (read-only) +//| +STATIC mp_obj_t samd_clock_get_enabled(mp_obj_t self_in) { + samd_clock_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(clock_get_enabled(self->type, self->index)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(samd_clock_get_enabled_obj, samd_clock_get_enabled); + +const mp_obj_property_t samd_clock_enabled_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&samd_clock_get_enabled_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj, + }, +}; + +//| .. attribute:: parent +//| +//| Clock parent. (read-only) +//| +STATIC mp_obj_t samd_clock_get_parent(mp_obj_t self_in) { + samd_clock_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint8_t p_type, p_index; + if (!clock_get_parent(self->type, self->index, &p_type, &p_index)) + return mp_const_none; + + const mp_map_t* samd_map = &samd_clock_globals.map; + for (uint8_t i = 0; i < samd_map->alloc; i++) { + samd_clock_obj_t *iter = samd_map->table[i].value; + if (iter->type == p_type && iter->index == p_index) + return iter; + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(samd_clock_get_parent_obj, samd_clock_get_parent); + +const mp_obj_property_t samd_clock_parent_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&samd_clock_get_parent_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj, + }, +}; + +//| .. attribute:: frequency +//| +//| Clock frequency. (read-only) +//| +STATIC mp_obj_t samd_clock_get_frequency(mp_obj_t self_in) { + samd_clock_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int_from_uint(clock_get_frequency(self->type, self->index)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(samd_clock_get_frequency_obj, samd_clock_get_frequency); + +const mp_obj_property_t samd_clock_frequency_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&samd_clock_get_frequency_obj, + (mp_obj_t)&mp_const_none_obj, + (mp_obj_t)&mp_const_none_obj, + }, +}; + +//| .. attribute:: calibration +//| +//| Clock calibration. Not all clocks can be calibrated. +//| +STATIC mp_obj_t samd_clock_get_calibration(mp_obj_t self_in) { + samd_clock_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int_from_uint(clock_get_calibration(self->type, self->index)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(samd_clock_get_calibration_obj, samd_clock_get_calibration); + +STATIC mp_obj_t samd_clock_set_calibration(mp_obj_t self_in, mp_obj_t calibration) { + samd_clock_obj_t *self = MP_OBJ_TO_PTR(self_in); + int ret = clock_set_calibration(self->type, self->index, mp_obj_get_int(calibration)); + if (ret == -2) + mp_raise_AttributeError("calibration is read only"); + if (ret == -1) + mp_raise_ValueError("calibration is out of range"); + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_2(samd_clock_set_calibration_obj, samd_clock_set_calibration); + +const mp_obj_property_t samd_clock_calibration_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&samd_clock_get_calibration_obj, + (mp_obj_t)&samd_clock_set_calibration_obj, + (mp_obj_t)&mp_const_none_obj, + }, +}; + +STATIC const mp_rom_map_elem_t samd_clock_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_enabled), MP_ROM_PTR(&samd_clock_enabled_obj) }, + { MP_ROM_QSTR(MP_QSTR_parent), MP_ROM_PTR(&samd_clock_parent_obj) }, + { MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&samd_clock_frequency_obj) }, + { MP_ROM_QSTR(MP_QSTR_calibration), MP_ROM_PTR(&samd_clock_calibration_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(samd_clock_locals_dict, samd_clock_locals_dict_table); + +const mp_obj_type_t samd_clock_type = { + { &mp_type_type }, + .name = MP_QSTR_Clock, + .print = samd_clock_print, + .locals_dict = (mp_obj_t)&samd_clock_locals_dict, +}; diff --git a/ports/atmel-samd/bindings/samd/Clock.h b/ports/atmel-samd/bindings/samd/Clock.h new file mode 100644 index 0000000000..0d13ae2533 --- /dev/null +++ b/ports/atmel-samd/bindings/samd/Clock.h @@ -0,0 +1,77 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Noralf Trønnes + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_BINDINGS_SAMD_CLOCK_H +#define MICROPY_INCLUDED_ATMEL_SAMD_BINDINGS_SAMD_CLOCK_H + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + const char *name; + uint8_t type; + uint8_t index; +} samd_clock_obj_t; + +#define CLOCK(_name, _type, _index) \ +const samd_clock_obj_t clock_ ## _name = { \ + { &samd_clock_type }, \ + .name = #_name, \ + .type = _type, \ + .index = _index, \ +} + +#define CLOCK_SOURCE(_name) \ +const samd_clock_obj_t clock_ ## _name = { \ + { &samd_clock_type }, \ + .name = #_name, \ + .type = 0, \ + .index = GCLK_SOURCE_ ## _name, \ +} + +#define CLOCK_GCLK(_name) \ +const samd_clock_obj_t clock_ ## _name = { \ + { &samd_clock_type }, \ + .name = #_name, \ + .type = 1, \ + .index = _name ## _GCLK_ID, \ +} + +#define CLOCK_GCLK_(_name, _extra) \ +const samd_clock_obj_t clock_ ## _name ## _ ## _extra = { \ + { &samd_clock_type }, \ + .name = #_name "_" #_extra, \ + .type = 1, \ + .index = _name ## _GCLK_ID_ ## _extra, \ +} + +#define CLOCK_ENTRY(_name) { MP_ROM_QSTR(MP_QSTR_ ## _name), MP_ROM_PTR(&clock_ ## _name) } +#define CLOCK_ENTRY_(_name, _extra) { MP_ROM_QSTR(MP_QSTR_ ## _name ## _ ## _extra), MP_ROM_PTR(&clock_ ## _name ## _ ## _extra) } + +extern const mp_obj_type_t samd_clock_type; +extern const mp_obj_dict_t samd_clock_globals; + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_BINDINGS_SAMD_CLOCK_H diff --git a/ports/atmel-samd/bindings/samd/__init__.c b/ports/atmel-samd/bindings/samd/__init__.c new file mode 100644 index 0000000000..55a10001d2 --- /dev/null +++ b/ports/atmel-samd/bindings/samd/__init__.c @@ -0,0 +1,72 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Noralf Trønnes + * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" +#include "py/runtime.h" + +#include "bindings/samd/Clock.h" + +//| :mod:`samd` --- SAMD implementation settings +//| ================================================= +//| +//| .. module:: samd +//| :synopsis: SAMD implementation settings +//| :platform: SAMD21 +//| +//| Libraries +//| +//| .. toctree:: +//| :maxdepth: 3 +//| +//| Clock +//| + +//| :mod:`samd.clock` --- samd clock names +//| -------------------------------------------------------- +//| +//| .. module:: samd.clock +//| :synopsis: samd clock names +//| :platform: SAMD21 +//| +//| References to clocks as named by the microcontroller +//| +const mp_obj_module_t samd_clock_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&samd_clock_globals, +}; + +STATIC const mp_rom_map_elem_t samd_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_samd) }, + { MP_ROM_QSTR(MP_QSTR_clock), MP_ROM_PTR(&samd_clock_module) }, +}; + +STATIC MP_DEFINE_CONST_DICT(samd_module_globals, samd_module_globals_table); + +const mp_obj_module_t samd_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&samd_module_globals, +}; diff --git a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h index 56e9c8bb71..c85f553ba9 100644 --- a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.h @@ -46,3 +46,5 @@ GD25Q16C #include "external_flash/external_flash.h" + +#define BOARD_HAS_CRYSTAL 1 diff --git a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h index a433e99495..e635acd593 100644 --- a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.h @@ -47,3 +47,5 @@ GD25Q16C #include "external_flash/external_flash.h" + +#define BOARD_HAS_CRYSTAL 1 diff --git a/ports/atmel-samd/clocks.c b/ports/atmel-samd/clocks.c index be2fe0836b..a890210bf4 100644 --- a/ports/atmel-samd/clocks.c +++ b/ports/atmel-samd/clocks.c @@ -56,77 +56,6 @@ uint8_t find_free_gclk(uint16_t divisor) { return 0xff; } -bool gclk_enabled(uint8_t gclk) { - #ifdef SAMD51 - return GCLK->GENCTRL[gclk].bit.GENEN; - #endif - #ifdef SAMD21 - common_hal_mcu_disable_interrupts(); - // Explicitly do a byte write so the peripheral knows we're just wanting to read the channel - // rather than write to it. - *((uint8_t*) &GCLK->GENCTRL.reg) = gclk; - while (GCLK->STATUS.bit.SYNCBUSY == 1) {} - bool enabled = GCLK->GENCTRL.bit.GENEN; - common_hal_mcu_enable_interrupts(); - return enabled; - #endif -} - -void disable_gclk(uint8_t gclk) { - #ifdef SAMD51 - while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {} - GCLK->GENCTRL[gclk].bit.GENEN = false; - while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {} - #endif - #ifdef SAMD21 - while (GCLK->STATUS.bit.SYNCBUSY == 1) {} - GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(gclk); - while (GCLK->STATUS.bit.SYNCBUSY == 1) {} - #endif -} - -void connect_gclk_to_peripheral(uint8_t gclk, uint8_t peripheral) { - #ifdef SAMD21 - GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(peripheral) | GCLK_CLKCTRL_GEN(gclk) | GCLK_CLKCTRL_CLKEN; - #endif - #ifdef SAMD51 - GCLK->PCHCTRL[peripheral].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(gclk); - while(GCLK->SYNCBUSY.reg != 0) {} - #endif -} - -void disconnect_gclk_from_peripheral(uint8_t gclk, uint8_t peripheral) { - #ifdef SAMD21 - GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(peripheral) | GCLK_CLKCTRL_GEN(gclk); - #endif - #ifdef SAMD51 - GCLK->PCHCTRL[peripheral].reg = 0; - #endif -} - -void enable_clock_generator(uint8_t gclk, uint8_t source, uint16_t divisor) { - #ifdef SAMD21 - GCLK->GENDIV.reg = GCLK_GENDIV_ID(gclk) | GCLK_GENDIV_DIV(divisor); - GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(gclk) | GCLK_GENCTRL_SRC(source) | GCLK_GENCTRL_GENEN; - while (GCLK->STATUS.bit.SYNCBUSY != 0) {} - #endif - #ifdef SAMD51 - GCLK->GENCTRL[gclk].reg = GCLK_GENCTRL_SRC(source) | GCLK_GENCTRL_DIV(divisor) | GCLK_GENCTRL_GENEN; - while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {} - #endif -} - -void disable_clock_generator(uint8_t gclk) { - #ifdef SAMD21 - GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(gclk); - while (GCLK->STATUS.bit.SYNCBUSY != 0) {} - #endif - #ifdef SAMD51 - GCLK->GENCTRL[gclk].reg = 0; - while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {} - #endif -} - void reset_gclks(void) { // Never reset GCLK0 because its used for the core #if CONF_GCLK_GEN_1_GENEN == 0 diff --git a/ports/atmel-samd/clocks.h b/ports/atmel-samd/clocks.h index 99787d4ac2..6b7cfedce0 100644 --- a/ports/atmel-samd/clocks.h +++ b/ports/atmel-samd/clocks.h @@ -31,6 +31,7 @@ #include #include "include/sam.h" +#include "mpconfigboard.h" // for BOARD_HAS_CRYSTAL #ifdef SAMD51 #define CLOCK_48MHZ GCLK_GENCTRL_SRC_DFLL_Val @@ -44,12 +45,29 @@ uint8_t find_free_gclk(uint16_t divisor); bool gclk_enabled(uint8_t gclk); +void disable_gclk(uint8_t gclk); void reset_gclks(void); void connect_gclk_to_peripheral(uint8_t gclk, uint8_t peripheral); void disconnect_gclk_from_peripheral(uint8_t gclk, uint8_t peripheral); -void enable_clock_generator(uint8_t gclk, uint8_t source, uint16_t divisor); +void enable_clock_generator(uint8_t gclk, uint32_t source, uint16_t divisor); void disable_clock_generator(uint8_t gclk); +static inline bool board_has_crystal(void) { +#ifdef BOARD_HAS_CRYSTAL + return BOARD_HAS_CRYSTAL == 1; +#else + return false; +#endif +} + +void clock_init(void); + +bool clock_get_enabled(uint8_t type, uint8_t index); +bool clock_get_parent(uint8_t type, uint8_t index, uint8_t *p_type, uint8_t *p_index); +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); + #endif // MICROPY_INCLUDED_ATMEL_SAMD_CLOCKS_H diff --git a/ports/atmel-samd/mpconfigport.h b/ports/atmel-samd/mpconfigport.h index 5ac06944ad..e6c3a201fe 100644 --- a/ports/atmel-samd/mpconfigport.h +++ b/ports/atmel-samd/mpconfigport.h @@ -167,6 +167,7 @@ extern const struct _mp_obj_module_t math_module; extern const struct _mp_obj_module_t os_module; extern const struct _mp_obj_module_t random_module; extern const struct _mp_obj_module_t rtc_module; +extern const struct _mp_obj_module_t samd_module; extern const struct _mp_obj_module_t storage_module; extern const struct _mp_obj_module_t struct_module; extern const struct _mp_obj_module_t time_module; @@ -236,6 +237,7 @@ extern const struct _mp_obj_module_t usb_hid_module; { MP_OBJ_NEW_QSTR(MP_QSTR_pulseio), (mp_obj_t)&pulseio_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_random), (mp_obj_t)&random_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_rtc), (mp_obj_t)&rtc_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_samd),(mp_obj_t)&samd_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_storage), (mp_obj_t)&storage_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_struct), (mp_obj_t)&struct_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_supervisor), (mp_obj_t)&supervisor_module }, \ diff --git a/ports/atmel-samd/samd21_clocks.c b/ports/atmel-samd/samd21_clocks.c new file mode 100644 index 0000000000..0fc4d63e50 --- /dev/null +++ b/ports/atmel-samd/samd21_clocks.c @@ -0,0 +1,425 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "clocks.h" + +#include "hpl_gclk_config.h" + +#include "bindings/samd/Clock.h" +#include "shared-bindings/microcontroller/__init__.h" + +#include "py/runtime.h" + +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 + // rather than write to it. + *((uint8_t*) &GCLK->GENCTRL.reg) = gclk; + while (GCLK->STATUS.bit.SYNCBUSY == 1) {} + bool enabled = GCLK->GENCTRL.bit.GENEN; + common_hal_mcu_enable_interrupts(); + return enabled; +} + +void disable_gclk(uint8_t gclk) { + while (GCLK->STATUS.bit.SYNCBUSY == 1) {} + GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(gclk); + while (GCLK->STATUS.bit.SYNCBUSY == 1) {} +} + +void connect_gclk_to_peripheral(uint8_t gclk, uint8_t peripheral) { + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(peripheral) | GCLK_CLKCTRL_GEN(gclk) | GCLK_CLKCTRL_CLKEN; +} + +void disconnect_gclk_from_peripheral(uint8_t gclk, uint8_t peripheral) { + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(peripheral) | GCLK_CLKCTRL_GEN(gclk); +} + +void enable_clock_generator(uint8_t gclk, uint32_t source, uint16_t divisor) { + uint32_t divsel = 0; + if (gclk == 2 && divisor > 31) { + divsel = GCLK_GENCTRL_DIVSEL; + for (int i = 15; i > 4; i++) { + if (divisor & (1 << i)) { + divisor = i - 1; + break; + } + } + } + GCLK->GENDIV.reg = GCLK_GENDIV_ID(gclk) | GCLK_GENDIV_DIV(divisor); + GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(gclk) | GCLK_GENCTRL_SRC(source) | divsel | GCLK_GENCTRL_OE | GCLK_GENCTRL_GENEN; + while (GCLK->STATUS.bit.SYNCBUSY != 0) {} +} + +void disable_clock_generator(uint8_t gclk) { + GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(gclk); + while (GCLK->STATUS.bit.SYNCBUSY != 0) {} +} + +static void init_clock_source_osc8m(void) { + // Preserve CALIB and FRANGE + SYSCTRL->OSC8M.bit.ONDEMAND = 0; + SYSCTRL->OSC8M.bit.PRESC = 3; + SYSCTRL->OSC8M.bit.ENABLE = 1; + while (!SYSCTRL->PCLKSR.bit.OSC8MRDY) {} +} + +static void init_clock_source_osc32k(void) { + uint32_t calib = (*((uint32_t *)FUSES_OSC32K_CAL_ADDR) & FUSES_OSC32K_CAL_Msk) >> FUSES_OSC32K_CAL_Pos; + SYSCTRL->OSC32K.reg = SYSCTRL_OSC32K_CALIB(calib) | + SYSCTRL_OSC32K_EN32K | + SYSCTRL_OSC32K_ENABLE; + while (!SYSCTRL->PCLKSR.bit.OSC32KRDY) {} +} + +static void init_clock_source_xosc32k(void) { + SYSCTRL->XOSC32K.reg = SYSCTRL_XOSC32K_EN32K | + SYSCTRL_XOSC32K_XTALEN | + SYSCTRL_XOSC32K_ENABLE; + while (!SYSCTRL->PCLKSR.bit.XOSC32KRDY) {} +} + +static void init_clock_source_dfll48m(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) + coarse = 0x1f; + SYSCTRL->DFLLVAL.reg = SYSCTRL_DFLLVAL_COARSE(coarse) | + SYSCTRL_DFLLVAL_FINE(512); + SYSCTRL->DFLLCTRL.reg = SYSCTRL_DFLLCTRL_CCDIS | + SYSCTRL_DFLLCTRL_USBCRM | + SYSCTRL_DFLLCTRL_MODE | + SYSCTRL_DFLLCTRL_ENABLE; + while (!SYSCTRL->PCLKSR.bit.DFLLRDY) {} + while (GCLK->STATUS.bit.SYNCBUSY) {} +} + +void clock_init(void) +{ + init_clock_source_osc8m(); + if (board_has_crystal()) + init_clock_source_xosc32k(); + else + init_clock_source_osc32k(); + 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 + enable_clock_generator(2, GCLK_GENCTRL_SRC_OSC32K_Val, 32); +} + +static bool clk_enabled(uint8_t clk) { + common_hal_mcu_disable_interrupts(); + *((uint8_t*) &GCLK->CLKCTRL.reg) = clk; + while (GCLK->STATUS.bit.SYNCBUSY == 1) {} + bool enabled = GCLK->CLKCTRL.bit.CLKEN; + common_hal_mcu_enable_interrupts(); + return enabled; +} + +static uint8_t clk_get_generator(uint8_t clk) { + common_hal_mcu_disable_interrupts(); + *((uint8_t*) &GCLK->CLKCTRL.reg) = clk; + while (GCLK->STATUS.bit.SYNCBUSY == 1) {} + uint8_t gen = GCLK->CLKCTRL.bit.GEN; + common_hal_mcu_enable_interrupts(); + return gen; +} + +static uint8_t generator_get_source(uint8_t gen) { + common_hal_mcu_disable_interrupts(); + *((uint8_t*) &GCLK->GENCTRL.reg) = gen; + while (GCLK->STATUS.bit.SYNCBUSY == 1) {} + uint8_t src = GCLK->GENCTRL.bit.SRC; + common_hal_mcu_enable_interrupts(); + return src; +} + +static bool osc_enabled(uint8_t index) { + switch (index) { + case GCLK_SOURCE_XOSC: + return SYSCTRL->XOSC.bit.ENABLE; + // TODO: GCLK_SOURCE_GCLKIN + // TODO: GCLK_SOURCE_GCLKGEN1 + case GCLK_SOURCE_OSCULP32K: + return true; + case GCLK_SOURCE_OSC32K: + return SYSCTRL->OSC32K.bit.ENABLE; + case GCLK_SOURCE_XOSC32K: + return SYSCTRL->XOSC32K.bit.ENABLE; + case GCLK_SOURCE_OSC8M: + return SYSCTRL->OSC8M.bit.ENABLE; + case GCLK_SOURCE_DFLL48M: + return SYSCTRL->DFLLCTRL.bit.ENABLE; + case GCLK_SOURCE_DPLL96M: + return SYSCTRL->DPLLCTRLA.bit.ENABLE; + }; + return false; +} + +static uint32_t osc_get_frequency(uint8_t index) { + switch (index) { + case GCLK_SOURCE_XOSC: + return 0; // unknown 0.4-32MHz + // TODO: GCLK_SOURCE_GCLKIN + // TODO: GCLK_SOURCE_GCLKGEN1 + case GCLK_SOURCE_OSCULP32K: + case GCLK_SOURCE_OSC32K: + case GCLK_SOURCE_XOSC32K: + return 32768; + case GCLK_SOURCE_OSC8M: + return 8000000; + case GCLK_SOURCE_DFLL48M: + return 48000000; + case GCLK_SOURCE_DPLL96M: + return 96000000; + } + return 0; +} + +bool clock_get_enabled(uint8_t type, uint8_t index) { + if (type == 0) + return osc_enabled(index); + if (type == 1) + return clk_enabled(index); + if (type == 2) + return SysTick->CTRL & SysTick_CTRL_ENABLE_Msk; + return false; +} + +bool clock_get_parent(uint8_t type, uint8_t index, uint8_t *p_type, uint8_t *p_index) { + if (type == 1 && index <= 0x24 && clk_enabled(index)) { + *p_type = 0; + *p_index = generator_get_source(clk_get_generator(index)); + return true; + } + if (type == 2 && index == 0) { + *p_type = 0; + *p_index = generator_get_source(0); + return true; + } + return false; +} + +uint32_t clock_get_frequency(uint8_t type, uint8_t index) { + if (type == 0) { + return osc_get_frequency(index); + } + if (type == 1) { + if (!clk_enabled(index)) + return 0; + + uint8_t gen = clk_get_generator(index); + + common_hal_mcu_disable_interrupts(); + *((uint8_t*) &GCLK->GENCTRL.reg) = gen; + *((uint8_t*) &GCLK->GENDIV.reg) = gen; + while (GCLK->STATUS.bit.SYNCBUSY == 1) {} + + uint8_t src = GCLK->GENCTRL.bit.SRC; + uint32_t div; + if (GCLK->GENCTRL.bit.DIVSEL) { + div = 1 << (GCLK->GENDIV.bit.DIV + 1); + } else { + div = GCLK->GENDIV.bit.DIV; + if (!div) + div = 1; + } + common_hal_mcu_enable_interrupts(); + + return osc_get_frequency(src) / div; + } + if (type == 2 && index == 0) { + return clock_get_frequency(0, generator_get_source(0)) / SysTick->LOAD; + } + return 0; +} + +uint32_t clock_get_calibration(uint8_t type, uint8_t index) { + if (type == 0) { + switch (index) { + case GCLK_SOURCE_OSCULP32K: + return SYSCTRL->OSCULP32K.bit.CALIB; + case GCLK_SOURCE_OSC32K: + return SYSCTRL->OSC32K.bit.CALIB; + case GCLK_SOURCE_OSC8M: + return SYSCTRL->OSC8M.bit.CALIB; + }; + } + if (type == 2 && index == 0) { + return SysTick->LOAD + 1; + } + return 0; +} + +int clock_set_calibration(uint8_t type, uint8_t index, uint32_t val) { + if (type == 0) { + switch (index) { + case GCLK_SOURCE_OSCULP32K: + if (val > 0x1f) + return -1; + SYSCTRL->OSCULP32K.bit.CALIB = val; + return 0; + case GCLK_SOURCE_OSC32K: + if (val > 0x7f) + return -1; + SYSCTRL->OSC32K.bit.CALIB = val; + return 0; + case GCLK_SOURCE_OSC8M: + if (val > 0xfff) + return -1; + SYSCTRL->OSC8M.bit.CALIB = val; + return 0; + }; + } + if (type == 2 && index == 0) { + if (val < 0x1000 || val > 0x1000000) + return -1; + SysTick->LOAD = val - 1; + return 0; + } + return -2; // calibration is read only +} + +#ifdef SAMD21_EXPOSE_ALL_CLOCKS +CLOCK_SOURCE(XOSC); +CLOCK_SOURCE(GCLKIN); +CLOCK_SOURCE(GCLKGEN1); +CLOCK_SOURCE(OSCULP32K); +#endif +CLOCK_SOURCE(OSC32K); +CLOCK_SOURCE(XOSC32K); +#ifdef SAMD21_EXPOSE_ALL_CLOCKS +CLOCK_SOURCE(OSC8M); +CLOCK_SOURCE(DFLL48M); +CLOCK_SOURCE(DPLL96M); + +CLOCK_GCLK_(SYSCTRL, DFLL48); +CLOCK_GCLK_(SYSCTRL, FDPLL); +CLOCK_GCLK_(SYSCTRL, FDPLL32K); +CLOCK_GCLK(WDT); +#endif +CLOCK_GCLK(RTC); +#ifdef SAMD21_EXPOSE_ALL_CLOCKS +CLOCK_GCLK(EIC); +CLOCK_GCLK(USB); +CLOCK_GCLK_(EVSYS, 0); +CLOCK_GCLK_(EVSYS, 1); +CLOCK_GCLK_(EVSYS, 2); +CLOCK_GCLK_(EVSYS, 3); +CLOCK_GCLK_(EVSYS, 4); +CLOCK_GCLK_(EVSYS, 5); +CLOCK_GCLK_(EVSYS, 6); +CLOCK_GCLK_(EVSYS, 7); +CLOCK_GCLK_(EVSYS, 8); +CLOCK_GCLK_(EVSYS, 9); +CLOCK_GCLK_(EVSYS, 10); +CLOCK_GCLK_(EVSYS, 11); +CLOCK(SERCOMx_SLOW, 1, 19); +CLOCK_GCLK_(SERCOM0, CORE); +CLOCK_GCLK_(SERCOM1, CORE); +CLOCK_GCLK_(SERCOM2, CORE); +CLOCK_GCLK_(SERCOM3, CORE); +CLOCK_GCLK_(SERCOM4, CORE); +CLOCK_GCLK_(SERCOM5, CORE); +CLOCK(TCC0_TCC1, 1, 26); +CLOCK(TCC2_TCC3, 1, 27); +CLOCK(TC4_TC5, 1, 28); +CLOCK(TC6_TC7, 1, 29); +CLOCK_GCLK(ADC); +CLOCK_GCLK_(AC, DIG); +CLOCK_GCLK_(AC, ANA); +CLOCK_GCLK(DAC); +CLOCK_GCLK(PTC); +CLOCK_GCLK_(I2S, 0); +CLOCK_GCLK_(I2S, 1); + +CLOCK(SYSTICK, 2, 0); +#endif + +STATIC const mp_rom_map_elem_t samd_clock_global_dict_table[] = { +#ifdef SAMD21_EXPOSE_ALL_CLOCKS + CLOCK_ENTRY(XOSC), + CLOCK_ENTRY(GCLKIN), + CLOCK_ENTRY(GCLKGEN1), + CLOCK_ENTRY(OSCULP32K), +#endif + CLOCK_ENTRY(OSC32K), + CLOCK_ENTRY(XOSC32K), +#ifdef SAMD21_EXPOSE_ALL_CLOCKS + CLOCK_ENTRY(OSC8M), + CLOCK_ENTRY(DFLL48M), + CLOCK_ENTRY(DPLL96M), + CLOCK_ENTRY_(SYSCTRL, DFLL48), + CLOCK_ENTRY_(SYSCTRL, FDPLL), + CLOCK_ENTRY_(SYSCTRL, FDPLL32K), + CLOCK_ENTRY(WDT), +#endif + CLOCK_ENTRY(RTC), +#ifdef SAMD21_EXPOSE_ALL_CLOCKS + CLOCK_ENTRY(EIC), + CLOCK_ENTRY(USB), + CLOCK_ENTRY_(EVSYS, 0), + CLOCK_ENTRY_(EVSYS, 1), + CLOCK_ENTRY_(EVSYS, 2), + CLOCK_ENTRY_(EVSYS, 3), + CLOCK_ENTRY_(EVSYS, 4), + CLOCK_ENTRY_(EVSYS, 5), + CLOCK_ENTRY_(EVSYS, 6), + CLOCK_ENTRY_(EVSYS, 7), + CLOCK_ENTRY_(EVSYS, 8), + CLOCK_ENTRY_(EVSYS, 9), + CLOCK_ENTRY_(EVSYS, 10), + CLOCK_ENTRY_(EVSYS, 11), + CLOCK_ENTRY(SERCOMx_SLOW), + CLOCK_ENTRY_(SERCOM0, CORE), + CLOCK_ENTRY_(SERCOM1, CORE), + CLOCK_ENTRY_(SERCOM2, CORE), + CLOCK_ENTRY_(SERCOM3, CORE), + CLOCK_ENTRY_(SERCOM4, CORE), + CLOCK_ENTRY_(SERCOM5, CORE), + CLOCK_ENTRY(TCC0_TCC1), + CLOCK_ENTRY(TCC2_TCC3), + CLOCK_ENTRY(TC4_TC5), + CLOCK_ENTRY(TC6_TC7), + CLOCK_ENTRY(ADC), + CLOCK_ENTRY_(AC, DIG), + CLOCK_ENTRY_(AC, ANA), + CLOCK_ENTRY(DAC), + CLOCK_ENTRY(PTC), + CLOCK_ENTRY_(I2S, 0), + CLOCK_ENTRY_(I2S, 1), + + CLOCK_ENTRY(SYSTICK), +#endif +}; +MP_DEFINE_CONST_DICT(samd_clock_globals, samd_clock_global_dict_table); diff --git a/ports/atmel-samd/samd51_clocks.c b/ports/atmel-samd/samd51_clocks.c new file mode 100644 index 0000000000..cc3dd7917a --- /dev/null +++ b/ports/atmel-samd/samd51_clocks.c @@ -0,0 +1,86 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "clocks.h" + +#include "hpl_gclk_config.h" + +#include "shared-bindings/microcontroller/__init__.h" + +#include "py/runtime.h" + +bool gclk_enabled(uint8_t gclk) { + return GCLK->GENCTRL[gclk].bit.GENEN; +} + +void disable_gclk(uint8_t gclk) { + while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {} + GCLK->GENCTRL[gclk].bit.GENEN = false; + while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {} +} + +void connect_gclk_to_peripheral(uint8_t gclk, uint8_t peripheral) { + GCLK->PCHCTRL[peripheral].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(gclk); + while(GCLK->SYNCBUSY.reg != 0) {} +} + +void disconnect_gclk_from_peripheral(uint8_t gclk, uint8_t peripheral) { + GCLK->PCHCTRL[peripheral].reg = 0; +} + +void enable_clock_generator(uint8_t gclk, uint32_t source, uint16_t divisor) { + GCLK->GENCTRL[gclk].reg = GCLK_GENCTRL_SRC(source) | GCLK_GENCTRL_DIV(divisor) | GCLK_GENCTRL_GENEN; + while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {} +} + +void disable_clock_generator(uint8_t gclk) { + GCLK->GENCTRL[gclk].reg = 0; + while ((GCLK->SYNCBUSY.vec.GENCTRL & (1 << gclk)) != 0) {} +} + +bool clock_get_enabled(uint8_t type, uint8_t index) { + return false; +} + +bool clock_get_parent(uint8_t type, uint8_t index, uint8_t *p_type, uint8_t *p_index) { + return false; +} + +uint32_t clock_get_frequency(uint8_t type, uint8_t index) { + return 0; +} + +uint32_t clock_get_calibration(uint8_t type, uint8_t index) { + return 0; +} + +int clock_set_calibration(uint8_t type, uint8_t index, uint32_t val) { + return -2; +} + +STATIC const mp_rom_map_elem_t samd_clock_global_dict_table[] = { +}; +MP_DEFINE_CONST_DICT(samd_clock_globals, samd_clock_global_dict_table); diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 103f39c753..8e6f2b1857 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -177,8 +177,14 @@ safe_mode_t port_init(void) { } #endif +#ifdef SAMD21 + hri_nvmctrl_set_CTRLB_RWS_bf(NVMCTRL, 2); + _pm_init(); + clock_init(); +#endif +#ifdef SAMD51 init_mcu(); - +#endif board_init(); // Configure millisecond timer initialization.