diff options
| author | Scott Shawcroft <scott.shawcroft@gmail.com> | 2018-05-29 18:21:19 -0700 |
|---|---|---|
| committer | Scott Shawcroft <scott.shawcroft@gmail.com> | 2018-06-01 15:08:48 -0700 |
| commit | d0fb6e7a2fd6b60ecc0fc8a332a9de5ed32f88af (patch) | |
| tree | 0004029e575afaac3af1e898eda534628acaad9c | |
| parent | fd71e56891749837a823a4b555b84150b6fbc1e8 (diff) | |
atmel-samd: Add rotary encoder support.
Fixes #283
21 files changed, 537 insertions, 381 deletions
diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 162e6b3e6..1a406c1d0 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -249,6 +249,7 @@ SRC_C = \ peripherals/clocks.c \ peripherals/dma.c \ peripherals/events.c \ + peripherals/external_interrupts.c \ peripherals/sercom.c \ peripherals/timers.c \ peripherals/$(CHIP_FAMILY)/adc.c \ @@ -256,6 +257,7 @@ SRC_C = \ peripherals/$(CHIP_FAMILY)/clocks.c \ peripherals/$(CHIP_FAMILY)/dma.c \ peripherals/$(CHIP_FAMILY)/events.c \ + peripherals/$(CHIP_FAMILY)/external_interrupts.c \ peripherals/$(CHIP_FAMILY)/pins.c \ peripherals/$(CHIP_FAMILY)/sercom.c \ peripherals/$(CHIP_FAMILY)/timers.c \ diff --git a/ports/atmel-samd/common-hal/busio/SPI.c b/ports/atmel-samd/common-hal/busio/SPI.c index a2ee93c6d..a9bd55903 100644 --- a/ports/atmel-samd/common-hal/busio/SPI.c +++ b/ports/atmel-samd/common-hal/busio/SPI.c @@ -37,7 +37,6 @@ #include "supervisor/shared/rgb_led_status.h" #include "peripherals/dma.h" -//#include "peripherals/pins.h" #include "peripherals/sercom.h" void common_hal_busio_spi_construct(busio_spi_obj_t *self, diff --git a/ports/atmel-samd/common-hal/pulseio/PulseIn.c b/ports/atmel-samd/common-hal/pulseio/PulseIn.c index 266a9251e..99a60a829 100644 --- a/ports/atmel-samd/common-hal/pulseio/PulseIn.c +++ b/ports/atmel-samd/common-hal/pulseio/PulseIn.c @@ -34,90 +34,28 @@ #include "mpconfigport.h" #include "py/gc.h" #include "py/runtime.h" +#include "peripherals/external_interrupts.h" #include "peripherals/pins.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/pulseio/PulseIn.h" -#ifdef SAMD21 -#include "hpl/gclk/hpl_gclk_base.h" -#endif - #include "tick.h" -static pulseio_pulsein_obj_t *active_pulseins[EIC_EXTINT_NUM]; -static uint64_t last_ms[EIC_EXTINT_NUM]; -static uint16_t last_us[EIC_EXTINT_NUM]; - -bool eic_get_enable(void) { - #ifdef SAMD51 - return EIC->CTRLA.bit.ENABLE; - #endif - #ifdef SAMD21 - return EIC->CTRL.bit.ENABLE; - #endif -} - -void eic_set_enable(bool value) { - #ifdef SAMD51 - EIC->CTRLA.bit.ENABLE = value; - while (EIC->SYNCBUSY.bit.ENABLE != 0) {} - // This won't actually block long enough in Rev A of SAMD51 and will miss edges in the first - // three cycles of the peripheral clock. See the errata for details. It shouldn't impact us. - #endif - #ifdef SAMD21 - EIC->CTRL.bit.ENABLE = value; - while (EIC->STATUS.bit.SYNCBUSY != 0) {} - #endif -} - -void eic_reset(void) { - #ifdef SAMD51 - EIC->CTRLA.bit.SWRST = true; - while (EIC->SYNCBUSY.bit.SWRST != 0) {} - // This won't actually block long enough in Rev A of SAMD51 and will miss edges in the first - // three cycles of the peripheral clock. See the errata for details. It shouldn't impact us. - #endif - #ifdef SAMD21 - EIC->CTRL.bit.SWRST = true; - while (EIC->STATUS.bit.SYNCBUSY != 0) {} - #endif -} - -void pulsein_reset(void) { - for (int i = 0; i < EIC_EXTINT_NUM; i++) { - active_pulseins[i] = NULL; - last_ms[i] = 0; - last_us[i] = 0; - #ifdef SAMD51 - NVIC_DisableIRQ(EIC_0_IRQn + i); - NVIC_ClearPendingIRQ(EIC_0_IRQn + i); - #endif - } - eic_reset(); - #ifdef SAMD21 - NVIC_DisableIRQ(EIC_IRQn); - NVIC_ClearPendingIRQ(EIC_IRQn); - #endif -} - static void pulsein_set_config(pulseio_pulsein_obj_t* self, bool first_edge) { - uint8_t sense_setting = EIC_CONFIG_FILTEN0; + uint32_t sense_setting; if (!first_edge) { - sense_setting |= EIC_CONFIG_SENSE0_BOTH_Val; + sense_setting = EIC_CONFIG_SENSE0_BOTH_Val; + configure_eic_channel(self->channel, sense_setting); + return; } else if (self->idle_state) { - sense_setting |= EIC_CONFIG_SENSE0_FALL_Val; + sense_setting = EIC_CONFIG_SENSE0_FALL_Val; } else { - sense_setting |= EIC_CONFIG_SENSE0_RISE_Val; + sense_setting = EIC_CONFIG_SENSE0_RISE_Val; } - eic_set_enable(false); - uint8_t config_index = self->channel / 8; - uint8_t position = (self->channel % 8) * 4; - uint32_t masked_value = EIC->CONFIG[config_index].reg & ~(0xf << position); - EIC->CONFIG[config_index].reg = masked_value | (sense_setting << position); - eic_set_enable(true); + turn_on_eic_channel(self->channel, sense_setting, EIC_HANDLER_PULSEIN); } -static void pulsein_interrupt_handler(uint8_t channel) { +void pulsein_interrupt_handler(uint8_t channel) { // Grab the current time first. uint32_t current_us; uint64_t current_ms; @@ -125,16 +63,16 @@ static void pulsein_interrupt_handler(uint8_t channel) { // current_tick gives us the remaining us until the next tick but we want the number since the // last ms. current_us = 1000 - current_us; - pulseio_pulsein_obj_t* self = active_pulseins[channel]; + pulseio_pulsein_obj_t* self = get_eic_channel_data(channel); if (self->first_edge) { self->first_edge = false; pulsein_set_config(self, false); } else { - uint32_t ms_diff = current_ms - last_ms[self->channel]; - uint16_t us_diff = current_us - last_us[self->channel]; + uint32_t ms_diff = current_ms - self->last_ms; + uint16_t us_diff = current_us - self->last_us; uint32_t total_diff = us_diff; - if (last_us[self->channel] > current_us) { - total_diff = 1000 + current_us - last_us[self->channel]; + if (self->last_us > current_us) { + total_diff = 1000 + current_us - self->last_us; if (ms_diff > 1) { total_diff += (ms_diff - 1) * 1000; } @@ -154,8 +92,8 @@ static void pulsein_interrupt_handler(uint8_t channel) { self->start++; } } - last_ms[self->channel] = current_ms; - last_us[self->channel] = current_us; + self->last_ms = current_ms; + self->last_us = current_us; } void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self, @@ -163,17 +101,7 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self, if (!pin->has_extint) { mp_raise_RuntimeError("No hardware support on pin"); } - uint32_t mask = 1 << pin->extint_channel; - if (active_pulseins[pin->extint_channel] != NULL || - (eic_get_enable() == 1 && -#ifdef SAMD51 - ((EIC->INTENSET.bit.EXTINT & mask) != 0 || - (EIC->EVCTRL.bit.EXTINTEO & mask) != 0))) { -#endif -#ifdef SAMD21 - ((EIC->INTENSET.vec.EXTINT & mask) != 0 || - (EIC->EVCTRL.vec.EXTINTEO & mask) != 0))) { -#endif + if (eic_get_enable() && !eic_channel_free(pin->extint_channel)) { mp_raise_RuntimeError("EXTINT channel already in use"); } @@ -188,42 +116,22 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self, self->start = 0; self->len = 0; self->first_edge = true; + self->last_us = 0; + self->last_ms = 0; - active_pulseins[pin->extint_channel] = self; + set_eic_channel_data(pin->extint_channel, (void*) self); // Check to see if the EIC is enabled and start it up if its not.' - // SAMD51 EIC can only be clocked up to 100mhz so we use the 48mhz clock. if (eic_get_enable() == 0) { - #ifdef SAMD51 - MCLK->APBAMASK.bit.EIC_ = true; - hri_gclk_write_PCHCTRL_reg(GCLK, EIC_GCLK_ID, - GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos)); - #endif - - #ifdef SAMD21 - PM->APBAMASK.bit.EIC_ = true; - _gclk_enable_channel(EIC_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK0_Val); - #endif - - - #ifdef SAMD21 - NVIC_DisableIRQ(EIC_IRQn); - NVIC_ClearPendingIRQ(EIC_IRQn); - NVIC_EnableIRQ(EIC_IRQn); - #endif + turn_on_external_interrupt_controller(); } gpio_set_pin_function(pin->pin, GPIO_PIN_FUNCTION_A); - #ifdef SAMD51 - NVIC_DisableIRQ(EIC_0_IRQn + self->channel); - NVIC_ClearPendingIRQ(EIC_0_IRQn + self->channel); - NVIC_EnableIRQ(EIC_0_IRQn + self->channel); - #endif + turn_on_cpu_interrupt(self->channel); // Set config will enable the EIC. pulsein_set_config(self, true); - EIC->INTENSET.reg = mask << EIC_INTENSET_EXTINT_Pos; } bool common_hal_pulseio_pulsein_deinited(pulseio_pulsein_obj_t* self) { @@ -234,39 +142,9 @@ void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t* self) { if (common_hal_pulseio_pulsein_deinited(self)) { return; } - uint32_t mask = 1 << self->channel; - EIC->INTENCLR.reg = mask << EIC_INTENSET_EXTINT_Pos; - #ifdef SAMD51 - NVIC_DisableIRQ(EIC_0_IRQn + self->channel); - NVIC_ClearPendingIRQ(EIC_0_IRQn + self->channel); - #endif - active_pulseins[self->channel] = NULL; + turn_off_eic_channel(self->channel); reset_pin(self->pin); self->pin = NO_PIN; - - bool all_null = true; - for (uint8_t i = 0; all_null && i < 16; i++) { - all_null = all_null && active_pulseins[i] == NULL; - } - #ifdef SAMD21 - if (all_null && EIC->INTENSET.reg == 0) { - NVIC_DisableIRQ(EIC_IRQn); - NVIC_ClearPendingIRQ(EIC_IRQn); - } - #endif - // Test if all channels are null and deinit everything if they are. - if (all_null && EIC->EVCTRL.reg == 0 && EIC->INTENSET.reg == 0) { - eic_set_enable(false); - #ifdef SAMD51 - MCLK->APBAMASK.bit.EIC_ = false; - hri_gclk_write_PCHCTRL_reg(GCLK, EIC_GCLK_ID, 0); - #endif - - #ifdef SAMD21 - PM->APBAMASK.bit.EIC_ = false; - hri_gclk_write_CLKCTRL_reg(GCLK, GCLK_CLKCTRL_ID(EIC_GCLK_ID)); - #endif - } } void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t* self) { @@ -289,9 +167,9 @@ void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self, } // Reconfigure the pin and make sure its set to detect the first edge. - last_ms[self->channel] = 0; - last_us[self->channel] = 0; self->first_edge = true; + self->last_ms = 0; + self->last_us = 0; gpio_set_pin_function(self->pin, GPIO_PIN_FUNCTION_A); uint32_t mask = 1 << self->channel; // Clear previous interrupt state and re-enable it. @@ -343,69 +221,3 @@ uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self, common_hal_mcu_enable_interrupts(); return value; } - -void external_interrupt_handler(uint8_t channel) { - pulsein_interrupt_handler(channel); - EIC->INTFLAG.reg = (1 << channel) << EIC_INTFLAG_EXTINT_Pos; -} - -#ifdef SAMD21 -void EIC_Handler(void) { - for (uint8_t i = 0; i < 16; i++) { - if ((EIC->INTFLAG.vec.EXTINT & (1 << i)) != 0) { - external_interrupt_handler(i); - } - } -} -#endif - -#ifdef SAMD51 -void EIC_0_Handler(void) { - external_interrupt_handler(0); -} -void EIC_1_Handler(void) { - external_interrupt_handler(1); -} -void EIC_2_Handler(void) { - external_interrupt_handler(2); -} -void EIC_3_Handler(void) { - external_interrupt_handler(3); -} -void EIC_4_Handler(void) { - external_interrupt_handler(4); -} -void EIC_5_Handler(void) { - external_interrupt_handler(5); -} -void EIC_6_Handler(void) { - external_interrupt_handler(6); -} -void EIC_7_Handler(void) { - external_interrupt_handler(7); -} -void EIC_8_Handler(void) { - external_interrupt_handler(8); -} -void EIC_9_Handler(void) { - external_interrupt_handler(9); -} -void EIC_10_Handler(void) { - external_interrupt_handler(10); -} -void EIC_11_Handler(void) { - external_interrupt_handler(11); -} -void EIC_12_Handler(void) { - external_interrupt_handler(12); -} -void EIC_13_Handler(void) { - external_interrupt_handler(13); -} -void EIC_14_Handler(void) { - external_interrupt_handler(14); -} -void EIC_15_Handler(void) { - external_interrupt_handler(15); -} -#endif diff --git a/ports/atmel-samd/common-hal/pulseio/PulseIn.h b/ports/atmel-samd/common-hal/pulseio/PulseIn.h index e3af42e0a..3d79b50c3 100644 --- a/ports/atmel-samd/common-hal/pulseio/PulseIn.h +++ b/ports/atmel-samd/common-hal/pulseio/PulseIn.h @@ -41,8 +41,12 @@ typedef struct { volatile uint16_t start; volatile uint16_t len; volatile bool first_edge; + volatile uint64_t last_ms; + volatile uint16_t last_us; } pulseio_pulsein_obj_t; void pulsein_reset(void); +void pulsein_interrupt_handler(uint8_t channel); + #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_PULSEIO_PULSEIN_H diff --git a/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c b/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c index 58119fa7e..d86f4763d 100644 --- a/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c @@ -26,133 +26,104 @@ #include "common-hal/rotaryio/IncrementalEncoder.h" -#include <stdint.h> - #include "atmel_start_pins.h" -#include "hal/include/hal_gpio.h" -#include "mpconfigport.h" -#include "peripherals/pins.h" -#include "py/gc.h" +#include "peripherals/external_interrupts.h" #include "py/runtime.h" -#include "shared-bindings/microcontroller/__init__.h" - -#ifdef SAMD21 -#include "hpl/gclk/hpl_gclk_base.h" -#endif void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t* self, - const mcu_pin_obj_t* pin, uint16_t maxlen, bool idle_state) { -// if (!pin->has_extint) { -// mp_raise_RuntimeError("No hardware support on pin"); -// } -// uint32_t mask = 1 << pin->extint_channel; -// if (active_incrementalencoders[pin->extint_channel] != NULL || -// (eic_get_enable() == 1 && -// #ifdef SAMD51 -// ((EIC->INTENSET.bit.EXTINT & mask) != 0 || -// (EIC->EVCTRL.bit.EXTINTEO & mask) != 0))) { -// #endif -// #ifdef SAMD21 -// ((EIC->INTENSET.vec.EXTINT & mask) != 0 || -// (EIC->EVCTRL.vec.EXTINTEO & mask) != 0))) { -// #endif -// mp_raise_RuntimeError("EXTINT channel already in use"); -// } -// -// self->buffer = (uint16_t *) m_malloc(maxlen * sizeof(uint16_t), false); -// if (self->buffer == NULL) { -// mp_raise_msg_varg(&mp_type_MemoryError, "Failed to allocate RX buffer of %d bytes", maxlen * sizeof(uint16_t)); -// } -// self->channel = pin->extint_channel; -// self->pin = pin->pin; -// self->maxlen = maxlen; -// self->idle_state = idle_state; -// self->start = 0; -// self->len = 0; -// self->first_edge = true; -// -// active_incrementalencoders[pin->extint_channel] = self; -// -// // Check to see if the EIC is enabled and start it up if its not.' -// // SAMD51 EIC can only be clocked up to 100mhz so we use the 48mhz clock. -// if (eic_get_enable() == 0) { -// #ifdef SAMD51 -// MCLK->APBAMASK.bit.EIC_ = true; -// hri_gclk_write_PCHCTRL_reg(GCLK, EIC_GCLK_ID, -// GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos)); -// #endif -// -// #ifdef SAMD21 -// PM->APBAMASK.bit.EIC_ = true; -// _gclk_enable_channel(EIC_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK0_Val); -// #endif -// -// -// #ifdef SAMD21 -// NVIC_DisableIRQ(EIC_IRQn); -// NVIC_ClearPendingIRQ(EIC_IRQn); -// NVIC_EnableIRQ(EIC_IRQn); -// #endif -// } -// -// gpio_set_pin_function(pin->pin, GPIO_PIN_FUNCTION_A); -// -// #ifdef SAMD51 -// NVIC_DisableIRQ(EIC_0_IRQn + self->channel); -// NVIC_ClearPendingIRQ(EIC_0_IRQn + self->channel); -// NVIC_EnableIRQ(EIC_0_IRQn + self->channel); -// #endif -// -// // Set config will enable the EIC. -// incrementalencoder_set_config(self, true); -// EIC->INTENSET.reg = mask << EIC_INTENSET_EXTINT_Pos; + const mcu_pin_obj_t* pin_a, const mcu_pin_obj_t* pin_b) { + if (!pin_a->has_extint || !pin_a->has_extint) { + mp_raise_RuntimeError("Both pins must support hardware interrupts"); + } + + // TODO: The SAMD51 has a peripheral dedicated to quadrature encoder debugging. Use it instead + // of the external interrupt. + + if (eic_get_enable()) { + if (!eic_channel_free(pin_a->extint_channel) || !eic_channel_free(pin_b->extint_channel)) { + mp_raise_RuntimeError("A hardware interrupt channel is already in use"); + } + } else { + turn_on_external_interrupt_controller(); + } + + // These default settings apply when the EIC isn't yet enabled. + self->eic_channel_a = pin_a->extint_channel; + self->eic_channel_b = pin_b->extint_channel; + self->pin_a = pin_a->pin; + self->pin_b = pin_b->pin; + + gpio_set_pin_function(self->pin_a, GPIO_PIN_FUNCTION_A); + gpio_set_pin_pull_mode(self->pin_a, GPIO_PULL_UP); + + gpio_set_pin_function(self->pin_b, GPIO_PIN_FUNCTION_A); + gpio_set_pin_pull_mode(self->pin_b, GPIO_PULL_UP); + + set_eic_channel_data(self->eic_channel_a, (void*) self); + set_eic_channel_data(self->eic_channel_b, (void*) self); + + bool pin_a_level = gpio_get_pin_level(self->pin_a); + bool pin_b_level = gpio_get_pin_level(self->pin_b); + if (!pin_a_level && !pin_b_level) { + self->last_state = 1; + } else if (!pin_a_level && pin_b_level) { + self->last_state = 2; + } else if (pin_a_level && pin_b_level) { + self->last_state = 3; + } else { + self->last_state = 4; + } + + turn_on_eic_channel(self->eic_channel_a, EIC_CONFIG_SENSE0_BOTH_Val, EIC_HANDLER_INCREMENTAL_ENCODER); + turn_on_eic_channel(self->eic_channel_b, EIC_CONFIG_SENSE0_BOTH_Val, EIC_HANDLER_INCREMENTAL_ENCODER); } bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t* self) { - //return self->pin == NO_PIN; - return true; + return self->pin_a == NO_PIN; } void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_obj_t* self) { - // if (common_hal_rotaryio_incrementalencoder_deinited(self)) { - // return; - // } - // uint32_t mask = 1 << self->channel; - // EIC->INTENCLR.reg = mask << EIC_INTENSET_EXTINT_Pos; - // #ifdef SAMD51 - // NVIC_DisableIRQ(EIC_0_IRQn + self->channel); - // NVIC_ClearPendingIRQ(EIC_0_IRQn + self->channel); - // #endif - // active_incrementalencoders[self->channel] = NULL; - // reset_pin(self->pin); - // self->pin = NO_PIN; - // - // bool all_null = true; - // for (uint8_t i = 0; all_null && i < 16; i++) { - // all_null = all_null && active_incrementalencoders[i] == NULL; - // } - // #ifdef SAMD21 - // if (all_null && EIC->INTENSET.reg == 0) { - // NVIC_DisableIRQ(EIC_IRQn); - // NVIC_ClearPendingIRQ(EIC_IRQn); - // } - // #endif - // // Test if all channels are null and deinit everything if they are. - // if (all_null && EIC->EVCTRL.reg == 0 && EIC->INTENSET.reg == 0) { - // eic_set_enable(false); - // #ifdef SAMD51 - // MCLK->APBAMASK.bit.EIC_ = false; - // hri_gclk_write_PCHCTRL_reg(GCLK, EIC_GCLK_ID, 0); - // #endif - // - // #ifdef SAMD21 - // PM->APBAMASK.bit.EIC_ = false; - // hri_gclk_write_CLKCTRL_reg(GCLK, GCLK_CLKCTRL_ID(EIC_GCLK_ID)); - // #endif - // } + if (common_hal_rotaryio_incrementalencoder_deinited(self)) { + return; + } + turn_off_eic_channel(self->eic_channel_a); + turn_off_eic_channel(self->eic_channel_b); + reset_pin(self->pin_a); + self->pin_a = NO_PIN; + reset_pin(self->pin_b); + self->pin_b = NO_PIN; } mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t* self) { - return 0; + return self->position; +} + +void incrementalencoder_interrupt_handler(uint8_t channel) { + rotaryio_incrementalencoder_obj_t* self = get_eic_channel_data(channel); + // TODO(tannewt): If we need more speed then read the pin directly. gpio_get_pin_level has + // smarts to compensate for pin direction we don't need. + bool pin_a = gpio_get_pin_level(self->pin_a); + bool pin_b = gpio_get_pin_level(self->pin_b); + + uint8_t this_state; + if (!pin_a && !pin_b) { + this_state = 1; + } else if (!pin_a && pin_b) { + this_state = 2; + } else if (pin_a && pin_b) { + this_state = 3; + } else { + this_state = 4; + } + + // Handle wrap around explicitly. + if (this_state == 4 && self->last_state == 1) { + self->position -= 1; + } else if (this_state == 1 && self->last_state == 4) { + self->position += 1; + } else { + self->position += (this_state - self->last_state); + } + self->last_state = this_state; } diff --git a/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.h b/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.h index c878239bc..ffdf7da17 100644 --- a/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.h +++ b/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.h @@ -35,6 +35,13 @@ typedef struct { mp_obj_base_t base; uint8_t pin_a; uint8_t pin_b; + uint8_t eic_channel_a:4; + uint8_t eic_channel_b:4; + uint8_t last_state; + mp_int_t position; } rotaryio_incrementalencoder_obj_t; + +void incrementalencoder_interrupt_handler(uint8_t channel); + #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_ROTARYIO_INCREMENTALENCODER_H diff --git a/ports/atmel-samd/common-hal/rotaryio/__init__.c b/ports/atmel-samd/common-hal/rotaryio/__init__.c index 2bee925bc..0aae79c26 100644 --- a/ports/atmel-samd/common-hal/rotaryio/__init__.c +++ b/ports/atmel-samd/common-hal/rotaryio/__init__.c @@ -1 +1 @@ -// No pulseio module functions. +// No rotaryio module functions. diff --git a/ports/atmel-samd/external_flash/external_flash.c b/ports/atmel-samd/external_flash/external_flash.c index b0e40fc70..e6c407e73 100644 --- a/ports/atmel-samd/external_flash/external_flash.c +++ b/ports/atmel-samd/external_flash/external_flash.c @@ -36,7 +36,6 @@ #include "py/obj.h" #include "py/runtime.h" #include "lib/oofatfs/ff.h" -//#include "peripherals.h" #include "shared-bindings/microcontroller/__init__.h" #include "supervisor/shared/rgb_led_status.h" diff --git a/ports/atmel-samd/peripherals/clocks.c b/ports/atmel-samd/peripherals/clocks.c index a890210bf..bfcca4547 100644 --- a/ports/atmel-samd/peripherals/clocks.c +++ b/ports/atmel-samd/peripherals/clocks.c @@ -56,41 +56,20 @@ uint8_t find_free_gclk(uint16_t divisor) { return 0xff; } +static uint8_t last_static_clock = 0; + +void init_dynamic_clocks(void) { + // Find the last statically initialized clock and save it. Everything after will be reset with + // the VM via reset_gclks. + for (uint8_t i = 0; i < GCLK_GEN_NUM; i++) { + if (gclk_enabled(i)) { + last_static_clock = i; + } + } +} + void reset_gclks(void) { - // Never reset GCLK0 because its used for the core - #if CONF_GCLK_GEN_1_GENEN == 0 - disable_gclk(1); - #endif - #if CONF_GCLK_GEN_2_GENEN == 0 - disable_gclk(2); - #endif - #if CONF_GCLK_GEN_3_GENEN == 0 - disable_gclk(3); - #endif - #if CONF_GCLK_GEN_4_GENEN == 0 - disable_gclk(4); - #endif - #if CONF_GCLK_GEN_5_GENEN == 0 - disable_gclk(5); - #endif - #if CONF_GCLK_GEN_6_GENEN == 0 - disable_gclk(6); - #endif - #if CONF_GCLK_GEN_7_GENEN == 0 - disable_gclk(7); - #endif - #ifdef SAMD51 - #if CONF_GCLK_GEN_8_GENEN == 0 - disable_gclk(8); - #endif - #if CONF_GCLK_GEN_9_GENEN == 0 - disable_gclk(9); - #endif - #if CONF_GCLK_GEN_10_GENEN == 0 - disable_gclk(10); - #endif - #if CONF_GCLK_GEN_11_GENEN == 0 - disable_gclk(11); - #endif - #endif + for (uint8_t i = last_static_clock + 1; i < GCLK_GEN_NUM; i++) { + disable_gclk(i); + } } diff --git a/ports/atmel-samd/peripherals/clocks.h b/ports/atmel-samd/peripherals/clocks.h index 6b7cfedce..78b2e2e3d 100644 --- a/ports/atmel-samd/peripherals/clocks.h +++ b/ports/atmel-samd/peripherals/clocks.h @@ -63,6 +63,7 @@ static inline bool board_has_crystal(void) { } void clock_init(void); +void init_dynamic_clocks(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); diff --git a/ports/atmel-samd/peripherals/events.c b/ports/atmel-samd/peripherals/events.c index 9a4ab3b0c..2f6aea716 100644 --- a/ports/atmel-samd/peripherals/events.c +++ b/ports/atmel-samd/peripherals/events.c @@ -27,9 +27,6 @@ #include <stdint.h> #include "peripherals/events.h" -// -// #include "clocks.h" -// #include "py/runtime.h" uint8_t find_async_event_channel(void) { diff --git a/ports/atmel-samd/peripherals/external_interrupts.c b/ports/atmel-samd/peripherals/external_interrupts.c new file mode 100644 index 000000000..dfec6c998 --- /dev/null +++ b/ports/atmel-samd/peripherals/external_interrupts.c @@ -0,0 +1,105 @@ +/* + * 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 "common-hal/pulseio/PulseIn.h" +#include "common-hal/rotaryio/IncrementalEncoder.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "peripherals/external_interrupts.h" + +#include "sam.h" + +// This structure is used to share per-channel storage amongst all users of external interrupts. +// Without this there would be multiple arrays even though they are disjoint because each channel +// has one user. +static void *channel_data[EIC_EXTINT_NUM]; +static uint8_t channel_handler[EIC_EXTINT_NUM]; + +void external_interrupt_handler(uint8_t channel) { + uint8_t handler = channel_handler[channel]; + if (handler == EIC_HANDLER_PULSEIN) { + pulsein_interrupt_handler(channel); + } else if (handler == EIC_HANDLER_INCREMENTAL_ENCODER) { + incrementalencoder_interrupt_handler(channel); + } + EIC->INTFLAG.reg = (1 << channel) << EIC_INTFLAG_EXTINT_Pos; +} + +void configure_eic_channel(uint8_t eic_channel, uint32_t sense_setting) { + uint8_t config_index = eic_channel / 8; + uint8_t position = (eic_channel % 8) * 4; + #ifdef SAMD51 + eic_set_enable(false); + #endif + common_hal_mcu_disable_interrupts(); + uint32_t masked_value = EIC->CONFIG[config_index].reg & ~(0xf << position); + EIC->CONFIG[config_index].reg = masked_value | (sense_setting << position); + common_hal_mcu_enable_interrupts(); + #ifdef SAMD51 + eic_set_enable(true); + #endif +} + +void turn_on_eic_channel(uint8_t eic_channel, uint32_t sense_setting, + uint8_t channel_interrupt_handler) { + // We do very light filtering using majority voting. + sense_setting |= EIC_CONFIG_FILTEN0; + configure_eic_channel(eic_channel, sense_setting); + uint32_t mask = 1 << eic_channel; + EIC->INTENSET.reg = mask << EIC_INTENSET_EXTINT_Pos; + if (channel_interrupt_handler != EIC_HANDLER_NO_INTERRUPT) { + channel_handler[eic_channel] = channel_interrupt_handler; + turn_on_cpu_interrupt(eic_channel); + } +} + +void turn_off_eic_channel(uint8_t eic_channel) { + uint32_t mask = 1 << eic_channel; + EIC->INTENCLR.reg = mask << EIC_INTENSET_EXTINT_Pos; + #ifdef SAMD51 + NVIC_DisableIRQ(EIC_0_IRQn + eic_channel); + NVIC_ClearPendingIRQ(EIC_0_IRQn + eic_channel); + #endif + channel_data[eic_channel] = NULL; + + #ifdef SAMD21 + if (EIC->INTENSET.reg == 0) { + NVIC_DisableIRQ(EIC_IRQn); + NVIC_ClearPendingIRQ(EIC_IRQn); + } + #endif + // Test if all channels are null and deinit everything if they are. + if (EIC->EVCTRL.reg == 0 && EIC->INTENSET.reg == 0) { + turn_off_external_interrupt_controller(); + } +} + +void* get_eic_channel_data(uint8_t eic_channel) { + return channel_data[eic_channel]; +} + +void set_eic_channel_data(uint8_t eic_channel, void* data) { + channel_data[eic_channel] = data; +} diff --git a/ports/atmel-samd/peripherals/external_interrupts.h b/ports/atmel-samd/peripherals/external_interrupts.h new file mode 100644 index 000000000..12d820677 --- /dev/null +++ b/ports/atmel-samd/peripherals/external_interrupts.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#ifndef MICROPY_INCLUDED_ATMEL_SAMD_PERIPHERALS_EXTERNAL_INTERRUPTS_H +#define MICROPY_INCLUDED_ATMEL_SAMD_PERIPHERALS_EXTERNAL_INTERRUPTS_H + +#include <stdbool.h> +#include <stdint.h> + +#define EIC_HANDLER_NO_INTERRUPT 0x0 +#define EIC_HANDLER_PULSEIN 0x1 +#define EIC_HANDLER_INCREMENTAL_ENCODER 0x2 + +void turn_on_external_interrupt_controller(void); +void turn_off_external_interrupt_controller(void); +void turn_on_cpu_interrupt(uint8_t eic_channel); +void turn_on_eic_channel(uint8_t eic_channel, uint32_t sense_setting, + uint8_t channel_interrupt_handler); +void configure_eic_channel(uint8_t eic_channel, uint32_t sense_setting); +void turn_off_eic_channel(uint8_t eic_channel); +bool eic_channel_free(uint8_t eic_channel); +bool eic_get_enable(void); +void eic_set_enable(bool value); +void eic_reset(void); + +void* get_eic_channel_data(uint8_t eic_channel); +void set_eic_channel_data(uint8_t eic_channel, void* data); + +void external_interrupt_handler(uint8_t channel); + +#endif // MICROPY_INCLUDED_ATMEL_SAMD_PERIPHERALS_EXTERNAL_INTERRUPTS_H diff --git a/ports/atmel-samd/peripherals/samd21/clocks.c b/ports/atmel-samd/peripherals/samd21/clocks.c index 94b515814..aad251a84 100644 --- a/ports/atmel-samd/peripherals/samd21/clocks.c +++ b/ports/atmel-samd/peripherals/samd21/clocks.c @@ -124,17 +124,23 @@ static void init_clock_source_dfll48m(void) { void clock_init(void) { init_clock_source_osc8m(); - if (board_has_crystal()) + if (board_has_crystal()) { init_clock_source_xosc32k(); - else + } 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()) + if (board_has_crystal()) { enable_clock_generator(2, GCLK_GENCTRL_SRC_XOSC32K_Val, 32); - else + } else { enable_clock_generator(2, GCLK_GENCTRL_SRC_OSC32K_Val, 32); + } + + // Do this after all static clock init so that they aren't used dynamically. + init_dynamic_clocks(); } static bool clk_enabled(uint8_t clk) { diff --git a/ports/atmel-samd/peripherals/samd21/external_interrupts.c b/ports/atmel-samd/peripherals/samd21/external_interrupts.c new file mode 100644 index 000000000..209c439c0 --- /dev/null +++ b/ports/atmel-samd/peripherals/samd21/external_interrupts.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 "peripherals/external_interrupts.h" + +#include "hpl/gclk/hpl_gclk_base.h" +#include "peripherals/clocks.h" +#include "sam.h" + +void turn_on_external_interrupt_controller(void) { + PM->APBAMASK.bit.EIC_ = true; + _gclk_enable_channel(EIC_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK0_Val); + eic_set_enable(true); +} + +void turn_off_external_interrupt_controller(void) { + eic_set_enable(false); + PM->APBAMASK.bit.EIC_ = false; + hri_gclk_write_CLKCTRL_reg(GCLK, GCLK_CLKCTRL_ID(EIC_GCLK_ID)); +} + +void turn_on_cpu_interrupt(uint8_t eic_channel) { + // Ignore the channel since the CPU interrupt line is shared. + (void) eic_channel; + + NVIC_DisableIRQ(EIC_IRQn); + NVIC_ClearPendingIRQ(EIC_IRQn); + NVIC_EnableIRQ(EIC_IRQn); +} + +bool eic_get_enable(void) { + return EIC->CTRL.bit.ENABLE; +} + +void eic_set_enable(bool value) { + EIC->CTRL.bit.ENABLE = value; + while (EIC->STATUS.bit.SYNCBUSY != 0) {} +} + +void eic_reset(void) { + EIC->CTRL.bit.SWRST = true; + while (EIC->STATUS.bit.SYNCBUSY != 0) {} + for (int i = 0; i < EIC_EXTINT_NUM; i++) { + set_eic_channel_data(i, NULL); + } + NVIC_DisableIRQ(EIC_IRQn); + NVIC_ClearPendingIRQ(EIC_IRQn); +} + +bool eic_channel_free(uint8_t eic_channel) { + uint32_t mask = 1 << eic_channel; + return get_eic_channel_data(eic_channel) == NULL && + (EIC->INTENSET.vec.EXTINT & mask) == 0 && + (EIC->EVCTRL.vec.EXTINTEO & mask) == 0; +} + +void EIC_Handler(void) { + for (uint8_t i = 0; i < 16; i++) { + if ((EIC->INTFLAG.vec.EXTINT & (1 << i)) != 0) { + external_interrupt_handler(i); + } + } +} diff --git a/ports/atmel-samd/peripherals/samd51/external_interrupts.c b/ports/atmel-samd/peripherals/samd51/external_interrupts.c new file mode 100644 index 000000000..6006d480e --- /dev/null +++ b/ports/atmel-samd/peripherals/samd51/external_interrupts.c @@ -0,0 +1,132 @@ +/* + * 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 "peripherals/external_interrupts.h" + +#include "peripherals/clocks.h" +#include "sam.h" + +void turn_on_external_interrupt_controller(void) { + MCLK->APBAMASK.bit.EIC_ = true; + + // We use the 48mhz clock to lightly filter the incoming pulse to reduce spurious interrupts. + connect_gclk_to_peripheral(GCLK_PCHCTRL_GEN_GCLK1_Val, EIC_GCLK_ID); + eic_set_enable(true); +} + +void turn_off_external_interrupt_controller(void) { + eic_set_enable(false); + MCLK->APBAMASK.bit.EIC_ = false; + disconnect_gclk_from_peripheral(GCLK_PCHCTRL_GEN_GCLK1_Val, EIC_GCLK_ID); +} + +void turn_on_cpu_interrupt(uint8_t eic_channel) { + // Ignore the channel since the CPU interrupt line is shared. + (void) eic_channel; + + NVIC_DisableIRQ(EIC_0_IRQn + eic_channel); + NVIC_ClearPendingIRQ(EIC_0_IRQn + eic_channel); + NVIC_EnableIRQ(EIC_0_IRQn + eic_channel); +} + +bool eic_get_enable(void) { + return EIC->CTRLA.bit.ENABLE; +} + +void eic_set_enable(bool value) { + EIC->CTRLA.bit.ENABLE = value; + while (EIC->SYNCBUSY.bit.ENABLE != 0) {} + // This won't actually block long enough in Rev A of SAMD51 and will miss edges in the first + // three cycles of the peripheral clock. See the errata for details. It shouldn't impact us. +} + +void eic_reset(void) { + EIC->CTRLA.bit.SWRST = true; + while (EIC->SYNCBUSY.bit.SWRST != 0) {} + // This won't actually block long enough in Rev A of SAMD51 and will miss edges in the first + // three cycles of the peripheral clock. See the errata for details. It shouldn't impact us. + for (int i = 0; i < EIC_EXTINT_NUM; i++) { + set_eic_channel_data(i, NULL); + NVIC_DisableIRQ(EIC_0_IRQn + i); + NVIC_ClearPendingIRQ(EIC_0_IRQn + i); + } +} + +bool eic_channel_free(uint8_t eic_channel) { + uint32_t mask = 1 << eic_channel; + return get_eic_channel_data(eic_channel) == NULL && + (EIC->INTENSET.bit.EXTINT & mask) == 0 && + (EIC->EVCTRL.bit.EXTINTEO & mask) == 0; +} + +void EIC_0_Handler(void) { + external_interrupt_handler(0); +} +void EIC_1_Handler(void) { + external_interrupt_handler(1); +} +void EIC_2_Handler(void) { + external_interrupt_handler(2); +} +void EIC_3_Handler(void) { + external_interrupt_handler(3); +} +void EIC_4_Handler(void) { + external_interrupt_handler(4); +} +void EIC_5_Handler(void) { + external_interrupt_handler(5); +} +void EIC_6_Handler(void) { + external_interrupt_handler(6); +} +void EIC_7_Handler(void) { + external_interrupt_handler(7); +} +void EIC_8_Handler(void) { + external_interrupt_handler(8); +} +void EIC_9_Handler(void) { + external_interrupt_handler(9); +} +void EIC_10_Handler(void) { + external_interrupt_handler(10); +} +void EIC_11_Handler(void) { + external_interrupt_handler(11); +} +void EIC_12_Handler(void) { + external_interrupt_handler(12); +} +void EIC_13_Handler(void) { + external_interrupt_handler(13); +} +void EIC_14_Handler(void) { + external_interrupt_handler(14); +} +void EIC_15_Handler(void) { + external_interrupt_handler(15); +} diff --git a/ports/atmel-samd/peripherals/samd51/sercom.c b/ports/atmel-samd/peripherals/samd51/sercom.c index fc4421422..6d0a02fbd 100644 --- a/ports/atmel-samd/peripherals/samd51/sercom.c +++ b/ports/atmel-samd/peripherals/samd51/sercom.c @@ -28,8 +28,6 @@ #include "hpl/gclk/hpl_gclk_base.h" #include "hri/hri_mclk_d51.h" -// FIXME(tannewt): Should this be called sercom.c? - // The clock initializer values are rather random, so we need to put them in // tables for lookup. We can't compute them. diff --git a/ports/atmel-samd/peripherals/samd51/timers.c b/ports/atmel-samd/peripherals/samd51/timers.c index 305acce38..f25dbc0e4 100644 --- a/ports/atmel-samd/peripherals/samd51/timers.c +++ b/ports/atmel-samd/peripherals/samd51/timers.c @@ -29,8 +29,6 @@ #include "peripherals/timers.h" -//#include "common-hal/pulseio/PulseOut.h" - #include "hri/hri_gclk_d51.h" const uint8_t tcc_cc_num[5] = {6, 4, 3, 2, 2}; diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 55e0a7ecf..e815d0f1e 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -58,6 +58,7 @@ #include "peripherals/cache.h" #include "peripherals/clocks.h" #include "peripherals/events.h" +#include "peripherals/external_interrupts.h" #include "peripherals/dma.h" #include "shared-bindings/rtc/__init__.h" #include "tick.h" @@ -183,6 +184,7 @@ safe_mode_t port_init(void) { _pm_init(); #endif clock_init(); + init_dynamic_clocks(); board_init(); @@ -248,7 +250,7 @@ void reset_port(void) { #ifdef SAMD21 touchin_reset(); #endif - pulsein_reset(); + eic_reset(); pulseout_reset(); pwmout_reset(); diff --git a/shared-bindings/index.rst b/shared-bindings/index.rst index 76b5e340b..d98587880 100644 --- a/shared-bindings/index.rst +++ b/shared-bindings/index.rst @@ -17,6 +17,9 @@ SAMD51, SAMD51 Express, and ESP8266. NOTE 2: **SAMD** and/or **SAMD Express** without additional numbers, means both SAMD21 & SAMD51 versions are supported. +NOTE 3: The `pIRkey SAMD21 board <https://www.adafruit.com/product/3364>`_ is specialized and may not +have modules as listed below. + ================= ============================== Module Supported Ports ================= ============================== @@ -38,6 +41,7 @@ Module Supported Ports `os` **All Supported** `pulseio` **SAMD/SAMD Express** `random` **All Supported** +`rotaryio` **SAMD51, SAMD Express** `storage` **All Supported** `struct` **All Supported** `supervisor` **SAMD/SAMD Express** diff --git a/shared-bindings/rotaryio/__init__.c b/shared-bindings/rotaryio/__init__.c index db1ff3d4c..a0166771a 100644 --- a/shared-bindings/rotaryio/__init__.c +++ b/shared-bindings/rotaryio/__init__.c @@ -33,8 +33,8 @@ #include "shared-bindings/rotaryio/__init__.h" #include "shared-bindings/rotaryio/IncrementalEncoder.h" -//| :mod:`rotaryio` --- Support for pulse based protocols -//| ===================================================== +//| :mod:`rotaryio` --- Support for reading rotation sensors +//| ======================================================== //| //| .. module:: rotaryio //| :synopsis: Support for reading rotation sensors |
