http://blog.ednchina.com/exbob/254847/message.aspx
Linux的帧缓冲设备
帧缓冲(framebuffer)是 Linux 为显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer 设备驱动来完成的。帧缓冲驱动的应用广泛,在 linux 的桌面系统中,Xwindow 服务器就是利用帧缓冲进行窗口的绘制。尤其是通过帧缓冲可显示汉字点阵,成为 Linux汉化的唯一可行方案。
帧缓冲设备对应的设备文件为/dev/fb*,如果系统有多个显示卡,Linux 下还可支持多个帧缓冲设备,最多可达 32个,分别为/dev/fb0 到/dev/fb31,而/dev/fb 则为当前缺省的帧缓冲设备,通常指向/dev/fb0。当然在嵌入式系统中支持一个显示设备就够了。帧缓冲设备为标准字符设备,主设备号为29,次设备号则从0到31。分别对应/dev/fb0-/dev/fb31。
通过/dev/fb,应用程序的操作主要有这几种:
1.读/写(read/write)/dev/fb:相当于读/写屏幕缓冲区。例如用 cp /dev/fb0 tmp 命令可将当前屏幕的内容拷贝到一个文件中,而命令 cp tmp > /dev/fb0 则将图形文件tmp显示在屏幕上。
2.映射(map)操作:由于 Linux 工作在保护模式,每个应用程序都有自己的虚拟地址空间,在应用程序中是不能直接访问物理缓冲区地址的。为此,Linux 在文件操作 file_operations 结构中提供了 mmap 函数,可将文件的内容映射到用户空间。对于帧缓冲设备,则可通过映射操作,可将屏幕缓冲区的物理地址映射到用户空间的一段虚拟地址中,之后用户就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图了。
3.I/O控制:对于帧缓冲设备,对设备文件的 ioctl操作可读取/设置显示设备及屏幕的参数,如分辨率,显示颜色数,屏幕大小等等。ioctl 的操作是由底层的驱动程序来完成的。
在应用程序中,操作/dev/fb的一般步骤如下:
1.打开/dev/fb设备文件。
2.用 ioctrl 操作取得当前显示屏幕的参数,如屏幕分辨率,每个像素点的比特数。根据屏幕参数可计算屏幕缓冲
区的大小。
3.将屏幕缓冲区映射到用户空间(mmap)。
4.映射后就可以直接读写屏幕缓冲区,进行绘图和图片显示了。
典型程序段如下:
------------------------
- #include <linux/fb.h>
- int main()
- {
- int fbfd = 0;
- struct fb_var_screeninfo vinfo;
- struct fb_fix_screeninfo finfo;
- long int screensize = 0;
- /*打开设备文件*/
- fbfd = open("/dev/fb0", O_RDWR);
- /*取得屏幕相关参数*/
- ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo);
- ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo);
- /*计算屏幕缓冲区大小*/
- screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
- /*映射屏幕缓冲区到用户地址空间*/
- fbp=(char*)mmap(0,screensize,PROT_READ|PROT_WRITE,MAP_SHARED, fbfd, 0);
- /*下面可通过 fbp指针读写缓冲区*/
- ……
- /*释放缓冲区,关闭设备*/
- munmap(fbp, screensize);
- close(fbfd);
- }
-----------------------
ioctl操作
ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)
获取fb_var_screeninfo结构的信息,在linux/include/linux/fb.h定义。
ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)
获取fb_fix_screeninfon结构的信息。在linux/include/linux/fb.h定义。
fbfd为设备文件号。
-----------------------
mmap函数
功能描述:
mmap函数是unix/linux下的系统调用
mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零。munmap执行相反的操作,删除特定地址区域的对象映射。
基于文件的映射,在mmap和munmap执行过程的任何时刻,被映射文件的st_atime可能被更新。如果st_atime字段在前述的情况下没有得到更新,首次对映射区的第一个页索引时会更新该字段的值。用PROT_WRITE 和 MAP_SHARED标志建立起来的文件映射,其st_ctime 和 st_mtime在对映射区写入之后,但在msync()通过MS_SYNC 和 MS_ASYNC两个标志调用之前会被更新。
用法:
#include <sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *start, size_t length);
参数:
start:映射区的开始地址。
length:映射区的长度。
prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起
PROT_EXEC //页内容可以被执行
PROT_READ //页内容可以被读取
PROT_WRITE //页可以被写入
PROT_NONE //页不可访问
flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体
MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。
MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。
MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。
MAP_DENYWRITE //这个标志被忽略。
MAP_EXECUTABLE //同上
MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。
MAP_LOCKED //锁定映射区的页面,从而防止页面被交换出内存。
MAP_GROWSDOWN //用于堆栈,告诉内核VM系统,映射区可以向下扩展。
MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。
MAP_ANON //MAP_ANONYMOUS的别称,不再被使用。
MAP_FILE //兼容标志,被忽略。
MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。
MAP_POPULATE //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。
MAP_NONBLOCK //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。
fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1。
offset:被映射对象内容的起点。
返回说明:
成功执行时,mmap()返回被映射区的指针,munmap()返回0。
失败时,mmap()返回MAP_FAILED[其值为(void *)-1],munmap返回-1。errno被设为以下的某个值
EACCES:访问出错
EAGAIN:文件已被锁定,或者太多的内存已被锁定
EBADF:fd不是有效的文件描述词
EINVAL:一个或者多个参数无效
ENFILE:已达到系统对打开文件的限制
ENODEV:指定文件所在的文件系统不支持内存映射
ENOMEM:内存不足,或者进程已超出最大内存映射数量
EPERM:权能不足,操作不允许
ETXTBSY:已写的方式打开文件,同时指定MAP_DENYWRITE标志
SIGSEGV:试着向只读区写入
SIGBUS:试着访问不属于进程的内存区
------------------------
我敲写了一遍 程序,然后补充了 fb_fix_screeninfo 和 fb_var_screeninfo 结构体元素的内容测试
完整的代码: framebuff-myself-press.rar
其中一部分的代码:
- ioctl(fdfd,FBIOGET_FSCREENINFO,&finfo);//获取 固定参数
- printf("id %s\n",finfo.id); s3c2410fb
- printf("smem_start %d\n",finfo.smem_start);//帧缓冲 内存开始地址,物理地址
- printf("smem_len %d\n",finfo.smem_len);// 帧缓冲 内存 长度
- printf("type %d\n",finfo.type);
- printf("type_aux %d\n",finfo.type_aux);//平面交织交替
- printf("visual %d\n",finfo.visual);//记录 色彩模式 2
- printf("xpanstep %d\n",finfo.xpanstep);//如果没有硬件panning,赋0
- printf("ypanstep %d\n",finfo.ypanstep);
- printf("line_length %d\n",finfo.line_length); 640
- printf("mmio_start %d\n",finfo.mmio_start);//内存映射IO开始地址 物理地址
- printf("mmio_len %d\n",finfo.mmio_len);//内存映射IO 长度
- printf("accel %d\n\n",finfo.accel);
-
- ioctl(fdfd,FBIOGET_VSCREENINFO,&vinfo);//获取可变参数
- printf("xres %d\n",vinfo.xres);//可见解析度 320
- printf("yres %d\n",vinfo.yres); 240
- printf("xres_virturl %d\n",vinfo.xres_virtual);//虚拟解析度 320
- printf("yres_virtual %d\n",vinfo.yres_virtual); 240
- printf("xoffset %d\n",vinfo.xoffset);//虚拟到可见的偏移 0
- printf("yoffset %d\n",vinfo.yoffset); 0
- printf("bits_per_pixel %d\n",vinfo.bits_per_pixel);//每像素位数 bpp 16
- printf("grayscale %d\n",vinfo.grayscale);//非零时,指灰度
-
- printf("fb_bitfield red.offset %d\n",vinfo.red.offset); 11 偏移11位
- printf("fb_bitfield .length %d\n",vinfo.red.length); 5
- printf("fb_bitfield .msb_right %d\n",vinfo.red.msb_right); 0
- printf("fb_bitfield green.offset %d\n",vinfo.green.offset); 5 偏移5位
- printf("fb_bitfield .length %d\n",vinfo.green.length); 6
- printf("fb_bitfield .msb_right %d\n",vinfo.green.msb_right); 0
- printf("fb_bitfield blue.offset %d\n",vinfo.blue.offset);
- printf("fb_bitfield .length %d\n",vinfo.blue.length);
- printf("fb_bitfield .msb_right %d\n",vinfo.blue.msb_right);
- printf("fb_bitfield transp.offset %d\n",vinfo.transp.offset);
- printf("fb_bitfield .length %d\n",vinfo.transp.length);
- printf("fb_bitfield .msb_right %d\n",vinfo.transp.msb_right);
- printf("nonstd %d\n",vinfo.nonstd);//!=0 非标准像素格式
- printf("activate %d\n",vinfo.activate);
- printf("height %d\n",vinfo.height);//高度/ 240
- printf("widht %d\n",vinfo.width); 320
- printf("accel_flags %d\n",vinfo.accel_flags);//看 fb_info.flags
- //定时,除了 pixclock之外,其他的都以像素时钟为单位
- printf("pixclock %d\n",vinfo.pixclock);//像素时钟,皮秒 80000
- printf("left_margin %d\n",vinfo.left_margin);//行切换:从同步到绘图之间的延迟 28
- printf("right_margin %d\n",vinfo.right_margin);//行切换:从绘图到同步之间的延迟 24
- printf("upper_margin %d\n",vinfo.upper_margin);//帧切换:从同步到绘图之间的延迟 6
- printf("lower_margin %d\n",vinfo.lower_margin);//帧切换:从绘图到同步之间的延迟 2
- printf("hsync_len %d\n",vinfo.hsync_len);//hor 水平同步的长度 42
- printf("vsync_len %d\n",vinfo.vsync_len);//vir 垂直同步的长度 12
- printf("sync %d\n",vinfo.sync);//
- printf("vmode %d\n",vinfo.vmode);
- printf("rotate %d\n",vinfo.rotate);
测试结果
- [root@yuweixian yu]#./framebuff-test
- the framebuffer device was opended successfully.
- id s3c2410fb
- smem_start 864813056
- smem_len 153600
- type 0
- type_aux 0
- visual 2
- xpanstep 0
- ypanstep 0
- line_length 640
- mmio_start 0
- mmio_len 0
- accel 0
- xres 320
- yres 240
- xres_virturl 320
- yres_virtual 240
- xoffset 0
- yoffset 0
- bits_per_pixel 16
- grayscale 0
- fb_bitfield red.offset 11
- fb_bitfield .length 5
- fb_bitfield .msb_right 0
- fb_bitfield green.offset 5
- fb_bitfield .length 6
- fb_bitfield .msb_right 0
- fb_bitfield blue.offset 0
- fb_bitfield .length 5
- fb_bitfield .msb_right 0
- fb_bitfield transp.offset 0
- fb_bitfield .length 0
- fb_bitfield .msb_right 0
- nonstd 0
- activate 0
- height 240
- widht 320
- accel_flags 0
- pixclock 80000
- left_margin 28
- right_margin 24
- upper_margin 6
- lower_margin 2
- hsync_len 42
- vsync_len 12
- sync 0
- vmode 0
- rotate 0
- the framebuffer devices was mapped to memory successfully
- bye the framebuffer
- [root@yuweixian yu]#
分析 1.
- printf("fb_bitfield red.offset %d\n",vinfo.red.offset); 11 偏移11位
- printf("fb_bitfield .length %d\n",vinfo.red.length); 5
- printf("fb_bitfield .msb_right %d\n",vinfo.red.msb_right); 0
- printf("fb_bitfield green.offset %d\n",vinfo.green.offset); 5 偏移5位
这个对应 SC32440 中 RGB565 设置方式
分析 2
http://www.lslnet.com/linux/dosc1/48/linux-328178.htm
- printf("smem_len %d\n",finfo.smem_len); 153600
printf("xres %d\n",vinfo.xres); 320
printf("yres %d\n",vinfo.yres); 240
printf("bits_per_pixel %d\n",vinfo.bits_per_pixel); 16
- 1.smem_len=153600 是这个 /dev/fb0 d的大小,也就是内存大小
因为是 16bpp 一个像素 占有2个字节大小,一行有320个点,所以line_length=640 字节大小
3. xres =320 yres=240 就是 x y 方向的分辨率,就是两个方向上的像素点 个数
4. bits_per_pixel=16bpp 每一个像素点 占用内存空间 2 字节
节=150KB,屏幕每一行占用内存640字节,153600/640=240,
我们发现,240 就是屏幕的高度。
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
*/
};
前几个成员决定了分辨率。xres和yres是在屏幕上可见的实际分辨率,在通常的vga模式将为640和400(也许是480,by highbar)。*res-virtual决定了构建屏幕时视频卡读取屏幕内存的方式。当实际的垂直分辨率为400,虚拟分辨率可以是800。这意味着800行的数据被保存在了屏幕内存区中。因为只有400行可以被显示,决定从那一行开始显示就是你的事了。这个可以通过设置*offset来实现。给yoffset赋0将显示前400行,赋35将显示第36行到第435行,如此重复。这个功能在许多情形下非常方便实用。它可以用来做双缓冲。双缓冲就是你的程序分配了可以填充两个屏幕的内存。将offset设为0,将显示前400行(假设是标准的vga),同时可以秘密的在400行到799行构建另一个屏幕,当构建结束时,将yoffset设为400,新的屏幕将立刻显示出来。现在将开始在第一块内存区中构建下一个屏幕的数据,如此继续。这在动画中十分有用。
另外一个应用就是用来平滑的滚动整个屏幕。就像在前面屏幕中一样,在内存分配800行的空间。每隔10毫秒设定一个定时器(timer,见man settimer和man
signal / man sigaction),将offset设为1或是比上次更多,瞧,你看到了一个平滑滚动的屏幕。确保你的信号(signal)不要因为最佳输出的原因被信号处理程序阻塞。
将
bits_per_pixel 设为1,2,4,8,16,24或32来改变颜色深度(color depth)。不是所有的视频卡和驱动都支持全部颜色深度。当颜色深度改变,驱动将自动改变fb-bitfields。这些指出,在一个特定的颜色基准上,多少和哪些比特被哪种颜色使用。如果bits-per-pixel小于8,则fb-bitfields将无定义而且颜色映射将启用。
在fb-var-screeninfo结构结尾的定时的设置是当你选择一个新的分辨率的时候用来设定视频定时的。(
EXAMINE AND EXPLAIN TIMINGS! )
我使用fbset来设定framebuffer的参数,为什么命令执行了以后啥都没反应???不知道是什么问题。
fbset -fb /dev/fb0 -xres 720 -yres 480 -vxres 720 -vyres 960 -depth 4 -nonstd 1
然后
fbset -i 查看framebuffer参数
发现没变啊
是什么原因呢??
mode "640x480-73"
# D: 30.720 MHz, H: 36.923 kHz, V: 73.260 Hz
geometry 640 480 640 480 16
timings 32552 80 32 16 4 80 4
rgba 5/11,6/5,5/0,0/0
endmode
Frame buffer device information:
Name : VESA VGA
Address : 0xf0000000
Size : 1228800
Type : PACKED PIXELS
Visual : TRUECOLOR
XPanStep : 0
YPanStep : 0
YWrapStep : 0
LineLength : 1280
Accelerator : No
=======================================================================================================
这个设定的生命期在fbset跑完之后就结束了。
比如你要运行gtk-demo设定分辨率为1024x768,
#gtk-demo --dfb:mode=1024x768
就可以,但是这个设定只在运行gtk-demo时候才有效。
要想改变所有应用程序的参数,可以直接编辑配置文件directfbrc
比如编辑 /etc/directfbrc
mode=1024x768
system=sdl
等等。
具体可以查 man directfbrc