From 7adc69baf98bfe17a1e94a23bd24ae5b5a32f963 Mon Sep 17 00:00:00 2001 From: Nick Moore Date: Wed, 6 Jun 2018 14:22:07 +1000 Subject: Initial pulseio.PulseIn implmentation for #716 ESP8266 --- ports/esp8266/common-hal/pulseio/PulseIn.c | 129 +++++++++++++++++++++++++++-- ports/esp8266/common-hal/pulseio/PulseIn.h | 13 ++- ports/esp8266/intr.c | 4 + 3 files changed, 139 insertions(+), 7 deletions(-) (limited to 'ports') diff --git a/ports/esp8266/common-hal/pulseio/PulseIn.c b/ports/esp8266/common-hal/pulseio/PulseIn.c index 783442b35..ae7d32f55 100644 --- a/ports/esp8266/common-hal/pulseio/PulseIn.c +++ b/ports/esp8266/common-hal/pulseio/PulseIn.c @@ -26,47 +26,164 @@ #include +#include +#include +#include "esp_mphal.h" + #include "mpconfigport.h" #include "py/gc.h" #include "py/runtime.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/pulseio/PulseIn.h" +#include "common-hal/microcontroller/__init__.h" + +// XXX map gpio pins to pulsein objects: kinda clumsy. +static pulseio_pulsein_obj_t *pulseio_pulsein_objs[GPIO_PIN_COUNT] = {0}; + +static void pulsein_set_interrupt(pulseio_pulsein_obj_t *self, bool rising, bool falling) { + ETS_GPIO_INTR_DISABLE(); + // Set interrupt mode + GPIO_REG_WRITE( + GPIO_PIN_ADDR(self->pin->gpio_number), + (GPIO_REG_READ(GPIO_PIN_ADDR(self->pin->gpio_number) & ~GPIO_PIN_INT_TYPE_MASK)) | + GPIO_PIN_INT_TYPE_SET( + (rising ? GPIO_PIN_INTR_POSEDGE : 0) | (falling ? GPIO_PIN_INTR_NEGEDGE : 0) + ) + ); + // Clear interrupt status + GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << self->pin->gpio_number); + ETS_GPIO_INTR_ENABLE(); +} + +void pulseio_pulsein_interrupt_handler(pulseio_pulsein_obj_t *self, uint32_t time_us) { + if (self->first_edge) { + self->first_edge = false; + pulsein_set_interrupt(self, true, true); + } else { + uint16_t elapsed_us = (uint16_t)(time_us - self->last_us); + uint16_t i = (self->start + self->len) % self->maxlen; + self->buffer[i] = elapsed_us; + if (self->len < self->maxlen) { + self->len++; + } else { + self->start++; + } + } + self->last_us = time_us; +} + +// XXX needs a better name, or a better abstraction +// XXX called from intr.c:pin_intr_handler_iram ... inelegantly +void pulsein_interrupt_handler(uint32_t status) { + uint32_t time_us = system_get_time(); + for (int i=0; igpio_number == NO_GPIO || pin->gpio_function == SPECIAL_CASE) { + mp_raise_msg_varg(&mp_type_ValueError, "No PulseIn support for %q", pin->name ); + } + PIN_FUNC_SELECT(pin->peripheral, pin->gpio_function); + PIN_PULLUP_DIS(pin->peripheral); + self->pin = pin; + + 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->maxlen = maxlen; + self->idle_state = idle_state; + self->start = 0; + self->len = 0; + self->first_edge = true; + self->last_us = 0; + pulseio_pulsein_objs[self->pin->gpio_number] = self; + pulsein_set_interrupt(self, !idle_state, idle_state); } bool common_hal_pulseio_pulsein_deinited(pulseio_pulsein_obj_t* self) { - return true; + return pulseio_pulsein_objs[self->pin->gpio_number] == NULL; } void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t* self) { + pulsein_set_interrupt(self, false, false); + pulseio_pulsein_objs[self->pin->gpio_number] = NULL; + PIN_FUNC_SELECT(self->pin->peripheral, 0); + m_free(self->buffer); } void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t* self) { + pulsein_set_interrupt(self, false, false); } void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self, uint16_t trigger_duration) { + // Make sure we're paused. + common_hal_pulseio_pulsein_pause(self); + + // Send the trigger pulse. + if (trigger_duration > 0) { + uint32_t mask = 1 << self->pin->gpio_number; + // switch pin to an output with state opposite idle state + gpio_output_set(self->idle_state ? 0 : mask, self->idle_state ? mask : 0, 0, 0); + gpio_output_set(0, 0, mask, 0); + common_hal_mcu_delay_us((uint32_t)trigger_duration); + // switch pin back to an open input + gpio_output_set(0, 0, 0, mask); + } + + common_hal_mcu_disable_interrupts(); + self->first_edge = true; + pulsein_set_interrupt(self, !self->idle_state, self->idle_state); + common_hal_mcu_enable_interrupts(); } void common_hal_pulseio_pulsein_clear(pulseio_pulsein_obj_t* self) { + common_hal_mcu_disable_interrupts(); + self->start = 0; + self->len = 0; + common_hal_mcu_enable_interrupts(); } uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t* self) { - return 0; + if (self->len == 0) { + mp_raise_IndexError("pop from an empty PulseIn"); + } + common_hal_mcu_disable_interrupts(); + uint16_t value = self->buffer[self->start]; + self->start = (self->start + 1) % self->maxlen; + self->len--; + common_hal_mcu_enable_interrupts(); + + return value; } uint16_t common_hal_pulseio_pulsein_get_maxlen(pulseio_pulsein_obj_t* self) { - return 0; + return self->maxlen; } uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t* self) { - return 0; + return self->len; } uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self, int16_t index) { - return 0; + common_hal_mcu_disable_interrupts(); + if (index < 0) { + index += self->len; + } + if (index < 0 || index >= self->len) { + common_hal_mcu_enable_interrupts(); + mp_raise_IndexError("index out of range"); + } + uint16_t value = self->buffer[(self->start + index) % self->maxlen]; + common_hal_mcu_enable_interrupts(); + return value; } diff --git a/ports/esp8266/common-hal/pulseio/PulseIn.h b/ports/esp8266/common-hal/pulseio/PulseIn.h index 437299f44..4b00ec1d6 100644 --- a/ports/esp8266/common-hal/pulseio/PulseIn.h +++ b/ports/esp8266/common-hal/pulseio/PulseIn.h @@ -28,11 +28,22 @@ #define MICROPY_INCLUDED_ESP8266_COMMON_HAL_PULSEIO_PULSEIN_H #include "py/obj.h" +#include "common-hal/microcontroller/Pin.h" typedef struct { mp_obj_base_t base; + const mcu_pin_obj_t *pin; + uint16_t *buffer; + uint16_t maxlen; + bool idle_state; + volatile uint16_t start; + volatile uint16_t len; + volatile bool first_edge; + volatile uint32_t last_us; } pulseio_pulsein_obj_t; -void pwmout_reset(void); +void pulsein_reset(void); + +void pulsein_interrupt_handler(uint32_t); #endif // MICROPY_INCLUDED_ESP8266_COMMON_HAL_PULSEIO_PULSEIN_H diff --git a/ports/esp8266/intr.c b/ports/esp8266/intr.c index 456d6cb04..394ad1c14 100644 --- a/ports/esp8266/intr.c +++ b/ports/esp8266/intr.c @@ -28,10 +28,14 @@ #include "ets_alt_task.h" #include "modmachine.h" +#include "common-hal/pulseio/PulseIn.h" // this is in a separate file so it can go in iRAM void pin_intr_handler_iram(void *arg) { uint32_t status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, status); pin_intr_handler(status); + + // XXX bit of a hack + pulsein_interrupt_handler(status); } -- cgit v1.2.3