Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable lv_binding_micropython as USER_C_MODULE #341

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,404 changes: 1,577 additions & 827 deletions gen/gen_mpy.py

Large diffs are not rendered by default.

93 changes: 63 additions & 30 deletions lib/lv_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,27 @@
# event_loop = lv_utils.event_loop()
#
#
# uasyncio example with SDL:
# asyncio example with SDL:
#
# SDL.init(auto_refresh=False)
# # Register SDL display driver.
# # Register SDL mouse driver
# event_loop = lv_utils.event_loop(asynchronous=True)
# uasyncio.Loop.run_forever()
# asyncio.Loop.run_forever()
#
# uasyncio example with ili9341:
# asyncio example with ili9341:
#
# event_loop = lv_utils.event_loop(asynchronous=True) # Optional!
# self.disp = ili9341(asynchronous=True)
# uasyncio.Loop.run_forever()
# asyncio.Loop.run_forever()
#
# MIT license; Copyright (c) 2021 Amir Gonnen
#
##############################################################################

import lvgl as lv
import micropython
import usys
import sys

# Try standard machine.Timer, or custom timer from lv_timer, if available

Expand All @@ -42,34 +42,45 @@
try:
from lv_timer import Timer
except:
raise RuntimeError("Missing machine.Timer implementation!")
if sys.platform != "darwin":
raise RuntimeError("Missing machine.Timer implementation!")
Timer = False

# Try to determine default timer id

default_timer_id = 0
if usys.platform == 'pyboard':
if sys.platform == "pyboard":
# stm32 only supports SW timer -1
default_timer_id = -1
if usys.platform == 'rp2':

if sys.platform == "rp2":
# rp2 only supports SW timer -1
default_timer_id = -1

# Try importing uasyncio, if available
# Try importing asyncio, if available

try:
import uasyncio
uasyncio_available = True
import asyncio

asyncio_available = True
except:
uasyncio_available = False
asyncio_available = False

##############################################################################

class event_loop():

class event_loop:
_current_instance = None

def __init__(self, freq=25, timer_id=default_timer_id, max_scheduled=2, refresh_cb=None, asynchronous=False, exception_sink=None):
def __init__(
self,
freq=25,
timer_id=default_timer_id,
max_scheduled=2,
refresh_cb=None,
asynchronous=False,
exception_sink=None,
):
if self.is_running():
raise RuntimeError("Event loop is already running!")

