framebuffer相关知识

什么是framebuffer 设备
    framebuffer 是一种能够提取图形的硬件设备,是用户进入图形界面很好的接口。有了framebuffer,用户的应用程序不需要对底层的驱动的深入了解就能够做出很好的图形。
    对于用户而言,它和/dev 下面的其他设备没有什么区别,用户可以把framebuffer 看成一块内存,既可以向这块内存中写入数据,也可以从这块内存中读取数据。
    第一个被注册的framebuffer 的minor 等于0,第二个被注册的framebuffer的minor 等于1,以此类推。


framebuffer 内部结构
    数据结构:framebuffer 设备很大程度上依靠了下面四个数据结构。这三个结构在fb.h 中声明。
Struct fb_var_screeninfo
Struct fb_fix_screeninfo
Struct fb_info
    第一个结构是用来描述图形卡的特性的。通常是被用户设置的。
    第二个结构定义了图形卡的硬件特性, 是不能改变的,用户选定了哪一个图形卡,那么它的硬件特性也就定下来了。
    第三个结构定义了当前图形卡framebuffer 设备的独立状态一个图形卡可能有两个framebuffer, 在这种情况下,就需要两个fb_info 结构。这个结构是唯一在内核空间可见的。

设计自己的framebuffer 设备驱动
    用户首先需要添加下面的代码到fbmem.c
  static struct {
    const char *name;
    int (*init)(void);
    int (*setup)(char*);
} fb_drivers[] __initdata = {
#ifdef CONFIG_FB_YOURCARD
    { "driver_name", xxxfb_init, xxxfb_setup },
#endif
    其次在xxfb.c 中根据自己的需要重新分配显存大小。例如: #define VIDEOMEMSIZE (1*1024*1024) /* 1 MB */
    再次根据自己的硬件设备修改相应的var 信息。主要修改xxfb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)函数。


如何添加framebuffer 设备驱动
    首先在config.in 文件中添加一行代表用户自己驱动的选项。具体做法请参考config.in 的帮助文档。
    在make menuconfig 的时候首先进入Character devices,选中里面的Virtual terminal 和Support for console on virtual terminal.
    退到上一层界面我们就可以看到Console device 的选项,进入后将光标落在Framebuffer Support 上,按回车键进入,在里面选择自己所需要的framebuffer
设备即可。自己所添加的设备驱动的类型(如果在uclinux 下,应该以*选中,而不是M 选中),在编译的时候就会产生相应的o 文件。
    在Advanced low level 中可以配置bpp packed pixel support,然后选中Select compiled-in fonts 即可。
    等操作系统运行以后就会在/dev 下面看到fb 这个设备。它的major 应该是29,第一个设备的minor 应该是0。


如何使用framebuffer 设备
    我们可以在几个支持图形显示的平台上开发一些图形界面。例如microwindows,minigui,Qtembed,等等。在这里我们就不详细介绍具体做法,感兴趣的朋友可以到我们网站上下载。
    这里我们假设用户已经通过tftp 或者mount 命令将测试例子demo 传到/var/test 目录下。在/test 目录下启动后台服务程序./nano-X &,这时可以看到屏幕有一个闪动,然后会变为黑色,接着就可以运行测试程序了,执行./demo 即可。

//////////////////////////////////////

【什么是FrameBuffer】

FrameBuffer直译就是,帧缓冲

Frame帧:你所看到的屏幕的图像,或者在一个窗口中的图像,就叫一帧

Buffer缓冲一段RAM,用来暂存图像数据,这些数据会被直接写入到显示设备。


    帧缓冲就相当于介于 图形操作图像输出中间的一个中间人。将程序对图形数据的处理操作,反馈到显示输出上

    显卡(显存中的数据) <-> 帧缓冲(程序对其中的数据进行处理) <-> 显示器(输出图像)


帧缓冲可用于,实现原先视频卡并不支持的分辨率。

    显卡可能并不支持你当前某个更大分辨率的显示器,但是可以通过帧缓冲获取显卡的显存中的数据,处理之后,实现更大的分辨率的图像,然后将数据直接输出到显示器上。

【双显示器例子】

    一个例子,可能就是双显示,最近刚刚看到实际某开发者的系统,就是两个显示器,鼠标移动超过单个显示器,到最右边的时候,就跑到另一个显示器了。对于常常用多系统或者需要打开很多东西的开发人员,这个功能很实用。

    帧缓冲可以用于 页面交换page flipping(也常叫做 双缓冲double buffering),许多游戏都是采用此技术,以实现更流畅的视频输出,以便用户获得更好的游戏体验。此技术也被用于3D图形加速。

