Skip to content

hard fault with qtpy rp2040 and dual GC9A01A display #10791

@mousethief

Description

@mousethief

CircuitPython version and board name

Adafruit CircuitPython 10.1.0-beta.1-dirty on 2026-01-28; Adafruit QT Py RP2040 with rp2040

Code/REPL

# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import supervisor
import time

# Wait for USB serial to settle ; otherwise may lose initial console output
while not supervisor.runtime.serial_connected:
	time.sleep(0.001)
print("Start USB Serial")

import board
import displayio
import terminalio
from adafruit_display_text.bitmap_label import Label
import adafruit_imageload
from fourwire import FourWire
from vectorio import Circle
from adafruit_gc9a01a import GC9A01A

dual_display_busses_p = 1
spi = board.SPI()
tft1_cs = board.A0
tft1_dc = board.A1
tft2_cs = board.A2
tft2_dc = board.A3
#tft_reset = board.A2
tft_reset = None

displayio.release_displays()

display1_bus = FourWire(spi, command=tft1_dc, chip_select=tft1_cs, reset=tft_reset)
display1 = GC9A01A(display1_bus, width=240, height=240, rotation=0, auto_refresh=False)
if dual_display_busses_p is 1:
	try:
		display2_bus = FourWire(spi, command=tft2_dc, chip_select=tft2_cs, reset=tft_reset)
		dual_display_busses_p = 2
	except RuntimeError:
		pass

if dual_display_busses_p is 2:
	display2 = GC9A01A(display2_bus, width=240, height=240, rotation=0, auto_refresh=False)

# --- Default Shapes/Text Demo ---
main_group1 = displayio.Group()
display1.root_group = main_group1
if dual_display_busses_p is 2:
	main_group2 = displayio.Group()
	display2.root_group = main_group2

bg_bitmap = displayio.Bitmap(240, 240, 2)
color_palette = displayio.Palette(2)
color_palette[0] = 0x00FF00  # Bright Green
color_palette[1] = 0xAA0088  # Purple

bg_sprite1 = displayio.TileGrid(bg_bitmap, pixel_shader=color_palette, x=0, y=0)
main_group1.append(bg_sprite1)
if dual_display_busses_p is 2:
	bg_sprite2 = displayio.TileGrid(bg_bitmap, pixel_shader=color_palette, x=0, y=0)
	main_group2.append(bg_sprite2)

inner_circle1 = Circle(pixel_shader=color_palette, x=120, y=120, radius=100, color_index=1)
main_group1.append(inner_circle1)
if dual_display_busses_p is 2:
	inner_circle2 = Circle(pixel_shader=color_palette, x=120, y=120, radius=100, color_index=1)
	main_group2.append(inner_circle2)

text_group1 = displayio.Group(scale=2, x=50, y=120)
text1 = "Hello World!"
text_area1 = Label(terminalio.FONT, text=text1, color=0xFFFF00)
text_group1.append(text_area1)  # Subgroup for text scaling
main_group1.append(text_group1)

if dual_display_busses_p is 2:
	text_group2 = displayio.Group(scale=2, x=50, y=120)
	text2 = "Hello World!"
	text_area2 = Label(terminalio.FONT, text=text2, color=0xFFFF00)
	text_group2.append(text_area2)  # Subgroup for text scaling
	main_group2.append(text_group2)

# --- ImageLoad Demo ---
blinka_group1 = displayio.Group()
bitmap, palette = adafruit_imageload.load("/blinka_round.bmp",
                                          bitmap=displayio.Bitmap,
                                          palette=displayio.Palette)

grid = displayio.TileGrid(bitmap, pixel_shader=palette)
blinka_group1.append(grid)

if dual_display_busses_p is 2:
	blinka_group2 = displayio.Group()
	bitmap, palette = adafruit_imageload.load("/blinka_round.bmp",
		                                      bitmap=displayio.Bitmap,
		                                      palette=displayio.Palette)

	grid = displayio.TileGrid(bitmap, pixel_shader=palette)
	blinka_group2.append(grid)

while True:
	# show shapes/text and blinka bitmap
	t1 = time.monotonic()
	display1.root_group = main_group1
	if dual_display_busses_p is 2:
		display2.root_group = blinka_group2
	display1.refresh()
	if dual_display_busses_p is 2:
		display2.refresh()
	# show blinka bitmap and shapes/texr
	display1.root_group = blinka_group1
	if dual_display_busses_p is 2:
		display2.root_group = main_group2
	display1.refresh()
	if dual_display_busses_p is 2:
		display2.refresh()
	t2 = time.monotonic()
#	print(f"Elapsed time: {t2 - t1}")

Behavior

USB serial terminal output

(power cycle or button reset)

[20:37:02.235] Waiting for tty device..
[20:37:53.285] Connected to /dev/serial/by-id/usb-Adafruit_QT_Py_RP2040_DF609072DB352B28-if00

(The modified demo code runs fine at first)

Start USB Serial

(...)

Code stopped by auto-reload. Reloading soon.
soft reboot

Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
code.py output:

[20:38:11.856] Disconnected
[20:38:12.857] Warning: Could not open /dev/serial/by-id/usb-Adafruit_QT_Py_RP2040_DF609072DB352B28-if00 (No such file or directory)
[20:38:12.857] Waiting for tty device..
[20:38:13.859] Connected to /dev/serial/by-id/usb-Adafruit_QT_Py_RP2040_DF609072DB352B28-if00
Auto-reload is off.
Running in safe mode! Not running saved code.

You are in safe mode because:
CircuitPython core code crashed hard. Whoops!
Hard fault: memory access or instruction error.
Please file an issue with your program at github.com/adafruit/circuitpython/issues.
Press reset to exit safe mode.

Press any key to enter the REPL. Use CTRL-D to reload.

Description

Additional information

(clean clone with minimal change, built on debian 13.3, referencing https://todbot.com/blog/2022/05/19/multiple-displays-in-circuitpython-compiling-custom-circuitpython/)

git clone https://github.com/adafruit/circuitpython.git
cd circuitpython/
git checkout 10.0.3
python3 -mvenv .py
source .py/bin/activate
make fetch-all-submodules
pip3 install --upgrade -r requirements-dev.txt
pip3 install --upgrade -r requirements-doc.txt
make -C mpy-cross

echo "#define CIRCUITPY_DISPLAY_LIMIT (2)" >> ports/raspberrypy/boards/adafruit_qtpy_rp2040/mpconfigboard.h

cd ports/raspberrypi/
make BOARD=adafruit_qtpy_rp2040

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions