在CodeBlocks中用SDL2在Windows上仿真LVGL嵌入式图形库

LVGL(Light and Versatile Graphics Library)是一个嵌入式图形库,主要用于开发嵌入式人机界面,文档在这里。

现在我们在CodeBlocks中基于SDL2创建一个项目,移植LVGL到SDL2。

1、创建一个SDL2项目(可参考这里)

2、下载LVGL和LVGL示例

1
2
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;

(2)复制lv_examples/lv_ex_conf_templ.h到lv_examples同级目录,重命名lv_ex_conf_templ.h为lv_ex_conf_.h;

按照文档添加port部分的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*main.c*/

#include <stdio.h>
#include <SDL.h>
#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 * 10];
    static lv_color_t buf_2[LV_HOR_RES_MAX * 10];                   /*Declare a buffer for 10 lines*/
    lv_disp_buf_init(&disp_buf, buf_1, buf_2, LV_HOR_RES_MAX * 10);    /*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();
#endif // LV_USE_DEMO_WIDGETS

#if LV_USE_DEMO_PRINTER
    lv_demo_printer();
#endif // LV_USE_DEMO_PRINTER
    printf("load demo ok!\n");

    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_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);
            SDL_SetRenderDrawColor(gRenderer, 0xff, 0xff, 0xff, 0xff);
            SDL_RenderClear(gRenderer);
            SDL_RenderCopy(gRenderer, gTexture, NULL, NULL);
            SDL_RenderPresent(gRenderer);
        }
    }

    SDL_CreateThread(tick_thread, "tick", NULL);
    printf("create tick thread ok!\n");
    SDL_CreateThread(lvtask_thread, "lvtask", NULL);
    printf("create lvtask thread ok!\n");
}

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);
    }
}

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、编译、运行:

项目地址:点击这里