From 6e1b516e80a77a5df9cae494e4cb28f7d60c4878 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Tue, 1 Apr 2025 15:46:37 -0400 Subject: [PATCH 01/14] Allow setting of CIRCUITPY_DISPLAY_LIMIT in settings.toml --- docs/environment.rst | 21 +++ main.c | 6 + ports/atmel-samd/mpconfigport.mk | 1 + .../stm32f411ve_discovery/mpconfigboard.mk | 1 + py/circuitpy_mpconfig.mk | 3 + shared-module/board/__init__.c | 28 ++- shared-module/displayio/__init__.c | 178 +++++++++++++----- shared-module/displayio/__init__.h | 5 + shared-module/epaperdisplay/EPaperDisplay.c | 40 +++- supervisor/shared/web_workflow/web_workflow.c | 24 ++- 10 files changed, 240 insertions(+), 67 deletions(-) diff --git a/docs/environment.rst b/docs/environment.rst index 442c340f1a4d4..1720aae01f739 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -179,6 +179,27 @@ Example: Configure the display to 640x480 black and white (1 bit per pixel): `Adafruit Feather RP2350 `_ `Adafruit Metro RP2350 `_ +CIRCUITPY_DISPLAY_LIMIT +~~~~~~~~~~~~~~~~~~~~~~~ +Set the maximum supported number of displayio displays. This value overrides the +CIRCUITPY_DISPLAY_LIMIT compile-time flag defined in mpconfigport.h. +It specifies the maximum number of active display objects that can be supported simultaneously +on a CircuitPython board. + +By default, the value of CIRCUITPY_DISPLAY_LIMIT is set to 1 for most boards, meaning only +one display can be active at a time. Users can modify this value by adding a settings.toml entry +to support additional displays. + +The value of CIRCUITPY_DISPLAY_LIMIT should be set to a value that is supported by the +hardware and does not exceed the available memory and resources on the board. Setting the +value too high may result in unexpected behavior or crashes. + +Example: Set the maximum supported number of displayio displays to 2: + +.. code-block:: + + CIRCUITPY_DISPLAY_LIMIT=2 + CIRCUITPY_TERMINAL_SCALE ~~~~~~~~~~~~~~~~~~~~~~~~ Allows the entry of a display scaling factor used during the terminalio console construction. diff --git a/main.c b/main.c index 0ea389635f40f..528576a67adfd 100644 --- a/main.c +++ b/main.c @@ -1051,6 +1051,12 @@ int __attribute__((used)) main(void) { alarm_reset(); #endif + #if CIRCUITPY_DISPLAYIO && CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + // If number of displays has been overridden in settings.toml allocate memory and + // move existing displays + malloc_display_memory(); + #endif + // Reset everything and prep MicroPython to run boot.py. reset_port(); // Port-independent devices, like CIRCUITPY_BLEIO_HCI. diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index 861ef37464633..4f5c12ab36968 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -13,6 +13,7 @@ CIRCUITPY_LTO = 1 CIRCUITPY_KEYPAD_DEMUX ?= 0 CIRCUITPY_LVFONTIO ?= 0 +CIRCUITPY_SET_DISPLAY_LIMIT ?= 0 ###################################################################### # Put samd21-only choices here. diff --git a/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk b/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk index 98d13f592ffcb..1c123c01fa869 100644 --- a/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk +++ b/ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk @@ -24,3 +24,4 @@ CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_CODEOP = 0 CIRCUITPY_MSGPACK = 0 CIRCUITPY_VECTORIO = 0 +CIRCUITPY_SET_DISPLAY_LIMIT = 0 diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 52520215b24d7..db87d014619ad 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -227,6 +227,9 @@ CFLAGS += -DCIRCUITPY_COUNTIO=$(CIRCUITPY_COUNTIO) CIRCUITPY_DISPLAYIO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_DISPLAYIO=$(CIRCUITPY_DISPLAYIO) +CIRCUITPY_SET_DISPLAY_LIMIT ?= $(CIRCUITPY_DISPLAYIO) +CFLAGS += -DCIRCUITPY_SET_DISPLAY_LIMIT=$(CIRCUITPY_SET_DISPLAY_LIMIT) + CIRCUITPY_BUSDISPLAY ?= $(CIRCUITPY_DISPLAYIO) CFLAGS += -DCIRCUITPY_BUSDISPLAY=$(CIRCUITPY_BUSDISPLAY) diff --git a/shared-module/board/__init__.c b/shared-module/board/__init__.c index 96735c8dbaa40..78e25488006de 100644 --- a/shared-module/board/__init__.c +++ b/shared-module/board/__init__.c @@ -10,6 +10,10 @@ #include "mpconfigboard.h" #include "py/runtime.h" +#if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT +#include "shared-module/os/__init__.h" +#endif + #if CIRCUITPY_BUSIO #include "shared-bindings/busio/I2C.h" #include "shared-bindings/busio/SPI.h" @@ -172,12 +176,22 @@ mp_obj_t common_hal_board_create_uart(const mp_int_t instance) { #endif void reset_board_buses(void) { + #if (CIRCUITPY_BOARD_I2C && CIRCUITPY_I2CDISPLAYBUS) || (CIRCUITPY_BOARD_SPI && (CIRCUITPY_FOURWIRE || CIRCUITPY_SHARPDISPLAY || CIRCUITPY_AURORA_EPAPER)) + mp_int_t max_num_displays = CIRCUITPY_DISPLAY_LIMIT; + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + #define DYN_DISPLAY_BUSES(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? display_buses[indx] : display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) + (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_LIMIT", &max_num_displays); + #else + #define DYN_DISPLAY_BUSES(indx) (display_buses[indx]) + #endif + #endif + #if CIRCUITPY_BOARD_I2C for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_I2C; instance++) { bool display_using_i2c = false; #if CIRCUITPY_I2CDISPLAYBUS - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (display_buses[i].bus_base.type == &i2cdisplaybus_i2cdisplaybus_type && display_buses[i].i2cdisplay_bus.bus == &i2c_obj[instance]) { + for (uint8_t i = 0; i < max_num_displays; i++) { + if (DYN_DISPLAY_BUSES(i).bus_base.type == &i2cdisplaybus_i2cdisplaybus_type && DYN_DISPLAY_BUSES(i).i2cdisplay_bus.bus == &i2c_obj[instance]) { display_using_i2c = true; break; } @@ -197,22 +211,22 @@ void reset_board_buses(void) { for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_SPI; instance++) { bool display_using_spi = false; #if CIRCUITPY_FOURWIRE || CIRCUITPY_SHARPDISPLAY || CIRCUITPY_AURORA_EPAPER - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t bus_type = display_buses[i].bus_base.type; + for (uint8_t i = 0; i < max_num_displays; i++) { + mp_const_obj_t bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; #if CIRCUITPY_FOURWIRE - if (bus_type == &fourwire_fourwire_type && display_buses[i].fourwire_bus.bus == &spi_obj[instance]) { + if (bus_type == &fourwire_fourwire_type && DYN_DISPLAY_BUSES(i).fourwire_bus.bus == &spi_obj[instance]) { display_using_spi = true; break; } #endif #if CIRCUITPY_SHARPDISPLAY - if (bus_type == &sharpdisplay_framebuffer_type && display_buses[i].sharpdisplay.bus == &spi_obj[instance]) { + if (bus_type == &sharpdisplay_framebuffer_type && DYN_DISPLAY_BUSES(i).sharpdisplay.bus == &spi_obj[instance]) { display_using_spi = true; break; } #endif #if CIRCUITPY_AURORA_EPAPER - if (bus_type == &aurora_epaper_framebuffer_type && display_buses[i].aurora_epaper.bus == &spi_obj[instance]) { + if (bus_type == &aurora_epaper_framebuffer_type && DYN_DISPLAY_BUSES(i).aurora_epaper.bus == &spi_obj[instance]) { display_using_spi = true; break; } diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 4b467b75589d1..9f94cbb6197d2 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -22,6 +22,10 @@ #include "py/mpconfig.h" +#if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT +#include "shared-module/os/__init__.h" +#endif + #if CIRCUITPY_BUSDISPLAY #include "shared-bindings/busdisplay/BusDisplay.h" #endif @@ -46,9 +50,27 @@ // The default indicates no primary display static int primary_display_number = -1; +static mp_int_t max_num_displays = CIRCUITPY_DISPLAY_LIMIT; primary_display_bus_t display_buses[CIRCUITPY_DISPLAY_LIMIT]; primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT]; +#if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT +primary_display_bus_t *display_buses_dyn = &display_buses[0]; +primary_display_t *displays_dyn = &displays[0]; +#define DYN_DISPLAY_BUSES(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? display_buses[indx] : display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) +#define DYN_DISPLAY_BUSES_ADR(indx, membr) (indx < CIRCUITPY_DISPLAY_LIMIT ? &display_buses[indx].membr : &display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT].membr) +#define DYN_DISPLAY_BUSES_ADR0(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? &display_buses[indx] : &display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) +#define DYN_DISPLAYS(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? displays[indx] : displays_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) +#define DYN_DISPLAYS_ADR(indx, membr) (indx < CIRCUITPY_DISPLAY_LIMIT ? &displays[indx].membr : &displays_dyn[indx - CIRCUITPY_DISPLAY_LIMIT].membr) +#define DYN_DISPLAYS_ADR0(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? &displays[indx] : &displays_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) +#else +#define DYN_DISPLAY_BUSES(indx) (display_buses[indx]) +#define DYN_DISPLAY_BUSES_ADR(indx, membr) (&display_buses[indx].membr) +#define DYN_DISPLAY_BUSES_ADR0(indx) (&display_buses[indx]) +#define DYN_DISPLAYS(indx) (displays[indx]) +#define DYN_DISPLAYS_ADR(indx, membr) (&displays[indx].membr) +#define DYN_DISPLAYS_ADR0(indx) (&displays[indx]) +#endif displayio_buffer_transform_t null_transform = { .x = 0, @@ -65,9 +87,9 @@ displayio_buffer_transform_t null_transform = { #if CIRCUITPY_RGBMATRIX || CIRCUITPY_IS31FL3741 || CIRCUITPY_VIDEOCORE || CIRCUITPY_PICODVI static bool any_display_uses_this_framebuffer(mp_obj_base_t *obj) { - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (displays[i].display_base.type == &framebufferio_framebufferdisplay_type) { - framebufferio_framebufferdisplay_obj_t *display = &displays[i].framebuffer_display; + for (uint8_t i = 0; i < max_num_displays; i++) { + if (DYN_DISPLAYS(i).display_base.type == &framebufferio_framebufferdisplay_type) { + framebufferio_framebufferdisplay_obj_t *display = DYN_DISPLAYS_ADR(i, framebuffer_display); if (display->framebuffer == obj) { return true; } @@ -87,8 +109,8 @@ void displayio_background(void) { return; } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t display_type = displays[i].display_base.type; + for (uint8_t i = 0; i < max_num_displays; i++) { + mp_const_obj_t display_type = DYN_DISPLAYS(i).display_base.type; if (display_type == NULL || display_type == &mp_type_NoneType) { // Skip null display. continue; @@ -96,15 +118,15 @@ void displayio_background(void) { if (false) { #if CIRCUITPY_BUSDISPLAY } else if (display_type == &busdisplay_busdisplay_type) { - busdisplay_busdisplay_background(&displays[i].display); + busdisplay_busdisplay_background(DYN_DISPLAYS_ADR(i, display)); #endif #if CIRCUITPY_FRAMEBUFFERIO } else if (display_type == &framebufferio_framebufferdisplay_type) { - framebufferio_framebufferdisplay_background(&displays[i].framebuffer_display); + framebufferio_framebufferdisplay_background(DYN_DISPLAYS_ADR(i, framebuffer_display)); #endif #if CIRCUITPY_EPAPERDISPLAY } else if (display_type == &epaperdisplay_epaperdisplay_type) { - epaperdisplay_epaperdisplay_background(&displays[i].epaper_display); + epaperdisplay_epaperdisplay_background(DYN_DISPLAYS_ADR(i, epaper_display)); #endif } } @@ -117,82 +139,120 @@ static void common_hal_displayio_release_displays_impl(bool keep_primary) { if (!keep_primary) { primary_display_number = -1; } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { + for (uint8_t i = (keep_primary ? CIRCUITPY_DISPLAY_LIMIT: 0); i < max_num_displays; i++) { if (i == primary_display_number) { continue; } - mp_const_obj_t display_type = displays[i].display_base.type; + mp_const_obj_t display_type = DYN_DISPLAYS(i).display_base.type; if (display_type == NULL || display_type == &mp_type_NoneType) { continue; #if CIRCUITPY_BUSDISPLAY } else if (display_type == &busdisplay_busdisplay_type) { - release_busdisplay(&displays[i].display); + release_busdisplay(DYN_DISPLAYS_ADR(i, display)); #endif #if CIRCUITPY_EPAPERDISPLAY } else if (display_type == &epaperdisplay_epaperdisplay_type) { - release_epaperdisplay(&displays[i].epaper_display); + release_epaperdisplay(DYN_DISPLAYS_ADR(i, epaper_display)); #endif #if CIRCUITPY_FRAMEBUFFERIO } else if (display_type == &framebufferio_framebufferdisplay_type) { - release_framebufferdisplay(&displays[i].framebuffer_display); + release_framebufferdisplay(DYN_DISPLAYS_ADR(i, framebuffer_display)); #endif } + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + if (i < CIRCUITPY_DISPLAY_LIMIT) { + displays[i].display_base.type = &mp_type_NoneType; + } else { + displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].display_base.type = &mp_type_NoneType; + } + #else displays[i].display_base.type = &mp_type_NoneType; + #endif } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t bus_type = display_buses[i].bus_base.type; + for (uint8_t i = (keep_primary ? CIRCUITPY_DISPLAY_LIMIT: 0); i < max_num_displays; i++) { + if (i == primary_display_number) { + continue; + } + mp_const_obj_t bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; if (bus_type == NULL || bus_type == &mp_type_NoneType) { continue; #if CIRCUITPY_FOURWIRE } else if (bus_type == &fourwire_fourwire_type) { - common_hal_fourwire_fourwire_deinit(&display_buses[i].fourwire_bus); + common_hal_fourwire_fourwire_deinit(DYN_DISPLAY_BUSES_ADR(i, fourwire_bus)); #endif #if CIRCUITPY_I2CDISPLAYBUS } else if (bus_type == &i2cdisplaybus_i2cdisplaybus_type) { - common_hal_i2cdisplaybus_i2cdisplaybus_deinit(&display_buses[i].i2cdisplay_bus); + common_hal_i2cdisplaybus_i2cdisplaybus_deinit(DYN_DISPLAY_BUSES_ADR(i, i2cdisplay_bus)); #endif #if CIRCUITPY_DOTCLOCKFRAMEBUFFER } else if (bus_type == &dotclockframebuffer_framebuffer_type) { - common_hal_dotclockframebuffer_framebuffer_deinit(&display_buses[i].dotclock); + common_hal_dotclockframebuffer_framebuffer_deinit(DYN_DISPLAY_BUSES_ADR(i, dotclock)); #endif #if CIRCUITPY_PARALLELDISPLAYBUS } else if (bus_type == ¶lleldisplaybus_parallelbus_type) { - common_hal_paralleldisplaybus_parallelbus_deinit(&display_buses[i].parallel_bus); + common_hal_paralleldisplaybus_parallelbus_deinit(DYN_DISPLAY_BUSES_ADR(i, parallel_bus)); #endif #if CIRCUITPY_RGBMATRIX } else if (bus_type == &rgbmatrix_RGBMatrix_type) { - common_hal_rgbmatrix_rgbmatrix_deinit(&display_buses[i].rgbmatrix); + common_hal_rgbmatrix_rgbmatrix_deinit(DYN_DISPLAY_BUSES_ADR(i, rgbmatrix)); #endif #if CIRCUITPY_IS31FL3741 } else if (bus_type == &is31fl3741_framebuffer_type) { - common_hal_is31fl3741_framebuffer_deinit(&display_buses[i].is31fl3741); + common_hal_is31fl3741_framebuffer_deinit(DYN_DISPLAY_BUSES_ADR(i, is31fl3741)); #endif #if CIRCUITPY_SHARPDISPLAY } else if (bus_type == &sharpdisplay_framebuffer_type) { - common_hal_sharpdisplay_framebuffer_deinit(&display_buses[i].sharpdisplay); + common_hal_sharpdisplay_framebuffer_deinit(DYN_DISPLAY_BUSES_ADR(i, sharpdisplay)); #endif #if CIRCUITPY_VIDEOCORE } else if (bus_type == &videocore_framebuffer_type) { - common_hal_videocore_framebuffer_deinit(&display_buses[i].videocore); + common_hal_videocore_framebuffer_deinit(DYN_DISPLAY_BUSES_ADR(i, videocore)); #endif #if CIRCUITPY_PICODVI } else if (bus_type == &picodvi_framebuffer_type) { - common_hal_picodvi_framebuffer_deinit(&display_buses[i].picodvi); + common_hal_picodvi_framebuffer_deinit(DYN_DISPLAY_BUSES_ADR(i, picodvi)); #endif } + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + if (i < CIRCUITPY_DISPLAY_LIMIT) { + display_buses[i].bus_base.type = &mp_type_NoneType; + } else { + display_buses_dyn[i - CIRCUITPY_DISPLAY_LIMIT].bus_base.type = &mp_type_NoneType; + } + #else display_buses[i].bus_base.type = &mp_type_NoneType; + #endif } - supervisor_stop_terminal(); + if (!keep_primary) { + supervisor_stop_terminal(); + } } void common_hal_displayio_release_displays(void) { common_hal_displayio_release_displays_impl(false); } +void malloc_display_memory(void) { + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_LIMIT", &max_num_displays); + if (max_num_displays > CIRCUITPY_DISPLAY_LIMIT) { + display_buses_dyn = (primary_display_bus_t *)port_malloc(sizeof(primary_display_bus_t) * (max_num_displays - CIRCUITPY_DISPLAY_LIMIT), false); + displays_dyn = (primary_display_t *)port_malloc(sizeof(primary_display_t) * (max_num_displays - CIRCUITPY_DISPLAY_LIMIT), false); + + for (uint8_t i = CIRCUITPY_DISPLAY_LIMIT; i < max_num_displays; i++) { + memset(&displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT], 0, sizeof(displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT])); + memset(&display_buses_dyn[i - CIRCUITPY_DISPLAY_LIMIT], 0, sizeof(display_buses_dyn[i - CIRCUITPY_DISPLAY_LIMIT])); + display_buses_dyn[i - CIRCUITPY_DISPLAY_LIMIT].bus_base.type = &mp_type_NoneType; + displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].display_base.type = &mp_type_NoneType; + } + } + #endif +} + void reset_displays(void) { // In CircuitPython 10, release secondary displays before doing anything else: - // common_hal_displayio_release_displays_impl(true); + common_hal_displayio_release_displays_impl(true); // The SPI buses used by FourWires may be allocated on the heap so we need to move them inline. for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { @@ -358,35 +418,35 @@ void reset_displays(void) { } void displayio_gc_collect(void) { - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t display_bus_type = display_buses[i].bus_base.type; + for (uint8_t i = 0; i < max_num_displays; i++) { + mp_const_obj_t display_bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) { continue; } #if CIRCUITPY_RGBMATRIX if (display_bus_type == &rgbmatrix_RGBMatrix_type) { - rgbmatrix_rgbmatrix_collect_ptrs(&display_buses[i].rgbmatrix); + rgbmatrix_rgbmatrix_collect_ptrs(DYN_DISPLAY_BUSES_ADR(i, rgbmatrix)); } #endif #if CIRCUITPY_IS31FL3741 if (display_bus_type == &is31fl3741_framebuffer_type) { - is31fl3741_framebuffer_collect_ptrs(&display_buses[i].is31fl3741); + is31fl3741_framebuffer_collect_ptrs(DYN_DISPLAY_BUSES_ADR(i, is31fl3741)); } #endif #if CIRCUITPY_SHARPDISPLAY if (display_bus_type == &sharpdisplay_framebuffer_type) { - common_hal_sharpdisplay_framebuffer_collect_ptrs(&display_buses[i].sharpdisplay); + common_hal_sharpdisplay_framebuffer_collect_ptrs(DYN_DISPLAY_BUSES_ADR(i, sharpdisplay)); } #endif #if CIRCUITPY_AURORA_EPAPER if (display_bus_type == &aurora_framebuffer_type) { - common_hal_aurora_epaper_framebuffer_collect_ptrs(&display_buses[i].aurora_epaper); + common_hal_aurora_epaper_framebuffer_collect_ptrs(DYN_DISPLAY_BUSES_ADR(i, aurora_epaper)); } #endif } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t display_type = displays[i].display_base.type; + for (uint8_t i = 0; i < max_num_displays; i++) { + mp_const_obj_t display_type = DYN_DISPLAYS(i).display_base.type; if (display_type == NULL || display_type == &mp_type_NoneType) { continue; @@ -394,15 +454,15 @@ void displayio_gc_collect(void) { // but this is more precise, and is the only field that needs marking. #if CIRCUITPY_BUSDISPLAY } else if (display_type == &busdisplay_busdisplay_type) { - busdisplay_busdisplay_collect_ptrs(&displays[i].display); + busdisplay_busdisplay_collect_ptrs(DYN_DISPLAYS_ADR(i, display)); #endif #if CIRCUITPY_FRAMEBUFFERIO } else if (display_type == &framebufferio_framebufferdisplay_type) { - framebufferio_framebufferdisplay_collect_ptrs(&displays[i].framebuffer_display); + framebufferio_framebufferdisplay_collect_ptrs(DYN_DISPLAYS_ADR(i, framebuffer_display)); #endif #if CIRCUITPY_EPAPERDISPLAY } else if (display_type == &epaperdisplay_epaperdisplay_type) { - epaperdisplay_epaperdisplay_collect_ptrs(&displays[i].epaper_display); + epaperdisplay_epaperdisplay_collect_ptrs(DYN_DISPLAYS_ADR(i, epaper_display)); #endif } } @@ -413,13 +473,21 @@ static bool is_display_active(mp_obj_base_t *display_maybe) { } primary_display_t *allocate_display(void) { - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (!is_display_active(&displays[i].display_base)) { + for (uint8_t i = 0; i < max_num_displays; i++) { + if (!is_display_active(DYN_DISPLAYS_ADR(i, display_base))) { // Clear this memory so it is in a known state before init. - memset(&displays[i], 0, sizeof(displays[i])); + memset(DYN_DISPLAYS_ADR0(i), 0, sizeof(displays[0])); // Default to None so that it works as board.DISPLAY. + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + if (i < CIRCUITPY_DISPLAY_LIMIT) { + displays[i].display_base.type = &mp_type_NoneType; + } else { + displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].display_base.type = &mp_type_NoneType; + } + #else displays[i].display_base.type = &mp_type_NoneType; - return &displays[i]; + #endif + return DYN_DISPLAYS_ADR0(i); } } return NULL; @@ -434,13 +502,21 @@ primary_display_t *allocate_display_or_raise(void) { } primary_display_bus_t *allocate_display_bus(void) { - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t display_bus_type = display_buses[i].bus_base.type; + for (uint8_t i = 0; i < max_num_displays; i++) { + mp_const_obj_t display_bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) { // Clear this memory so it is in a known state before init. - memset(&display_buses[i], 0, sizeof(display_buses[i])); + memset(DYN_DISPLAY_BUSES_ADR0(i), 0, sizeof(display_buses[0])); + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + if (i < CIRCUITPY_DISPLAY_LIMIT) { + display_buses[i].bus_base.type = &mp_type_NoneType; + } else { + display_buses_dyn[i - CIRCUITPY_DISPLAY_LIMIT].bus_base.type = &mp_type_NoneType; + } + #else display_buses[i].bus_base.type = &mp_type_NoneType; - return &display_buses[i]; + #endif + return DYN_DISPLAY_BUSES_ADR0(i); } } return NULL; @@ -455,10 +531,10 @@ primary_display_bus_t *allocate_display_bus_or_raise(void) { } mp_obj_t common_hal_displayio_get_primary_display(void) { - if (primary_display_number == -1 || primary_display_number >= CIRCUITPY_DISPLAY_LIMIT) { + if (primary_display_number == -1 || primary_display_number >= max_num_displays) { return mp_const_none; } - mp_obj_base_t *primary_display = &displays[primary_display_number].display_base; + mp_obj_base_t *primary_display = DYN_DISPLAYS_ADR(primary_display_number, display_base); if (is_display_active(primary_display)) { return MP_OBJ_FROM_PTR(primary_display); } @@ -470,8 +546,8 @@ void common_hal_displayio_set_primary_display(mp_obj_t new_primary_display) { primary_display_number = -1; return; } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_obj_t display = MP_OBJ_FROM_PTR(&displays[i]); + for (uint8_t i = 0; i < max_num_displays; i++) { + mp_obj_t display = MP_OBJ_FROM_PTR(DYN_DISPLAYS_ADR0(i)); if (new_primary_display == display && is_display_active(display)) { primary_display_number = i; return; @@ -485,8 +561,8 @@ void common_hal_displayio_auto_primary_display(void) { if (primary_display_number != -1) { return; } - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (is_display_active(&displays[i].display_base)) { + for (uint8_t i = 0; i < max_num_displays; i++) { + if (is_display_active(DYN_DISPLAYS_ADR(i, display_base))) { primary_display_number = i; return; } diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index 29b8c64c97240..9d67fa32fd6a1 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -101,11 +101,16 @@ typedef struct { extern primary_display_bus_t display_buses[CIRCUITPY_DISPLAY_LIMIT]; extern primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT]; +#if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT +extern primary_display_bus_t *display_buses_dyn; +extern primary_display_t *displays_dyn; +#endif extern displayio_group_t circuitpython_splash; void displayio_background(void); void reset_displays(void); +void malloc_display_memory(void); void displayio_gc_collect(void); primary_display_t *allocate_display(void); diff --git a/shared-module/epaperdisplay/EPaperDisplay.c b/shared-module/epaperdisplay/EPaperDisplay.c index 14fbc3341b5ff..dfb074cab8e72 100644 --- a/shared-module/epaperdisplay/EPaperDisplay.c +++ b/shared-module/epaperdisplay/EPaperDisplay.c @@ -20,6 +20,10 @@ #include "supervisor/usb.h" #endif +#if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT +#include "shared-module/os/__init__.h" +#endif + #include #include @@ -501,13 +505,45 @@ void epaperdisplay_epaperdisplay_collect_ptrs(epaperdisplay_epaperdisplay_obj_t } size_t maybe_refresh_epaperdisplay(void) { - for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { + mp_int_t max_num_displays = CIRCUITPY_DISPLAY_LIMIT; + + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_LIMIT", &max_num_displays); + #endif + + for (uint8_t i = 0; i < max_num_displays; i++) { + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + if (i < CIRCUITPY_DISPLAY_LIMIT) { + if (displays[i].epaper_display.base.type != &epaperdisplay_epaperdisplay_type || + displays[i].epaper_display.core.current_group != &circuitpython_splash) { + // Skip regular displays and those not showing the splash. + continue; + } + } else { + if (displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].epaper_display.base.type != &epaperdisplay_epaperdisplay_type || + displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].epaper_display.core.current_group != &circuitpython_splash) { + // Skip regular displays and those not showing the splash. + continue; + } + } + #else if (displays[i].epaper_display.base.type != &epaperdisplay_epaperdisplay_type || displays[i].epaper_display.core.current_group != &circuitpython_splash) { // Skip regular displays and those not showing the splash. continue; } - epaperdisplay_epaperdisplay_obj_t *display = &displays[i].epaper_display; + #endif + + epaperdisplay_epaperdisplay_obj_t *display; + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + if (i < CIRCUITPY_DISPLAY_LIMIT) { + display = &displays[i].epaper_display; + } else { + display = &displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].epaper_display; + } + #else + display = &displays[i].epaper_display; + #endif size_t time_to_refresh = common_hal_epaperdisplay_epaperdisplay_get_time_to_refresh(display); if (time_to_refresh > 0) { return time_to_refresh; diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index eea11bd5fb4af..31a6282c2802c 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -1554,24 +1554,34 @@ static void _process_request(socketpool_socket_obj_t *socket, _request *request) } static bool supervisor_filesystem_access_could_block(void) { + bool could_block = false; #if CIRCUITPY_FOURWIRE mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); if (!vfs->next) { // Assume that the CIRCUITPY root is not sharing a SPI bus with the display SPI bus return false; } - // Check display 0 to see if it's on a fourwire (SPI) bus. If it is, blocking is possible - // in theory other displays could block but also in reality there's generally 0 or 1 displays - for (size_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (display_buses[i].bus_base.type != &fourwire_fourwire_type) { + + mp_int_t max_num_displays = CIRCUITPY_DISPLAY_LIMIT; + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_LIMIT", &max_num_displays); + #define DYN_DISPLAY_BUSES(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? display_buses[indx] : display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) + #define DYN_DISPLAY_BUSES_ADR(indx, membr) (indx < CIRCUITPY_DISPLAY_LIMIT ? &display_buses[indx].membr : &display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT].membr) + #else + #define DYN_DISPLAY_BUSES(indx) (display_buses[indx]) + #define DYN_DISPLAY_BUSES_ADR(indx, membr) (&display_buses[indx].membr) + #endif + // Check displays to see if it's on a fourwire (SPI) bus. If it is, blocking is possible + for (mp_int_t i = 0; i < max_num_displays; i++) { + if (DYN_DISPLAY_BUSES(i).bus_base.type != &fourwire_fourwire_type) { continue; } - if (!common_hal_fourwire_fourwire_bus_free(MP_OBJ_FROM_PTR(&display_buses[i].bus_base))) { - return true; + if (!common_hal_fourwire_fourwire_bus_free(MP_OBJ_FROM_PTR(DYN_DISPLAY_BUSES_ADR(i, bus_base)))) { + could_block = true; } } #endif - return false; + return could_block; } void supervisor_web_workflow_background(void *data) { From 330e6e909313cd39690a81d7643422dec0aec56e Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 7 Apr 2025 19:07:37 -0400 Subject: [PATCH 02/14] Move epaper refresh test --- shared-module/epaperdisplay/EPaperDisplay.c | 29 +++++---------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/shared-module/epaperdisplay/EPaperDisplay.c b/shared-module/epaperdisplay/EPaperDisplay.c index dfb074cab8e72..db1b578273ddc 100644 --- a/shared-module/epaperdisplay/EPaperDisplay.c +++ b/shared-module/epaperdisplay/EPaperDisplay.c @@ -512,28 +512,6 @@ size_t maybe_refresh_epaperdisplay(void) { #endif for (uint8_t i = 0; i < max_num_displays; i++) { - #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT - if (i < CIRCUITPY_DISPLAY_LIMIT) { - if (displays[i].epaper_display.base.type != &epaperdisplay_epaperdisplay_type || - displays[i].epaper_display.core.current_group != &circuitpython_splash) { - // Skip regular displays and those not showing the splash. - continue; - } - } else { - if (displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].epaper_display.base.type != &epaperdisplay_epaperdisplay_type || - displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].epaper_display.core.current_group != &circuitpython_splash) { - // Skip regular displays and those not showing the splash. - continue; - } - } - #else - if (displays[i].epaper_display.base.type != &epaperdisplay_epaperdisplay_type || - displays[i].epaper_display.core.current_group != &circuitpython_splash) { - // Skip regular displays and those not showing the splash. - continue; - } - #endif - epaperdisplay_epaperdisplay_obj_t *display; #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT if (i < CIRCUITPY_DISPLAY_LIMIT) { @@ -544,6 +522,13 @@ size_t maybe_refresh_epaperdisplay(void) { #else display = &displays[i].epaper_display; #endif + + if (display.base.type != &epaperdisplay_epaperdisplay_type || + display.core.current_group != &circuitpython_splash) { + // Skip regular displays and those not showing the splash. + continue; + } + size_t time_to_refresh = common_hal_epaperdisplay_epaperdisplay_get_time_to_refresh(display); if (time_to_refresh > 0) { return time_to_refresh; From 41dcecc52eed20948f1c3a6ba8790da9e3dc2935 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Mon, 7 Apr 2025 21:14:08 -0400 Subject: [PATCH 03/14] fix pointer syntax --- shared-module/epaperdisplay/EPaperDisplay.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared-module/epaperdisplay/EPaperDisplay.c b/shared-module/epaperdisplay/EPaperDisplay.c index db1b578273ddc..2609081bf45ad 100644 --- a/shared-module/epaperdisplay/EPaperDisplay.c +++ b/shared-module/epaperdisplay/EPaperDisplay.c @@ -523,8 +523,8 @@ size_t maybe_refresh_epaperdisplay(void) { display = &displays[i].epaper_display; #endif - if (display.base.type != &epaperdisplay_epaperdisplay_type || - display.core.current_group != &circuitpython_splash) { + if (display->base.type != &epaperdisplay_epaperdisplay_type || + display->core.current_group != &circuitpython_splash) { // Skip regular displays and those not showing the splash. continue; } From 472f9b4da0bc46183c174460dab2da14bb8a655f Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Tue, 8 Apr 2025 16:26:37 -0400 Subject: [PATCH 04/14] Use m_malloc helpers instead of port_malloc --- main.c | 6 --- shared-module/board/__init__.c | 6 +-- shared-module/displayio/__init__.c | 47 +++++++++++++------ shared-module/displayio/__init__.h | 1 + shared-module/epaperdisplay/EPaperDisplay.c | 8 +--- supervisor/shared/web_workflow/web_workflow.c | 4 +- 6 files changed, 37 insertions(+), 35 deletions(-) diff --git a/main.c b/main.c index 528576a67adfd..0ea389635f40f 100644 --- a/main.c +++ b/main.c @@ -1051,12 +1051,6 @@ int __attribute__((used)) main(void) { alarm_reset(); #endif - #if CIRCUITPY_DISPLAYIO && CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT - // If number of displays has been overridden in settings.toml allocate memory and - // move existing displays - malloc_display_memory(); - #endif - // Reset everything and prep MicroPython to run boot.py. reset_port(); // Port-independent devices, like CIRCUITPY_BLEIO_HCI. diff --git a/shared-module/board/__init__.c b/shared-module/board/__init__.c index 78e25488006de..610a1bfb8381c 100644 --- a/shared-module/board/__init__.c +++ b/shared-module/board/__init__.c @@ -177,10 +177,8 @@ mp_obj_t common_hal_board_create_uart(const mp_int_t instance) { void reset_board_buses(void) { #if (CIRCUITPY_BOARD_I2C && CIRCUITPY_I2CDISPLAYBUS) || (CIRCUITPY_BOARD_SPI && (CIRCUITPY_FOURWIRE || CIRCUITPY_SHARPDISPLAY || CIRCUITPY_AURORA_EPAPER)) - mp_int_t max_num_displays = CIRCUITPY_DISPLAY_LIMIT; #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT #define DYN_DISPLAY_BUSES(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? display_buses[indx] : display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) - (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_LIMIT", &max_num_displays); #else #define DYN_DISPLAY_BUSES(indx) (display_buses[indx]) #endif @@ -190,7 +188,7 @@ void reset_board_buses(void) { for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_I2C; instance++) { bool display_using_i2c = false; #if CIRCUITPY_I2CDISPLAYBUS - for (uint8_t i = 0; i < max_num_displays; i++) { + for (uint8_t i = 0; i < max_allocated_display; i++) { if (DYN_DISPLAY_BUSES(i).bus_base.type == &i2cdisplaybus_i2cdisplaybus_type && DYN_DISPLAY_BUSES(i).i2cdisplay_bus.bus == &i2c_obj[instance]) { display_using_i2c = true; break; @@ -211,7 +209,7 @@ void reset_board_buses(void) { for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_SPI; instance++) { bool display_using_spi = false; #if CIRCUITPY_FOURWIRE || CIRCUITPY_SHARPDISPLAY || CIRCUITPY_AURORA_EPAPER - for (uint8_t i = 0; i < max_num_displays; i++) { + for (uint8_t i = 0; i < max_allocated_display; i++) { mp_const_obj_t bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; #if CIRCUITPY_FOURWIRE if (bus_type == &fourwire_fourwire_type && DYN_DISPLAY_BUSES(i).fourwire_bus.bus == &spi_obj[instance]) { diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 9f94cbb6197d2..fc5f64bf3d76a 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -24,6 +24,7 @@ #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT #include "shared-module/os/__init__.h" +#include "py/misc.h" #endif #if CIRCUITPY_BUSDISPLAY @@ -51,6 +52,7 @@ // The default indicates no primary display static int primary_display_number = -1; static mp_int_t max_num_displays = CIRCUITPY_DISPLAY_LIMIT; +mp_int_t max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; primary_display_bus_t display_buses[CIRCUITPY_DISPLAY_LIMIT]; primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT]; @@ -87,7 +89,7 @@ displayio_buffer_transform_t null_transform = { #if CIRCUITPY_RGBMATRIX || CIRCUITPY_IS31FL3741 || CIRCUITPY_VIDEOCORE || CIRCUITPY_PICODVI static bool any_display_uses_this_framebuffer(mp_obj_base_t *obj) { - for (uint8_t i = 0; i < max_num_displays; i++) { + for (uint8_t i = 0; i < max_allocated_display; i++) { if (DYN_DISPLAYS(i).display_base.type == &framebufferio_framebufferdisplay_type) { framebufferio_framebufferdisplay_obj_t *display = DYN_DISPLAYS_ADR(i, framebuffer_display); if (display->framebuffer == obj) { @@ -109,7 +111,7 @@ void displayio_background(void) { return; } - for (uint8_t i = 0; i < max_num_displays; i++) { + for (uint8_t i = 0; i < max_allocated_display; i++) { mp_const_obj_t display_type = DYN_DISPLAYS(i).display_base.type; if (display_type == NULL || display_type == &mp_type_NoneType) { // Skip null display. @@ -139,7 +141,7 @@ static void common_hal_displayio_release_displays_impl(bool keep_primary) { if (!keep_primary) { primary_display_number = -1; } - for (uint8_t i = (keep_primary ? CIRCUITPY_DISPLAY_LIMIT: 0); i < max_num_displays; i++) { + for (uint8_t i = (keep_primary ? CIRCUITPY_DISPLAY_LIMIT: 0); i < max_allocated_display; i++) { if (i == primary_display_number) { continue; } @@ -169,7 +171,7 @@ static void common_hal_displayio_release_displays_impl(bool keep_primary) { displays[i].display_base.type = &mp_type_NoneType; #endif } - for (uint8_t i = (keep_primary ? CIRCUITPY_DISPLAY_LIMIT: 0); i < max_num_displays; i++) { + for (uint8_t i = (keep_primary ? CIRCUITPY_DISPLAY_LIMIT: 0); i < max_allocated_display; i++) { if (i == primary_display_number) { continue; } @@ -237,15 +239,14 @@ void malloc_display_memory(void) { #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_LIMIT", &max_num_displays); if (max_num_displays > CIRCUITPY_DISPLAY_LIMIT) { - display_buses_dyn = (primary_display_bus_t *)port_malloc(sizeof(primary_display_bus_t) * (max_num_displays - CIRCUITPY_DISPLAY_LIMIT), false); - displays_dyn = (primary_display_t *)port_malloc(sizeof(primary_display_t) * (max_num_displays - CIRCUITPY_DISPLAY_LIMIT), false); + display_buses_dyn = m_new0(primary_display_bus_t, (max_num_displays - CIRCUITPY_DISPLAY_LIMIT)); + displays_dyn = m_new0(primary_display_t, (max_num_displays - CIRCUITPY_DISPLAY_LIMIT)); for (uint8_t i = CIRCUITPY_DISPLAY_LIMIT; i < max_num_displays; i++) { - memset(&displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT], 0, sizeof(displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT])); - memset(&display_buses_dyn[i - CIRCUITPY_DISPLAY_LIMIT], 0, sizeof(display_buses_dyn[i - CIRCUITPY_DISPLAY_LIMIT])); display_buses_dyn[i - CIRCUITPY_DISPLAY_LIMIT].bus_base.type = &mp_type_NoneType; displays_dyn[i - CIRCUITPY_DISPLAY_LIMIT].display_base.type = &mp_type_NoneType; } + max_allocated_display = max_num_displays; } #endif } @@ -418,7 +419,7 @@ void reset_displays(void) { } void displayio_gc_collect(void) { - for (uint8_t i = 0; i < max_num_displays; i++) { + for (uint8_t i = 0; i < max_allocated_display; i++) { mp_const_obj_t display_bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) { continue; @@ -445,7 +446,7 @@ void displayio_gc_collect(void) { #endif } - for (uint8_t i = 0; i < max_num_displays; i++) { + for (uint8_t i = 0; i < max_allocated_display; i++) { mp_const_obj_t display_type = DYN_DISPLAYS(i).display_base.type; if (display_type == NULL || display_type == &mp_type_NoneType) { continue; @@ -473,7 +474,7 @@ static bool is_display_active(mp_obj_base_t *display_maybe) { } primary_display_t *allocate_display(void) { - for (uint8_t i = 0; i < max_num_displays; i++) { + for (uint8_t i = 0; i < max_allocated_display; i++) { if (!is_display_active(DYN_DISPLAYS_ADR(i, display_base))) { // Clear this memory so it is in a known state before init. memset(DYN_DISPLAYS_ADR0(i), 0, sizeof(displays[0])); @@ -490,6 +491,14 @@ primary_display_t *allocate_display(void) { return DYN_DISPLAYS_ADR0(i); } } + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + // malloc_display_memory allocates both displays and buses + if (max_allocated_display == CIRCUITPY_DISPLAY_LIMIT) { + // See if we can allocate more displays and buses + malloc_display_memory(); + return &displays_dyn[0]; + } + #endif return NULL; } @@ -502,7 +511,7 @@ primary_display_t *allocate_display_or_raise(void) { } primary_display_bus_t *allocate_display_bus(void) { - for (uint8_t i = 0; i < max_num_displays; i++) { + for (uint8_t i = 0; i < max_allocated_display; i++) { mp_const_obj_t display_bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) { // Clear this memory so it is in a known state before init. @@ -519,6 +528,14 @@ primary_display_bus_t *allocate_display_bus(void) { return DYN_DISPLAY_BUSES_ADR0(i); } } + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + // malloc_display_memory allocates both displays and buses + if (max_allocated_display == CIRCUITPY_DISPLAY_LIMIT) { + // See if e can allocate more displays and buses + malloc_display_memory(); + return &display_buses_dyn[0]; + } + #endif return NULL; } @@ -531,7 +548,7 @@ primary_display_bus_t *allocate_display_bus_or_raise(void) { } mp_obj_t common_hal_displayio_get_primary_display(void) { - if (primary_display_number == -1 || primary_display_number >= max_num_displays) { + if (primary_display_number == -1 || primary_display_number >= max_allocated_display) { return mp_const_none; } mp_obj_base_t *primary_display = DYN_DISPLAYS_ADR(primary_display_number, display_base); @@ -546,7 +563,7 @@ void common_hal_displayio_set_primary_display(mp_obj_t new_primary_display) { primary_display_number = -1; return; } - for (uint8_t i = 0; i < max_num_displays; i++) { + for (uint8_t i = 0; i < max_allocated_display; i++) { mp_obj_t display = MP_OBJ_FROM_PTR(DYN_DISPLAYS_ADR0(i)); if (new_primary_display == display && is_display_active(display)) { primary_display_number = i; @@ -561,7 +578,7 @@ void common_hal_displayio_auto_primary_display(void) { if (primary_display_number != -1) { return; } - for (uint8_t i = 0; i < max_num_displays; i++) { + for (uint8_t i = 0; i < max_allocated_display; i++) { if (is_display_active(DYN_DISPLAYS_ADR(i, display_base))) { primary_display_number = i; return; diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index 9d67fa32fd6a1..4d774c3c927d8 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -101,6 +101,7 @@ typedef struct { extern primary_display_bus_t display_buses[CIRCUITPY_DISPLAY_LIMIT]; extern primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT]; +extern mp_int_t max_allocated_display; #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT extern primary_display_bus_t *display_buses_dyn; extern primary_display_t *displays_dyn; diff --git a/shared-module/epaperdisplay/EPaperDisplay.c b/shared-module/epaperdisplay/EPaperDisplay.c index 2609081bf45ad..570a6ec657ae3 100644 --- a/shared-module/epaperdisplay/EPaperDisplay.c +++ b/shared-module/epaperdisplay/EPaperDisplay.c @@ -505,13 +505,7 @@ void epaperdisplay_epaperdisplay_collect_ptrs(epaperdisplay_epaperdisplay_obj_t } size_t maybe_refresh_epaperdisplay(void) { - mp_int_t max_num_displays = CIRCUITPY_DISPLAY_LIMIT; - - #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT - (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_LIMIT", &max_num_displays); - #endif - - for (uint8_t i = 0; i < max_num_displays; i++) { + for (uint8_t i = 0; i < max_allocated_display; i++) { epaperdisplay_epaperdisplay_obj_t *display; #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT if (i < CIRCUITPY_DISPLAY_LIMIT) { diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index 31a6282c2802c..87b73cb8992b6 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -1562,9 +1562,7 @@ static bool supervisor_filesystem_access_could_block(void) { return false; } - mp_int_t max_num_displays = CIRCUITPY_DISPLAY_LIMIT; #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT - (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_LIMIT", &max_num_displays); #define DYN_DISPLAY_BUSES(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? display_buses[indx] : display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) #define DYN_DISPLAY_BUSES_ADR(indx, membr) (indx < CIRCUITPY_DISPLAY_LIMIT ? &display_buses[indx].membr : &display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT].membr) #else @@ -1572,7 +1570,7 @@ static bool supervisor_filesystem_access_could_block(void) { #define DYN_DISPLAY_BUSES_ADR(indx, membr) (&display_buses[indx].membr) #endif // Check displays to see if it's on a fourwire (SPI) bus. If it is, blocking is possible - for (mp_int_t i = 0; i < max_num_displays; i++) { + for (mp_int_t i = 0; i < max_allocated_display; i++) { if (DYN_DISPLAY_BUSES(i).bus_base.type != &fourwire_fourwire_type) { continue; } From 412c4bc8c82af10705d177fec1c0487691d2b348 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Tue, 8 Apr 2025 16:46:44 -0400 Subject: [PATCH 05/14] max_num_displays is no longer used outside of malloc_display_memory --- shared-module/displayio/__init__.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index fc5f64bf3d76a..1a0baeae98cc1 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -51,7 +51,6 @@ // The default indicates no primary display static int primary_display_number = -1; -static mp_int_t max_num_displays = CIRCUITPY_DISPLAY_LIMIT; mp_int_t max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; primary_display_bus_t display_buses[CIRCUITPY_DISPLAY_LIMIT]; @@ -237,6 +236,7 @@ void common_hal_displayio_release_displays(void) { void malloc_display_memory(void) { #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT + mp_int_t max_num_displays = CIRCUITPY_DISPLAY_LIMIT; (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_LIMIT", &max_num_displays); if (max_num_displays > CIRCUITPY_DISPLAY_LIMIT) { display_buses_dyn = m_new0(primary_display_bus_t, (max_num_displays - CIRCUITPY_DISPLAY_LIMIT)); From 156af59eec887ae90cfae8bc761ad0eee12f12bb Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 9 Apr 2025 09:53:50 -0400 Subject: [PATCH 06/14] reset display allocation indicator upon board reset --- shared-module/displayio/__init__.c | 1 + 1 file changed, 1 insertion(+) diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 1a0baeae98cc1..85a2f75aa8423 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -224,6 +224,7 @@ static void common_hal_displayio_release_displays_impl(bool keep_primary) { display_buses[i].bus_base.type = &mp_type_NoneType; #endif } + max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; if (!keep_primary) { supervisor_stop_terminal(); From 3f3cf0f2afe71373952288a8c3e6a190180362b1 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 9 Apr 2025 09:59:46 -0400 Subject: [PATCH 07/14] Better place for resetting indicator --- shared-module/displayio/__init__.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 85a2f75aa8423..cfa5bab7d82e4 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -224,8 +224,6 @@ static void common_hal_displayio_release_displays_impl(bool keep_primary) { display_buses[i].bus_base.type = &mp_type_NoneType; #endif } - max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; - if (!keep_primary) { supervisor_stop_terminal(); } @@ -255,6 +253,8 @@ void malloc_display_memory(void) { void reset_displays(void) { // In CircuitPython 10, release secondary displays before doing anything else: common_hal_displayio_release_displays_impl(true); + // Set dynamically allocated displays to 0 (CIRCUITPY_DISPLAY_LIMIT) + max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; // The SPI buses used by FourWires may be allocated on the heap so we need to move them inline. for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { From 4fcc8e1e9315e4a4ef75c7d8b3dcb6933a8e7f9a Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 9 Apr 2025 10:18:25 -0400 Subject: [PATCH 08/14] Dynamic memory cleanup --- shared-module/displayio/__init__.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index cfa5bab7d82e4..870c84905744b 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -100,7 +100,6 @@ static bool any_display_uses_this_framebuffer(mp_obj_base_t *obj) { } #endif - void displayio_background(void) { if (mp_hal_is_interrupted()) { return; @@ -253,8 +252,15 @@ void malloc_display_memory(void) { void reset_displays(void) { // In CircuitPython 10, release secondary displays before doing anything else: common_hal_displayio_release_displays_impl(true); + #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT // Set dynamically allocated displays to 0 (CIRCUITPY_DISPLAY_LIMIT) max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; + // Does this need to be done or dos port_free effectively do this? + m_del(primary_display_bus_t, display_buses_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); + m_del(primary_display_t, displays_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); + display_buses_dyn = NULL; + displays_dyn = NULL; + #endif // The SPI buses used by FourWires may be allocated on the heap so we need to move them inline. for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { From 20c20e4f47c8c7349935004c6e65735b23bd8cd7 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 9 Apr 2025 10:21:38 -0400 Subject: [PATCH 09/14] Thats what happens when you trust code gen too much :/ --- shared-module/displayio/__init__.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 870c84905744b..85fef1488d318 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -253,13 +253,13 @@ void reset_displays(void) { // In CircuitPython 10, release secondary displays before doing anything else: common_hal_displayio_release_displays_impl(true); #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT - // Set dynamically allocated displays to 0 (CIRCUITPY_DISPLAY_LIMIT) - max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; - // Does this need to be done or dos port_free effectively do this? + // Does this need to be done or does port_free effectively do this? m_del(primary_display_bus_t, display_buses_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); m_del(primary_display_t, displays_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); display_buses_dyn = NULL; displays_dyn = NULL; + // Set dynamically allocated displays to 0 (CIRCUITPY_DISPLAY_LIMIT) + max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; #endif // The SPI buses used by FourWires may be allocated on the heap so we need to move them inline. From 60df4be65ecd2da38400f486fb7f480dedbd76e7 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 9 Apr 2025 10:27:22 -0400 Subject: [PATCH 10/14] don't cleanup if we havent used it yet. --- shared-module/displayio/__init__.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 85fef1488d318..2c6b6eea62b7f 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -254,10 +254,14 @@ void reset_displays(void) { common_hal_displayio_release_displays_impl(true); #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT // Does this need to be done or does port_free effectively do this? - m_del(primary_display_bus_t, display_buses_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); - m_del(primary_display_t, displays_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); - display_buses_dyn = NULL; - displays_dyn = NULL; + if (max_allocated_display > CIRCUITPY_DISPLAY_LIMIT) { + // Free the dynamically allocated display buses and displays. + m_del(primary_display_bus_t, display_buses_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); + m_del(primary_display_bus_t, display_buses_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); + m_del(primary_display_t, displays_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); + display_buses_dyn = NULL; + displays_dyn = NULL; + } // Set dynamically allocated displays to 0 (CIRCUITPY_DISPLAY_LIMIT) max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; #endif From 18ddd487554851d9659c6dca09f5c400ea2d87f0 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 9 Apr 2025 10:28:57 -0400 Subject: [PATCH 11/14] Duplicate line error --- shared-module/displayio/__init__.c | 1 - 1 file changed, 1 deletion(-) diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 2c6b6eea62b7f..f6682eb14e8d1 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -257,7 +257,6 @@ void reset_displays(void) { if (max_allocated_display > CIRCUITPY_DISPLAY_LIMIT) { // Free the dynamically allocated display buses and displays. m_del(primary_display_bus_t, display_buses_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); - m_del(primary_display_bus_t, display_buses_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); m_del(primary_display_t, displays_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); display_buses_dyn = NULL; displays_dyn = NULL; From 4a7081cf4e3c4b90b32dc31449c1476f06775c29 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 9 Apr 2025 16:33:20 -0400 Subject: [PATCH 12/14] Set primary_display_number so supervisor.runtime.display survives VM --- shared-module/displayio/__init__.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index f6682eb14e8d1..b83606458deb6 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -56,8 +56,8 @@ mp_int_t max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; primary_display_bus_t display_buses[CIRCUITPY_DISPLAY_LIMIT]; primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT]; #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT -primary_display_bus_t *display_buses_dyn = &display_buses[0]; -primary_display_t *displays_dyn = &displays[0]; +primary_display_bus_t *display_buses_dyn; +primary_display_t *displays_dyn; #define DYN_DISPLAY_BUSES(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? display_buses[indx] : display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) #define DYN_DISPLAY_BUSES_ADR(indx, membr) (indx < CIRCUITPY_DISPLAY_LIMIT ? &display_buses[indx].membr : &display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT].membr) #define DYN_DISPLAY_BUSES_ADR0(indx) (indx < CIRCUITPY_DISPLAY_LIMIT ? &display_buses[indx] : &display_buses_dyn[indx - CIRCUITPY_DISPLAY_LIMIT]) @@ -260,9 +260,9 @@ void reset_displays(void) { m_del(primary_display_t, displays_dyn, (max_allocated_display - CIRCUITPY_DISPLAY_LIMIT)); display_buses_dyn = NULL; displays_dyn = NULL; + // Set dynamically allocated displays to 0 (CIRCUITPY_DISPLAY_LIMIT) + max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; } - // Set dynamically allocated displays to 0 (CIRCUITPY_DISPLAY_LIMIT) - max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; #endif // The SPI buses used by FourWires may be allocated on the heap so we need to move them inline. @@ -498,6 +498,10 @@ primary_display_t *allocate_display(void) { #else displays[i].display_base.type = &mp_type_NoneType; #endif + if (i == 0) { + // The first display is the primary display. + primary_display_number = 0; + } return DYN_DISPLAYS_ADR0(i); } } From 567259722e6def6eb8811c37877c0a9a837cc358 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 9 Apr 2025 18:12:34 -0400 Subject: [PATCH 13/14] static display bus doesn't show up, assume used if static display exists --- shared-module/displayio/__init__.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index b83606458deb6..00c33f487b708 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -527,7 +527,8 @@ primary_display_t *allocate_display_or_raise(void) { primary_display_bus_t *allocate_display_bus(void) { for (uint8_t i = 0; i < max_allocated_display; i++) { mp_const_obj_t display_bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; - if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) { + if ((display_bus_type == NULL || display_bus_type == &mp_type_NoneType) && + (i >= CIRCUITPY_DISPLAY_LIMIT || (!is_display_active(DYN_DISPLAYS_ADR(i, display_base))))) { // Clear this memory so it is in a known state before init. memset(DYN_DISPLAY_BUSES_ADR0(i), 0, sizeof(display_buses[0])); #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT From 1f2beae2da86f716d7359c5864f8b9dbd5a562b5 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 9 Apr 2025 21:30:51 -0400 Subject: [PATCH 14/14] i2c display issue in library not core --- shared-module/displayio/__init__.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 00c33f487b708..bf8979c6d8881 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -49,8 +49,8 @@ #include "supervisor/spi_flash_api.h" #endif -// The default indicates no primary display -static int primary_display_number = -1; +// The default indicates the first display is the primary display +static int primary_display_number = 0; mp_int_t max_allocated_display = CIRCUITPY_DISPLAY_LIMIT; primary_display_bus_t display_buses[CIRCUITPY_DISPLAY_LIMIT]; @@ -527,8 +527,7 @@ primary_display_t *allocate_display_or_raise(void) { primary_display_bus_t *allocate_display_bus(void) { for (uint8_t i = 0; i < max_allocated_display; i++) { mp_const_obj_t display_bus_type = DYN_DISPLAY_BUSES(i).bus_base.type; - if ((display_bus_type == NULL || display_bus_type == &mp_type_NoneType) && - (i >= CIRCUITPY_DISPLAY_LIMIT || (!is_display_active(DYN_DISPLAYS_ADR(i, display_base))))) { + if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) { // Clear this memory so it is in a known state before init. memset(DYN_DISPLAY_BUSES_ADR0(i), 0, sizeof(display_buses[0])); #if CIRCUITPY_OS_GETENV && CIRCUITPY_SET_DISPLAY_LIMIT