close

來源::電子產品世界

長期以來,常見的掌上電腦(PDA)等小型掌上型設備上,由於硬體條件的限制,我們看到的顯示器件通常是單色LCD,用戶介面也非常簡單,幾乎看不到PC機上美觀整齊的圖形介面(GUI)支援。因為早期嵌入式處理器的速度有限,在處理圖形和多媒體資料方面顯得力不從心。 
   隨著高性能嵌入式處理器的普及和硬體成本的不斷降低,尤其是ARM系列處理器的推出,嵌入式系統的功能也越來越強。在多媒體應用的推動下,彩色LCD也越來越多地應用到了嵌入式系統中,如新一代掌上電腦(PDA)多採用TFT顯示器件,支援彩色圖形介面,圖片顯示和視頻媒體播放。掌上電腦(PDA)的作業系統有微軟Window CEPalmOS等。而Linux做為開放源代碼的作業系統也在市場中佔據了一席之地。由於Linux成本低廉,任何人都可以得到其源代碼並在其基礎上進行開發,成為各家廠商極力發展的作業系統,加上其核心小,潛力可觀。 
   在應用需求的推動下,Linux下也出現了許多圖形介面套裝軟體,如MiniGUITrolletech公司的Embedded QT等,其圖形介面及開發工具與Windows CE不相上下。在圖形套裝軟體的開發和移植工作中都牽涉到底層LCD的驅動問題。筆者參與了一個很有代表性的專案,是基於ARM9PDA系統的開發,用的是Motorola公司龍珠系列的MC9328MX1。軟體採用Linux 2.4.18/>平臺,編譯器為gccARM交叉編譯器。在移植QT的過程中牽涉到了底層驅動的開發,本文就對相關問題作一探討。

 硬體平臺 
   MC9328MX1(以下簡稱MX1)Motorola公司基於ARM核心的第一款MCU,主要面向高端嵌入式應用。內部採用ARM920T內核,並集成了SDRAM/FlashLCDUSB,藍牙,多媒體快閃記憶體卡(MMC/SD, Memory Stick)CMOS攝像頭等控制器。 
   LCD控制器的功能是產生顯示驅動信號,驅動LCD顯示器。用戶只需要通過讀寫一系列的寄存器,完成配製和顯示控制。MX1中的LCD控制器可支援單色/彩色LCD顯示器。支援彩色TFT時,可提供4/8/12/>/16位元顏色模式,其中16位元顏色模式下可以顯示64k種顏色。配置LCD控制器重要的一步是指定顯示緩衝區,顯示的內容就是從緩衝區中讀出的,其大小由螢幕解析度和顯示顏色數決定。在本例中,筆者採用的是夏普LQ035Q2DD54 TFT 顯示模組,在240×320解析度下可提供16位元彩色顯示。

  Linux下的設備驅動 
   Linux將設備分為最基本的兩大類,字元設備和塊設備。字元設備是以單個位元組為單位進行順序讀寫操作,通常不使用緩衝技術,如滑鼠等,驅動程式實現比較簡單;而塊設備則是以固定大小的資料塊進行存儲和讀寫的,如硬碟,軟碟等。為提高效率,系統對於塊設備的讀寫提供了緩存機制,由於涉及緩衝區管理,調度,同步等問題,實現起來比字元設備複雜得多。 
   Linux的設備管理是和檔系統解密結合的,各種設備都以檔的形式存放在/dev目錄下,稱為設備檔。應用程式可以打開,關閉,讀寫這些設備檔,完成對設備的操作,就象操作普通的資料檔案一樣。為了管理這些設備,系統為設備編了號,每個設備號又分為主設備號和次設備號。主設備號用來區分不同種類的設備,而次設備號用來區分同一類型的多個設備。對於常用設備,Linux有約定俗成的編號,如硬碟主設備號是3 
   Unix / Linux的特點之一,是為所有的檔,包括設備檔,提供了統一的操作函數介面,定義如下: 
   struct file_operations {
   struct module *owner;
   loff_t (*llseek) (struct file *, loff_t, int);
   ssize_t (*read) (struct file *, char *, size_t, loff_t *);
   ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
   int (*readdir) (struct file *, void *, filldir_t);
   unsigned int (*poll) (struct file *, struct poll_table_struct *);
   int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
   int (*mmap) (struct file *, struct vm_area_struct *);
   int (*open) (struct inode *, struct file *);
   int (*flush) (struct file *);

   int (*release) (struct inode *, struct file *);
   int (*fsync) (struct file *, struct dentry *, int datasync);
   int (*fasync) (int, struct file *, int);
   int (*lock) (struct file *, int, struct file_lock *);
   ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
   ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
   ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
   unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
   };
   結構體中的成員為一系列的介面函數,如用於讀/寫的read/ write函數,用於控制的ioctl等。打開一個檔就是調用這個檔file_operations中的open操作。不同類型的檔有不同的file_operations成員函數。如普通的磁片資料檔案,介面函數完成磁片資料塊讀寫操作;而對於各種設備檔,則最終調用各自驅動程式中的I/O函數進行具體設備的操作。這樣,應用程式根本不用考慮操作的是設備還是普通檔,可一律當作檔處理,具有非常清晰統一的I/O介面。所以file_operations是檔層次的I/O介面。 
   但是,由於外設的種類繁多,操作方式也各不相同。如聲音設備驅動要使用DMA通道,顯示設備驅動要提供對顯存的操作,硬碟驅動要處理複雜的緩衝區結構,網路設備驅動和socket聯繫緊密。如果file_operations中的函數都讓驅動程式的開發人員來寫,則就要處理大量的細節,幾乎是不可能的。為了解決設備多樣性的問題,Linux採用了特殊情況特殊處理的辦法,為不同設備定義好了檔層次file_operations結構中的介面函數,其中處理了大多數設備相關的操作,如各種緩衝區的申請和釋放等等,而具體操作底層硬體的一小部分則留給開發人員。所以Linux另外提供一個檔層到底層驅動程式的介面,通常為一個結構體,其中包含成員變數和函數指標。不同的設備驅動有不同的結構體。這樣,一方面保證了檔層I/O介面file_operations的一致性,另一方面驅動程式的開發人員也不用瞭解太多細節,只專注於硬體相關的I/O操作就可以了。例如,一個有代表性的特殊設備是聲音設備,其檔層的file_operations定義如下: 

 struct file_operations oss_sound_fops = {
   owner: THIS_MODULE,
   llseek: sound_lseek,
   read: sound_read,
   write: sound_write,
   poll: sound_poll,
   ioctl: sound_ioctl,
   mmap: sound_mmap,
   open: sound_open,
   release: sound_release,
   };
   其中的sound_readsound_write等函數Linux都已提供,處理了與聲音設備相關的許多細節,如DMA的申請,釋放和操作等。而檔層到驅動程式的介面為audio_driver結構,其中包含底層操作函數。檔層的sound_readsound_write會在需要時調用audio_driver中的函數。開發人員只要編寫audio_driver中的函數就可以了。這樣,最大程度地減小了驅動開發的工作量。下面我們將看到,Linux為顯示設備提供的幀緩衝驅動也是這種“檔層 - 驅動層”的介面方式。 
   
   Linux的幀緩衝設備 
   幀緩衝(framebuffer)Linux為顯示設備提供的一個介面,把顯存抽象後的一種設備,他允許上層應用程式在圖形模式下直接對顯示緩衝區進行讀寫操作。這種操作是抽象的,統一的。用戶不必關心物理顯存的位置、換頁機制等等具體細節。這些都是由Framebuffer設備驅動來完成的。幀緩衝驅動的應用廣泛,在Linux的桌面系統中,Xwindow伺服器就是利用幀緩衝進行視窗的繪製。尤其是通過幀緩衝可顯示漢字點陣,成為Linux漢化的唯一可行方案。 
   幀緩衝設備對應的設備檔為/dev/fb*,如果系統有多個顯示卡,Linux下還可支援多個幀緩衝設備,最多可達32個,分別為/dev/fb0/dev/fb31,而/dev/fb則為當前缺省的幀緩衝設備,通常指向/dev/fb0

