• 调试 lvgl 的一个例子


    发现一个新的 vector graphic 的库,用 C 写的,效果丰富,接口简单,而且是 MIT License,所以想试一试。因为它支持 framebuffer,所以,在 linux 上先走一个。

    项目主页:https://littlevgl.com/

    1. 文件准备

    项目组织还不是很好,所以需要手动配置项目,需要的文件包括:

    - lvgl 主项目 

    - lv_driver 目前支持的驱动。基本上如果使用渲染缓存的话,只要考虑怎么把渲染缓存里的东西搬移到显示缓存中即可。即最简实现 disp_init 和 disp_flush。

    - lv_example 示例代码。本实验尝试了 demo 和 benchmark,记录的是 benchmark 的调试。

    另外,作者还提供了一个基于 SDL 的模拟器,如果不是特别熟悉 LInux 的话,从 SDL 开始会是个比较好的选择。作者 github 上可以找到。

    2. 配置

    我使用的环境是 vmware+mint19。

    项目需要建一个新的目录,将上面准备的三个文件夹拷贝到项目目录下,也可以用软连接链过来,随意。

    在项目目录下,需要添加下面几个文件:

    1)lv_conf.h 这个文件从 lvgl 目录下的 lv_config_temple.h 拷贝而来。我这里修改了分辨率为 800x400,color_depth = 24

    2)lv_drv_conf.h 这个文件从 lv_drv_conf_temple.h 拷贝而来。将 USE_FBDEV(frame_buffer) 和 USE_EVDEV(/dev/input/event) 修改为 1.

    3)  lv_ex_conf.h 这个文件从 lv_ex_conf_temple.h 拷贝而来。将 BENCHMARK 改为1 就好了。

    4)mian.c 最终版本如下。从官网的例子修改而来,benchmark 的 demo,然后使用 /dev/input/event2 作为鼠标输入,还给鼠标加了个图标。

    #include    "lvgl/lvgl.h"
    #include    "lv_drivers/display/fbdev.h"
    #include    "lv_drivers/indev/evdev.h"
    #include    "lv_examples/lv_apps/benchmark/benchmark.h"
    #include    <unistd.h>
    
    int main(void)
    {
        lv_init();
        fbdev_init();
        
        lv_disp_drv_t disp_drv;
        lv_disp_drv_init(&disp_drv);
        disp_drv.disp_flush = fbdev_flush;
        lv_disp_drv_register(&disp_drv);
    
        evdev_init();
        lv_indev_drv_t indev_drv;
        lv_indev_drv_init(&indev_drv);
        indev_drv.type = LV_INDEV_TYPE_POINTER;
        indev_drv.read = evdev_read;
        lv_indev_drv_register(&indev_drv);
    
        lv_indev_t *mouse = lv_indev_next(NULL);
        lv_obj_t *cursor = lv_label_create(lv_scr_act(), NULL);
        lv_label_set_recolor(cursor, true);
        lv_label_set_text(cursor, "#ff0000 .cursor");
        lv_indev_set_cursor(mouse, cursor);
    
        benchmark_create();
    
        while(1) {
            lv_tick_inc(5);
            lv_task_handler();
            usleep(5000);
        }
    
        return 0;
    }

    5)Makefile 用的下面这个,从哪里找来的,找不到出处了,自己稍微做了修改

    #
    # Makefile
    #
    CC = gcc
    CFLAGS = -Wall -Wshadow -Wundef -Wmaybe-uninitialized
    CFLAGS += -O3 -g3 -I./
    #LDFLAGS += -lSDL2 -lm
    BIN = demo
    VPATH = 
    
    MAINSRC = main.c
    
    #LIBRARIES
    include ./lvgl/lv_core/lv_core.mk
    include ./lvgl/lv_hal/lv_hal.mk
    include ./lvgl/lv_objx/lv_objx.mk
    include ./lvgl/lv_misc/lv_fonts/lv_fonts.mk
    include ./lvgl/lv_misc/lv_misc.mk
    include ./lvgl/lv_themes/lv_themes.mk
    include ./lvgl/lv_draw/lv_draw.mk
    
    #DRIVERS
    include ./lv_drivers/display/display.mk
    include ./lv_drivers/indev/indev.mk
    
    #EXAMPLE
    include ./lv_examples/lv_apps/benchmark/benchmark.mk
    
    OBJEXT ?= .o
    
    AOBJS = $(ASRCS:.S=$(OBJEXT))
    COBJS = $(CSRCS:.c=$(OBJEXT))
    
    MAINOBJ = $(MAINSRC:.c=$(OBJEXT))
    
    SRCS = $(ASRCS) $(CSRCS) $(MAINSRC)
    OBJS = $(AOBJS) $(COBJS)
    
    ## MAINOBJ -> OBJFILES
    
    all: clean default
    
    %.o: %.c
        @$(CC)  $(CFLAGS) -c $< -o $@
        @echo "CC $<"
        
    default: $(AOBJS) $(COBJS) $(MAINOBJ)
        $(CC) -o $(BIN) $(MAINOBJ) $(AOBJS) $(COBJS) $(LDFLAGS)
    
    clean: 
        rm -f $(BIN) $(AOBJS) $(COBJS) $(MAINOBJ)

    3. 做的修改

    原作者的代码可能是测试环境不一样,我第一次没跑出来,经过调试,最终做了下面修改后得到想要的结果。

    1)第一处是 fbdev 的驱动,修改了 /lv_driver/display/fbdev.c。第一次编译后,显示一团浆糊,仔细看可以辨认是填图的时候错位了,借鉴了后面附1 的代码,修改了 flush 函数如下。

        if(vinfo.bits_per_pixel == 32 || vinfo.bits_per_pixel == 24) {
            uint32_t *fbp32 = (uint32_t*)fbp;
            uint32_t x;
            uint32_t y;
    
            int stride = finfo.line_length / 4;
    
            for(y = act_y1; y <= act_y2; y++) {
                for(x = act_x1; x <= act_x2; x++) {
                    location = (x+vinfo.xoffset) + (y+vinfo.yoffset) * stride;
                    fbp32[location] = color_p->full;
                    color_p++;
                }
    
                color_p += x2 - act_x2;
            }
        }

    重新计算了 buffer 中一行的长度,作者原来直接是把 vinfo.xref 拿来用了,我这里是 800。上面代码算出来的 stride 是 1176,但是能正常显示。

    2)针对 event 设备的修改。修改了文件 lv_drivers/indev/evdev.c。这里的问题是,驱动里面的 x 范围只有 0 - LV_HOR_RES。大概是作者的理解中,鼠标的坐标范围是和分辨率一致的。但是,我用 附录2 的代码以及 hexdump 看到的,这个范围是 0-65535。所以,这个坐标需要归一化。我添加了两个变量。

    #if USE_FBDEV
    extern int fb_x_max;
    extern int fb_y_max;
    #endif

    这两个全局变量,需要先在 fbdev.c 中声明过,他们取下面的值:

        // Get variable screen information
        if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
            perror("Error reading variable information");
            return;
        }
    
        printf("%dx%d, %dbpp
    ", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
        printf("%d 
    ", finfo.line_length);
    
        fb_x_max = vinfo.xres;
        fb_y_max = vinfo.yres;

    因此,main 函数中,初始化 event 设备,要在初始化 fbdev 之后,要不然,这两个值取不到。然后,在 evdev_init 中 将这两个值转化为鼠标的分辨率。

    void evdev_init(void)
    {
        evdev_fd = open(EVDEV_NAME, O_RDWR|O_NOCTTY|O_NDELAY);
        if (evdev_fd == -1) {
            perror("unable open evdev interface:");
            return;
        }
    
        fcntl(evdev_fd, F_SETFL, O_ASYNC|O_NONBLOCK);
    
        evdev_root_x = 0;
        evdev_root_y = 0;
        evdev_button = LV_INDEV_STATE_REL;
    
    #if USE_FBDEV
        //global var: double evdev_x_resolution, evdev_y_resolution
        evdev_x_resolution = (float)fb_x_max / 65535.0;
        evdev_y_resolution = (float)fb_y_max / 65535.0;
    #else
        evdev_x_resolution = 1;
        evdev_y_resolution = 1;
    #endif
        
    }

    然后,读取鼠标坐标的时候,进行归一化:

        while(read(evdev_fd, &in, sizeof(struct input_event)) > 0) {
            if (in.type == EV_REL) {
                if (in.code == REL_X)
                    evdev_root_x += in.value;
                else if (in.code == REL_Y)
                    evdev_root_y += in.value;
            } else if (in.type == EV_ABS) {
                if (in.code == ABS_X)
                    evdev_root_x = in.value * evdev_x_resolution;
                else if (in.code == ABS_Y)
                    evdev_root_y = in.value * evdev_y_resolution;
            } else if (in.type == EV_KEY) {
                if (in.code == BTN_MOUSE || in.code == BTN_TOUCH) {
                    if (in.value == 0)
                        evdev_button = LV_INDEV_STATE_REL;
                    else if (in.value == 1)
                        evdev_button = LV_INDEV_STATE_PR;
                }
                //printf("BTN ClICKED(%d, %d) 
    ", evdev_root_x, evdev_root_y);
            }
        }

    这样,/dev/intpu/event 中读到的坐标就可以被 lvgl 识别了。

    4. 运行

    需要关闭图形系统,来运行这个程序。首先按 ctr+alt+f1 登录到 tty,然后使用 sudo service ligthdm stop 来关闭系统的图形系统。

    然后,就可以 sudo .demo 来查看效果了。

    想回到图形系统,直接 sudo service lightdm start 即可。

     

    附1. framebuffer 测试程序

    #include <stdlib.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <linux/fb.h>
    #include <linux/kd.h>
    #include <sys/mman.h>
    #include <sys/ioctl.h>
    #include <sys/time.h>
    #include <string.h>
    #include <errno.h>
    
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    char *frameBuffer = 0;
    
    //打印fb驱动中fix结构信息,注:在fb驱动加载后,fix结构不可被修改。
    void
    printFixedInfo ()
    {
        printf ("Fixed screen info:
    "
                "	id: %s
    "
                "	smem_start: 0x%lx
    "
                "	smem_len: %d
    "
                "	type: %d
    "
                "	type_aux: %d
    "
                "	visual: %d
    "
                "	xpanstep: %d
    "
                "	ypanstep: %d
    "
                "	ywrapstep: %d
    "
                "	line_length: %d
    "
                "	mmio_start: 0x%lx
    "
                "	mmio_len: %d
    "
                "	accel: %d
    "
                "
    ",
                finfo.id, finfo.smem_start, finfo.smem_len, finfo.type,
                finfo.type_aux, finfo.visual, finfo.xpanstep, finfo.ypanstep,
                finfo.ywrapstep, finfo.line_length, finfo.mmio_start,
                finfo.mmio_len, finfo.accel);
    }
    
    //打印fb驱动中var结构信息,注:fb驱动加载后,var结构可根据实际需要被重置
    void
    printVariableInfo ()
    {
        printf ("Variable screen info:
    "
                "	xres: %d
    "
                "	yres: %d
    "
                "	xres_virtual: %d
    "
                "	yres_virtual: %d
    "
                "	yoffset: %d
    "
                "	xoffset: %d
    "
                "	bits_per_pixel: %d
    "
                "	grayscale: %d
    "
                "	red: offset: %2d, length: %2d, msb_right: %2d
    "
                "	green: offset: %2d, length: %2d, msb_right: %2d
    "
                "	blue: offset: %2d, length: %2d, msb_right: %2d
    "
                "	transp: offset: %2d, length: %2d, msb_right: %2d
    "
                "	nonstd: %d
    "
                "	activate: %d
    "
                "	height: %d
    "
                "	 %d
    "
                "	accel_flags: 0x%x
    "
                "	pixclock: %d
    "
                "	left_margin: %d
    "
                "	right_margin: %d
    "
                "	upper_margin: %d
    "
                "	lower_margin: %d
    "
                "	hsync_len: %d
    "
                "	vsync_len: %d
    "
                "	sync: %d
    "
                "	vmode: %d
    "
                "
    ",
                vinfo.xres, vinfo.yres, vinfo.xres_virtual, vinfo.yres_virtual,
                vinfo.xoffset, vinfo.yoffset, vinfo.bits_per_pixel,
                vinfo.grayscale, vinfo.red.offset, vinfo.red.length,
                vinfo.red.msb_right, vinfo.green.offset, vinfo.green.length,
                vinfo.green.msb_right, vinfo.blue.offset, vinfo.blue.length,
                vinfo.blue.msb_right, vinfo.transp.offset, vinfo.transp.length,
                vinfo.transp.msb_right, vinfo.nonstd, vinfo.activate,
                vinfo.height, vinfo.width, vinfo.accel_flags, vinfo.pixclock,
                vinfo.left_margin, vinfo.right_margin, vinfo.upper_margin,
                vinfo.lower_margin, vinfo.hsync_len, vinfo.vsync_len,
                vinfo.sync, vinfo.vmode);
    }
    
    //画大小为width*height的同色矩阵,8alpha+8reds+8greens+8blues
    void
    drawRect_rgb32 (int x0, int y0, int width, int height, int color)
    {
        const int bytesPerPixel = 4;
        const int stride = finfo.line_length / bytesPerPixel;
    
        int *dest = (int *) (frameBuffer)
            + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset);
    
        int x, y;
        for (y = 0; y < height; ++y)
        {
            for (x = 0; x < width; ++x)
            {
                dest[x] = color;
            }
            dest += stride;
        }
    }
    
    //画大小为width*height的同色矩阵,5reds+6greens+5blues
    void
    drawRect_rgb16 (int x0, int y0, int width, int height, int color)
    {
        const int bytesPerPixel = 2;
        const int stride = finfo.line_length / bytesPerPixel;
        const int red = (color & 0xff0000) >> (16 + 3);
        const int green = (color & 0xff00) >> (8 + 2);
        const int blue = (color & 0xff) >> 3;
        const short color16 = blue | (green << 5) | (red << (5 + 6));
    
        short *dest = (short *) (frameBuffer)
            + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset);
    
        int x, y;
        for (y = 0; y < height; ++y)
        {
            for (x = 0; x < width; ++x)
            {
                dest[x] = color16;
            }
            dest += stride;
        }
    }
    
    //画大小为width*height的同色矩阵,5reds+5greens+5blues
    void
    drawRect_rgb15 (int x0, int y0, int width, int height, int color)
    {
        const int bytesPerPixel = 2;
        const int stride = finfo.line_length / bytesPerPixel;
        const int red = (color & 0xff0000) >> (16 + 3);
        const int green = (color & 0xff00) >> (8 + 3);
        const int blue = (color & 0xff) >> 3;
        const short color15 = blue | (green << 5) | (red << (5 + 5)) | 0x8000;
    
        short *dest = (short *) (frameBuffer)
            + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset);
    
        int x, y;
        for (y = 0; y < height; ++y)
        {
            for (x = 0; x < width; ++x)
            {
                dest[x] = color15;
            }
            dest += stride;
        }
    }
    
    void
    drawRect (int x0, int y0, int width, int height, int color)
    {
        switch (vinfo.bits_per_pixel)
        {
        case 32:
            drawRect_rgb32 (x0, y0, width, height, color);
            break;
        case 16:
            drawRect_rgb16 (x0, y0, width, height, color);
            break;
        case 15:
            drawRect_rgb15 (x0, y0, width, height, color);
            break;
        default:
            printf ("Warning: drawRect() not implemented for color depth %i
    ",
                    vinfo.bits_per_pixel);
            break;
        }
    }
    
    #define PERFORMANCE_RUN_COUNT 5
    void
    performSpeedTest (void *fb, int fbSize)
    {
        int i, j, run;
        struct timeval startTime, endTime;
        unsigned long long results[PERFORMANCE_RUN_COUNT];
        unsigned long long average;
        unsigned int *testImage;
    
        unsigned int randData[17] = {
            0x3A428472, 0x724B84D3, 0x26B898AB, 0x7D980E3C, 0x5345A084,
            0x6779B66B, 0x791EE4B4, 0x6E8EE3CC, 0x63AF504A, 0x18A21B33,
            0x0E26EB73, 0x022F708E, 0x1740F3B0, 0x7E2C699D, 0x0E8A570B,
            0x5F2C22FB, 0x6A742130
        };
    
        printf ("Frame Buffer Performance test...
    ");
        for (run = 0; run < PERFORMANCE_RUN_COUNT; ++run)
        {
            /* Generate test image with random(ish) data: */
            testImage = (unsigned int *) malloc (fbSize);
            j = run;
            for (i = 0; i < (int) (fbSize / sizeof (int)); ++i)
            {
                testImage[i] = randData[j];
                j++;
                if (j >= 17)
                    j = 0;
            }
    
            gettimeofday (&startTime, NULL);
            memcpy (fb, testImage, fbSize);
            gettimeofday (&endTime, NULL);
    
            long secsDiff = endTime.tv_sec - startTime.tv_sec;
            results[run] =
                secsDiff * 1000000 + (endTime.tv_usec - startTime.tv_usec);
    
            free (testImage);
        }
    
        average = 0;
        for (i = 0; i < PERFORMANCE_RUN_COUNT; ++i)
            average += results[i];
        average = average / PERFORMANCE_RUN_COUNT;
    
        printf (" Average: %llu usecs
    ", average);
        printf (" Band %.03f MByte/Sec
    ",
                (fbSize / 1048576.0) / ((double) average / 1000000.0));
        printf (" Max. FPS: %.03f fps
    
    ",
                1000000.0 / (double) average);
    
        /* Clear the framebuffer back to black again: */
        memset (fb, 0, fbSize);
    }
    
    int
    main (int argc, char **argv)
    {
        const char *devfile = "/dev/fb0";
        long int screensize = 0;
        int fbFd = 0;
    
    
        /* Open the file for reading and writing */
        fbFd = open (devfile, O_RDWR);
        if (fbFd == -1)
        {
            perror ("Error: cannot open framebuffer device");
            exit (1);
        }
    
        //获取finfo信息并显示
        if (ioctl (fbFd, FBIOGET_FSCREENINFO, &finfo) == -1)
        {
            perror ("Error reading fixed information");
            exit (2);
        }
        printFixedInfo ();
        //获取vinfo信息并显示
        if (ioctl (fbFd, FBIOGET_VSCREENINFO, &vinfo) == -1)
        {
            perror ("Error reading variable information");
            exit (3);
        }
        printVariableInfo ();
    
        /* Figure out the size of the screen in bytes */
        screensize = finfo.smem_len;
    
        /* Map the device to memory */
        frameBuffer =
            (char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
                         fbFd, 0);
        if (frameBuffer == MAP_FAILED)
        {
            perror ("Error: Failed to map framebuffer device to memory");
            exit (4);
        }
    
        //测试virt fb的性能
        performSpeedTest (frameBuffer, screensize);
    
        printf ("Will draw 3 rectangles on the screen,
    "
                "they should be colored red, green and blue (in that order).
    ");
        drawRect (vinfo.xres / 8, vinfo.yres / 8,
                 vinfo.xres / 4, vinfo.yres / 4, 0xffff0000);
        drawRect (vinfo.xres * 3 / 8, vinfo.yres * 3 / 8,
                 vinfo.xres / 4, vinfo.yres / 4, 0xff00ff00);
        drawRect (vinfo.xres * 5 / 8, vinfo.yres * 5 / 8,
                 vinfo.xres / 4, vinfo.yres / 4, 0xff0000ff);
    
        sleep (5);
        printf (" Done.
    ");
    
        munmap (frameBuffer, screensize);    //解除内存映射,与mmap对应
    
        close (fbFd);
        return 0;
    }

    附2 event 的测试程序

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <linux/input.h>
     
    #define DEV_NAME   "/dev/input/event2"
    #define DBG_PRINTF printf
    //#define DBG_PRINTF(...)
    struct input_event input_mouse;
     
    int main(int argc, char **argv)
    {
        int fd,retval;
        fd_set readfds;
        fd = open(DEV_NAME, O_RDONLY);
        if (fd < 0)
        {
            printf("can't open %s
    ",DEV_NAME);
            return -1;
        }
        
    while(1)
    {
        FD_ZERO( &readfds );  
        FD_SET( fd, &readfds );    
        retval = select( fd+1, &readfds, NULL, NULL, NULL);  
        if(retval==0) 
        {  
            printf( "Time out!
    " );  
        }  
        if(FD_ISSET(fd,&readfds)) 
        {  
            read(fd, &input_mouse,sizeof(struct input_event));
    //        printf("mouse.type = %d, mouse.code = %d
    ", input_mouse.type, input_mouse.code);
            switch(input_mouse.type)
            {
                case EV_KEY:
                {/* have key is press */
                    switch(input_mouse.code)
                    {
                        case BTN_LEFT:
                        {
                            if(input_mouse.value==1)
                                DBG_PRINTF("the left is press!
    ");
                        }
                        break;
                        case BTN_RIGHT:
                        {
                            if(input_mouse.value==1)
                                DBG_PRINTF("the right is press!
    ");    
                        }
                        break;
                        case BTN_MIDDLE:
                        {
                            if(input_mouse.value==1)
                                DBG_PRINTF("the middle is press!
    ");
                        }
                        break;
                    
                    }
                }
                 break;
                case EV_REL:
                case EV_ABS:
                {   
                    switch(input_mouse.code)
                    {
                        case REL_X:
                        {
                            /*
                            if(input_mouse.value>0)
                                DBG_PRINTF("X slip is right!
    ");
                                else if(input_mouse.value<0)
                                    DBG_PRINTF("X slip is left!
    ");
                                    */
                            printf("POS( %d, ", input_mouse.value);
                        }
                        break;
                        case REL_Y:
                        {/*
                            if(input_mouse.value<0)
                                DBG_PRINTF("Y slip is up!
    ");
                                else if(input_mouse.value>0)
                                    DBG_PRINTF("Y slip is down!
    ");
                                    */
                            printf("%d ) 
    ", input_mouse.value);
                        }
                        break;
                    }
                }
                break;
            }
        }    
    }
    close(fd);
    return 0;
    }
  • 相关阅读:
    企业移动视频通话会议EasyRTC视频会议通话系统开拓视频会议行业新前景
    安防网络摄像头海康大华硬盘录像机视频流媒体服务器EasyNVR调用接口时提示未授权问题解决方案
    安防RTSP_Onvif网络摄像头互联网直播视频流媒体服务器在使用过程中如何保存用户登录时的信息
    RTSP、RTMP、HTTP-FLV、 HLS安防网络摄像头互联网直播音视频流媒体服务器EasyNVR如何实现密码的MD5加密
    安防RTSP_Onvif网络摄像头互联网直播视频流媒体服务器EasyNVR如何解决视频流Ajax跨域访问的问题
    Python之网路编程利用threading模块开线程
    Python之网路编程之线程介绍
    Python之网路编程之进程池及回调函数
    Python之网路编程之-互斥锁与进程间的通信(IPC)及生产者消费者模型
    Python之网路编程利用multiprocessing开进程
  • 原文地址:https://www.cnblogs.com/pied/p/9517834.html
Copyright © 2020-2023  润新知