您尚未登录。

楼主 #1 2021-01-20 09:58:34

TeveT
会员
注册时间: 2019-07-01
已发帖子: 148
积分: 91

LVGL的linux_frame_buffer项目加入FB双缓

下载下来的linux_frame_buffer 跑在V3S tina
跑起来没有支持双缓,画面切换撕裂很难受呀,
可以支持FBIOPAN_DISPLAY的平台应该都可以相同的操作。
话不多说,上代码:

/**
 * @file fbdev.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "fbdev.h"
#if USE_FBDEV || USE_BSD_FBDEV

#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#if USE_BSD_FBDEV
#include <sys/fcntl.h>
#include <sys/time.h>
#include <sys/consio.h>
#include <sys/fbio.h>
#else  /* USE_BSD_FBDEV */
#include <linux/fb.h>
#include   <pthread.h>
#include <sys/epoll.h>
#endif /* USE_BSD_FBDEV */

/*********************
 *      DEFINES
 *********************/
#ifndef FBDEV_PATH
#define FBDEV_PATH  "/dev/fb0"
#endif

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *      STRUCTURES
 **********************/

struct bsd_fb_var_info{
    uint32_t xoffset;
    uint32_t yoffset;
    uint32_t xres;
    uint32_t yres;
    int bits_per_pixel;
 };

struct bsd_fb_fix_info{
    long int line_length;
    long int smem_len;
};

/**********************
 *  STATIC PROTOTYPES
 **********************/

/**********************
 *  STATIC VARIABLES
 **********************/
#if USE_BSD_FBDEV
static struct bsd_fb_var_info vinfo;
static struct bsd_fb_fix_info finfo;
#else
static struct fb_var_screeninfo vinfo;
static struct fb_fix_screeninfo finfo;
#endif /* USE_BSD_FBDEV */
static char *fbp = 0,*fbpbase = 0;
static long int screensize = 0;
static long int half_screensize = 0;
static int fbfd = 0;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/


void fbdev_init(void)
{
    // Open the file for reading and writing
    fbfd = open(FBDEV_PATH, O_RDWR);
    if(fbfd == -1) {
        perror("Error: cannot open framebuffer device");
        return;
    }
    printf("The framebuffer device was opened successfully.\n");

#if USE_BSD_FBDEV
    struct fbtype fb;
    unsigned line_length;

    //Get fb type
    if (ioctl(fbfd, FBIOGTYPE, &fb) != 0) {
        perror("ioctl(FBIOGTYPE)");
        return;
    }

    //Get screen width
    if (ioctl(fbfd, FBIO_GETLINEWIDTH, &line_length) != 0) {
        perror("ioctl(FBIO_GETLINEWIDTH)");
        return;
    }

    vinfo.xres = (unsigned) fb.fb_width;
    vinfo.yres = (unsigned) fb.fb_height;
    vinfo.bits_per_pixel = fb.fb_depth + 8;
    vinfo.xoffset = 0;
    vinfo.yoffset = 0;
    finfo.line_length = line_length;
    finfo.smem_len = finfo.line_length * vinfo.yres;
#else /* USE_BSD_FBDEV */

    // Get fixed screen information
    if(ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) {
        perror("Error reading fixed information");
        return;
    }

    // Get variable screen information
    if(ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
        perror("Error reading variable information");
        return;
    }
#endif /* USE_BSD_FBDEV */

    printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

    // Figure out the size of the screen in bytes
    screensize =  finfo.smem_len; //finfo.line_length * vinfo.yres;    
    half_screensize = screensize/2;
    // Map the device to memory
    fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
	fbpbase =  fbp;
    if((intptr_t)fbp == -1) {
        perror("Error: failed to map framebuffer device to memory");
        return;
    }
    memset(fbp, 0, screensize);

    printf("The framebuffer device was mapped to memory successfully.\n");

}

void fbdev_exit(void)
{
    close(fbfd);
}




char toggle = 0;
/**
 * Flush a buffer to the marked area
 * @param drv pointer to driver where this function belongs
 * @param area an area where to copy `color_p`
 * @param color_p an array of pixel to copy to the `area` part of the screen
 */
