摘 要: 本文在分析嵌入式系統中圖形系統的軟件體系結構后,系統介紹了實現圖形化輸出的底層驅動的相關數據結構和算法實現,并介紹了將圖形顯示驅動安裝到嵌入式操作系統?滋Clinux內核的方法。
關鍵詞: μClinux 驅動 FrameBuffer
隨著計算機技術的迅速發展,嵌入式系統得到了廣泛的應用。因為嵌入式系統本身固有的嵌入性和專用性,所以對嵌入式系統的開發有別于傳統意義上的計算機系統開發。嵌入式操作系統的普及使得基于嵌入式操作系統的開發成為嵌入式應用系統開發的主導方式。
嵌入式系統對完整的圖形系統的要求越來越迫切,GUI(圖形化接口)已經成為嵌入式系統信息輸出的一種重要方式。本文詳細闡述了基于?滋Clinux操作系統的圖形應用的軟件結構,并在此基礎上介紹了圖形顯示的底層硬件驅動技術,系統地給出了實現驅動的數據結構和算法。
1 μClinux簡介
μClinux由Linux2.0內核發展而來,它繼承了Linux的主要特點,并針對微控制領域中不具有MMU(存儲管理單元)的處理器做了修改。μClinux重寫了內核中大部分的二進制代碼和源代碼,因此內核比Linux2.0小很多,但它同時卻保留了Linux操作系統的穩定性以及出色地支持多種文件系統的特性。?滋Clinux已被廣泛應用于嵌入式系統中,本文將基于該操作系統研究圖形硬件驅動。
2 圖形系統的體系結構
嵌入式系統的顯示輸出分為圖形輸出和純文本輸出二部分。μClinux操作系統中控制臺(Console)處理純文本的輸出,而幀緩沖(Frame Buffer)負責圖形信息的輸出。
μClinux操作系統的圖形系統從軟件結構角度可以分為如圖1所示的三層。
最底層是圖形硬件驅動程序,用來操作圖形硬件設備。μClinux系統中對圖形硬件設備的操作通過標準化的調用接口映射到該層實施。
中間層是GUI圖形引擎,該層把最底層提供的基本圖形輸出操作結合起來完成較為復雜的圖形輸出。為該層設計的GUI圖形引擎已經有成熟的產品,如國內的MiniGUI、國外的MicroWindows以及Embedded QT等。
最高層是圖形應用程序層,各種圖形應用程序都在該層實現。應用程序調用中間層的圖形引擎完成各種復雜圖形效果的輸出。
本文研究的對象是圖形系統最底層的硬件驅動。
3 圖形硬件驅動
3.1 μClinux設備驅動簡介
μClinux將設備分為字符設備、塊設備和網絡設備三大類。圖形顯示硬件設備屬于字符設備。
μClinux驅動程序的基本結構和Linux驅動程序的結構類似。不同的是:Linux使用模塊化(module)的方式處理設備驅動,可以根據需求將所需驅動加載到系統內核中。μClinux雖然也支持模塊化的處理方式,但是,由于存儲空間的限制以及嵌入式系統具有針對性的功能要求,所以,通常在編譯內核時便放棄了對模塊化的支持而采用將驅動直接編譯進內核的方式安裝驅動。
μClinux的設備驅動程序和文件系統緊密地結合在一起,各種設備以文件的形式存放在/dev目錄下,稱為設備文件。用戶程序使用open( )、close( )、read( )、write( )等標準調用函數操作硬件設備。內核通過file_operations結構調用驅動程序中的函數。這是一個通用的文件操作函數指針的集合,包含了?滋Clinux提供的全部文件系統操作函數。在硬件驅動程序中只需實現該硬件所需的部分函數,而將其他的函數指針置空。
3.2 圖形硬件驅動的研究與實現
3.2.1 圖形硬件驅動
圖形硬件的驅動在?滋Clinux中是比較復雜的驅動,同它相關的函數及文件分為二類。
(1)Frame Buffer驅動程序。驅動程序的代碼存放在fbmem.c文件中。Frame Buffer為顯示設備提供一個通用接口,它是將顯存抽象后的設備。它通過地址映射允許上層用戶在圖形模式下對顯示緩沖區進行讀寫。Frame Buffer的存在使用戶不必關心物理顯存的位置、換頁機制等細節問題,同時簡化了用戶程序代碼在不同硬件平臺間的移植。
(2)Frmae Buffer的輔助函數。這些函數聲明在fb.h中。不同的嵌入式系統中,圖形顯示硬件不完全相同,硬件獨有的狀態數據由這些輔助函數記錄并修改。Frame Buffer調用輔助函數控制顯示硬件設備。
下面從這二方面探討圖形硬件驅動的實現。
3.2.2 Frame Buffer驅動
Frame Buffer的驅動代碼存放在fbmem.c中,該文件最早出現在Linux-1.3.94內核版本中。其中最核心的數據結構是包含操作Frame Buffer函數的指針集合struct file_operations fb_fops。
static struct file_operations fb_fops={
NULL, //lseek
fb_read, //read
fb_write, //write
NULL, //readdir
NULL, //select
fb_ioctl, //ioctl
fb_mmap, //mmap
fb_open, //open
fb_release, //release
NULL //fsync
};
Frame Buffer設備文件的特征決定了其只需要實現文件操作函數中的部分調用,如:fb_read、fb_write、fb_mmap等。
由于處理器不支持MMU,μClinux操作系統對內存的管理不同于標準的Linux。在沒有MMU的嵌入式系統中,顯存的空間是獨立且固定的,μClinux操作系統可以線性地訪問顯存空間。基于此,?滋Clinux中的fb_mmap可以修改成如下代碼:
static int fb_mmap(struct inode*inode,struct file*file,
struct vm_area_struct*vma) {
struct fb_ops*fb=registered_fb[ GET_FB_IDX(inode->i_rdev)];
struct fb_fix_screeninfo fix;
if(!fb)
return -ENODEV;
fb->fb_get_fix(&fix,PROC_CONSOLE( ));
vma->vm_start=fix.smem_start+vma->vm_offset;
return 0;
}
由于μClinux直接通過地址總線訪問顯存空間,所以地址映射被處理成直接訪問內存地址的方式。
fb_open、fb_write、fb_read等函數完成驅動Frame Buffer所必須的另外幾個操作,函數fb_ioctl則用來調用輔助函數記錄和修改硬件狀態數據。這些函數只需做微小的修改便可以滿足嵌入式系統圖形顯示的需要。限于篇幅,在此不作詳細說明。
3.2.3 Frame Buffer的輔助函數
Frame Buffer調用顯示驅動的輔助函數記錄與修改顯示硬件狀態數據。由于不同顯示硬件設備的工作方式不同,所以需要為它們定制特別的輔助函數。Frame Buffer的實現離不開輔助函數,因此include/linux/fb.h最初和fbmem.c一起出現在Linux-1.3.94內核版本中。fb.h中聲明了輔助函數的接口,函數實現代碼則需要根據具體的硬件結構編寫并保存在文件xxxfb.c中。
fb.h文件中定義了記錄圖形硬件固有狀態參數的struct fb_fix_screeninfo和記錄圖形硬件可變參數的struct fb_var_screeninfo,同時聲明了操作這二組數據的函數指針集合struct fb_ops:
struct fb_ops {
//讀取固有參數
int (*fb_get_fix) (struct fb_fix_screeninfo*,int);
//讀取可變參數
int (*fb_get_var) (struct fb_var_screeninfo*,int);
//設置可變參數
int (*fb_set_var) (struct fb_var_screeninfo*,int);
//讀取color map
int (*fb_get_cmap) (struct fb_cmap*,int,int);
//設置color map
int (*fb_set_cmap) (struct fb_cmap*,int,int);
//平面顯示函數
int (*fb_pan_display) (struct fb_var_screeninfo*,int);
int (*fb_ioctl)(struct inode*,struct file*,unsigned int,
unsigned long,int);
};
在xxxfb.c文件中必須聲明這樣兩個變量:
static struct fb_fix_screeninfo xxx_fb_fix;/*硬件固有參數*/
static struct fb_var_screeninfo xxx_fb_var;/*硬件可變參數*/
fb_ops中的函數指針在xxxfb.c文件中完成函數代碼:
static struct fb_ops xxxfb_ops={
xxxfb_get_fix,
xxxfb_get_var,
xxxfb_set_var,
xxxfb_get_cmap,
xxxfb_set_cmap,
xxxfb_pan_display,
xxxfb_ioctl
};
結合顯示硬件結構特征實現這幾個函數,其中關鍵的函數有xxxfb_get_fix、xxxfb_set_var和xxxfb_get_var。這三個函數分別對xxx_fb_fix和xxx_fb_var中的參數進行讀取和設置。
以xxxfb_get_fix為例,該函數從xxx_fb_fix中讀取硬件的固有狀態參數。有二種實現方法:(1)從xxx_fb_fix中逐個讀取需要的參數。對具體的硬件,fb_fix_screeninfo中只有部分數據是需要被處理的,因此只需要讀取有效數據。(2)調用系統的memcpy( )函數將xxx_fb_fix完全拷貝出來。這種方法方便,但對嵌入式系統來說是以加大存儲空間的開銷為代價的。
另外二個函數xxxfb_get_var和xxxfb_set_var也可以做類似的處理。
在xxxfb.c文件中,啟動顯示硬件的函數是xxxfb_init( )。該函數將當前的顯示硬件注冊到系統中供Frame buffer調用,同時還完成對xxx_fb_fix的賦值。具體代碼如下:
void xxxfb_init(void)
{
……
/*硬件的固有數據是固定的,因此在這里對xxx_fb_fix進行賦值*/
……
//將顯示硬件注冊到系統中
err=register_framebuffer(&fb_info.gen.info);
if (err<0)
return err;
……
return mem_start;//返回顯存起始地址
}
xxxfb_init( )函數被fbmem.c文件的fb_open( )函數調用。由于圖形硬件的多樣性,fb_open( )函數根據具體硬件信息選擇xxxfb_init( )啟動輔助函數。
μClinux中線性的顯存訪問是實現圖形顯示驅動首先要注意的問題,處理好上面提到的函數和數據就可以為特定嵌入式系統的顯示設備開發出合適的輔助函數。
3.2.4 安裝驅動
下面介紹將顯示驅動安裝進內核的步驟。
(1)圖形顯示硬件屬字符設備,因此將fbme.c文件保存到目錄/linux-2.0/driver/char/中。
(2)將圖形設備加入到的Makefile文件中。
(3)在/linux/init/main.c文件中添加驅動的啟動函數fbmem_init( )。
(4)修改編譯選項文件,在/linux/arm/armnommu/config.in文件中加入:
bool′ framebuffer support′ CONFIG_FB_XXX
(5)為文件系統的open( )調用提供設備文件名。在/vendors/<VENDOR>/<BOARD>/Makefile的‘DEVICES=’中,加入‘xxxfb,c,29,0’作為顯示設備驅動的入口。其中29是主設備號,從設備號可以根據需要改變,這里將它設為0。
(6)使用make工具重新編譯內核,就可將圖形顯示的驅動編譯進μClinux內核。
以上六步將顯示硬件驅動安裝到μClinux的內核中,在此基礎上選用合適的圖形包就可以在嵌入式系統中方便地開發圖形應用程序。
4 結束語
在某些特殊場合應用的嵌入式系統可能需要支持多個顯示硬件的輸出,依照本文的方法可以為不同的顯示硬件設計輔助函數來支持多終端圖形輸出。
至此,本文完成了基于?滋Clinux操作系統的嵌入式設備的圖形顯示驅動的探討并給出了部分實現算法。
參考文獻
1 Bovet D P,Cesati M著,陳莉君譯.深入理解Linux內核(第二版).北京:中國電力出版社,2004
2 Rubini A,Corbet J著,魏永明譯.Linux設備驅動程序(第二版).北京:中國電力出版社,2002
3 毛德操,胡希明.Linux內核源代碼情景分析.杭州:浙江大學出版社,2001