【双缓冲的主要实现原理】

    假如你的显示器是VGA模式,640×400,也就是虚拟的分辨率是640X800,也就是800线(每一行的数据,称为一条线,也就是640X1的数据了)。800线的数据存储于Framebuffer,而实际的显示内容,只是400线,Linux内核中的Framebuffer模型中,对应有个变量yoffset,就是表示的这个具体的纵坐标,默认是0,所以显示的内容就是,0-399线,由于和实际显示 页面大小等同,所以此处可以简称为第一帧。如果yoffset改变了,比如此例中变为400,那就是显示剩余的部分,400-799线。此处简称为第二帧。

    在系统显示第一帧的时候,系统在后台悄悄地准备第二帧的数据,所以,等第一帧显示完成,多数时候,第二帧的数据也准备好了,就可以直接显示,同时系统又在准备接下来的一帧的数据,这样就可以大大提高显示效率。


【平滑地滚动页面的实现原理】

    同上,在显示完第一帧数据的时候,也就是0-399线的时候,将yoffset设置为1,就可以显示1-400线的数据了,显示完成后,再设置yoffset为2,就显示2-401线的数据,这样,就可以一点点地,平滑地显示整个滚动画面了。其实也就是画面在垂直方向的滚动。其中yoffset的增加,可以使用定时器,各个一段时间,比如10us,增加1,系统自动会更新显示对应的内容,这样我们所看到的内容就是滚动的画面了。

    此外,Linux中的Framebuffer模型中,提供了一些ioctl功能,给定一些参数,然后系统可以实现对应的功能,其中有个参数就是FBIOPAN_DISPLAY。具体也就是类似如下调用:

    ioctl (framebuffer_handler, FBIOPAN_DISPLAY, &variable_info);
    而这个调用,如果显示不支持framebuffer的双缓冲的话,那么其framebuffer的缓冲大小,就是和物理上的显示器大小等同,那么对应的yoffset也就不会像双缓冲那样变化了。

    也就是说,如果显卡/显示屏控制器不支持双缓冲,那么yoffset就应该一直为0,并且在运行时候,也不应该改变,也不应该去给FBIOPAN_DISPLAY的参数调用ioctl。

小tip:

【测试显示屏是否正常工作】

    在加载了显示屏驱动后,不知道是否已经工作正常的话,

    可以通过在某个文件夹下有稍微多些文件的地方,去ls,以便显示有出来东西,然后通过

    ls > /dev/fb0

    将当前文件夹列表出来的一些数据,送到framebuffer的默认设备/dev/fb0,如果显示屏驱动已经正常加载,显示屏可以正常工作的话,那么你会在显示屏左上角看到一些乱码花屏一类的东西,这就是你刚刚ls送过去的数据。

    发现其他教程上的命令,效果更好:

    cat screenshot >/dev/fb0

    即将当前截屏内容送给LCD显示。


【LCD液晶显示器的坐标轴】

左上角为(0,0)水平向左是X轴正向,垂直向下,是Y轴正向。即:

(0,0) X→

Y


【写驱动前预先要了解的知识】

1. 软件方面:

    要搞懂Linux的Framebuffer驱动的框架。

    其中就包括熟悉Linux下,为了framebuffer专门实现的数据结构,尤其是

struct fb_fix_screeninfo {

char id[16]; /* identification string eg "TT Builtin" */

unsigned long smem_start; /* Start of frame buffer mem */

/* (physical address) */

__u32 smem_len; /* Length of frame buffer mem */

__u32 type; /* see FB_TYPE_* */

__u32 type_aux; /* Interleave for interleaved Planes */

__u32 visual; /* see FB_VISUAL_* */

__u16 xpanstep; /* zero if no hardware panning */

__u16 ypanstep; /* zero if no hardware panning */

__u16 ywrapstep; /* zero if no hardware ywrap */

__u32 line_length; /* length of a line in bytes */

unsigned long mmio_start; /* Start of Memory Mapped I/O */

/* (physical address) */

__u32 mmio_len; /* Length of Memory Mapped I/O */

__u32 accel; /* Type of acceleration available */

__u16 reserved[3]; /* Reserved for future compatibility */

};

/* more kernel header files copied shamelessly */

struct fb_bitfield {

__u32 offset; /* beginning of bitfield */

__u32 length; /* length of bitfield */

__u32 msb_right; /* != 0 : Most significant bit is */

/* right */

};


