帧缓冲设备

最后修订:2001年5月10日

0. 简介

帧缓冲设备为图形硬件提供了一个抽象层。它代表某些视频硬件的帧缓冲区,并允许应用程序软件通过定义良好的接口访问图形硬件,因此软件无需了解底层(硬件寄存器)的东西。

该设备通过特殊的设备节点访问,通常位于 /dev 目录中,例如 /dev/fb*。

1. 用户对 /dev/fb* 的看法

从用户的角度来看,帧缓冲设备看起来就像 /dev 中的任何其他设备。它是一个使用主设备号 29 的字符设备;次设备号指定帧缓冲区的编号。

按照惯例,使用以下设备节点(数字表示设备次设备号)

 0 = /dev/fb0      First frame buffer
 1 = /dev/fb1      Second frame buffer
     ...
31 = /dev/fb31     32nd frame buffer

为了向后兼容,您可能需要创建以下符号链接

/dev/fb0current -> fb0
/dev/fb1current -> fb1

等等...

帧缓冲设备也是普通内存设备,这意味着您可以读取和写入其内容。 例如,您可以通过以下方式制作屏幕快照

cp /dev/fb0 myfile

一次也可以有多个帧缓冲区,例如,如果您除了内置硬件之外还有一张显卡。相应的帧缓冲设备(/dev/fb0 和 /dev/fb1 等)独立工作。

使用帧缓冲设备的应用程序软件(例如 X 服务器)默认情况下将使用 /dev/fb0(较旧的软件使用 /dev/fb0current)。您可以通过将环境变量 $FRAMEBUFFER 设置为帧缓冲设备的路径名来指定备用帧缓冲设备,例如(对于 sh/bash 用户)

export FRAMEBUFFER=/dev/fb1

或(对于 csh 用户)

setenv FRAMEBUFFER /dev/fb1

之后,X 服务器将使用第二个帧缓冲区。

2. 程序员对 /dev/fb* 的看法

如您所知,帧缓冲设备是一个像 /dev/mem 这样的内存设备,并且具有相同的功能。您可以读取它、写入它、在其中寻找某个位置并 mmap() 它(主要用法)。不同之处在于,特殊文件中出现的内存不是全部内存,而是某些视频硬件的帧缓冲区。

/dev/fb* 还允许在其上进行多个 ioctl 操作,通过这些操作可以查询和设置有关硬件的大量信息。 颜色映射处理也通过 ioctl 进行。请查看 <linux/fb.h>,以获取有关存在哪些 ioctl 以及它们所操作的数据结构的更多信息。 这里只是一个简短的概述

  • 您可以请求有关硬件的不可更改的信息,例如名称、屏幕内存的组织(平面、打包像素等)以及屏幕内存的地址和长度。

  • 您可以请求和更改有关硬件的可变信息,例如可见和虚拟几何形状、深度、颜色映射格式、时序等。 如果您尝试更改该信息,则驱动程序可能会四舍五入某些值以满足硬件的功能(如果不可能,则返回 EINVAL)。

  • 您可以获取和设置颜色映射的一部分。 通信使用每个颜色部分(红、绿、蓝、透明)16 位完成,以支持所有现有硬件。 驱动程序完成将其应用于硬件所需的所有计算(向下舍入到更少的位数,可能会丢弃透明度)。

所有这些硬件抽象使得应用程序的实现更容易且更具可移植性。 例如,X 服务器完全在 /dev/fb* 上运行,因此无需知道,例如,具体硬件的颜色寄存器是如何组织的。 XF68_FBDev 是用于位图、未加速视频硬件的通用 X 服务器。 必须构建到应用程序中的唯一内容是屏幕组织(位平面或块状像素等),因为它直接作用于帧缓冲区图像数据。

将来,计划将显卡等帧缓冲驱动程序实现为在运行时加载的内核模块。 此类驱动程序只需调用 register_framebuffer() 并提供一些函数。 独立于内核编写和分发此类驱动程序将省去很多麻烦...

3. 帧缓冲区分辨率维护

