diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index b8d186621f..ca9583df7e 100755 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -101,6 +101,7 @@ SRC_NRFX = $(addprefix nrfx/,\ drivers/src/nrfx_timer.c \ drivers/src/nrfx_twim.c \ drivers/src/nrfx_uarte.c \ + drivers/src/nrfx_gpiote.c \ ) ifdef EXTERNAL_FLASH_DEVICES diff --git a/ports/nrf/common-hal/neopixel_write/__init__.c b/ports/nrf/common-hal/neopixel_write/__init__.c index c9da436b8a..f810a44cc3 100644 --- a/ports/nrf/common-hal/neopixel_write/__init__.c +++ b/ports/nrf/common-hal/neopixel_write/__init__.c @@ -141,7 +141,7 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout for ( uint16_t n = 0; n < numBytes; n++ ) { uint8_t pix = pixels[n]; - for ( uint8_t mask = 0x80, i = 0; mask > 0; mask >>= 1, i++ ) { + for ( uint8_t mask = 0x80; mask > 0; mask >>= 1 ) { pixels_pattern[pos] = (pix & mask) ? MAGIC_T1H : MAGIC_T0H; pos++; } diff --git a/ports/nrf/common-hal/pulseio/PulseIn.c b/ports/nrf/common-hal/pulseio/PulseIn.c index 3d6d266af3..6e5825af62 100644 --- a/ports/nrf/common-hal/pulseio/PulseIn.c +++ b/ports/nrf/common-hal/pulseio/PulseIn.c @@ -27,6 +27,7 @@ #include "common-hal/pulseio/PulseIn.h" #include +#include #include "py/mpconfig.h" #include "py/gc.h" @@ -35,50 +36,247 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/pulseio/PulseIn.h" -void pulsein_reset(void) { +#include "tick.h" +#include "nrfx_gpiote.h" +// obj array to map pin -> self since nrfx hide the mapping +static pulseio_pulsein_obj_t* _objs[GPIOTE_CH_NUM]; + +// return index of the object in array +static int _find_pulsein_obj(pulseio_pulsein_obj_t* obj) { + for(int i = 0; i < NRFX_ARRAY_SIZE(_objs); i++ ) { + if ( _objs[i] == obj) { + return i; + } + } + + return -1; +} + +static void _pulsein_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { + // Grab the current time first. + 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 = NULL; + for(int i = 0; i < NRFX_ARRAY_SIZE(_objs); i++ ) { + if ( _objs[i] && _objs[i]->pin == pin ) { + self = _objs[i]; + break; + } + } + if ( !self ) return; + + if (self->first_edge) { + // first pulse is opposite state from idle + bool state = nrf_gpio_pin_read(self->pin); + if ( self->idle_state != state ) { + self->first_edge = false; + } + }else { + 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 (self->last_us > current_us) { + total_diff = 1000 + current_us - self->last_us; + 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++; + } + } + + self->last_ms = current_ms; + self->last_us = current_us; +} + +void pulsein_reset(void) { + if ( nrfx_gpiote_is_init() ) { + nrfx_gpiote_uninit(); + } + nrfx_gpiote_init(); + + memset(_objs, 0, sizeof(_objs)); } void common_hal_pulseio_pulsein_construct(pulseio_pulsein_obj_t* self, const mcu_pin_obj_t* pin, uint16_t maxlen, bool idle_state) { - mp_raise_NotImplementedError(NULL); + int idx = _find_pulsein_obj(NULL); + if ( idx < 0 ) { + mp_raise_NotImplementedError(NULL); + } + _objs[idx] = self; + + self->buffer = (uint16_t *) m_malloc(maxlen * sizeof(uint16_t), false); + if (self->buffer == NULL) { + mp_raise_msg_varg(&mp_type_MemoryError, translate("Failed to allocate RX buffer of %d bytes"), maxlen * sizeof(uint16_t)); + } + + self->pin = pin->number; + self->maxlen = maxlen; + self->idle_state = idle_state; + self->start = 0; + self->len = 0; + self->first_edge = true; + self->paused = false; + self->last_us = 0; + self->last_ms = 0; + + claim_pin(pin); + + nrfx_gpiote_in_config_t cfg = { + .sense = NRF_GPIOTE_POLARITY_TOGGLE, + .pull = NRF_GPIO_PIN_NOPULL, // idle_state ? NRF_GPIO_PIN_PULLDOWN : NRF_GPIO_PIN_PULLUP, + .is_watcher = false, // nrf_gpio_cfg_watcher vs nrf_gpio_cfg_input + .hi_accuracy = true, + .skip_gpio_setup = false + }; + nrfx_gpiote_in_init(self->pin, &cfg, _pulsein_handler); + nrfx_gpiote_in_event_enable(self->pin, true); } bool common_hal_pulseio_pulsein_deinited(pulseio_pulsein_obj_t* self) { - return 1; + return self->pin == NO_PIN; } void common_hal_pulseio_pulsein_deinit(pulseio_pulsein_obj_t* self) { + if (common_hal_pulseio_pulsein_deinited(self)) { + return; + } + nrfx_gpiote_in_event_disable(self->pin); + nrfx_gpiote_in_uninit(self->pin); + + // mark local array as invalid + int idx = _find_pulsein_obj(self); + if ( idx < 0 ) { + mp_raise_NotImplementedError(NULL); + } + _objs[idx] = NULL; + + reset_pin_number(self->pin); + self->pin = NO_PIN; } void common_hal_pulseio_pulsein_pause(pulseio_pulsein_obj_t* self) { - + nrfx_gpiote_in_event_disable(self->pin); + self->paused = true; } void common_hal_pulseio_pulsein_resume(pulseio_pulsein_obj_t* self, uint16_t trigger_duration) { + // Make sure we're paused. + if ( !self->paused ) { + common_hal_pulseio_pulsein_pause(self); + } + // Send the trigger pulse. + if (trigger_duration > 0) { + nrfx_gpiote_in_uninit(self->pin); + + nrf_gpio_cfg_output(self->pin); + nrf_gpio_pin_write(self->pin, !self->idle_state); + common_hal_mcu_delay_us((uint32_t)trigger_duration); + nrf_gpio_pin_write(self->pin, self->idle_state); + + nrfx_gpiote_in_config_t cfg = { + .sense = NRF_GPIOTE_POLARITY_TOGGLE, + .pull = NRF_GPIO_PIN_NOPULL, // idle_state ? NRF_GPIO_PIN_PULLDOWN : NRF_GPIO_PIN_PULLUP, + .is_watcher = false, // nrf_gpio_cfg_watcher vs nrf_gpio_cfg_input + .hi_accuracy = true, + .skip_gpio_setup = false + }; + nrfx_gpiote_in_init(self->pin, &cfg, _pulsein_handler); + } + + self->first_edge = true; + self->paused = false; + self->last_ms = 0; + self->last_us = 0; + + nrfx_gpiote_in_event_enable(self->pin, true); } void common_hal_pulseio_pulsein_clear(pulseio_pulsein_obj_t* self) { + if ( !self->paused ) { + nrfx_gpiote_in_event_disable(self->pin); + } -} + self->start = 0; + self->len = 0; -uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t* self) { - return 0; -} - -uint16_t common_hal_pulseio_pulsein_get_maxlen(pulseio_pulsein_obj_t* self) { - return 0xadaf; -} - -bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t* self) { - return false; -} - -uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t* self) { - return 0xadaf; + if ( !self->paused ) { + nrfx_gpiote_in_event_enable(self->pin, true); + } } uint16_t common_hal_pulseio_pulsein_get_item(pulseio_pulsein_obj_t* self, int16_t index) { - return 0xadaf; + if ( !self->paused ) { + nrfx_gpiote_in_event_disable(self->pin); + } + + if (index < 0) { + index += self->len; + } + if (index < 0 || index >= self->len) { + if ( !self->paused ) { + nrfx_gpiote_in_event_enable(self->pin, true); + } + mp_raise_IndexError(translate("index out of range")); + } + uint16_t value = self->buffer[(self->start + index) % self->maxlen]; + + if ( !self->paused ) { + nrfx_gpiote_in_event_enable(self->pin, true); + } + + return value; +} + +uint16_t common_hal_pulseio_pulsein_popleft(pulseio_pulsein_obj_t* self) { + if (self->len == 0) { + mp_raise_IndexError(translate("pop from an empty PulseIn")); + } + + if ( !self->paused ) { + nrfx_gpiote_in_event_disable(self->pin); + } + + uint16_t value = self->buffer[self->start]; + self->start = (self->start + 1) % self->maxlen; + self->len--; + + if ( !self->paused ) { + nrfx_gpiote_in_event_enable(self->pin, true); + } + + return value; +} + +uint16_t common_hal_pulseio_pulsein_get_maxlen(pulseio_pulsein_obj_t* self) { + return self->maxlen; +} + +bool common_hal_pulseio_pulsein_get_paused(pulseio_pulsein_obj_t* self) { + return self->paused; +} + +uint16_t common_hal_pulseio_pulsein_get_len(pulseio_pulsein_obj_t* self) { + return self->len; } diff --git a/ports/nrf/common-hal/pulseio/PulseIn.h b/ports/nrf/common-hal/pulseio/PulseIn.h index 666f5a1648..4b2c6eee3f 100644 --- a/ports/nrf/common-hal/pulseio/PulseIn.h +++ b/ports/nrf/common-hal/pulseio/PulseIn.h @@ -33,15 +33,19 @@ typedef struct { mp_obj_base_t base; - uint8_t channel; + uint8_t pin; + bool idle_state; + bool paused; + volatile bool first_edge; + uint16_t* buffer; uint16_t maxlen; - bool idle_state; + volatile uint16_t start; volatile uint16_t len; - volatile bool first_edge; - uint16_t ticks_per_ms; + volatile uint16_t last_us; + volatile uint64_t last_ms; } pulseio_pulsein_obj_t; void pulsein_reset(void); diff --git a/ports/nrf/nrfx_config.h b/ports/nrf/nrfx_config.h index 1d3085e2b8..8676bd7f80 100644 --- a/ports/nrf/nrfx_config.h +++ b/ports/nrf/nrfx_config.h @@ -84,4 +84,9 @@ #define NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY 7 +// GPIO interrupt +#define NRFX_GPIOTE_ENABLED 1 +#define NRFX_GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS 1 +#define NRFX_GPIOTE_CONFIG_IRQ_PRIORITY 7 + #endif // NRFX_CONFIG_H__ diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index ece5bae297..2e25c7d151 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -42,6 +42,7 @@ #include "common-hal/busio/SPI.h" #include "common-hal/pulseio/PWMOut.h" #include "common-hal/pulseio/PulseOut.h" +#include "common-hal/pulseio/PulseIn.h" #include "tick.h" static void power_warning_handler(void) { @@ -84,6 +85,7 @@ void reset_port(void) { spi_reset(); pwmout_reset(); pulseout_reset(); + pulsein_reset(); timers_reset(); reset_all_pins();