00 LVGL nedir
LVGL (Light and Versatile Graphics Library), gömülü sistemler için geliştirilmiş açık kaynak bir grafik kütüphanesidir. MCU'dan Linux'a kadar geniş bir hedef yelpazesinde çalışır.
Temel özellikler
Desteklenen platformlar
MCU : STM32, ESP32, RP2040, nRF52840, PIC32, AVR32
RTOS : FreeRTOS, Zephyr, ThreadX, RT-Thread, NuttX
Linux : fbdev (/dev/fb0), DRM/KMS, SDL2, X11, Wayland
Simülasyon: PC (SDL2 ile) — hedef donanım olmadan geliştirme
LVGL sürüm yapısı
# En son stabil (v9.x)
git clone --branch release/v9.2 --depth 1 \
https://github.com/lvgl/lvgl.git
# Simülatör ile başla (SDL2 backend)
git clone https://github.com/lvgl/lv_port_pc_eclipse.git
# Linux port (fbdev + DRM)
git clone https://github.com/lvgl/lv_port_linux.git
01 Porting — lv_conf.h, display driver, tick callback
LVGL'i yeni bir platforma taşımak için üç şey gerekir: konfigürasyon dosyası, display flush callback ve millisaniye tick kaynağı.
lv_conf.h — temel ayarlar
#define LV_COLOR_DEPTH 16 /* RGB565; 32 = ARGB8888 */
#define LV_HOR_RES_MAX 480 /* yatay çözünürlük */
#define LV_VER_RES_MAX 320 /* dikey çözünürlük */
/* Bellek havuzu boyutu (internal renderer için) */
#define LV_MEM_SIZE (64 * 1024U) /* 64KB */
/* Render buffer sayısı */
#define LV_DISP_BUF_SIZE (LV_HOR_RES_MAX * 10)
/* Animasyon, font ve diğer özellikler */
#define LV_USE_ANIMATION 1
#define LV_USE_FONT_MONTSERRAT_16 1
#define LV_USE_PERF_MONITOR 1 /* FPS sayacı */
/* Log seviyesi */
#define LV_USE_LOG 1
#define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
Display driver — flush_cb
#include "lvgl/lvgl.h"
static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf1[LV_HOR_RES_MAX * 10]; /* render tamponu */
/* LVGL bu fonksiyonu çağırır: render bölgesini ekrana yaz */
static void disp_flush(lv_disp_drv_t *drv,
const lv_area_t *area,
lv_color_t *color_p)
{
/* platform'a özgü: fbdev'e, SPI'a veya SDRAM'a yaz */
int32_t x, y;
for (y = area->y1; y <= area->y2; y++) {
for (x = area->x1; x <= area->x2; x++) {
fb_pixels[y * FB_WIDTH + x] =
lv_color_to16(*color_p);
color_p++;
}
}
/* Flush tamamlandı — LVGL'e bildir (zorunlu!) */
lv_disp_flush_ready(drv);
}
void display_init(void)
{
lv_init();
lv_disp_draw_buf_init(&draw_buf, buf1, NULL,
LV_HOR_RES_MAX * 10);
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = LV_HOR_RES_MAX;
disp_drv.ver_res = LV_VER_RES_MAX;
disp_drv.flush_cb = disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
}
Tick callback ve task handler
#include <time.h>
#include <pthread.h>
/* Tick thread: her 1ms lv_tick_inc() çağır */
static void *tick_thread(void *arg)
{
(void)arg;
while (1) {
usleep(1000); /* 1ms */
lv_tick_inc(1);
}
return NULL;
}
/* Ana döngü */
int main(void)
{
display_init();
input_init(); /* touch/mouse driver */
ui_init(); /* widget'ları oluştur */
pthread_t tid;
pthread_create(&tid, NULL, tick_thread, NULL);
while (1) {
lv_task_handler(); /* LVGL görevlerini işle */
usleep(5000); /* ~200 FPS max */
}
return 0;
}
Linux /dev/fb0 backend (hazır port)
cd lv_port_linux
# lv_conf.h ayarlarını düzenle
cmake -B build \
-DLV_CONF_PATH=$(pwd)/lv_conf.h \
-DBACKEND=fbdev
cmake --build build -j$(nproc)
# /dev/fb0 erişimi için gerekirse
sudo ./build/lvgl_demo
02 Widget sistemi
LVGL'deki tüm görsel elemanlar lv_obj_t tipindedir. Her widget bu temel tipten türer ve aynı event/style/layout altyapısını paylaşır.
Temel widget'lar
/* Label */
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Sıcaklık: 72°C");
lv_obj_set_pos(label, 10, 10);
/* Button */
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 120, 50);
lv_obj_add_event_cb(btn, btn_handler, LV_EVENT_CLICKED, NULL);
lv_obj_t *btn_lbl = lv_label_create(btn);
lv_label_set_text(btn_lbl, "Başlat");
lv_obj_center(btn_lbl);
/* Slider */
lv_obj_t *slider = lv_slider_create(lv_scr_act());
lv_obj_set_size(slider, 200, 10);
lv_slider_set_range(slider, 0, 100);
lv_slider_set_value(slider, 60, LV_ANIM_ON);
lv_obj_add_event_cb(slider, slider_handler,
LV_EVENT_VALUE_CHANGED, NULL);
/* Chart (gerçek zamanlı grafik) */
lv_obj_t *chart = lv_chart_create(lv_scr_act());
lv_obj_set_size(chart, 300, 150);
lv_chart_set_type(chart, LV_CHART_TYPE_LINE);
lv_chart_series_t *ser =
lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED),
LV_CHART_AXIS_PRIMARY_Y);
lv_chart_set_next_value(chart, ser, 72); /* veri ekle */
Tabview ve Tileview
/* Tabview: üstte tab bar */
lv_obj_t *tabview = lv_tabview_create(lv_scr_act(),
LV_DIR_TOP, 50);
lv_obj_t *tab1 = lv_tabview_add_tab(tabview, "Sensörler");
lv_obj_t *tab2 = lv_tabview_add_tab(tabview, "Kontrol");
lv_obj_t *tab3 = lv_tabview_add_tab(tabview, "Ayarlar");
/* Tab 1 içeriği */
lv_obj_t *lbl = lv_label_create(tab1);
lv_label_set_text(lbl, "Sıcaklık: 72°C");
/* Kaydırmalı tile view */
lv_obj_t *tileview = lv_tileview_create(lv_scr_act());
lv_obj_t *tile1 = lv_tileview_add_tile(tileview,
0, 0, LV_DIR_HOR);
lv_obj_t *tile2 = lv_tileview_add_tile(tileview,
1, 0, LV_DIR_HOR);
Event callback sistemi
static void btn_handler(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t *btn = lv_event_get_target(e);
if (code == LV_EVENT_CLICKED) {
static int count = 0;
lv_obj_t *lbl = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(lbl, "Tıklama: %d", ++count);
} else if (code == LV_EVENT_LONG_PRESSED) {
LV_LOG_USER("Uzun basış!");
}
}
03 Style sistemi — CSS-benzeri kalıtım, state, opacity
LVGL'in style sistemi CSS'e benzer: style nesneleri oluşturulur, widget'lara ve state'lere atanır, kalıtımla alt nesnelere aktarılır.
lv_style_t kullanımı
static lv_style_t style_btn;
static lv_style_t style_btn_pressed;
void styles_init(void)
{
/* Normal durum */
lv_style_init(&style_btn);
lv_style_set_bg_color(&style_btn,
lv_color_hex(0x2196F3)); /* mavi */
lv_style_set_bg_opa(&style_btn, LV_OPA_COVER);
lv_style_set_radius(&style_btn, 8);
lv_style_set_pad_all(&style_btn, 12);
lv_style_set_text_color(&style_btn,
lv_color_white());
lv_style_set_shadow_width(&style_btn, 4);
lv_style_set_shadow_ofs_y(&style_btn, 4);
/* Basılı durum */
lv_style_init(&style_btn_pressed);
lv_style_set_bg_color(&style_btn_pressed,
lv_color_hex(0x1565C0)); /* koyu mavi */
lv_style_set_translate_y(&style_btn_pressed, 2);
lv_style_set_shadow_width(&style_btn_pressed, 0);
}
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_add_style(btn, &style_btn, LV_STATE_DEFAULT);
lv_obj_add_style(btn, &style_btn_pressed, LV_STATE_PRESSED);
State'ler
lv_obj_add_state(obj, LV_STATE_DISABLED) ile ayarla.Tema sistemi
/* Yerleşik temalar */
lv_theme_t *theme = lv_theme_default_init(
lv_disp_get_default(),
lv_palette_main(LV_PALETTE_BLUE), /* primary renk */
lv_palette_main(LV_PALETTE_RED), /* secondary renk */
true, /* dark mode */
LV_FONT_DEFAULT);
lv_disp_set_theme(lv_disp_get_default(), theme);
04 Layout — Flexbox, Grid, scroll, snap
LVGL v8+, CSS Flexbox ve CSS Grid benzeri yerleşim sistemleri sunar. Sabit koordinat yerine dinamik yerleşim, farklı çözünürlüklerde uyumlu arayüz sağlar.
Flexbox layout
lv_obj_t *flex_box = lv_obj_create(lv_scr_act());
lv_obj_set_size(flex_box, LV_PCT(100), LV_CONTENT);
/* Flex layout: yatay, merkeze hizalı, sarma */
lv_obj_set_layout(flex_box, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(flex_box, LV_FLEX_FLOW_ROW_WRAP);
lv_obj_set_flex_align(flex_box,
LV_FLEX_ALIGN_CENTER, /* main axis (yatay) */
LV_FLEX_ALIGN_CENTER, /* cross axis (dikey) */
LV_FLEX_ALIGN_START); /* satır hizalama */
lv_obj_set_style_pad_column(flex_box, 10, 0);
lv_obj_set_style_pad_row(flex_box, 10, 0);
/* Çocuk nesneler otomatik yerleşir */
for (int i = 0; i < 6; i++) {
lv_obj_t *btn = lv_btn_create(flex_box);
lv_obj_set_size(btn, 100, 50);
lv_obj_t *lbl = lv_label_create(btn);
lv_label_set_text_fmt(lbl, "BTN %d", i + 1);
lv_obj_center(lbl);
}
Grid layout
/* 3 sütun, 2 satır grid */
static lv_coord_t col_dsc[] = {
LV_GRID_FR(1), LV_GRID_FR(2), LV_GRID_FR(1),
LV_GRID_TEMPLATE_LAST
};
static lv_coord_t row_dsc[] = {
60, 60,
LV_GRID_TEMPLATE_LAST
};
lv_obj_t *grid = lv_obj_create(lv_scr_act());
lv_obj_set_size(grid, 320, 160);
lv_obj_set_layout(grid, LV_LAYOUT_GRID);
lv_obj_set_grid_dsc_array(grid, col_dsc, row_dsc);
/* Nesneyi grid hücresine yerleştir */
lv_obj_t *obj = lv_obj_create(grid);
lv_obj_set_grid_cell(obj,
LV_GRID_ALIGN_STRETCH, 1, 1, /* sütun: pos=1, span=1 */
LV_GRID_ALIGN_STRETCH, 0, 1); /* satır: pos=0, span=1 */
Scroll ve Snap
/* Yatay scroll snap listesi */
lv_obj_t *list = lv_obj_create(lv_scr_act());
lv_obj_set_size(list, 320, 100);
lv_obj_set_flex_flow(list, LV_FLEX_FLOW_ROW);
lv_obj_set_scroll_dir(list, LV_DIR_HOR);
lv_obj_set_scroll_snap_x(list, LV_SCROLL_SNAP_CENTER);
for (int i = 0; i < 10; i++) {
lv_obj_t *item = lv_obj_create(list);
lv_obj_set_size(item, 100, 80); /* tam snap için eşit genişlik */
lv_obj_t *lbl = lv_label_create(item);
lv_label_set_text_fmt(lbl, "Sayfa %d", i + 1);
lv_obj_center(lbl);
}
lv_obj_update_snap(list, LV_ANIM_ON);
05 Animasyon — lv_anim_t, path_cb, timeline
LVGL animasyon sistemi, herhangi bir sayısal özelliği başlangıç ve bitiş değeri arasında smooth geçişle değiştirir. Easing fonksiyonları ve timeline ile karmaşık sekanslar mümkündür.
Temel animasyon
lv_anim_t a;
lv_anim_init(&a);
/* Hedef nesne ve setter fonksiyonu */
lv_anim_set_var(&a, obj);
lv_anim_set_exec_cb(&a,
(lv_anim_exec_xcb_t) lv_obj_set_x); /* X konumunu değiştir */
/* Değer aralığı ve süre */
lv_anim_set_values(&a, 0, 200); /* 0 → 200 px */
lv_anim_set_time(&a, 500); /* 500 ms */
lv_anim_set_delay(&a, 100); /* 100 ms gecikme */
/* Easing: kolaydan zora */
lv_anim_set_path_cb(&a, lv_anim_path_ease_in_out);
/* Tekrar: -1 = sonsuz */
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_set_repeat_delay(&a, 200);
lv_anim_set_playback_time(&a, 500); /* geri oynat */
lv_anim_start(&a);
Hazır easing fonksiyonları
Animasyon timeline
lv_anim_timeline_t *timeline = lv_anim_timeline_create();
/* obj1: 0ms'de başla */
lv_anim_t a1;
lv_anim_init(&a1);
lv_anim_set_var(&a1, obj1);
lv_anim_set_exec_cb(&a1, (lv_anim_exec_xcb_t)lv_obj_set_y);
lv_anim_set_values(&a1, -50, 10);
lv_anim_set_time(&a1, 400);
lv_anim_set_path_cb(&a1, lv_anim_path_ease_out);
lv_anim_timeline_add(timeline, 0, &a1);
/* obj2: 200ms'de başla (overlap) */
lv_anim_t a2;
lv_anim_init(&a2);
lv_anim_set_var(&a2, obj2);
lv_anim_set_exec_cb(&a2, (lv_anim_exec_xcb_t)lv_obj_set_opacity);
lv_anim_set_values(&a2, LV_OPA_TRANSP, LV_OPA_COVER);
lv_anim_set_time(&a2, 300);
lv_anim_timeline_add(timeline, 200, &a2);
lv_anim_timeline_start(timeline);
06 Squareline Studio — GUI tasarımı ve C export
Squareline Studio, LVGL için sürükle-bırak GUI tasarım aracıdır. Tasarlanan arayüzü C koduna export eder; bu kod doğrudan LVGL projesine entegre edilir.
Squareline Studio iş akışı
Squareline Studio (PC)
↓
Widget'ları sürükle-bırak
Style ve animasyon tanımla
↓
Export → C source (ui.c, ui.h)
↓
LVGL projesine kopyala
↓
main() içinde ui_init() çağır
Export edilen kodun kullanımı
/* Squareline export'tan gelen dosyalar */
#include "ui.h" /* ui_init(), ui_Screen1, lv_obj'ler */
int main(void)
{
lv_init();
display_init();
input_init();
/* Squareline tarafından üretilen UI'ı başlat */
ui_init();
/* Squareline'dan event binding — C callback'e bağla */
lv_obj_add_event_cb(ui_StartButton, on_start_clicked,
LV_EVENT_CLICKED, NULL);
while (1) {
lv_task_handler();
usleep(5000);
}
return 0;
}
Image converter — RGB565 / ARGB8888
# Python tabanlı LVGL image converter
pip install lv_img_conv
# PNG → RGB565 C array
lv_img_conv logo.png -f CF_RGB565 -o logo.c
# PNG → ARGB8888 (alpha kanal ile)
lv_img_conv icon.png -f CF_TRUE_COLOR_ALPHA -o icon.c
# Kullanım:
# LV_IMG_DECLARE(logo);
# lv_img_set_src(img_obj, &logo);
07 Zephyr + LVGL — CONFIG_LVGL, nRF52840 OLED
Zephyr RTOS, LVGL'i display subsystem üzerinden entegre eder. CONFIG_LVGL ile otomatik tick ve display driver bağlantısı yapılır.
prj.conf yapılandırması
CONFIG_LVGL=y
CONFIG_LV_Z_MEM_POOL_SIZE=8192
# Display subsystem
CONFIG_DISPLAY=y
CONFIG_SSD1306=y # SSD1306 OLED sürücüsü
CONFIG_DISPLAY_LOG_LEVEL_ERR=y
# Touch input (dokunmatik varsa)
CONFIG_INPUT=y
CONFIG_INPUT_LVGL=y
# Tick kaynağı (otomatik)
CONFIG_LV_Z_TICK_PERIOD_MS=1
# Font
CONFIG_LV_FONT_MONTSERRAT_14=y
# Thread stack
CONFIG_MAIN_STACK_SIZE=4096
Zephyr LVGL uygulama
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/display.h>
#include <lvgl.h>
int main(void)
{
const struct device *display =
DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
if (!device_is_ready(display)) {
printk("Display hazır değil\n");
return -1;
}
display_blanking_off(display);
/* Basit sayaç UI */
lv_obj_t *count_label = lv_label_create(lv_scr_act());
lv_label_set_text(count_label, "0");
lv_obj_align(count_label, LV_ALIGN_CENTER, 0, 0);
int count = 0;
while (1) {
lv_label_set_text_fmt(count_label, "%d", count++);
lv_task_handler();
k_msleep(100);
}
return 0;
}
Device tree — SSD1306 OLED bağlantısı
&i2c0 {
ssd1306: ssd1306@3c {
compatible = "solomon,ssd1306fb";
reg = <0x3c>;
width = <128>;
height = <64>;
segment-offset = <0>;
page-offset = <0>;
display-offset = <0>;
multiplex-ratio = <63>;
prechargep = <0x22>;
};
};
/ {
chosen {
zephyr,display = &ssd1306;
};
};
08 Pratik
Üç gerçek dünya senaryosu: Linux /dev/fb0 backend ile full-screen demo, STM32F429 Discovery dokunmatik ekran ve Raspberry Pi performans benchmarkı.
Linux /dev/fb0 — full-screen LVGL demo
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include "lvgl/lvgl.h"
static int fb_fd;
static uint16_t *fb_pixels; /* RGB565 */
static uint32_t fb_line_length;
static void fbdev_flush(lv_disp_drv_t *drv,
const lv_area_t *area,
lv_color_t *color_p)
{
for (int32_t y = area->y1; y <= area->y2; y++) {
memcpy(&fb_pixels[y * (fb_line_length / 2) + area->x1],
color_p,
(area->x2 - area->x1 + 1) * 2);
color_p += (area->x2 - area->x1 + 1);
}
lv_disp_flush_ready(drv);
}
void fbdev_init(int width, int height)
{
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
fb_fd = open("/dev/fb0", O_RDWR);
ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo);
ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo);
fb_line_length = finfo.line_length;
size_t fb_size = finfo.line_length * vinfo.yres;
fb_pixels = mmap(NULL, fb_size,
PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);
}
STM32F429 Discovery — touchscreen
/* STM32 HAL + STMPE811 touch controller */
static void touch_read(lv_indev_drv_t *drv,
lv_indev_data_t *data)
{
static uint16_t last_x, last_y;
if (BSP_TS_GetState(&ts_state) == TS_OK) {
if (ts_state.TouchDetected) {
data->point.x = ts_state.X;
data->point.y = ts_state.Y;
data->state = LV_INDEV_STATE_PR;
last_x = data->point.x;
last_y = data->point.y;
} else {
data->point.x = last_x;
data->point.y = last_y;
data->state = LV_INDEV_STATE_REL;
}
}
}
/* Kayıt */
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touch_read;
lv_indev_drv_register(&indev_drv);
Raspberry Pi performans benchmarkı
# lv_conf.h içinde benchmark'ı etkinleştir
# #define LV_USE_DEMO_BENCHMARK 1
# Derle ve çalıştır
cmake -B build -DLV_CONF_PATH=... -DBACKEND=fbdev
cmake --build build -j4
sudo ./build/lvgl_demo
# Örnek sonuçlar (RGB565, yazılım renderer):
# RPi Zero 2W (1GHz Cortex-A53): ~45 FPS
# RPi 4B (1.8GHz Cortex-A72): ~120 FPS
# STM32F429 (168MHz Cortex-M4): ~30 FPS
# DMA2D (Chrom-ART) ile STM32: ~60 FPS