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.