LVGL(Light and Versatile Graphics Library)是一个嵌入式图形库,主要用于开发嵌入式人机界面,文档在这里。
现在我们在CodeBlocks中基于SDL2创建一个项目,移植LVGL到SDL2。
1、创建一个SDL2项目(可参考这里)
2、下载LVGL和LVGL示例
git clone https://github.com/lvgl/lvgl.git
git clone https://github.com/lvgl/lv_examples.git
clone在项目文件夹内:
(1)复制lvgl/lv_conf_template.h到lvgl同级目录,重命名lv_conf_template.h为lv_conf.h;
/*lv_conf.h的修改部分如下*/
/* Maximal horizontal and vertical resolution to support by the library.*/
#define LV_HOR_RES_MAX (800) /*屏幕宽度*/
#define LV_VER_RES_MAX (480) /*屏幕高度*/
/* Color depth:
* - 1: 1 byte per pixel
* - 8: RGB332
* - 16: RGB565
* - 32: ARGB8888
*/
#define LV_COLOR_DEPTH 32 /*颜色深度*/
/*...*/
/* Montserrat fonts with bpp = 4
* https://fonts.google.com/specimen/Montserrat */ /*打开所用到的字体*/
#define LV_FONT_MONTSERRAT_12 0
#define LV_FONT_MONTSERRAT_14 1
#define LV_FONT_MONTSERRAT_16 1
#define LV_FONT_MONTSERRAT_18 0
#define LV_FONT_MONTSERRAT_20 0
#define LV_FONT_MONTSERRAT_22 1
#define LV_FONT_MONTSERRAT_24 0
#define LV_FONT_MONTSERRAT_26 0
#define LV_FONT_MONTSERRAT_28 1
#define LV_FONT_MONTSERRAT_30 0
#define LV_FONT_MONTSERRAT_32 1
#define LV_FONT_MONTSERRAT_34 0
#define LV_FONT_MONTSERRAT_36 0
#define LV_FONT_MONTSERRAT_38 0
#define LV_FONT_MONTSERRAT_40 0
#define LV_FONT_MONTSERRAT_42 0
#define LV_FONT_MONTSERRAT_44 0
#define LV_FONT_MONTSERRAT_46 0
#define LV_FONT_MONTSERRAT_48 0
(2)复制lv_examples/lv_ex_conf_templ.h到lv_examples同级目录,重命名lv_ex_conf_templ.h为lv_ex_conf_.h;
/*在lv_ex_conf.h中选择打开所仿真的示例*/
/**
* @file lv_ex_conf.h
*
*/
/*
* COPY THIS FILE AS lv_ex_conf.h
*/
#if 1 /*Set it to "1" to enable the content*/
#ifndef LV_EX_CONF_H
#define LV_EX_CONF_H
/*******************
* GENERAL SETTING
*******************/
#define LV_EX_PRINTF 0 /*Enable printf-ing data in demoes and examples*/
#define LV_EX_KEYBOARD 0 /*Add PC keyboard support to some examples (`lv_drivers` repository is required)*/
#define LV_EX_MOUSEWHEEL 0 /*Add 'encoder' (mouse wheel) support to some examples (`lv_drivers` repository is required)*/
/*********************
* DEMO USAGE
*********************/
/*Show some widget*/
#define LV_USE_DEMO_WIDGETS 0
#if LV_USE_DEMO_WIDGETS
#define LV_DEMO_WIDGETS_SLIDESHOW 0
#endif
/*Printer demo, optimized for 800x480*/
#define LV_USE_DEMO_PRINTER 1
/*Demonstrate the usage of encoder and keyboard*/
#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0
/*Benchmark your system*/
#define LV_USE_DEMO_BENCHMARK 0
/*Stress test for LVGL*/
#define LV_USE_DEMO_STRESS 0
#endif /*LV_EX_CONF_H*/
#endif /*End of "Content enable"*/
按照文档添加port部分的代码:
To use the graphics library you have to initialize it and the other components too. The order of the initialization is:
Call lv_init().
Initialize your drivers.
Register the display and input devices drivers in LVGL.
Call
lv_tick_inc(x)
in everyx
milliseconds in an interrupt to tell the elapsed time.Call
lv_task_handler()
periodically in every few milliseconds to handle LVGL related tasks.
/*main.c*/
#include
#include
#include "lvgl/lvgl.h"
#include "lv_examples/lv_examples.h"
SDL_Window *gWin = NULL;
SDL_Renderer *gRenderer = NULL;
SDL_Texture *gTexture = NULL;
static uint32_t tft_buf[LV_HOR_RES_MAX * LV_VER_RES_MAX];
typedef struct InputSDL {
bool pressed;
lv_coord_t x;
lv_coord_t y;
} InputSDL_t;
InputSDL_t gInput = {false, 0, 0};
static void hal_init(void);
static int tick_thread(void *data);
static int lvtask_thread(void *data);
void my_disp_flush(lv_disp_t * disp, const lv_area_t * area, lv_color_t * color_p);
void my_disp_flush_ex(lv_disp_t * disp, const lv_area_t * area, lv_color_t * color_p);
void my_set_pixel(int32_t x, int32_t y, lv_color_t *color_p);
void my_set_rect(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t *color_p);
bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data);
bool my_touchpad_is_pressed(void);
void touchpad_get_xy(lv_coord_t *x, lv_coord_t *y);
int main(int argc, char* argv[]){
//1,Call lv_init().
lv_init();
printf("lvgl init ok!\n");
//2,Initialize your drivers.
hal_init();
printf("hal init ok!\n");
//3,Register the display and input devices drivers in LittlevGL.
/*Static or global buffer(s). The second buffer is optional*/
static lv_disp_buf_t disp_buf;
static lv_color_t buf_1[LV_HOR_RES_MAX * 100];
static lv_color_t buf_2[LV_HOR_RES_MAX * 100]; /*Declare a buffer for 100 lines*/
lv_disp_buf_init(&disp_buf, buf_1, buf_2, LV_HOR_RES_MAX * 100); /*Initialize the display buffer*/
lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
disp_drv.flush_cb = my_disp_flush_ex; /*Set your driver function*/
disp_drv.buffer = &disp_buf; /*Assign the buffer to the display*/
lv_disp_drv_register(&disp_drv); /*Finally register the driver*/
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv); /*Descriptor of a input device driver*/
indev_drv.type = LV_INDEV_TYPE_POINTER; /*Touch pad is a pointer-like device*/
indev_drv.read_cb = my_touchpad_read; /*Set your driver function*/
lv_indev_drv_register(&indev_drv); /*Finally register the driver*/
printf("display and input device set ok!\n");
#if LV_USE_DEMO_WIDGETS
lv_demo_widgets();
printf("load demo widgets ok!\n");
#elif LV_USE_DEMO_PRINTER
lv_demo_printer();
printf("load demo printer ok!\n");
#elif LV_USE_DEMO_BENCHMARK
lv_demo_benchmark();
printf("load demo benchmark ok!\n");
#elif LV_USE_DEMO_STRESS
lv_demo_stress();
printf("load demo stress ok!\n");
#elif LV_USE_DEMO_KEYPAD_AND_ENCODER
lv_demo_keypad_encoder();
printf("load demo keypad and encoder ok!\n");
#endif
bool quit = false;
SDL_Event e;
while(!quit){
while(SDL_PollEvent(&e) != 0){
switch(e.type){
case SDL_QUIT:
quit = true;
break;
case SDL_MOUSEBUTTONDOWN:
gInput.pressed = true;
gInput.x = e.motion.x;
gInput.y = e.motion.y;
printf("state= %d, x=%d, y=%d\n", e.button.state, e.motion.x, e.motion.y);
break;
case SDL_MOUSEBUTTONUP:
gInput.pressed = false;
printf("state= %d, x=%d, y=%d\n", e.button.state, e.motion.x, e.motion.y);
break;
case SDL_MOUSEMOTION:
gInput.x = e.motion.x;
gInput.y = e.motion.y;
//printf("mouse motion, x=%d, y=%d\n", e.motion.x, e.motion.y);
break;
}
}
}
printf("program quit!\n");
SDL_DestroyWindow(gWin);
SDL_DestroyRenderer(gRenderer);
SDL_DestroyTexture(gTexture);
SDL_Quit();
return 0;
}
void hal_init(void){
if(SDL_Init(SDL_INIT_VIDEO) != 0){
printf("SDL init failed: %s\n", SDL_GetError());
return;
}else{
if(SDL_CreateWindowAndRenderer(LV_HOR_RES_MAX, LV_VER_RES_MAX, SDL_WINDOW_OPENGL, &gWin, &gRenderer) != 0){
printf("SDL create window and renderer failed: %s\n", SDL_GetError());
return;
}else{
SDL_SetWindowTitle(gWin, "LittlevGL PC Simulator based on SDL2");
gTexture = SDL_CreateTexture(gRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, LV_HOR_RES_MAX, LV_VER_RES_MAX);
if(!gTexture){
printf("create texture failed: %s\n", SDL_GetError());
}else{
SDL_SetTextureBlendMode(gTexture, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(gRenderer, 0xff, 0xff, 0xff, 0xff);
SDL_RenderClear(gRenderer);
SDL_RenderCopy(gRenderer, gTexture, NULL, NULL);
SDL_RenderPresent(gRenderer);
}
}
}
SDL_Thread *tickThread = SDL_CreateThread(tick_thread, "tick", NULL);
if(tickThread) printf("create tick thread ok!\n");
else printf("create tick thread failed: %s\n", SDL_GetError());
SDL_Thread *lvtaskThread = SDL_CreateThread(lvtask_thread, "lvtask", NULL);
if(lvtaskThread) printf("create lvtask thread ok!\n");
else printf("create lvtask thread failed: %s\n", SDL_GetError());
}
static int tick_thread(void *data){
while(1) {
lv_tick_inc(10);
SDL_Delay(10); /*Sleep for 1 millisecond*/
}
return 0;
}
static int lvtask_thread(void *data){
while(1){
lv_task_handler();
SDL_Delay(5);
}
return 0;
}
void my_disp_flush(lv_disp_t * disp, const lv_area_t * area, lv_color_t * color_p){
int32_t x, y;
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
my_set_pixel(x, y, color_p); /* Put a pixel to the display.*/
color_p++;
}
}
SDL_RenderPresent(gRenderer);
lv_disp_flush_ready(disp); /* Indicate you are ready with the flushing*/
}
void my_disp_flush_ex(lv_disp_t * disp, const lv_area_t * area, lv_color_t * color_p){
int32_t x1 = area->x1;
int32_t x2 = area->x2;
int32_t y1 = area->y1;
int32_t y2 = area->y2;
if(x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0 ||
x1 > LV_HOR_RES_MAX - 1 || x2 > LV_HOR_RES_MAX - 1 ||
y1 > LV_VER_RES_MAX - 1 || y2 > LV_VER_RES_MAX - 1){
lv_disp_flush_ready(disp);
return;
}
uint32_t w = x2 - x1 + 1;
for(int32_t y = y1; y <= y2; y++) {
memcpy(&tft_buf[y * LV_HOR_RES_MAX + x1], color_p, w * sizeof(lv_color_t));
color_p += w;
}
SDL_UpdateTexture(gTexture, NULL, tft_buf, LV_HOR_RES_MAX * sizeof(uint32_t));
SDL_RenderClear(gRenderer);
SDL_RenderCopy(gRenderer, gTexture, NULL, NULL);
SDL_RenderPresent(gRenderer);
lv_disp_flush_ready(disp); /* Indicate you are ready with the flushing*/
}
void my_set_pixel(int32_t x, int32_t y, lv_color_t *color_p){
SDL_SetRenderDrawColor(gRenderer, color_p->ch.red, color_p->ch.green, color_p->ch.blue, color_p->ch.alpha);
SDL_RenderDrawPoint(gRenderer, x, y);
}
bool my_touchpad_read(lv_indev_drv_t * indev_driver, lv_indev_data_t * data){
data->state = my_touchpad_is_pressed() ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
if(data->state == LV_INDEV_STATE_PR) touchpad_get_xy(&data->point.x, &data->point.y);
return false; /*Return `false` because we are not buffering and no more data to read*/
}
bool my_touchpad_is_pressed(void){
return gInput.pressed;
}
void touchpad_get_xy(lv_coord_t *x, lv_coord_t *y){
*x = gInput.x;
*y = gInput.y;
}
3、编译、运行:
4、项目地址:点击这里