diff --git a/ports/nrf/common-hal/neopixel_write/__init__.c b/ports/nrf/common-hal/neopixel_write/__init__.c index 8495a359d..0a4036aae 100644 --- a/ports/nrf/common-hal/neopixel_write/__init__.c +++ b/ports/nrf/common-hal/neopixel_write/__init__.c @@ -25,6 +25,7 @@ */ #include "py/mphal.h" +#include "py/mpstate.h" #include "shared-bindings/neopixel_write/__init__.h" #include "nrf_pwm.h" @@ -97,6 +98,13 @@ static NRF_PWM_Type* find_free_pwm (void) { return NULL; } +static size_t pixels_pattern_heap_size = 0; +// Called during reset_port() to free the pattern buffer +void neopixel_write_reset(void) { + MP_STATE_VM(pixels_pattern_heap) = NULL; + pixels_pattern_heap_size = 0; +} + uint64_t next_start_tick_ms = 0; uint32_t next_start_tick_us = 1000; @@ -119,10 +127,8 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout // We need space for at least 10 pixels for Circuit Playground, but let's choose 24 // to handle larger NeoPixel rings without malloc'ing. #define STACK_PIXELS 24 - uint32_t pattern_size = PATTERN_SIZE(numBytes); uint16_t* pixels_pattern = NULL; - bool pattern_on_heap = false; // Use the stack to store STACK_PIXEL's worth of PWM data. uint32_t to ensure alignment. // It is 3*STACK_PIXELS to handle RGB. @@ -138,16 +144,34 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout } else { uint8_t sd_en = 0; (void) sd_softdevice_is_enabled(&sd_en); - if (sd_en) { - // If the soft device is enabled then we must use PWM to - // transmit. This takes a bunch of memory to do so raise an - // exception if we can't. - pixels_pattern = (uint16_t *) m_malloc(pattern_size, false); - } else { - pixels_pattern = (uint16_t *) m_malloc_maybe(pattern_size, false); - } - pattern_on_heap = true; + if (pixels_pattern_heap_size < pattern_size) { + // Current heap buffer is too small. + if (MP_STATE_VM(pixels_pattern_heap)) { + // Old pixels_pattern_heap will be gc'd; don't free it. + pixels_pattern = NULL; + pixels_pattern_heap_size = 0; + } + + // realloc routines fall back to a plain malloc if the incoming ptr is NULL. + if (sd_en) { + // If the soft device is enabled then we must use PWM to + // transmit. This takes a bunch of memory to do so raise an + // exception if we can't. + MP_STATE_VM(pixels_pattern_heap) = + (uint16_t *) m_realloc(MP_STATE_VM(pixels_pattern_heap), pattern_size); + } else { + // Might return NULL. + MP_STATE_VM(pixels_pattern_heap) = + // true means move if necessary. + (uint16_t *) m_realloc_maybe(MP_STATE_VM(pixels_pattern_heap), pattern_size, true); + } + if (MP_STATE_VM(pixels_pattern_heap)) { + pixels_pattern_heap_size = pattern_size; + } + } + // Might be NULL, which means we failed to allocate. + pixels_pattern = MP_STATE_VM(pixels_pattern_heap); } } @@ -155,7 +179,7 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout wait_until(next_start_tick_ms, next_start_tick_us); // Use the identified device to choose the implementation - // If a PWM device is available use DMA + // If a PWM device is available and we have a buffer, use DMA. if ( (pixels_pattern != NULL) && (pwm != NULL) ) { uint16_t pos = 0; // bit position @@ -229,10 +253,6 @@ void common_hal_neopixel_write (const digitalio_digitalinout_obj_t* digitalinout nrf_pwm_disable(pwm); nrf_pwm_pins_set(pwm, (uint32_t[]) {0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL} ); - if (pattern_on_heap) { - m_free(pixels_pattern); - } - } // End of DMA implementation // --------------------------------------------------------------------- else { diff --git a/ports/nrf/common-hal/neopixel_write/__init__.h b/ports/nrf/common-hal/neopixel_write/__init__.h new file mode 100644 index 000000000..b8dce85ad --- /dev/null +++ b/ports/nrf/common-hal/neopixel_write/__init__.h @@ -0,0 +1,32 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Lucian Copeland 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_STM32F4_COMMON_HAL_NEOPIXEL_WRITE_INIT_H +#define MICROPY_INCLUDED_STM32F4_COMMON_HAL_NEOPIXEL_WRITE_INIT_H + +void neopixel_write_reset(void); + +#endif // MICROPY_INCLUDED_STM32F4_COMMON_HAL_NEOPIXEL_WRITE_INIT_H \ No newline at end of file diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 473169e94..44e51fc5f 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -164,6 +164,7 @@ #define MICROPY_PORT_ROOT_POINTERS \ CIRCUITPY_COMMON_ROOT_POINTERS \ + uint16_t* pixels_pattern_heap; \ ble_drv_evt_handler_entry_t* ble_drv_evt_handler_entries; \ diff --git a/ports/nrf/supervisor/port.c b/ports/nrf/supervisor/port.c index 6a4813c66..cbe0cec54 100644 --- a/ports/nrf/supervisor/port.c +++ b/ports/nrf/supervisor/port.c @@ -47,6 +47,7 @@ #include "common-hal/pulseio/PulseOut.h" #include "common-hal/pulseio/PulseIn.h" #include "common-hal/rtc/RTC.h" +#include "common-hal/neopixel_write/__init__.h" #include "tick.h" #include "shared-bindings/rtc/__init__.h" @@ -103,6 +104,7 @@ void reset_port(void) { i2c_reset(); spi_reset(); uart_reset(); + neopixel_write_reset(); #if CIRCUITPY_AUDIOBUSIO i2s_reset();