帧缓冲区分辨率使用实用程序 fbset 进行维护。 它可以更改帧缓冲设备的视频模式属性。 它的主要用途是更改当前的视频模式,例如在您的 /etc/rc.*/etc/init.d/* 文件之一中启动期间。

Fbset 使用存储在配置文件中的视频模式数据库,因此您可以轻松添加自己的模式并使用简单的标识符引用它们。

4. X 服务器

X 服务器 (XF68_FBDev) 是帧缓冲设备最著名的应用程序。 从 XFree86 3.2 版本开始,X 服务器是 XFree86 的一部分,并且有 2 种模式

  • 如果 /etc/XF86Config 文件中 fbdev 驱动程序的 Display 子部分包含一个

    Modes "default"
    

    行,X 服务器将使用上述方案,即它将以 /dev/fb0(如果设置了 $FRAMEBUFFER,则为 $FRAMEBUFFER)确定的分辨率启动。 您仍然需要指定颜色深度(使用 Depth 关键字)和虚拟分辨率(使用 Virtual 关键字)。 这是 XFree86 随附的配置文件的默认设置。 它是最简单的配置,但有一些限制。

  • 因此,也可以在 /etc/XF86Config 文件中指定分辨率。 这允许在保留相同虚拟桌面大小的同时进行动态分辨率切换。 所使用的帧缓冲设备仍然是 /dev/fb0current(或 $FRAMEBUFFER),但可用分辨率现在由 /etc/XF86Config 定义。 缺点是您必须以不同的格式指定时序(但 fbset -x 可能会有所帮助)。

要调整视频模式,您可以使用 fbset 或 xvidtune。 请注意,xvidtune 不能 100% 与 XF68_FBDev 一起使用:报告的时钟值始终不正确。

5. 视频模式时序

监视器通过使用电子束(彩色模型为 3 个电子束,单色监视器为 1 个电子束)在屏幕上绘制图像。 屏幕正面覆盖着彩色磷光体(像素)图案。 如果磷光体受到电子的撞击,它就会发射光子,从而变得可见。

电子束从左到右以及从屏幕的顶部到底部绘制水平线(扫描线)。 通过修改电子束的强度,可以显示具有各种颜色和强度的像素。

在每条扫描线之后,电子束必须移回屏幕的左侧并移到下一行:这称为水平回扫。 在绘制完整个屏幕(帧)后,光束将移回左上角:这称为垂直回扫。 在水平和垂直回扫期间,电子束会被关闭(消隐)。

电子束绘制像素的速度由图形板中的点时钟决定。 例如,对于 28.37516 MHz(每秒数百万个周期)的点时钟,每个像素长 35242 ps(皮秒)

1/(28.37516E6 Hz) = 35.242E-9 s

如果屏幕分辨率为 640x480,则需要

640*35.242E-9 s = 22.555E-6 s

绘制一条扫描线上的 640 个 (xres) 像素。 但是,水平回扫也需要时间(例如,272 像素),因此一条完整的扫描线需要

(640+272)*35.242E-9 s = 32.141E-6 s

我们将说水平扫描率约为 31 kHz

1/(32.141E-6 s) = 31.113E3 Hz

一个完整的屏幕计算 480 (yres) 行,但我们也必须考虑垂直回扫(例如 49 )。 因此,一个完整的屏幕将需要

(480+49)*32.141E-6 s = 17.002E-3 s

垂直扫描率约为 59 Hz

1/(17.002E-3 s) = 58.815 Hz

这意味着屏幕数据每秒刷新约 59 次。 为了获得没有可见闪烁的稳定图像,VESA 建议垂直扫描率至少为 72 Hz。 但是,感知的闪烁非常依赖于人:有些人可以使用 50 Hz 而没有任何问题,而如果小于 80 Hz,我会注意到它。

由于监视器不知道新扫描线何时开始,因此图形板将为每条扫描线提供同步脉冲(水平同步或 hsync)。 同样,它为每个新帧提供同步脉冲(垂直同步或 vsync)。 图像在屏幕上的位置受同步脉冲发生时刻的影响。

下图总结了所有时序。 水平回扫时间是左边距、右边距和 hsync 长度的总和,而垂直回扫时间是上边距、下边距和 vsync 长度的总和

+----------+---------------------------------------------+----------+-------+
|          |                ↑                            |          |       |
|          |                |upper_margin                |          |       |
|          |                ↓                            |          |       |
+----------###############################################----------+-------+
|          #                ↑                            #          |       |
|          #                |                            #          |       |
|          #                |                            #          |       |
|          #                |                            #          |       |
|   left   #                |                            #  right   | hsync |
|  margin  #                |       xres                 #  margin  |  len  |
|<-------->#<---------------+--------------------------->#<-------->|<----->|
|          #                |                            #          |       |
|          #                |                            #          |       |
|          #                |                            #          |       |
|          #                |yres                        #          |       |
|          #                |                            #          |       |
|          #                |                            #          |       |
|          #                |                            #          |       |
|          #                |                            #          |       |
|          #                |                            #          |       |
|          #                |                            #          |       |
|          #                |                            #          |       |
|          #                |                            #          |       |
|          #                ↓                            #          |       |
+----------###############################################----------+-------+
|          |                ↑                            |          |       |
|          |                |lower_margin                |          |       |
|          |                ↓                            |          |       |
+----------+---------------------------------------------+----------+-------+
|          |                ↑                            |          |       |
|          |                |vsync_len                   |          |       |
|          |                ↓                            |          |       |
+----------+---------------------------------------------+----------+-------+

帧缓冲设备期望所有水平时序以点时钟数(以皮秒为单位,1E-12 秒)为单位,垂直时序以扫描线数为单位。

6. 将 XFree86 时序值转换为帧缓冲设备时序

XFree86 模式行由以下字段组成

"800x600"     50      800  856  976 1040    600  637  643  666
< name >     DCF       HR  SH1  SH2  HFL     VR  SV1  SV2  VFL

帧缓冲设备使用以下字段

  • pixclock:以 ps(皮秒)为单位的像素时钟

  • left_margin:从同步到图像的时间

  • right_margin:从图像到同步的时间

  • upper_margin:从同步到图像的时间

  • lower_margin:从图像到同步的时间

  • hsync_len:水平同步的长度

  • vsync_len:垂直同步的长度

  1. 像素时钟

    xfree:以 MHz 为单位

    fb:以皮秒 (ps) 为单位

    pixclock = 1000000 / DCF

  2. 水平时序

    left_margin = HFL - SH2

    right_margin = SH1 - HR

    hsync_len = SH2 - SH1

  3. 垂直时序

    upper_margin = VFL - SV2

    lower_margin = SV1 - VR

    vsync_len = SV2 - SV1

在 XFree86 源代码树的 “xc/programs/Xserver/hw/xfree86/doc/modeDB.txt” 下,可以找到 VESA 时序的很好的示例。

7. 参考资料

有关帧缓冲设备及其应用的更多具体信息,请参考 Linux-fbdev 网站

以及以下文档

  • fbset 的手册页:fbset(8), fb.modes(5)

  • XFree86 的手册页:XF68_FBDev(1), XF86Config(4/5)

  • 强大的内核源代码

    • linux/drivers/video/

    • linux/include/linux/fb.h

    • linux/include/video/

8. 邮件列表

kernel.org 上有一个与帧缓冲设备相关的邮件列表:linux-fbdev@vger.kernel.org

请将您的 Web 浏览器指向 http://sourceforge.net/projects/linux-fbdev/ 以获取订阅信息和浏览存档。

9. 下载

所有必要的文件都可以在以下位置找到

及其镜像站点。

fbset 的最新版本可以在以下位置找到

10. 致谢

本自述文件由 Geert Uytterhoeven 编写,部分基于 Roman Hodek 和 Martin Schaller 的原始 X-framebuffer.README。第 6 节由 Frank Neumann 提供。

帧缓冲设备抽象由 Martin Schaller 设计。