This repository was archived by the owner on Oct 30, 2026. It is now read-only.
Repository navigation
Build as a CircuitPython 11 user C module; survive a reload; never spin a board off USB - #19
Merged
Merged
Conversation
micropython.mk lets CircuitPython 11's USER_C_MODULES build LVGL and the generated bindings with no patches to CircuitPython's tree: LVGL in SRC_USERMOD_LIB_C (never scanned for qstrs), the module, bindings and allocator in SRC_USERMOD_C, the board ports' late -Werror flags turned off per object, a stop when gifio is on (its AnimatedGIF collides with LVGL's), and four-byte qstr offsets where CircuitPython has them. Without a sibling lvgl-bindings checkout the pinned commit is fetched into .deps/. A reload starts a new VM without clearing root pointers, so LVGL's state pointed into the previous VM's heap and the first display_create asked for megabytes. The module now answers __init__, which runs on its first import in each VM, and drops that state. Pins lvgl-bindings at the CircuitPython 11 integer fix (PR branch).
LVGL's allocator raised MemoryError when the heap was full, a longjmp out of LVGL's C that left a refresh half done; the next pass spun on it, and a board spinning in C drops its drive and serial port until someone presses reset. The allocator now returns NULL (m_malloc_maybe, m_realloc_maybe), and lv_assert_circuitpython.c is LVGL's assert handler on CircuitPython (lvgl-bindings' lv_conf.h names it): safe mode on a board, abort() on unix. Pins lvgl-bindings at the commit that names the handler (PR branch).
Built-in modules stay out of sys.modules, so the import system ran the reload hook on every `import lvgl` and dropped live LVGL state: a second import after display_driver had set LVGL up segfaulted the unix build in lv_display_get_default(). The hook now puts lvgl in sys.modules, which a later import in the same VM finds first and a new VM starts without.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
CircuitPython 11 builds user C modules the way MicroPython does, so this repository can now be handed to any port's
makeasUSER_C_MODULES, with no patches to CircuitPython's tree (micropython.mk). The patch-script route for 10.x still works.SRC_USERMOD_LIB_C, compiled and linked but never scanned for qstrs; the module, bindings, allocator, JPEG shim and assert handler inSRC_USERMOD_C.-Werrorflags are turned off per object, for this module's objects only.CIRCUITPY_GIFIOis 1 (gifio's AnimatedGIF collides with LVGL's).lvgl-bindings/the pinned commit is fetched into.deps/(scripts/fetch_bindings.sh).-DMICROPY_QSTR_OFFSET_BYTES=4: CircuitPython 11.0 caps a qstr pool at 64 KB of names and LVGL brings about 53 KB. The setting needs a CircuitPython that has it (a patch, for now); elsewhere it is ignored.Two fixes that matter on a board:
lv.is_initialized()read garbage and the firstdisplay_createasked for 7.6 MB. The module now answers__init__by delegation, drops that state, and puts itself insys.modules, so the hook runs once per VM rather than on everyimport lvgl(built-ins aren't kept there otherwise, and a second import used to drop live state).MemoryErrorthrough LVGL's C, leaving a refresh half done for the next pass to spin on, and a board spinning in C drops off USB until someone presses reset. The allocator now returns NULL, and LVGL's assertion callslv_circuitpython_assert_failed(): safe mode on a board,abort()on unix. On unix, filling the heap and creating labels spun until killed before and aborts now.Checked: unix (
coverage) renders and the pixels read back exact. On a Feather RP2040 + 3.5" TFT FeatherWing with this as its only user C module, a full LVGL screen through pydevices' display driver runs fromcode.pyat boot (first 480x320 frame in 10 flushes, about 0.4 s, 50 KB of heap left), and a 1/8-scale capture of the frame sent to the panel matches the unix render sample for sample, outside the machine-name line and the animated spinner.import lvgla second time after LVGL is running no longer crashes.Pins lvgl-bindings 2858c42 (PyDevices/lvgl-bindings#29).