Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/environment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,27 @@ Example: Configure the display to 640x480 black and white (1 bit per pixel):
`Adafruit Feather RP2350 <https://circuitpython.org/board/adafruit_feather_rp2350/>`_
`Adafruit Metro RP2350 <https://circuitpython.org/board/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.
Expand Down
6 changes: 6 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you are doing this really early, can you just have one dynamically allocated array? It'll make all of the other accessing code simpler.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my original attempt, however the boards that had displays defined as board.DISPLAY didn't like the dynamic memory allocation. I tried multiple pointer configurations to try and get the compile to work but didn't look into modifying how board.DISPLAY was defined.

I can go back and dig into the board.DISPLAY setup and try and get it to work with the dynamically allocated memory structure if you think that makes sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right. board.DISPLAY is static.

I wonder if it'd be more fruitful to just try and allocate displays to the CP heap first and then move them over if needed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tannewt I thought you mentioned somewhere else about replacing board.DISPLAY with something else in 10 or 11? Would board.display() or similar be helpful?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't have a working understanding of the complete CP memory model so I'm not sure how I would do that, is there a mechanism like port_malloc that would allocate heap space?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well the user code displays seem to be working with m_malloc now. I've held off working on moving displays to the static storage as I think that will be related to the primary display features and I thought it would be better to work on that in a follow up PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! Can you have the first display use the single static allocation?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean if it hasn't been used by picodvi, board.DISPLAY, or anything else yet. Yea, if I do that it will save allocating the dynamic storage for a single user display. I'll give it a go 😁

Copy link
Author

@RetiredWizard RetiredWizard Apr 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually maybe I'm misunderstanding, I think it already grabs the static storage first.

Edit: Yep, just tested and first user screen survives VM (ctrl-D) but I wasn't setting the primary display properly, just fixed with a new commit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, sorry about that. I hadn't looked at the updated code. Will do shortly!

#endif

// Reset everything and prep MicroPython to run boot.py.
reset_port();
// Port-independent devices, like CIRCUITPY_BLEIO_HCI.
Expand Down
1 change: 1 addition & 0 deletions ports/atmel-samd/mpconfigport.mk
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ CIRCUITPY_LTO = 1

CIRCUITPY_KEYPAD_DEMUX ?= 0
CIRCUITPY_LVFONTIO ?= 0
CIRCUITPY_SET_DISPLAY_LIMIT ?= 0

######################################################################
# Put samd21-only choices here.
Expand Down
1 change: 1 addition & 0 deletions ports/stm/boards/stm32f411ve_discovery/mpconfigboard.mk
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ CIRCUITPY_BLEIO_HCI = 0
CIRCUITPY_CODEOP = 0
CIRCUITPY_MSGPACK = 0
CIRCUITPY_VECTORIO = 0
CIRCUITPY_SET_DISPLAY_LIMIT = 0
3 changes: 3 additions & 0 deletions py/circuitpy_mpconfig.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
28 changes: 21 additions & 7 deletions shared-module/board/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down
Loading