Refactor LVGL into module for POSIX

This commit is contained in:
Ken Van Hoeylandt 2026-02-01 17:10:40 +01:00
parent 2cf5c3c853
commit 73319c7ba4
11 changed files with 42 additions and 161 deletions

View File

@ -1,107 +0,0 @@
#include "LvglTask.h"
#include <Tactility/Logger.h>
#include <Tactility/RecursiveMutex.h>
#include <Tactility/Thread.h>
#include <tactility/check.h>
#include <Tactility/lvgl/LvglSync.h>
#include <lvgl.h>
static const auto LOGGER = tt::Logger("LvglTask");
// Mutex for LVGL drawing
static tt::RecursiveMutex lvgl_mutex;
static tt::RecursiveMutex task_mutex;
static uint32_t task_max_sleep_ms = 10;
// Mutex for LVGL task state (to modify task_running state)
static bool task_running = false;
lv_disp_t* displayHandle = nullptr;
static void lvgl_task(void* arg);
static bool task_lock(TickType_t timeout) {
return task_mutex.lock(timeout);
}
static void task_unlock() {
task_mutex.unlock();
}
static void task_set_running(bool running) {
assert(task_lock(configTICK_RATE_HZ / 100));
task_running = running;
task_unlock();
}
bool lvgl_task_is_running() {
assert(task_lock(configTICK_RATE_HZ / 100));
bool result = task_running;
task_unlock();
return result;
}
static bool lvgl_lock(uint32_t timeoutMillis) {
return lvgl_mutex.lock(pdMS_TO_TICKS(timeoutMillis));
}
static void lvgl_unlock() {
lvgl_mutex.unlock();
}
void lvgl_task_interrupt() {
check(task_lock(portMAX_DELAY));
task_set_running(false); // interrupt task with boolean as flag
task_unlock();
}
void lvgl_task_start() {
LOGGER.info("LVGL task starting");
// Create the main app loop, like ESP-IDF
BaseType_t task_result = xTaskCreate(
lvgl_task,
"lvgl",
8192,
nullptr,
static_cast<UBaseType_t>(tt::Thread::Priority::High), // Should be higher than main app task
nullptr
);
assert(task_result == pdTRUE);
}
static void lvgl_task(void* arg) {
LOGGER.info("LVGL task started");
/** Ideally. the display handle would be created during Simulator.start(),
* but somehow that doesn't work. Waiting here from a ThreadFlag when that happens
* also doesn't work. It seems that it must be called from this task. */
displayHandle = lv_sdl_window_create(320, 240);
lv_sdl_window_set_title(displayHandle, "Tactility");
uint32_t task_delay_ms = task_max_sleep_ms;
task_set_running(true);
while (lvgl_task_is_running()) {
if (lvgl_lock(10)) {
task_delay_ms = lv_timer_handler();
lvgl_unlock();
}
if ((task_delay_ms > task_max_sleep_ms) || (1 == task_delay_ms)) {
task_delay_ms = task_max_sleep_ms;
} else if (task_delay_ms < 1) {
task_delay_ms = 1;
}
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
}
lv_disp_remove(displayHandle);
displayHandle = nullptr;
vTaskDelete(nullptr);
}

View File

@ -1,5 +0,0 @@
#pragma once
void lvgl_task_start();
bool lvgl_task_is_running();
void lvgl_task_interrupt();

View File

