LVGL와 Linux Framebuffer 연동하기
임베디드 시스템에서 GUI를 구현하려면 먼저 하드웨어 추상화 계층을 구성해야 한다. LVGL(Light and Versatile Graphics Library)은 경량화된 임베디드 GUI 라이브러리로, 다양한 디스플레이 드라이버를 지원한다. 그중 Linux Framebuffer는 비교적 오래된 인터페이스이지만, 간단한 임베디드 환경에서 즉시 사용 가능한 해결책을 제공한다.
지원 가능한 그래픽 인터페이스
Linux 기반 임베디드 시스템에서는 여러 그래픽 인터페이스를 사용할 수 있다. Framebuffer는 가장 기본적인 방식으로, 장치 의존성이 낮고 구현이 단순하다는 장점이 있다. 그 외에도 SDL, DRM, DirectFB, Wayland 같은 고성능 인터페이스가 있지만, 이번 글에서는 Framebuffer를 중심으로 설명한다.
코드 포팅 과정
개발 환경 구축
LVGL 소스코드는 GitHub에서 얻을 수 있다. Raspberry Pi와 같은 단일보드컴퓨터에서 직접 컴파일하는 방법이 가장简便하다. 단, LVGL은 의존성 관리가 까다로울 수 있어, 공식 포트 레포지를 사용하는 것이 좋다.
git clone https://github.com/lvgl/lv_port_linux.git
cd lv_port_linux
mkdir build && cd build
cmake ..
make
빌드가 완료되면 bin 디렉토리에 실행 파일이 생성된다. 이를 그대로 실행하면 LVGL 데모 화면이 나타난다.
터미널 출력 문제 해결
Framebuffer를 사용하는 프로그램 실행 시 터미널에 잔상 문제가 발생할 수 있다. 이를 해결하려면 /etc/profile 파일에 다음 설정을 추가한다.
echo -e "\033[?251"
설정 적용 후 프로그램을 실행하면 깨끗한 화면을 유지할 수 있다.
기본 데모 구현
LVGL의 최소 초기화 코드는 다음과 같다. 이 예제는 Framebuffer 디바이스(/dev/fb0)를 통해 화면에 출력을 수행한다.
#include "lvgl/lvgl.h"
#include "lvgl/demos/lv_demos.h"
#include "lvgl/src/drivers/evdev/lv_evdev.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
static const char* get_env_with_default(const char* name, const char* fallback)
{
return getenv(name) ? : fallback;
}
static void platform_display_init(void)
{
const char* fb_device = get_env_with_default("LV_LINUX_FBDEV_DEVICE", "/dev/fb0");
lv_display_t* display = lv_linux_fbdev_create();
lv_linux_fbdev_set_file(display, fb_device);
}
int main(void)
{
lv_init();
platform_display_init();
lv_demo_widgets();
lv_demo_widgets_start_slideshow();
while(1) {
lv_timer_handler();
usleep(5000);
}
return 0;
}
간단한 UI 구성하기
위 코드를基础上 레이블과 커서를 추가한 확장 버전이다.
#include "lvgl/lvgl.h"
#include "lvgl/demos/lv_demos.h"
#include "lvgl/src/drivers/evdev/lv_evdev.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
static const char* get_env_with_default(const char* name, const char* fallback)
{
return getenv(name) ? : fallback;
}
static void platform_display_init(void)
{
const char* fb_device = get_env_with_default("LV_LINUX_FBDEV_DEVICE", "/dev/fb0");
lv_display_t* display = lv_linux_fbdev_create();
lv_linux_fbdev_set_file(display, fb_device);
}
int main(void)
{
lv_init();
platform_display_init();
lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x003a57), LV_PART_MAIN);
lv_obj_t* message = lv_label_create(lv_scr_act());
lv_label_set_text(message, "Embedded System GUI");
lv_obj_set_style_text_color(lv_scr_act(), lv_color_hex(0xffffff), LV_PART_MAIN);
lv_obj_align(message, LV_ALIGN_CENTER, 0, 0);
LV_IMG_DECLARE(mouse_cursor_icon);
lv_obj_t* cursor_img = lv_img_create(lv_scr_act());
lv_img_set_src(cursor_img, &mouse_cursor_icon);
lv_indev_set_cursor(mouse_indev, cursor_img);
while(1) {
lv_task_handler();
usleep(5000);
}
return 0;
}
Framebuffer 설정 상세
Raspberry Pi에서 LVGL과 Framebuffer를 연동하려면 lv_drivers 패키지의 설정이 필요하다. lv_drv_conf.h 파일에서 다음을 활성화한다.
#define USE_FBDEV 1
LVGL의 디스플레이 드라이버 초기화는 다음과 같은 단계로 진행된다. 먼저 Framebuffer를 초기화하고, 显示 버퍼를 할당한 후 드라이버를 등록한다.
#include "lvgl/lvgl.h"
#include "lv_drivers/display/fbdev.h"
#include <unistd.h>
int main(void)
{
lv_init();
fbdev_init();
static lv_disp_buf_t display_buffer;
static lv_color_t buffer_a[LV_HOR_RES_MAX * 10];
lv_disp_buf_init(&display_buffer, buffer_a, NULL, LV_HOR_RES_MAX * 10);
lv_disp_drv_t driver;
lv_disp_drv_init(&driver);
driver.buffer = &display_buffer;
driver.flush_cb = fbdev_flush;
lv_disp_drv_register(&driver);
lv_obj_t* text_label = lv_label_create(lv_scr_act(), NULL);
lv_label_set_text(text_label, "Framebuffer Demo");
lv_obj_align(text_label, NULL, LV_ALIGN_CENTER, 0, 0);
while(1) {
lv_task_handler();
usleep(5000);
}
return 0;
}
CMakeLists.txt 구성
cmake_minimum_required(VERSION 3.0)
project(lvgl_fbdev_demo)
set(CMAKE_C_STANDARD 99)
include_directories(lvgl lv_drivers)
add_executable(lvgl_fbdev_demo main.c)
target_link_libraries(lvgl_fbdev_demo pthread)
빌드 및 실행은 다음 명령어로 수행한다.
mkdir build
cd build
cmake ..
make
sudo ./lvgl_fbdev_demo
주의사항
Framebuffer 디바이스 파일은 시스템 환경에 따라 다를 수 있다. Raspberry Pi에서는 일반적으로 /dev/fb0를 사용하며, ls /dev/fb* 명령으로 확인 가능하다. 또한 /boot/config.txt에서 Framebuffer 관련 설정을 조정해야 할 경우가 있다.