Hey there! I’m trying to get my RP2 Pico working with LVGL but I had no luck so far.
Maybe I’m missing something obvious?
I am building lv_micropython
using this GitHub Action:
on:
push:
branches:
- main
jobs:
local-build:
runs-on: ubuntu-latest
steps:
- name: Checkout Project
uses: actions/checkout@v4
- name: Checkout LVGL
uses: actions/checkout@v4
with:
repository: lvgl/lv_micropython
ref: "b1301ee8359454fd8b68b4063874e5034b4d9fe5" # Last commit on master
path: "./lv_micropython"
submodules: true
- name: Update apt-get
run: sudo apt-get update
- name: Install packages
run: sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential cmake git python3 -y
- name: Init Submodules
run: |
cd "$GITHUB_WORKSPACE/lv_micropython"
git submodule update --init --recursive lib/lv_bindings
- name: Make RP2 / PICO Submodules
run: |
cd "$GITHUB_WORKSPACE/lv_micropython"
make -j$(nproc) -C ports/rp2 BOARD=PICO USER_C_MODULES=../../lib/lv_bindings/bindings.cmake submodules
- name: Make mpy-cross
run: |
cd "$GITHUB_WORKSPACE/lv_micropython"
make -j$(nproc) -C mpy-cross
- name: Make Firmware
run: |
cd "$GITHUB_WORKSPACE/lv_micropython"
make -j$(nproc) -C ports/rp2 BOARD=PICO USER_C_MODULES=../../lib/lv_bindings/bindings.cmake
mv $GITHUB_WORKSPACE/lv_micropython/ports/rp2/build-PICO/firmware.uf2 $GITHUB_WORKSPACE/firmware.uf2
The build works. Once I flash the UF2 to my Pico I can for example successfully import and init lvgl via
import lvgl as lv
lv.init()
but once I try to register a display driver I get the AttributeError: 'module' object has no attribute 'disp_drv_t'
error.
# Register the display driver
disp_drv = lv.disp_drv_t() # This line fails
disp_drv.init()
disp_drv.flush_cb = lvgl_flush_cb
disp_drv.hor_res = DISPLAY_WIDTH
disp_drv.ver_res = DISPLAY_HEIGHT
disp_drv.register()
Does anybody know what the issue is here?
Thanks in advance!
PS: Here is the complete main.py file that I am trying to run:
import lvgl as lv
import machine
import rp2_dma
import time
# Constants for the display
SPI_FREQUENCY = const(40000000) # 40 MHz
SPI_MOSI = 7 # Change to your MOSI pin
SPI_SCK = 6 # Change to your SCK pin
SPI_CS = 9 # Change to your CS pin
DISPLAY_WIDTH = const(240)
DISPLAY_HEIGHT = const(320)
# Initialize LVGL
lv.init()
# Configure the SPI bus
spi = machine.SPI(
0,
baudrate=SPI_FREQUENCY,
sck=machine.Pin(SPI_SCK, machine.Pin.OUT),
mosi=machine.Pin(SPI_MOSI, machine.Pin.OUT),
)
# Configure the CS pin for the display
cs = machine.Pin(SPI_CS, machine.Pin.OUT)
cs(1) # Deselect the display
# Initialize DMA
dma = rp2_dma.DMA(0) # Use DMA channel 0
# Configure the SPI for the ILI9341 initialization
spi.init(baudrate=SPI_FREQUENCY)
# Write ILI9341 initialization sequence using spi.write()
# ... (omitted for brevity, see ILI9341 datasheet or existing drivers for commands)
# Helper function to transfer data using DMA
def dma_transfer(spi, cs, data):
cs(0) # Select the display
# Configure the DMA to transfer data to SPI TX, then start the transfer
dma.config(
src_addr=uctypes.addressof(data),
dst_addr=spi._spi[1], # SPI1 TX FIFO
count=len(data),
src_inc=True,
dst_inc=False,
trig_dreq=rp2_dma.DMA.DREQ_SPI1_TX
)
dma.enable()
# Wait for the transfer to complete
while dma.is_busy():
pass
cs(1) # Deselect the display
# LVGL Display Driver Flush Callback
def lvgl_flush_cb(drv, area, color_p):
# Convert the LVGL color buffer to a byte array compatible with DMA transfer
color_size = lv.color_t.SIZE * area.get_size()
buf = bytearray(color_size)
color_p_buf = uctypes.bytearray_at(color_p.__dereference__(color_size), color_size)
buf[:] = color_p_buf
# Use the helper function to transfer the buffer to the display
dma_transfer(spi, cs, buf)
# Indicate to LVGL that flushing is done
drv.flush_ready()
# Register the display driver
disp_drv = lv.disp_drv_t()
disp_drv.init()
disp_drv.flush_cb = lvgl_flush_cb
disp_drv.hor_res = DISPLAY_WIDTH
disp_drv.ver_res = DISPLAY_HEIGHT
disp_drv.register()
# Create a screen and a simple label
scr = lv.obj()
label = lv.label(scr)
label.set_text("Hello, ILI9341!")
label.align(lv.ALIGN.CENTER, 0, 0)
lv.scr_load(scr)
# Your main loop
while True:
lv.task_handler()
time.sleep_ms(5)