107 lines
3.1 KiB
C++
107 lines
3.1 KiB
C++
#include "lv_setup.hpp"
|
|
#include "esp_heap_caps.h"
|
|
#include "esp_log.h"
|
|
#include "esp_timer.h"
|
|
#include "freertos/task.h"
|
|
#include "types.hpp"
|
|
|
|
internal const char *kTagLvgl = "LVGL";
|
|
|
|
SemaphoreHandle_t g_LvglMutex = nullptr;
|
|
lv_display_t *g_LvglDisplay = nullptr;
|
|
uint8_t *g_LvglDrawBuffer = nullptr;
|
|
|
|
internal void lvgl_tick_task(void *arg)
|
|
{
|
|
while (true)
|
|
{
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
|
|
if (xSemaphoreTake(g_LvglMutex, portMAX_DELAY) == pdTRUE)
|
|
{
|
|
lv_timer_handler();
|
|
xSemaphoreGive(g_LvglMutex);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal uint32_t my_tick_get_cb()
|
|
{
|
|
return (uint32_t)(esp_timer_get_time() / 1000);
|
|
}
|
|
|
|
internal void lv_dummy_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
|
|
{
|
|
// Headless display, so we don't actually flush to SPI/I2C.
|
|
// We just tell LVGL that the "flush" is completed so it unblocks wait_for_flushing.
|
|
lv_display_flush_ready(disp);
|
|
}
|
|
|
|
internal void lv_draw_sample_ui()
|
|
{
|
|
lv_obj_t *scr = lv_screen_active();
|
|
// Default background to white for the grayscale PNG
|
|
lv_obj_set_style_bg_color(scr, lv_color_white(), 0);
|
|
|
|
lv_obj_t *label = lv_label_create(scr);
|
|
lv_label_set_text(label, "Calendink Provider\nLVGL Headless Renderer");
|
|
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
|
|
}
|
|
|
|
void setup_lvgl()
|
|
{
|
|
ESP_LOGI(kTagLvgl, "Initializing LVGL");
|
|
|
|
g_LvglMutex = xSemaphoreCreateMutex();
|
|
|
|
lv_init();
|
|
lv_tick_set_cb(my_tick_get_cb);
|
|
|
|
uint32_t width = CONFIG_CALENDINK_DISPLAY_WIDTH;
|
|
uint32_t height = CONFIG_CALENDINK_DISPLAY_HEIGHT;
|
|
|
|
// Create a virtual display
|
|
g_LvglDisplay = lv_display_create(width, height);
|
|
lv_display_set_flush_cb(g_LvglDisplay, lv_dummy_flush_cb);
|
|
|
|
// Initialize LodePNG custom bump allocator
|
|
lodepng_allocator_init();
|
|
|
|
// Allocate draw buffers in PSRAM
|
|
// Using LV_COLOR_FORMAT_L8 (1 byte per pixel)
|
|
size_t buf_size = LV_DRAW_BUF_SIZE(width, height, LV_COLOR_FORMAT_L8);
|
|
|
|
// Fallback to MALLOC_CAP_DEFAULT if we can't get SPIRAM (for debugging without it)
|
|
void *buf1 = heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM);
|
|
if (!buf1)
|
|
{
|
|
ESP_LOGW(kTagLvgl, "Failed to allocate LVGL draw buffer in PSRAM, falling back to internal RAM");
|
|
buf1 = heap_caps_malloc(buf_size, MALLOC_CAP_DEFAULT);
|
|
}
|
|
|
|
if (!buf1)
|
|
{
|
|
ESP_LOGE(kTagLvgl, "Failed to allocate LVGL draw buffer entirely.");
|
|
return;
|
|
}
|
|
|
|
g_LvglDrawBuffer = (uint8_t *)buf1;
|
|
|
|
lv_display_set_buffers(g_LvglDisplay, buf1, nullptr, buf_size, LV_DISPLAY_RENDER_MODE_FULL);
|
|
|
|
// Explicitly set the color format of the display if it's set in sdkconfig/driver
|
|
lv_display_set_color_format(g_LvglDisplay, LV_COLOR_FORMAT_L8);
|
|
|
|
// Create the background task for the LVGL timer
|
|
xTaskCreate(lvgl_tick_task, "LVGL Tick", 4096, nullptr, 5, nullptr);
|
|
|
|
// Draw the sample UI
|
|
if (xSemaphoreTake(g_LvglMutex, portMAX_DELAY) == pdTRUE)
|
|
{
|
|
lv_draw_sample_ui();
|
|
xSemaphoreGive(g_LvglMutex);
|
|
}
|
|
|
|
ESP_LOGI(kTagLvgl, "LVGL fully initialized. Display %lux%lu created.", width, height);
|
|
}
|