void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p)
{
    if(fbp == NULL ||
            area->x2 < 0 ||
            area->y2 < 0 ||
            area->x1 > (int32_t)vinfo.xres - 1 ||
            area->y1 > (int32_t)vinfo.yres - 1) {
        lv_disp_flush_ready(drv);
        return;
    }

	if(toggle == 0)
	{
		toggle = 1;

		vinfo.yoffset = 0;
		fbp =fbpbase;
	}
	else
	{
		toggle = 0;
		vinfo.yoffset = vinfo.yres;	
		fbp =  fbpbase + half_screensize;


	
	}	
	
	
	

    /*Truncate the area to the screen*/
    int32_t act_x1 = area->x1 < 0 ? 0 : area->x1;
    int32_t act_y1 = area->y1 < 0 ? 0 : area->y1;
    int32_t act_x2 = area->x2 > (int32_t)vinfo.xres - 1 ? (int32_t)vinfo.xres - 1 : area->x2;
    int32_t act_y2 = area->y2 > (int32_t)vinfo.yres - 1 ? (int32_t)vinfo.yres - 1 : area->y2;


    lv_coord_t w = (act_x2 - act_x1 + 1);
    long int location = 0;
    long int byte_location = 0;
    unsigned char bit_location = 0;

    /*32 or 24 bit per pixel*/
    if(vinfo.bits_per_pixel == 32 || vinfo.bits_per_pixel == 24) {
        uint32_t * fbp32 = (uint32_t *)fbp;
        int32_t y;
        for(y = act_y1; y <= act_y2; y++) {
            location = (act_x1 ) + (y ) * finfo.line_length / 4;
            memcpy(&fbp32[location], (uint32_t *)color_p, (act_x2 - act_x1 + 1) * 4);
            color_p += w;
        }
    }
  else {
        /*Not supported bit per pixel*/
    }
	
	

	
//	memcpy(fbp,tmpbuf,640*480*4);

	if (ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo) < 0) {
		fprintf(stderr, "active fb swap failed\n");
	}
	
	
    //May be some direct update command is required
    //ret = ioctl(state->fd, FBIO_UPDATE, (unsigned long)((uintptr_t)rect));

    lv_disp_flush_ready(drv);

}

/**********************
 *   STATIC FUNCTIONS
 **********************/

#endif

其实就是标准的linux下的ioctl双缓操作,
另外,这个操作要在初始化lvgl的时候加入双缓冲区。

    static lv_color_t buf[DISP_BUF_SIZE],buf1[DISP_BUF_SIZE];
    static lv_disp_buf_t disp_buf;
    lv_disp_buf_init(&disp_buf, buf, buf1, DISP_BUF_SIZE);

这里的 DISP_BUF_SIZE 要和显存大小一致,我这里用了ARGB 8888。


编译操作一下,画面撕裂消失,起飞!

最近编辑记录 TeveT (2021-01-20 14:29:06)

离线

#2 2021-01-20 10:09:07

raspberryman
会员
注册时间: 2019-12-27
已发帖子: 503
积分: 465

Re: LVGL的linux_frame_buffer项目加入FB双缓

楼主用的是哪个平台呢?我用Ubuntu测试貌似没有出现撕裂的问题

离线

楼主 #3 2021-01-20 14:23:31

TeveT
会员
注册时间: 2019-07-01
已发帖子: 148
积分: 91

Re: LVGL的linux_frame_buffer项目加入FB双缓

我这边用的是V3S tina,
手头还有个SSD202的开发板里边附带的LVGL例程也是有裂开的现象,后面有空再看看。
ubuntu上没用过,但是在v3s上是肯定有的

raspberryman 说:

楼主用的是哪个平台呢?我用Ubuntu测试貌似没有出现撕裂的问题

离线

楼主 #4 2021-01-26 09:59:39

TeveT
会员
注册时间: 2019-07-01
已发帖子: 148
积分: 91

Re: LVGL的linux_frame_buffer项目加入FB双缓

已在LVGL7 的linux_frame_buffer 测试过,一样给力!

离线

#5 2021-01-26 19:50:00

tango_zhu
会员
注册时间: 2018-04-12
已发帖子: 115
积分: 2

Re: LVGL的linux_frame_buffer项目加入FB双缓

用你的方法在海思上 产生了段错误

离线

楼主 #6 2021-01-27 09:13:22

TeveT
会员
注册时间: 2019-07-01
已发帖子: 148
积分: 91

Re: LVGL的linux_frame_buffer项目加入FB双缓

你的海思的内核支持了FBIOPAN_DISPLAY了吗,ioctl读取回来的缓存大小是你显示器的两倍吗

tango_zhu 说:

用你的方法在海思上 产生了段错误

离线

#7 2021-01-29 16:26:52

tango_zhu
会员
注册时间: 2018-04-12
已发帖子: 115
积分: 2

Re: LVGL的linux_frame_buffer项目加入FB双缓

TeveT 说:

你的海思的内核支持了FBIOPAN_DISPLAY了吗,ioctl读取回来的缓存大小是你显示器的两倍吗

