Chinaunix首页 | 论坛 | 博客
  • 博客访问: 613026
  • 博文数量: 152
  • 博客积分: 2684
  • 博客等级: 少校
  • 技术积分: 1126
  • 用 户 组: 普通用户
  • 注册时间: 2010-10-29 11:03
文章分类
文章存档

2012年(6)

2011年(96)

2010年(50)

分类: 嵌入式

2010-10-29 16:03:34

最近接触了一些关于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非交互模式(不支持双缓冲?)
等等。

本人目前了解到的就是这么多,欢迎其他高手指出不妥之处和互相交流。
 
本博文转载于:“刘立的嵌入式DaVinci开发”的博客
阅读(1791) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~