LCD
hub.lcd (hub._lcd_module) drives an ST7735
128x160 SPI panel plus an LVGL display layer on top.
Init
The C firmware initialises the panel hardware itself; LVGL init must run from Python (it runs from boot.py) because LVGL allocates through the MicroPython GC:
import hub, lvgl as lv
hub.lcd.init() # lv_init + draw buffers + tick timer
# you do not need to run hub.lcd.init(), because boot.py already contains it.
After init() returns, import lvgl as lv
is usable — create screens/widgets as normal.
Warning
Do not import lvgl from boot.py before hub.lcd.init()
— LVGL’s mp_lv_roots structure needs its allocator alive.
Rendering loop
The C-side Hub::loop() already runs lv_timer_handler under
the GIL every ~5 ms. You do not need to call it from Python.
Backlight
Backlight is driven by the shared PCA9685:
hub.lcd.on() # full
hub.lcd.off() # off
hub.lcd.backlight(64) # 0..255 → PCA9685 duty
Raw panel access
Useful before LVGL is up, or when bringing up a new panel variant:
hub.lcd.fill(0xF800) # solid red (RGB565)
hub.lcd.setInvert(True) # INVON
hub.lcd.setMadctl(0xA8) # MY MV BGR
hub.lcd.setOffset(2, 3) # GreenTab column/row offsets
hub.lcd.cmd(0x21, b"") # arbitrary command
hub.lcd.reset() # pulse RESET + re-init
MADCTL bits (see ST7735 datasheet): MY=0x80, MX=0x40,
MV=0x20, ML=0x10, BGR=0x08.
Soft reset
The port takes care of LVGL teardown on MicroPython soft-reset. You
do not need to call lv_deinit yourself.