From 07230f263789df2b7ecaaf8f82415496f9b95de2 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 14 Feb 2018 16:59:04 -0800 Subject: [PATCH] pulsein compiles but doesn't work --- ports/atmel-samd/common-hal/pulseio/PulseIn.c | 270 +++++++++++++----- ports/atmel-samd/supervisor/port.c | 3 +- 2 files changed, 207 insertions(+), 66 deletions(-) diff --git a/ports/atmel-samd/common-hal/pulseio/PulseIn.c b/ports/atmel-samd/common-hal/pulseio/PulseIn.c index 5f7ec7a091..d8d3e56c9d 100644 --- a/ports/atmel-samd/common-hal/pulseio/PulseIn.c +++ b/ports/atmel-samd/common-hal/pulseio/PulseIn.c @@ -28,6 +28,7 @@ #include +#include "atmel_start_pins.h" #include "hal/include/hal_gpio.h" #include "mpconfigport.h" @@ -45,84 +46,94 @@ static uint16_t last_us[EIC_EXTINT_NUM]; void pulsein_reset(void) { for (int i = 0; i < EIC_EXTINT_NUM; i++) { - if (active_pulseins[i] != NULL) { - //extint_chan_disable_callback(i, EXTINT_CALLBACK_TYPE_DETECT); - } 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->CTRLA.bit.SWRST = true; + while (EIC->SYNCBUSY.bit.SWRST != 0) {} + #ifdef SAMD21 + NVIC_DisableIRQ(EIC_IRQn); + NVIC_ClearPendingIRQ(EIC_IRQn); + #endif } static void pulsein_set_config(pulseio_pulsein_obj_t* self, bool first_edge) { - // struct extint_chan_conf config; - // extint_chan_get_config_defaults(&config); - // config.gpio_pin = self->pin; - // config.gpio_pin_pull = EXTINT_PULL_NONE; - // config.filter_input_signal = true; - + uint8_t sense_setting = EIC_CONFIG_FILTEN0; if (!first_edge) { - //config.detection_criteria = EXTINT_DETECT_BOTH; + sense_setting |= EIC_CONFIG_SENSE0_BOTH_Val; } else if (self->idle_state) { - //config.detection_criteria = EXTINT_DETECT_FALLING; + sense_setting |= EIC_CONFIG_SENSE0_FALL_Val; } else { - //config.detection_criteria = EXTINT_DETECT_RISING; + sense_setting |= EIC_CONFIG_SENSE0_RISE_Val; } - //extint_chan_disable_callback(self->channel, EXTINT_CALLBACK_TYPE_DETECT); - //extint_chan_set_config(self->channel, &config); - // Clear any interrupts that may have triggered without notifying the CPU. - EIC->INTFLAG.reg |= (1UL << self->channel); - //extint_chan_enable_callback(self->channel, EXTINT_CALLBACK_TYPE_DETECT); + EIC->CTRLA.bit.ENABLE = false; + while (EIC->SYNCBUSY.bit.ENABLE != 0) {} + 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->CTRLA.bit.ENABLE = true; + 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. } -//static void pulsein_callback(void) { +static void pulsein_interrupt_handler(uint8_t channel) { // Grab the current time first. - //uint16_t current_us = tc_get_count_value(&ms_timer); - // Add the overflow flag to account for tick interrupts that are blocked by - // this interrupt. - //uint64_t current_ms = ticks_ms + TC5->COUNT16.INTFLAG.bit.OVF; - //pulseio_pulsein_obj_t* self = active_pulseins[extint_get_current_channel()]; - //current_us = current_us * 1000 / self->ticks_per_ms; - // 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 total_diff = us_diff; - // if (last_us[self->channel] > current_us) { - // total_diff = 1000 + current_us - last_us[self->channel]; - // if (ms_diff > 1) { - // total_diff += (ms_diff - 1) * 1000; - // } - // } else { - // total_diff += ms_diff * 1000; - // } - // uint16_t duration = 0xffff; - // if (total_diff < duration) { - // duration = total_diff; - // } - // - // uint16_t i = (self->start + self->len) % self->maxlen; - // self->buffer[i] = duration; - // if (self->len < self->maxlen) { - // self->len++; - // } else { - // self->start++; - // } - // } -// last_ms[self->channel] = current_ms; -// last_us[self->channel] = current_us; -// } + uint32_t current_us; + uint64_t current_ms; + current_tick(¤t_ms, ¤t_us); + // 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]; + 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 total_diff = us_diff; + if (last_us[self->channel] > current_us) { + total_diff = 1000 + current_us - last_us[self->channel]; + if (ms_diff > 1) { + total_diff += (ms_diff - 1) * 1000; + } + } else { + total_diff += ms_diff * 1000; + } + uint16_t duration = 0xffff; + if (total_diff < duration) { + duration = total_diff; + } + + uint16_t i = (self->start + self->len) % self->maxlen; + self->buffer[i] = duration; + if (self->len < self->maxlen) { + self->len++; + } else { + self->start++; + } + } + last_ms[self->channel] = current_ms; + last_us[self->channel] = current_us; +} void common_hal_pulseio_pulsein_construct(pulseio_pulsein_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"); } - // TODO(tannewt): Switch to checking actual extint peripheral state when other - // classes use extints. - if (active_pulseins[pin->extint_channel] != NULL) { + uint32_t mask = 1 << pin->extint_channel; + if (active_pulseins[pin->extint_channel] != NULL || + (EIC->CTRLA.bit.ENABLE == 1 && + ((EIC->INTENSET.bit.EXTINT & mask) != 0 || + (EIC->EVCTRL.bit.EXTINTEO & mask) != 0))) { mp_raise_RuntimeError("EXTINT channel already in use"); } @@ -140,12 +151,39 @@ void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self, active_pulseins[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->CTRLA.bit.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. pulsein_set_config(self, true); - //extint_register_callback( - // pulsein_callback, - // self->channel, - // EXTINT_CALLBACK_TYPE_DETECT); - //extint_chan_enable_callback(self->channel, EXTINT_CALLBACK_TYPE_DETECT); + EIC->INTENSET.reg = mask << EIC_INTENSET_EXTINT_Pos; } bool common_hal_pulseio_pulsein_deinited(pulseio_pulsein_obj_t* self) { @@ -156,14 +194,44 @@ void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t* self) { if (common_hal_pulseio_pulsein_deinited(self)) { return; } - //extint_chan_disable_callback(self->channel, EXTINT_CALLBACK_TYPE_DETECT); + 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; 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->CTRLA.bit.ENABLE = 0; + #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; + _gclk_disable_channel(EIC_GCLK_ID, GCLK_CLKCTRL_GEN_GCLK0_Val); + #endif + } } void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t* self) { - //extint_chan_disable_callback(self->channel, EXTINT_CALLBACK_TYPE_DETECT); + uint32_t mask = 1 << self->channel; + EIC->INTENCLR.reg = mask << EIC_INTENSET_EXTINT_Pos; } void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self, @@ -184,6 +252,12 @@ void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self, last_ms[self->channel] = 0; last_us[self->channel] = 0; self->first_edge = true; + gpio_set_pin_function(self->pin, GPIO_PIN_FUNCTION_A); + uint32_t mask = 1 << self->channel; + // Clear previous interrupt state and re-enable it. + EIC->INTFLAG.reg = mask << EIC_INTFLAG_EXTINT_Pos; + EIC->INTENSET.reg = mask << EIC_INTENSET_EXTINT_Pos; + pulsein_set_config(self, true); } @@ -229,3 +303,69 @@ 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.bit.EXTINT & (1 << i)) == 1) { + 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/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index c120b01369..726a48d060 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -46,6 +46,7 @@ #include "common-hal/analogio/AnalogIn.h" #include "common-hal/analogio/AnalogOut.h" #include "common-hal/microcontroller/Pin.h" +#include "common-hal/pulseio/PulseIn.h" #include "common-hal/pulseio/PulseOut.h" #include "common-hal/pulseio/PWMOut.h" #include "tick.h" @@ -206,8 +207,8 @@ void reset_port(void) { // audioout_reset(); // touchin_reset(); // pdmin_reset(); -// pulsein_reset(); // #endif + pulsein_reset(); pulseout_reset(); pwmout_reset();