struct fb_var_screeninfo {

__u32 xres; /* visible resolution */

__u32 yres;

__u32 xres_virtual; /* virtual resolution */

__u32 yres_virtual;

__u32 xoffset; /* offset from virtual to visible */

__u32 yoffset; /* resolution */


__u32 bits_per_pixel; /* guess what */

__u32 grayscale; /* != 0 Graylevels instead of colors */


struct fb_bitfield red; /* bitfield in fb mem if true color, */

struct fb_bitfield green; /* else only length is significant */

struct fb_bitfield blue;

struct fb_bitfield transp; /* transparency */


__u32 nonstd; /* != 0 Non standard pixel format */


__u32 activate; /* see FB_ACTIVATE_* */


__u32 height; /* height of picture in mm */

__u32 width; /* width of picture in mm */


__u32 accel_flags; /* acceleration flags (hints) */


/* Timing: All values in pixclocks, except pixclock (of course) */

__u32 pixclock; /* pixel clock in ps (pico seconds) */

__u32 left_margin; /* time from sync to picture */

__u32 right_margin; /* time from picture to sync */

__u32 upper_margin; /* time from sync to picture */

__u32 lower_margin;

__u32 hsync_len; /* length of horizontal sync */

__u32 vsync_len; /* length of vertical sync */

__u32 sync; /* see FB_SYNC_* */

__u32 vmode; /* see FB_VMODE_* */

__u32 reserved[6]; /* Reserved for future compatibility */

};

    此处简单描述一下,fb_fix_screeninfo对应的是物理设备的相关属性,包括映射后的显存的起始地址,长度,类型等。

    fb_var_screeninfo包含了很多具体驱动运行时候的,framebuffer,即显示内容的具体属性,

    包括实际的显示器上可见的分辨率大小,xres和yres,

    Framebuffer的分辨率大小xres_virtual和 yres_virtual,如果是上面提到的双缓冲,那么这个分辨率就是可以显示的分辨率的两倍了。

    xoffset 和yoffset就是上面提到的,如果是双缓冲,对应不同的应用,就会在运行时刻,改变对应的xoffset 或yoffset,以实现不同的显示效果。

    其他一些成员,具体看注释就知道了。


    关于Framebuffer的使用,详情请参考第二个链接,里面已经说得很好很详细了,我就没必要再一一翻译了。


2. 硬件方面

    了解自己的开发板上的LCD显示屏的大小,具体支持的哪些特性,比如是否支持双缓冲等。把这些相关信息,在驱动编写的时候,进行对应的赋值。


【如何写framebuffer的LCD驱动】

    主要是实现Linux的Framebuffer框架下的,一些函数,最基本的一些就是,


以Linux内核中的S3c2410举例,

在s3c2410fb.c中,有个对应的结构体:


static struct fb_ops s3c2410fb_ops = {

.owner = THIS_MODULE,

.fb_check_var = s3c2410fb_check_var,

.fb_set_par = s3c2410fb_set_par,

.fb_blank = s3c2410fb_blank,

.fb_setcolreg = s3c2410fb_setcolreg,

.fb_fillrect = cfb_fillrect,

.fb_copyarea = cfb_copyarea,

.fb_imageblit = cfb_imageblit,

};

    可以看到,这里,将自己实现的那些对framebuffer的操作函数,包括设置LCD的参数,剪切,拷贝,清空等,赋值给那个结构体变量,这样上层framebuffer框架中的函数调用的时候,就会调用你自己实现的那些函数了。


    有些函数,如fb_pan_display等,如果你没有实现,那么系统就会调用上层框架中默认实现的函数来去做对应的操作。

    上层的默认的函数实现在 drivers/video/fbmem.c中。

    相关具体的细节,感兴趣的自己去看吧。


    关于fb驱动中的probe函数,在insmod对应驱动后执行,主要就是一些初始化,其中就包括了上面提到的几个全局变量,结构体的初始化,比如此例中的s3c2410fb_probe()中:

fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;

fbinfo->fix.type_aux = 0;

fbinfo->fix.xpanstep = 0;

fbinfo->fix.ypanstep = 0;

fbinfo->fix.ywrapstep = 0;

fbinfo->fix.accel = FB_ACCEL_NONE;


fbinfo->var.nonstd = 0;

fbinfo->var.activate = FB_ACTIVATE_NOW;

fbinfo->var.accel_flags = 0;

fbinfo->var.vmode = FB_VMODE_NONINTERLACED;


    设置了fb和var的各自属性,是FB_ACTIVATE_NOW 立即激活类型,FB_VMODE_NONINTERLACED非交互模式(不支持双缓冲?)等等。


    本人目前了解到的就是这么多,欢迎其他高手指出不妥之处和互相交流。


【参考资料】

【1】What is the Framebuffer?


【2】Console programming HOWTO - 7. framebuffer


【3】Linux Framebuffer Driver Writing HOWTO


【4】frame buffer device驱动程序

原文地址:https://www.cnblogs.com/Akagi201/p/2418040.html