tango_zhu 说:

用你的方法在海思上 产生了段错误

你这边的宽度需要设置成实际宽度的两倍吗

离线

楼主 #8 2021-01-30 10:41:49

TeveT
会员
注册时间: 2019-07-01
已发帖子: 148
积分: 91

Re: LVGL的linux_frame_buffer项目加入FB双缓

并不是哈, fb驱动原生是有2个缓冲区的,总的是屏幕缓冲区长度2倍,IOCTL读回来就是2倍的,finfo.smem_len 打印出来看下

tango_zhu 说:
TeveT 说:

你的海思的内核支持了FBIOPAN_DISPLAY了吗,ioctl读取回来的缓存大小是你显示器的两倍吗

tango_zhu 说:

用你的方法在海思上 产生了段错误

你这边的宽度需要设置成实际宽度的两倍吗

离线

#9 2021-01-30 10:46:46

raspberryman
会员
注册时间: 2019-12-27
已发帖子: 503
积分: 465

Re: LVGL的linux_frame_buffer项目加入FB双缓

我想起来了,确实有这么回事,以前还纳闷呢。

TeveT 说:

并不是哈, fb驱动原生是有2个缓冲区的,总的是屏幕缓冲区长度2倍,IOCTL读回来就是2倍的,finfo.smem_len 打印出
来看下

tango_zhu 说:
TeveT 说:

你的海思的内核支持了FBIOPAN_DISPLAY了吗,ioctl读取回来的缓存大小是你显示器的两倍吗

你这边的宽度需要设置成实际宽度的两倍吗

离线

#10 2021-01-30 10:52:30

pythinker
会员
注册时间: 2019-02-12
已发帖子: 215
积分: 215

Re: LVGL的linux_frame_buffer项目加入FB双缓

请教楼主,Ubuntu的Framebuffer可以用吗?

离线

楼主 #11 2021-01-30 12:28:27

TeveT
会员
注册时间: 2019-07-01
已发帖子: 148
积分: 91

Re: LVGL的linux_frame_buffer项目加入FB双缓

哥哥你可以自己试一下

pythinker 说:

请教楼主,Ubuntu的Framebuffer可以用吗?

离线

#12 2021-01-30 14:50:35

dso_2019
会员
注册时间: 2019-10-13
已发帖子: 39
积分: 3.5

Re: LVGL的linux_frame_buffer项目加入FB双缓

这个撕裂是什么现象

离线

#13 2021-01-30 15:16:18

湘楚浪子
会员
注册时间: 2019-12-22
已发帖子: 40
积分: 39.5

Re: LVGL的linux_frame_buffer项目加入FB双缓

dso_2019 说:

这个撕裂是什么现象

一部分显示上一帧,一部分显示本帧。

离线

#14 2021-02-19 22:10:42

fogwizard
会员
注册时间: 2019-03-19
已发帖子: 50
积分: 13

Re: LVGL的linux_frame_buffer项目加入FB双缓

搭车请教一个问题,如果平台(SSD201)的framebuffer打印出来的len是4196304,而按一帧1024@600计算应该是2457600,两倍的关系应该是4915200,目前打印的finfo.smem_len比4915200,在这个情况下还能使用双缓冲吗? 谢谢

离线

楼主 #15 2021-02-21 12:55:04

TeveT
会员
注册时间: 2019-07-01
已发帖子: 148
积分: 91

Re: LVGL的linux_frame_buffer项目加入FB双缓

不行哈,我这边搞了一波SSD202 ,测试了下,是要修改 /config/fbdev.ini 修改缓冲区到2倍的大小 4800x1024,然后重启一下。
详见 https://whycan.com/t_6006.html 第14、15楼

另外附上 SSD20X 跑LVGL 的 fbdev相关文件 和 Makefile
LVGL7.10-SSD20X-TEVET_MODIFY.zip

fogwizard 说:

搭车请教一个问题,如果平台(SSD201)的framebuffer打印出来的len是4196304,而按一帧1024@600计算应该是2457600,两倍的关系应该是4915200,目前打印的finfo.smem_len比4915200,在这个情况下还能使用双缓冲吗? 谢谢

离线

#16 2021-02-22 09:31:29

fogwizard
会员
注册时间: 2019-03-19
已发帖子: 50
积分: 13

Re: LVGL的linux_frame_buffer项目加入FB双缓

原来如此,感谢指导!

离线

#17 2021-03-21 10:43:46

lvzaina
会员
注册时间: 2020-01-04
已发帖子: 2
积分: 2

Re: LVGL的linux_frame_buffer项目加入FB双缓