@ -1,4 +1,3 @@
#include "LvglTask.h"
#include "hal/SdlDisplay.h" #include "hal/SdlDisplay.h"
#include "hal/SdlKeyboard.h" #include "hal/SdlKeyboard.h"
#include "hal/SimulatorPower.h" #include "hal/SimulatorPower.h"
@ -11,23 +10,6 @@
using namespace tt::hal; using namespace tt::hal;
static bool initBoot() {
lv_init();
lvgl_task_start();
return true;
}
static void deinitPower() {
lvgl_task_interrupt();
while (lvgl_task_is_running()) {
tt::kernel::delayMillis(10);
}
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
lv_deinit();
#endif
}
static std::vector<std::shared_ptr<tt::hal::Device>> createDevices() { static std::vector<std::shared_ptr<tt::hal::Device>> createDevices() {
return { return {
std::make_shared<SdlDisplay>(), std::make_shared<SdlDisplay>(),
@ -38,7 +20,7 @@ static std::vector<std::shared_ptr<tt::hal::Device>> createDevices() {
} }
extern const Configuration hardwareConfiguration = { extern const Configuration hardwareConfiguration = {
.initBoot = initBoot, .initBoot = nullptr,
.createDevices = createDevices, .createDevices = createDevices,
.uart = { .uart = {
uart::Configuration { uart::Configuration {

View File

@ -13,21 +13,24 @@ public:
std::string getName() const override { return "SDL Display"; } std::string getName() const override { return "SDL Display"; }
std::string getDescription() const override { return ""; } std::string getDescription() const override { return ""; }
bool start() override { bool start() override { return true; }
bool stop() override { return true; }
bool supportsLvgl() const override { return true; }
bool startLvgl() override {
displayHandle = lv_sdl_window_create(320, 240); displayHandle = lv_sdl_window_create(320, 240);
lv_sdl_window_set_title(displayHandle, "Tactility"); lv_sdl_window_set_title(displayHandle, "Tactility");
return true; return displayHandle != nullptr;
} }
bool stop() override { bool stopLvgl() override {
lv_display_delete(displayHandle); lv_display_delete(displayHandle);
displayHandle = nullptr; displayHandle = nullptr;
return true; return true;
} }
bool supportsLvgl() const override { return true; }
bool startLvgl() override { return displayHandle != nullptr; }
bool stopLvgl() override { check(false, "Not supported"); }
lv_display_t* getLvglDisplay() const override { return displayHandle; } lv_display_t* getLvglDisplay() const override { return displayHandle; }
std::shared_ptr<tt::hal::touch::TouchDevice> getTouchDevice() override { return std::make_shared<SdlTouch>(); } std::shared_ptr<tt::hal::touch::TouchDevice> getTouchDevice() override { return std::make_shared<SdlTouch>(); }

View File

@ -7,7 +7,6 @@ if (DEFINED ENV{ESP_IDF_VERSION})
idf_component_register( idf_component_register(
SRCS ${SOURCE_FILES} SRCS ${SOURCE_FILES}
INCLUDE_DIRS "Include/" INCLUDE_DIRS "Include/"
PRIV_INCLUDE_DIRS "Private/"
REQUIRES lvgl esp_lvgl_port REQUIRES lvgl esp_lvgl_port
) )
@ -19,7 +18,6 @@ else ()
target_include_directories(lvgl-module target_include_directories(lvgl-module
PUBLIC Include/ PUBLIC Include/
PRIVATE Private/
) )
target_link_libraries(lvgl-module PUBLIC target_link_libraries(lvgl-module PUBLIC

View File

@ -1,11 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
bool lvgl_is_stop_requested();
#ifdef __cplusplus
}
#endif

View File

@ -10,7 +10,9 @@
#include <lvgl.h> #include <lvgl.h>
#include "lvgl_module_private.h" #include "tactility/lvgl_module.h"
extern struct LvglModuleConfig lvgl_module_config;
// Mutex for LVGL drawing // Mutex for LVGL drawing
static struct RecursiveMutex lvgl_mutex; static struct RecursiveMutex lvgl_mutex;
@ -80,6 +82,8 @@ static void lvgl_task(void* arg) {
check(!lvgl_task_is_interrupt_requested()); check(!lvgl_task_is_interrupt_requested());
if (lvgl_module_config.on_start) lvgl_module_config.on_start();
while (!lvgl_task_is_interrupt_requested()) { while (!lvgl_task_is_interrupt_requested()) {
if (lvgl_try_lock_timed(10)) { if (lvgl_try_lock_timed(10)) {
task_delay_ms = lv_timer_handler(); task_delay_ms = lv_timer_handler();
@ -93,10 +97,14 @@ static void lvgl_task(void* arg) {
vTaskDelay(pdMS_TO_TICKS(task_delay_ms)); vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
} }
if (lvgl_module_config.on_stop) lvgl_module_config.on_stop();
vTaskDelete(nullptr); vTaskDelete(nullptr);
} }
error_t lvgl_arch_start() { error_t lvgl_arch_start() {
lv_init();
// Create the main app loop, like ESP-IDF // Create the main app loop, like ESP-IDF
BaseType_t task_result = xTaskCreate( BaseType_t task_result = xTaskCreate(
lvgl_task, lvgl_task,
@ -119,6 +127,9 @@ error_t lvgl_arch_stop() {
return ERROR_TIMEOUT; return ERROR_TIMEOUT;
} }
} }
lv_deinit();
return ERROR_NONE; return ERROR_NONE;
} }

View File

@ -8,13 +8,13 @@ error_t lvgl_arch_stop();
static bool is_running; static bool is_running;
static struct LvglModuleConfig config = { struct LvglModuleConfig lvgl_module_config = {
nullptr, nullptr,
nullptr nullptr
}; };
void lvgl_module_configure(const struct LvglModuleConfig in_config) { void lvgl_module_configure(const struct LvglModuleConfig config) {
memcpy(&config, &in_config, sizeof(struct LvglModuleConfig)); memcpy(&lvgl_module_config, &config, sizeof(struct LvglModuleConfig));
} }
static error_t start() { static error_t start() {
@ -25,7 +25,6 @@ static error_t start() {
error_t result = lvgl_arch_start(); error_t result = lvgl_arch_start();
if (result == ERROR_NONE) { if (result == ERROR_NONE) {
is_running = true; is_running = true;
if (config.on_start) config.on_start();
} }
return result; return result;
@ -39,7 +38,6 @@ static error_t stop() {
error_t error = lvgl_arch_stop(); error_t error = lvgl_arch_stop();
if (error == ERROR_NONE) { if (error == ERROR_NONE) {
is_running = false; is_running = false;
if (config.on_stop) config.on_stop();
} }
return error; return error;

View File

@ -0,0 +1,11 @@
#pragma once
#include <Tactility/lvgl/Lvgl.h>
namespace tt::lvgl {
void attachDevices();
void detachDevices();
}

View File

@ -14,7 +14,7 @@
#include <Tactility/hal/HalPrivate.h> #include <Tactility/hal/HalPrivate.h>
#include <Tactility/Logger.h> #include <Tactility/Logger.h>
#include <Tactility/LogMessages.h> #include <Tactility/LogMessages.h>
#include <Tactility/lvgl/Lvgl.h> #include <Tactility/lvgl/LvglPrivate.h>
#include <Tactility/MountPoints.h> #include <Tactility/MountPoints.h>
#include <Tactility/network/NtpPrivate.h> #include <Tactility/network/NtpPrivate.h>
#include <Tactility/service/ServiceManifest.h> #include <Tactility/service/ServiceManifest.h>
@ -358,8 +358,8 @@ void run(const Configuration& config, Module* platformModule, Module* deviceModu
registerAndStartPrimaryServices(); registerAndStartPrimaryServices();
lvgl_module_configure((LvglModuleConfig) { lvgl_module_configure((LvglModuleConfig) {
.on_start = lvgl::start, .on_start = lvgl::attachDevices,
.on_stop = lvgl::stop .on_stop = lvgl::detachDevices
}); });
module_set_parent(&lvgl_module, &tactility_module_parent); module_set_parent(&lvgl_module, &tactility_module_parent);
lvgl::start(); lvgl::start();

View File

@ -23,7 +23,7 @@ bool isStarted() {
return module_is_started(&lvgl_module); return module_is_started(&lvgl_module);
} }
static void on_start() { void attachDevices() {
LOGGER.info("Adding devices"); LOGGER.info("Adding devices");
auto lock = getSyncLock()->asScopedLock(); auto lock = getSyncLock()->asScopedLock();
@ -120,7 +120,7 @@ static void on_start() {
} }
} }
static void on_stop() { void detachDevices() {
LOGGER.info("Removing devices"); LOGGER.info("Removing devices");
auto lock = getSyncLock()->asScopedLock(); auto lock = getSyncLock()->asScopedLock();
@ -174,6 +174,7 @@ static void on_stop() {
} }
void start() { void start() {
tt::lvgl::syncSet(&lvgl_try_lock_timed, &lvgl_unlock);
check(module_start(&lvgl_module) == ERROR_NONE); check(module_start(&lvgl_module) == ERROR_NONE);
} }