From ff1942cff68a4e192f984a14a83b59800186e45a Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Thu, 11 Feb 2021 16:37:34 -0600 Subject: [PATCH] Enable protomatter on RP2040 builds Also found a race condition between timer_disable and redraw, which would happen if I debugger-paused inside common_hal_rgbmatrix_timer_disable or put a delay or print inside it. That's what pausing inside reconstruct fixes. So that the "right timer" can be chosen, `timer_allocate` now gets the `self` pointer. It's guaranteed at this point that the pin information is accurate, so you can e.g., find a PWM unit related to the pins themselves. This required touching each port to add the parameter even though it's unused everywhere but raspberrypi. --- lib/protomatter | 2 +- .../common-hal/rgbmatrix/RGBMatrix.c | 2 +- .../common-hal/rgbmatrix/RGBMatrix.h | 4 +- ports/nrf/common-hal/rgbmatrix/RGBMatrix.c | 2 +- ports/nrf/common-hal/rgbmatrix/RGBMatrix.h | 4 +- ports/raspberrypi/common-hal/pwmio/PWMOut.c | 80 ++++++++++++------- ports/raspberrypi/common-hal/pwmio/PWMOut.h | 5 ++ .../common-hal/rgbmatrix/RGBMatrix.c | 70 ++++++++++++++++ .../common-hal/rgbmatrix/RGBMatrix.h | 37 +++++++++ .../common-hal/rgbmatrix/__init__.c | 0 ports/raspberrypi/mpconfigport.mk | 2 + ports/stm/common-hal/rgbmatrix/RGBMatrix.c | 2 +- ports/stm/common-hal/rgbmatrix/RGBMatrix.h | 4 +- shared-bindings/pwmio/PWMOut.h | 2 +- shared-module/rgbmatrix/RGBMatrix.c | 4 +- shared-module/rgbmatrix/RGBMatrix.h | 1 + 16 files changed, 183 insertions(+), 38 deletions(-) create mode 100644 ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.c create mode 100644 ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.h create mode 100644 ports/raspberrypi/common-hal/rgbmatrix/__init__.c diff --git a/lib/protomatter b/lib/protomatter index 5e925cea7a..78cde80475 160000 --- a/lib/protomatter +++ b/lib/protomatter @@ -1 +1 @@ -Subproject commit 5e925cea7a55273e375a6129761cb29b4e750d4f +Subproject commit 78cde804759c2e910f393b077368dc9fe1f6a630 diff --git a/ports/atmel-samd/common-hal/rgbmatrix/RGBMatrix.c b/ports/atmel-samd/common-hal/rgbmatrix/RGBMatrix.c index 55b0c2f125..6daf20aeaa 100644 --- a/ports/atmel-samd/common-hal/rgbmatrix/RGBMatrix.c +++ b/ports/atmel-samd/common-hal/rgbmatrix/RGBMatrix.c @@ -31,7 +31,7 @@ #include "samd/timers.h" #include "timer_handler.h" -void *common_hal_rgbmatrix_timer_allocate() { +void *common_hal_rgbmatrix_timer_allocate(rgbmatrix_rgbmatrix_obj_t *self) { uint8_t timer_index = find_free_timer(); if (timer_index == 0xff) { return NULL; diff --git a/ports/atmel-samd/common-hal/rgbmatrix/RGBMatrix.h b/ports/atmel-samd/common-hal/rgbmatrix/RGBMatrix.h index 48de4dcb21..2378cf1735 100644 --- a/ports/atmel-samd/common-hal/rgbmatrix/RGBMatrix.h +++ b/ports/atmel-samd/common-hal/rgbmatrix/RGBMatrix.h @@ -27,7 +27,9 @@ #ifndef MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_RGBMATRIX_RGBMATRIX_H #define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_RGBMATRIX_RGBMATRIX_H -void *common_hal_rgbmatrix_timer_allocate(void); +#include "shared-module/rgbmatrix/RGBMatrix.h" + +void *common_hal_rgbmatrix_timer_allocate(rgbmatrix_rgbmatrix_obj_t *self); void common_hal_rgbmatrix_timer_enable(void*); void common_hal_rgbmatrix_timer_disable(void*); void common_hal_rgbmatrix_timer_free(void*); diff --git a/ports/nrf/common-hal/rgbmatrix/RGBMatrix.c b/ports/nrf/common-hal/rgbmatrix/RGBMatrix.c index e13b761abf..d8289f56ff 100644 --- a/ports/nrf/common-hal/rgbmatrix/RGBMatrix.c +++ b/ports/nrf/common-hal/rgbmatrix/RGBMatrix.c @@ -32,7 +32,7 @@ extern void _PM_IRQ_HANDLER(void); -void *common_hal_rgbmatrix_timer_allocate() { +void *common_hal_rgbmatrix_timer_allocate(rgbmatrix_rgbmatrix_obj_t *self) { nrfx_timer_t *timer = nrf_peripherals_allocate_timer_or_throw(); nrf_peripherals_timer_never_reset(timer); return timer->p_reg; diff --git a/ports/nrf/common-hal/rgbmatrix/RGBMatrix.h b/ports/nrf/common-hal/rgbmatrix/RGBMatrix.h index 24d86f1779..6cf7354fc2 100644 --- a/ports/nrf/common-hal/rgbmatrix/RGBMatrix.h +++ b/ports/nrf/common-hal/rgbmatrix/RGBMatrix.h @@ -27,7 +27,9 @@ #ifndef MICROPY_INCLUDED_NRF_COMMON_HAL_RGBMATRIX_RGBMATRIX_H #define MICROPY_INCLUDED_NRF_COMMON_HAL_RGBMATRIX_RGBMATRIX_H -void *common_hal_rgbmatrix_timer_allocate(void); +#include "shared-module/rgbmatrix/RGBMatrix.h" + +void *common_hal_rgbmatrix_timer_allocate(rgbmatrix_rgbmatrix_obj_t *self); void common_hal_rgbmatrix_timer_enable(void*); void common_hal_rgbmatrix_timer_disable(void*); void common_hal_rgbmatrix_timer_free(void*); diff --git a/ports/raspberrypi/common-hal/pwmio/PWMOut.c b/ports/raspberrypi/common-hal/pwmio/PWMOut.c index e861bab5a9..fe45db05c5 100644 --- a/ports/raspberrypi/common-hal/pwmio/PWMOut.c +++ b/ports/raspberrypi/common-hal/pwmio/PWMOut.c @@ -49,14 +49,22 @@ static uint32_t _mask(uint8_t slice, uint8_t channel) { return 1 << (slice * CHANNELS_PER_SLICE + channel); } +void pwmout_never_reset(uint8_t slice, uint8_t channel) { + never_reset_channel |= _mask(slice, channel); +} + +void pwmout_reset_ok(uint8_t slice, uint8_t channel) { + never_reset_channel &= ~_mask(slice, channel); +} + void common_hal_pwmio_pwmout_never_reset(pwmio_pwmout_obj_t *self) { - never_reset_channel |= _mask(self->slice, self->channel); + pwmout_never_reset(self->slice, self->channel); never_reset_pin_number(self->pin->number); } void common_hal_pwmio_pwmout_reset_ok(pwmio_pwmout_obj_t *self) { - never_reset_channel &= ~_mask(self->slice, self->channel); + pwmout_reset_ok(self->slice, self->channel); } void pwmout_reset(void) { @@ -80,21 +88,7 @@ void pwmout_reset(void) { } } -pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t* self, - const mcu_pin_obj_t* pin, - uint16_t duty, - uint32_t frequency, - bool variable_frequency) { - self->pin = pin; - self->variable_frequency = variable_frequency; - self->duty_cycle = duty; - - if (frequency == 0 || frequency > (common_hal_mcu_processor_get_frequency() / 2)) { - return PWMOUT_INVALID_FREQUENCY; - } - - uint8_t slice = pwm_gpio_to_slice_num(pin->number); - uint8_t channel = pwm_gpio_to_channel(pin->number); +pwmout_result_t pwmout_allocate(uint8_t slice, uint8_t channel, bool variable_frequency, uint32_t frequency) { uint32_t channel_use_mask = _mask(slice, channel); // Check the channel first. @@ -116,14 +110,39 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t* self, return PWMOUT_ALL_TIMERS_ON_PIN_IN_USE; } } - self->slice = slice; - self->channel = channel; channel_use |= channel_use_mask; if (variable_frequency) { slice_variable_frequency |= 1 << slice; } + return PWMOUT_OK; +} + +pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t* self, + const mcu_pin_obj_t* pin, + uint16_t duty, + uint32_t frequency, + bool variable_frequency) { + self->pin = pin; + self->variable_frequency = variable_frequency; + self->duty_cycle = duty; + + if (frequency == 0 || frequency > (common_hal_mcu_processor_get_frequency() / 2)) { + return PWMOUT_INVALID_FREQUENCY; + } + + uint8_t slice = pwm_gpio_to_slice_num(pin->number); + uint8_t channel = pwm_gpio_to_channel(pin->number); + + int r = pwmout_allocate(slice, channel, variable_frequency, frequency); + if (r != PWMOUT_OK) { + return r; + } + + self->slice = slice; + self->channel = channel; + if (target_slice_frequencies[slice] != frequency) { // Reset the counter and compare values. pwm_hw->slice[slice].ctr = PWM_CH0_CTR_RESET; @@ -144,20 +163,23 @@ bool common_hal_pwmio_pwmout_deinited(pwmio_pwmout_obj_t* self) { return self->pin == NULL; } +void pwmout_free(uint8_t slice, uint8_t channel) { + uint32_t channel_mask = _mask(slice, channel); + channel_use &= ~channel_mask; + never_reset_channel &= ~channel_mask; + uint32_t slice_mask = ((1 << CHANNELS_PER_SLICE) - 1) << (slice * CHANNELS_PER_SLICE + channel); + if ((channel_use & slice_mask) == 0) { + target_slice_frequencies[slice] = 0; + slice_variable_frequency &= ~(1 << slice); + pwm_set_enabled(slice, false); + } +} + void common_hal_pwmio_pwmout_deinit(pwmio_pwmout_obj_t* self) { if (common_hal_pwmio_pwmout_deinited(self)) { return; } - uint32_t channel_mask = _mask(self->slice, self->channel); - channel_use &= ~channel_mask; - never_reset_channel &= ~channel_mask; - uint32_t slice_mask = ((1 << CHANNELS_PER_SLICE) - 1) << (self->slice * CHANNELS_PER_SLICE + self->channel); - if ((channel_use & slice_mask) == 0) { - target_slice_frequencies[self->slice] = 0; - slice_variable_frequency &= ~(1 << self->slice); - pwm_set_enabled(self->slice, false); - } - + pwmout_free(self->slice, self->channel); reset_pin_number(self->pin->number); self->pin = NULL; } diff --git a/ports/raspberrypi/common-hal/pwmio/PWMOut.h b/ports/raspberrypi/common-hal/pwmio/PWMOut.h index cbb0b707a3..4b633e8b1f 100644 --- a/ports/raspberrypi/common-hal/pwmio/PWMOut.h +++ b/ports/raspberrypi/common-hal/pwmio/PWMOut.h @@ -45,5 +45,10 @@ typedef struct { void pwmout_reset(void); // Private API for AudioPWMOut. void pwmio_pwmout_set_top(pwmio_pwmout_obj_t* self, uint32_t top); +// Private APIs for RGBMatrix +enum pwmout_result_t pwmout_allocate(uint8_t slice, uint8_t channel, bool variable_frequency, uint32_t frequency); +void pwmout_free(uint8_t slice, uint8_t channel); +void pwmout_never_reset(uint8_t slice, uint8_t channel); +void pwmout_reset_ok(uint8_t slice, uint8_t channel); #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_PWMIO_PWMOUT_H diff --git a/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.c b/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.c new file mode 100644 index 0000000000..657c7f0a6c --- /dev/null +++ b/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.c @@ -0,0 +1,70 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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 + +#include "py/mphal.h" + +#include "common-hal/rgbmatrix/RGBMatrix.h" +#include "shared-bindings/pwmio/PWMOut.h" +#include "shared-module/rgbmatrix/RGBMatrix.h" + +#include "src/rp2_common/hardware_pwm/include/hardware/pwm.h" + +void *common_hal_rgbmatrix_timer_allocate(rgbmatrix_rgbmatrix_obj_t *self) { + // Choose a PWM channel based on the first RGB pin + uint8_t slice = pwm_gpio_to_slice_num(self->rgb_pins[0]); + uint8_t channel = pwm_gpio_to_channel(self->rgb_pins[0]); + int result = pwmout_allocate(slice, channel, true, 125000000/3); + if (result == PWMOUT_OK) { + // return value must be nonzero (but slice and channel can both be + // zero), so set bit 16... + pwmout_never_reset(slice, channel); + return (void*)(intptr_t)(slice | (channel << 8) | 0x10000); + } + return NULL; +} + +void common_hal_rgbmatrix_timer_enable(void* ptr) { + int8_t slice = ((intptr_t)ptr) & 0xff; + pwm_set_enabled(slice, false); + pwm_clear_irq(slice); + pwm_set_enabled(slice, true); +} + +void common_hal_rgbmatrix_timer_disable(void* ptr) { + int8_t slice = ((intptr_t)ptr) & 0xff; + pwm_set_enabled(slice, false); +} + +void common_hal_rgbmatrix_timer_free(void* ptr) { + intptr_t value = (intptr_t)ptr; + uint8_t slice = value & 0xff; + uint8_t channel = value >> 8; + pwm_set_enabled(slice, false); + pwmout_free(slice, channel); + return; +} diff --git a/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.h b/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.h new file mode 100644 index 0000000000..2378cf1735 --- /dev/null +++ b/ports/raspberrypi/common-hal/rgbmatrix/RGBMatrix.h @@ -0,0 +1,37 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler 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_COMMON_HAL_RGBMATRIX_RGBMATRIX_H +#define MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_RGBMATRIX_RGBMATRIX_H + +#include "shared-module/rgbmatrix/RGBMatrix.h" + +void *common_hal_rgbmatrix_timer_allocate(rgbmatrix_rgbmatrix_obj_t *self); +void common_hal_rgbmatrix_timer_enable(void*); +void common_hal_rgbmatrix_timer_disable(void*); +void common_hal_rgbmatrix_timer_free(void*); + +#endif diff --git a/ports/raspberrypi/common-hal/rgbmatrix/__init__.c b/ports/raspberrypi/common-hal/rgbmatrix/__init__.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index dac7231dac..4657380ced 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -23,8 +23,10 @@ else CIRCUITPY_NEOPIXEL_WRITE = 0 endif +CIRCUITPY_FRAMEBUFFERIO = 1 CIRCUITPY_FULL_BUILD = 1 CIRCUITPY_PWMIO = 1 +CIRCUITPY_RGBMATRIX = 1 # Things that need to be implemented. CIRCUITPY_COUNTIO = 0 # Use PWM interally diff --git a/ports/stm/common-hal/rgbmatrix/RGBMatrix.c b/ports/stm/common-hal/rgbmatrix/RGBMatrix.c index 2826cf53e9..36ed6d0183 100644 --- a/ports/stm/common-hal/rgbmatrix/RGBMatrix.c +++ b/ports/stm/common-hal/rgbmatrix/RGBMatrix.c @@ -33,7 +33,7 @@ extern void _PM_IRQ_HANDLER(void); -void *common_hal_rgbmatrix_timer_allocate() { +void *common_hal_rgbmatrix_timer_allocate(rgbmatrix_rgbmatrix_obj_t *self) { TIM_TypeDef * timer = stm_peripherals_find_timer(); stm_peripherals_timer_reserve(timer); stm_peripherals_timer_never_reset(timer); diff --git a/ports/stm/common-hal/rgbmatrix/RGBMatrix.h b/ports/stm/common-hal/rgbmatrix/RGBMatrix.h index 323878d202..2debb6a76e 100644 --- a/ports/stm/common-hal/rgbmatrix/RGBMatrix.h +++ b/ports/stm/common-hal/rgbmatrix/RGBMatrix.h @@ -27,7 +27,9 @@ #ifndef MICROPY_INCLUDED_STM_COMMON_HAL_RGBMATRIX_RGBMATRIX_H #define MICROPY_INCLUDED_STM_COMMON_HAL_RGBMATRIX_RGBMATRIX_H -void *common_hal_rgbmatrix_timer_allocate(void); +#include "shared-module/rgbmatrix/RGBMatrix.h" + +void *common_hal_rgbmatrix_timer_allocate(rgbmatrix_rgbmatrix_obj_t *self); void common_hal_rgbmatrix_timer_enable(void*); void common_hal_rgbmatrix_timer_disable(void*); void common_hal_rgbmatrix_timer_free(void*); diff --git a/shared-bindings/pwmio/PWMOut.h b/shared-bindings/pwmio/PWMOut.h index 1a99914ea5..de2ebd1cf4 100644 --- a/shared-bindings/pwmio/PWMOut.h +++ b/shared-bindings/pwmio/PWMOut.h @@ -32,7 +32,7 @@ extern const mp_obj_type_t pwmio_pwmout_type; -typedef enum { +typedef enum pwmout_result_t { PWMOUT_OK, PWMOUT_INVALID_PIN, PWMOUT_INVALID_FREQUENCY, diff --git a/shared-module/rgbmatrix/RGBMatrix.c b/shared-module/rgbmatrix/RGBMatrix.c index b8db0d8939..a688d7fd6f 100644 --- a/shared-module/rgbmatrix/RGBMatrix.c +++ b/shared-module/rgbmatrix/RGBMatrix.c @@ -56,7 +56,7 @@ void common_hal_rgbmatrix_rgbmatrix_construct(rgbmatrix_rgbmatrix_obj_t *self, i self->tile = tile; self->serpentine = serpentine; - self->timer = timer ? timer : common_hal_rgbmatrix_timer_allocate(); + self->timer = timer ? timer : common_hal_rgbmatrix_timer_allocate(self); if (self->timer == NULL) { mp_raise_ValueError(translate("No timer available")); } @@ -68,6 +68,8 @@ void common_hal_rgbmatrix_rgbmatrix_construct(rgbmatrix_rgbmatrix_obj_t *self, i } void common_hal_rgbmatrix_rgbmatrix_reconstruct(rgbmatrix_rgbmatrix_obj_t* self, mp_obj_t framebuffer) { + self->paused = 1; + common_hal_rgbmatrix_timer_disable(self->timer); if (framebuffer) { self->framebuffer = framebuffer; diff --git a/shared-module/rgbmatrix/RGBMatrix.h b/shared-module/rgbmatrix/RGBMatrix.h index 4a0e9235e7..65bfc9799e 100644 --- a/shared-module/rgbmatrix/RGBMatrix.h +++ b/shared-module/rgbmatrix/RGBMatrix.h @@ -26,6 +26,7 @@ #pragma once +#include "py/obj.h" #include "lib/protomatter/src/core.h" extern const mp_obj_type_t rgbmatrix_RGBMatrix_type;