谢谢大神,请问下怎么判断目标内核支持双缓存?

离线

楼主 #18 2021-05-15 17:43:41

TeveT
会员
注册时间: 2019-07-01
已发帖子: 148
积分: 91

Re: LVGL的linux_frame_buffer项目加入FB双缓

一个是 ioctl 拿到的 FBIOGET_FSCREENINFO 的缓冲区大小
另外就是
查看驱动代码的ioctl, FBIOPAN_DISPLAY 是否有被处理,处理后是否调用了硬件显示资源。

lvzaina 说:

谢谢大神,请问下怎么判断目标内核支持双缓存?

离线

#19 2022-03-18 16:00:54

岁月快快快
会员
注册时间: 2021-12-25
已发帖子: 61
积分: 72

Re: LVGL的linux_frame_buffer项目加入FB双缓

main.c:
#include "lvgl/lvgl.h"
#include "lv_drivers/display/fbdev.h"
#include "lv_demos/lv_demo.h"
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>

#define DISP_BUF_SIZE (480 * 480)
//#define DISP_BUF_SIZE (480 * 1024)

int main(void)
{
    /*LittlevGL init*/
    lv_init();

    /*Linux frame buffer device init*/
    fbdev_init();

    /*A small buffer for LittlevGL to draw the screen's content*/
    static lv_color_t buf[DISP_BUF_SIZE], buf1[DISP_BUF_SIZE];

    /*Initialize a descriptor for the buffer*/
    static lv_disp_draw_buf_t disp_buf;
    lv_disp_draw_buf_init(&disp_buf, buf, buf1, DISP_BUF_SIZE);

    /*Initialize and register a display driver*/
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.draw_buf   = &disp_buf;
    disp_drv.flush_cb   = fbdev_flush;
    disp_drv.hor_res    = 480;
    disp_drv.ver_res    = 480;
    lv_disp_drv_register(&disp_drv);

    /*Create a Demo*/
    //lv_demo_widgets();
    lv_demo_gif();

    /*Handle LitlevGL tasks (tickless mode)*/
    while(1) {
        lv_task_handler();
        usleep(5000);
    }

    return 0;
}

/*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/
uint32_t custom_tick_get(void)
{
    static uint64_t start_ms = 0;
    if(start_ms == 0) {
        struct timeval tv_start;
        gettimeofday(&tv_start, NULL);
        start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
    }

    struct timeval tv_now;
    gettimeofday(&tv_now, NULL);
    uint64_t now_ms;
    now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;

    uint32_t time_ms = now_ms - start_ms;
    return time_ms;
}

fbdev.c:
/**
 * @file fbdev.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "fbdev.h"
#if USE_FBDEV || USE_BSD_FBDEV

#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#if USE_BSD_FBDEV
#include <sys/fcntl.h>
#include <sys/time.h>
#include <sys/consio.h>
#include <sys/fbio.h>
#else  /* USE_BSD_FBDEV */
#include <linux/fb.h>
#endif /* USE_BSD_FBDEV */

/*********************
 *      DEFINES
 *********************/
#ifndef FBDEV_PATH
#define FBDEV_PATH  "/dev/fb0"
#endif

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *      STRUCTURES
 **********************/

struct bsd_fb_var_info{
    uint32_t xoffset;
    uint32_t yoffset;
    uint32_t xres;
    uint32_t yres;
    int bits_per_pixel;
 };

struct bsd_fb_fix_info{
    long int line_length;
    long int smem_len;
};

/**********************
 *  STATIC PROTOTYPES
 **********************/

/**********************
 *  STATIC VARIABLES
 **********************/
#if USE_BSD_FBDEV
static struct bsd_fb_var_info vinfo;
static struct bsd_fb_fix_info finfo;
#else
static struct fb_var_screeninfo vinfo;
static struct fb_fix_screeninfo finfo;
#endif /* USE_BSD_FBDEV */
static char *fbp = 0,*fbpbase = 0;
static long int screensize = 0;
static long int half_screensize = 0;
static int fbfd = 0;

/**********************
 *      MACROS
 **********************/


/**********************
 *   GLOBAL FUNCTIONS
 **********************/

