Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1156892
  • 博文数量: 222
  • 博客积分: 5262
  • 博客等级: 大校
  • 技术积分: 3028
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-22 19:10
文章分类

全部博文(222)

文章存档

2012年(2)

2011年(192)

2010年(28)

分类: 嵌入式

2011-01-04 17:27:09

Linux下Framebuffer驱动简介

Version: V0.1

Time:09/06/2008

Author:Green-waste@163.com

最近接触了一些关于Linux下framebuffer方面的东西和LCD的framebuffer驱动,所以去了解了一些相关基础知识。为了避免其他人走我走过的弯路,所以就把我的一些很少的心得,和大家分享一下,希望对有些人有帮助。


【什么是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驱动程序
 

 
阅读(1112) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~