Expand All @@ -80,28 +91,41 @@ def __init__(self, freq=25, timer_id=default_timer_id, max_scheduled=2, refresh_

self.delay = 1000 // freq
self.refresh_cb = refresh_cb
self.exception_sink = exception_sink if exception_sink else self.default_exception_sink
self.exception_sink = (
exception_sink if exception_sink else self.default_exception_sink
)

self.asynchronous = asynchronous
if self.asynchronous:
if not uasyncio_available:
raise RuntimeError("Cannot run asynchronous event loop. uasyncio is not available!")
self.refresh_event = uasyncio.Event()
self.refresh_task = uasyncio.create_task(self.async_refresh())
self.timer_task = uasyncio.create_task(self.async_timer())
if not asyncio_available:
raise RuntimeError(
"Cannot run asynchronous event loop. asyncio is not available!"
)
self.refresh_event = asyncio.Event()
self.refresh_task = asyncio.create_task(self.async_refresh())
self.timer_task = asyncio.create_task(self.async_timer())
else:
self.timer = Timer(timer_id)
if Timer:
self.timer = Timer(timer_id)
self.timer.init(
mode=Timer.PERIODIC, period=self.delay, callback=self.timer_cb
)
self.task_handler_ref = self.task_handler # Allocation occurs here
self.timer.init(mode=Timer.PERIODIC, period=self.delay, callback=self.timer_cb)
self.max_scheduled = max_scheduled
self.scheduled = 0

def init_async(self):
self.refresh_event = asyncio.Event()
self.refresh_task = asyncio.create_task(self.async_refresh())
self.timer_task = asyncio.create_task(self.async_timer())

def deinit(self):
if self.asynchronous:
self.refresh_task.cancel()
self.timer_task.cancel()
else:
self.timer.deinit()
if Timer:
self.timer.deinit()
event_loop._current_instance = None

def disable(self):
Expand All @@ -122,12 +146,21 @@ def task_handler(self, _):
try:
if lv._nesting.value == 0:
lv.task_handler()
if self.refresh_cb: self.refresh_cb()
if self.refresh_cb:
self.refresh_cb()
self.scheduled -= 1
except Exception as e:
if self.exception_sink:
self.exception_sink(e)

def tick(self):
self.timer_cb(None)

def run(self):
if sys.platform == "darwin":
while True:
self.tick()

def timer_cb(self, t):
# Can be called in Interrupt context
# Use task_handler_ref since passing self.task_handler would cause allocation.
Expand All @@ -149,15 +182,15 @@ async def async_refresh(self):
except Exception as e:
if self.exception_sink:
self.exception_sink(e)
if self.refresh_cb: self.refresh_cb()
if self.refresh_cb:
self.refresh_cb()

async def async_timer(self):
while True:
await uasyncio.sleep_ms(self.delay)
await asyncio.sleep_ms(self.delay)
lv.tick_inc(self.delay)
self.refresh_event.set()


def default_exception_sink(self, e):
usys.print_exception(e)
event_loop.current_instance().deinit()
sys.print_exception(e)
# event_loop.current_instance().deinit()
2 changes: 2 additions & 0 deletions lv_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,9 @@
/*Garbage Collector settings
*Used if LVGL is bound to higher level language and the memory is managed by that language*/
extern void mp_lv_init_gc();
extern void mp_lv_deinit_gc();
#define LV_GC_INIT() mp_lv_init_gc()
#define LV_GC_DEINIT() mp_lv_deinit_gc()

#define LV_ENABLE_GLOBAL_CUSTOM 1
#if LV_ENABLE_GLOBAL_CUSTOM
Expand Down
2 changes: 1 addition & 1 deletion lvgl
Submodule lvgl updated 473 files
52 changes: 52 additions & 0 deletions micropython.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

# This file is to be given as "make USER_C_MODULES=..." when building Micropython port

# Include LVGL component, ignore KCONFIG

idf_build_set_property(LV_MICROPYTHON 1)
idf_build_component(${CMAKE_CURRENT_LIST_DIR}/lvgl)
idf_build_set_property(COMPILE_DEFINITIONS "-DLV_KCONFIG_IGNORE" APPEND)
separate_arguments(LV_CFLAGS_ENV UNIX_COMMAND $ENV{LV_CFLAGS})
idf_build_set_property(COMPILE_DEFINITIONS "${LV_CFLAGS}" APPEND)
idf_build_set_property(COMPILE_OPTIONS "-Wno-unused-function" APPEND)
idf_build_set_property(SRCS "${LV_SRC}" APPEND)
idf_build_set_property(INCLUDE_DIRS "${LV_INCLUDE}" APPEND)

# Fix for idf 5.2.x
idf_build_get_property(component_targets __COMPONENT_TARGETS)
string(REPLACE "___idf_lvgl" "" component_targets "${component_targets}")
idf_build_set_property(__COMPONENT_TARGETS "${component_targets}")

include(${CMAKE_CURRENT_LIST_DIR}/mkrules_usermod.cmake)

# Add lv_bindings rules

all_lv_bindings()


# # # make usermod (target declared by Micropython for all user compiled modules) link to bindings
# # # this way the bindings (and transitively lvgl_interface) get proper compilation flags
# target_link_libraries(usermod INTERFACE usermod_lvgl)

file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_LIST_DIR}/lvgl/src/*.c)

add_library(lvgl_interface INTERFACE)

target_sources(lvgl_interface INTERFACE ${SOURCES})
target_compile_options(lvgl_interface INTERFACE ${LV_CFLAGS})

# # lvgl bindings target (the mpy module)

add_library(usermod_lvgl INTERFACE)
target_sources(usermod_lvgl INTERFACE ${LV_SRC})
target_include_directories(usermod_lvgl INTERFACE ${LV_INCLUDE})


file(WRITE ${LV_MP} "")

target_link_libraries(usermod_lvgl INTERFACE lvgl_interface)

# # # make usermod (target declared by Micropython for all user compiled modules) link to bindings
# # # this way the bindings (and transitively lvgl_interface) get proper compilation flags
target_link_libraries(usermod INTERFACE usermod_lvgl)

69 changes: 69 additions & 0 deletions micropython.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

################################################################################
# LVGL unix optional libraries
# Update CFLAGS_EXTMOD and LDFLAGS_EXTMOD for LVGL extenral library,
# but do that only on the unix port, for unix specific dependencies
ifeq ($(notdir $(CURDIR)),unix)
ifneq ($(UNAME_S),Darwin)
CFLAGS_EXTMOD += -DMICROPY_FB=1
endif

SDL_CFLAGS_EXTMOD := $(shell pkg-config --silence-errors --cflags sdl2)
SDL_LDFLAGS_EXTMOD := $(shell pkg-config --silence-errors --libs sdl2)
ifneq ($(SDL_LDFLAGS_EXTMOD),)
CFLAGS_EXTMOD += $(SDL_CFLAGS_EXTMOD) -DMICROPY_SDL=1
LDFLAGS_EXTMOD += $(SDL_LDFLAGS_EXTMOD)
endif

RLOTTIE_CFLAGS_EXTMOD := $(shell pkg-config --silence-errors --cflags rlottie)
RLOTTIE_LDFLAGS_EXTMOD := $(shell pkg-config --silence-errors --libs rlottie)
ifneq ($(RLOTTIE_LDFLAGS_EXTMOD),)
CFLAGS_EXTMOD += $(RLOTTIE_CFLAGS_EXTMOD) -DMICROPY_RLOTTIE=1
LDFLAGS_EXTMOD += $(RLOTTIE_LDFLAGS_EXTMOD)
endif

FREETYPE_CFLAGS_EXTMOD := $(shell pkg-config --silence-errors --cflags freetype2)
FREETYPE_LDFLAGS_EXTMOD := $(shell pkg-config --silence-errors --libs freetype2)
ifneq ($(FREETYPE_LDFLAGS_EXTMOD),)
CFLAGS_EXTMOD += $(FREETYPE_CFLAGS_EXTMOD) -DMICROPY_FREETYPE=1
LDFLAGS_EXTMOD += $(FREETYPE_LDFLAGS_EXTMOD)
endif

FFMPEG_LIBS := libavformat libavcodec libswscale libavutil
FFMPEG_CFLAGS_EXTMOD := $(shell pkg-config --silence-errors --cflags $(FFMPEG_LIBS))
FFMPEG_LDFLAGS_EXTMOD := $(shell pkg-config --silence-errors --libs $(FFMPEG_LIBS))
ifneq ($(FFMPEG_LDFLAGS_EXTMOD),)
CFLAGS_EXTMOD += $(FFMPEG_CFLAGS_EXTMOD) -DMICROPY_FFMPEG=1
LDFLAGS_EXTMOD += $(FFMPEG_LDFLAGS_EXTMOD)
endif
endif

################################################################################

# LVGL build rules


LVGL_BINDING_DIR := $(USERMOD_DIR)

LVGL_DIR = $(LVGL_BINDING_DIR)/lvgl
LVGL_GENERIC_DRV_DIR = $(LVGL_BINDING_DIR)/driver/generic
INC += -I$(LVGL_BINDING_DIR)
ALL_LVGL_SRC = $(shell find $(LVGL_DIR) -type f -name '*.h') $(LVGL_BINDING_DIR)/lv_conf.h
LVGL_PP = $(BUILD)/lvgl/lvgl.pp.c
LVGL_MPY = $(BUILD)/lvgl/lv_mpy.c
LVGL_MPY_METADATA = $(BUILD)/lvgl/lv_mpy.json
CFLAGS_EXTMOD += $(LV_CFLAGS)

$(LVGL_MPY): $(ALL_LVGL_SRC) $(LVGL_BINDING_DIR)/gen/gen_mpy.py
$(ECHO) "LVGL-GEN $@"
$(Q)mkdir -p $(dir $@)
$(Q)$(CPP) $(CFLAGS_EXTMOD) -DPYCPARSER -x c -I $(LVGL_BINDING_DIR)/pycparser/utils/fake_libc_include $(INC) $(LVGL_DIR)/lvgl.h > $(LVGL_PP)
$(Q)$(PYTHON) $(LVGL_BINDING_DIR)/gen/gen_mpy.py -M lvgl -MP lv -MD $(LVGL_MPY_METADATA) -E $(LVGL_PP) $(LVGL_DIR)/lvgl.h > $@

.PHONY: LVGL_MPY
LVGL_MPY: $(LVGL_MPY)

CFLAGS_EXTMOD += -Wno-unused-function
CFLAGS_EXTRA += -Wno-unused-function
SRC_THIRDPARTY_C += $(subst $(TOP)/,,$(shell find $(LVGL_DIR)/src $(LVGL_DIR)/examples $(LVGL_GENERIC_DRV_DIR) -type f -name "*.c"))
SRC_EXTMOD_C += $(LVGL_MPY)
Loading