void fbdev_init(void)
{
    // Open the file for reading and writing
    fbfd = open(FBDEV_PATH, O_RDWR);
    if(fbfd == -1) {
        perror("Error: cannot open framebuffer device");
        return;
    }
    LV_LOG_INFO("The framebuffer device was opened successfully");

    // Make sure that the display is on.

#if USE_BSD_FBDEV
    struct fbtype fb;
    unsigned line_length;

    //Get fb type
    if (ioctl(fbfd, FBIOGTYPE, &fb) != 0) {
        perror("ioctl(FBIOGTYPE)");
        return;
    }

    //Get screen width
    if (ioctl(fbfd, FBIO_GETLINEWIDTH, &line_length) != 0) {
        perror("ioctl(FBIO_GETLINEWIDTH)");
        return;
    }

    vinfo.xres = (unsigned) fb.fb_width;
    vinfo.yres = (unsigned) fb.fb_height;
    vinfo.bits_per_pixel = fb.fb_depth + 8;
    vinfo.xoffset = 0;
    vinfo.yoffset = 0;
    finfo.line_length = line_length;
    finfo.smem_len = finfo.line_length * vinfo.yres;
#else /* USE_BSD_FBDEV */

    // Get fixed screen information
    if(ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) == -1) {
        perror("Error reading fixed information");
        return;
    }

    // Get variable screen information
    if(ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo) == -1) {
        perror("Error reading variable information");
        return;
    }
#endif /* USE_BSD_FBDEV */

    LV_LOG_INFO("%dx%d, %dbpp", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

    // Figure out the size of the screen in bytes
    screensize =  finfo.smem_len; //finfo.line_length * vinfo.yres;    
    half_screensize = screensize/2;
    // Map the device to memory
    fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbfd, 0);
	fbpbase =  fbp;
    if((intptr_t)fbp == -1) {
        perror("Error: failed to map framebuffer device to memory");
        return;
    }
    memset(fbp, 0, screensize);
    // Don't initialise the memory to retain what's currently displayed / avoid clearing the screen.
    // This is important for applications that only draw to a subsection of the full framebuffer.

    LV_LOG_INFO("The framebuffer device was mapped to memory successfully");

}

void fbdev_exit(void)
{
    close(fbfd);
}

char toggle = 0;
/**
 * Flush a buffer to the marked area
 * @param drv pointer to driver where this function belongs
 * @param area an area where to copy `color_p`
 * @param color_p an array of pixels to copy to the `area` part of the screen
 */
void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p)
{
    if(fbp == NULL ||
            area->x2 < 0 ||
            area->y2 < 0 ||
            area->x1 > (int32_t)vinfo.xres - 1 ||
            area->y1 > (int32_t)vinfo.yres - 1) {
        lv_disp_flush_ready(drv);
        return;
    }

	if(toggle == 0)
	{
		toggle = 1;

		vinfo.yoffset = 0;
		fbp =fbpbase;
	}
	else
	{
		toggle = 0;
		vinfo.yoffset = vinfo.yres;	
		fbp =  fbpbase + half_screensize;


	
	}	
	
	
	

    /*Truncate the area to the screen*/
    int32_t act_x1 = area->x1 < 0 ? 0 : area->x1;
    int32_t act_y1 = area->y1 < 0 ? 0 : area->y1;
    int32_t act_x2 = area->x2 > (int32_t)vinfo.xres - 1 ? (int32_t)vinfo.xres - 1 : area->x2;
    int32_t act_y2 = area->y2 > (int32_t)vinfo.yres - 1 ? (int32_t)vinfo.yres - 1 : area->y2;


    lv_coord_t w = (act_x2 - act_x1 + 1);
    long int location = 0;
    long int byte_location = 0;
    unsigned char bit_location = 0;

    /*32 or 24 bit per pixel*/
    if(vinfo.bits_per_pixel == 32 || vinfo.bits_per_pixel == 24) {
        uint32_t * fbp32 = (uint32_t *)fbp;
        int32_t y;
        for(y = act_y1; y <= act_y2; y++) {
            location = (act_x1 ) + (y ) * finfo.line_length / 4;
            memcpy(&fbp32[location], (uint32_t *)color_p, (act_x2 - act_x1 + 1) * 4);
            color_p += w;
        }
    } else {
        /*Not supported bit per pixel*/
    }
    
    if (ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo) < 0) {
        fprintf(stderr, "active fb swap failed\n");
    }
    //May be some direct update command is required
    //ret = ioctl(state->fd, FBIO_UPDATE, (unsigned long)((uintptr_t)rect));

    lv_disp_flush_ready(drv);
}



/**********************
 *   STATIC FUNCTIONS
 **********************/

#endif

楼主,我是在f1c100s上面跑的lvgl8.2,播放gif动图太卡了,所以就试了一下你这样改成双缓冲,改了之后动图就播放不了了(只显示了一张图片就停了),没改fbdev.c之前是可以正常播放的,可能是什么原因呢

最近编辑记录 岁月快快快 (2022-03-18 16:05:16)

离线

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn