什么是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驅動程序