當然在嵌入式系統中支援一個顯示設備就夠了。幀緩衝設備為標準字元設備,主設備號為29,次設備號則從031。分別對應/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 將螢幕緩衝區映射到用戶空間。 
   4 映射後就可以直接讀寫螢幕緩衝區,進行繪圖和圖片顯示了。 
   典型程式段如下:

 #include
   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指針讀寫緩衝區*/
   …… 
   }
   
   幀緩衝驅動的編寫 
   幀緩衝設備屬於字元設備,與聲音設備一樣,也採用“檔層-驅動層”的介面方式。在檔層次上,Linux為其定義了:

 static struct file_operations fb_fops = {
   owner: THIS_MODULE,
   read: fb_read, /* 讀操作 */
   write: fb_write, /* 寫操作 */
   ioctl: fb_ioctl, /* 控制操作 */
   mmap: fb_mmap, /* 映射操作 */
   open: fb_open, /* 打開操作 */
   release: fb_release, /*關閉操作*/
   };
   其中的成員函數都在檔linux/driver/video/fbmem.c中定義。 
   由於顯示設備的特殊性,在驅動層的介面中不但要包含底層函數,還要有一些記錄設備狀態的資料。Linux為幀緩衝設備定義的驅動層介面為struct fb_info結構,在include/linux/fb.h中定義。這個結構比較長,限於篇幅,文章中就不全部列出了。幸運的是,嵌入式系統要求的顯示操作比較簡單,只涉及到結構中少數幾個成員,下面只對編寫驅動中要用到的幾個關鍵成員作一說明。 
   fb_info中紀錄了幀緩衝設備的全部資訊,包括設備的設置參數,狀態以及操作函數指標。每一個幀緩衝設備都必須對應一個fb_info結構。其中成員變數modename為設備名稱,fontname為顯示字體,fbops為指向底層操作的函數的指標,這些函數是需要驅動程式開發人員編寫的。成員fb_var_screeninfo fb_fix_screeninfo也是結構體。其中fb_var_screeninfo記錄用戶可修改的顯示控制器參數,包括螢幕解析度和每個圖元點的比特數。fb_var_screeninfo中的xres定義螢幕一行有多少個點, yres定義螢幕一列有多少個點, bits_per_pixel定義每個點用多少個位元組表示。而fb_fix_screeninfo中記錄用戶不能修改的顯示控制器的參數,如螢幕緩衝區的物理位址,長度。當對幀緩衝設備進行映射操作的時候,就是從fb_fix_screeninfo中取得緩衝區物理位址的。上面所說的資料成員都是需要在驅動程式中初始化和設置的。 

 在瞭解了上面所述的概念後,編寫幀緩衝驅動的實際工作並不複雜,需要做的工作是: 
   1 編寫初始化函數:初始化函數首先初始化LCD控制器,通過寫寄存器設置顯示模式和顯示顏色數,然後分配LCD顯示緩衝區。在Linux可通過kmalloc函數分配一片連續的空間。筆者採用的LCD顯示方式為240x32016位元彩色。需要分配的顯示緩衝區為240x320x2 = 150k位元組,緩衝區通常分配在大容量的片外SDRAM中,起始位址保存在LCD控制器寄存器中。 
   最後是初始化一個fb_info結構,填充其中的成員變數,並調用register_framebuffer(&fb_info),將fb_info登記入內核。 
   2 編寫結構fb_info中函數指標fb_ops對應的成員函數:對於嵌入式系統的簡單實現,只需要下列三個函數就可以了: 
   struct fb_ops {
   …… 
   int (*fb_get_fix)(struct fb_fix_screeninfo *fix, int con, struct fb_info *info);
   int (*fb_get_var)(struct fb_var_screeninfo *var, int con, struct fb_info *info);
   int (*fb_set_var)(struct fb_var_screeninfo *var, int con,struct fb_info *info);
   …… 
   };
   struct fb_opsinclude/linux/fb.h中定義。這些函數都是用來設置/獲取fb_info結構中的成員變數的。當應用程式對設備檔進行Ioctl操作時候會調用它們,讀者可參考前文中的應用程式例子。例如,對於fb_get_fix(),應用程式傳入的是fb_fix_screeninfo結構,在函數中對其成員變數賦值,主要是smem_start(緩衝區起始地址)smem_len(緩衝區長度),最終返回給應用程式。而fb_set_var()函數的傳入參數是fb_var_screeninfo,函數中需要對xresyres,bits_per_pixel賦值。 
   驅動程式編寫完成後,開發者可選擇將其編譯為動態載入模組,或靜態地編譯入內核中。由於篇幅所限,有關這方面的內容請讀者參考相關驅動程式文檔。

結語 
   由於篇幅所限,本文中僅對幀緩衝設備驅動的基本原理和框架做了簡單介紹。幸運的是,在Linux的發佈版本中,包含了大量的設備驅動程式源代碼,其中drvers/video下提供了多種顯示卡的幀緩衝設備驅動程式程式,用戶自己的驅動程式可參考成熟的代碼編寫或直接修改得到。

';$(".articleExtAd").append(notVIP);setTimeout(function() {$('.top-toolbar').data('top-toolbar').setAD({title: "\u5d4c\u5165\u5f0fLinux\u4e0b\u5f69\u8272LCD\u9a45\u52d5\u7684\u8a2d\u8a08\u8207\u5be6\u73fe",label_id: 163,label_name: "\u96fb\u8166\u786c\u9ad4"});}, 2000);

jojo / Xuite日誌 / 回應(0) / 引用(1) / 好文轉寄
分析list_head結構&建...|日誌首頁|基於Windows CE的車載...上一篇分析list_head結構&建立雙向鏈表的一種常見方法...下一篇基於Windows CE的車載電腦系統人機界面的實現...
回應








離婚證人

台北離婚證人新竹離婚證人彰化離婚證人高雄離婚見證人







遺囑見證人結婚證人

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 hemangae3gya 的頭像
    hemangae3gya

    封裝測試

    hemangae3gya 發表在 痞客邦 留言(0) 人氣()