《電子技術(shù)應(yīng)用》
您所在的位置:首頁 > 嵌入式技術(shù) > 設(shè)計(jì)應(yīng)用 > 基于S3C6410平臺的嵌入式Linux系統(tǒng)LCD驅(qū)動模塊
基于S3C6410平臺的嵌入式Linux系統(tǒng)LCD驅(qū)動模塊
來源:微型機(jī)與應(yīng)用2013年第13期
黃相平,余水寶,夏 燦
(浙江師范大學(xué) 數(shù)理與信息工程學(xué)院,浙江 金華 321004)
摘要: 以S3C6410處理器為核心,采用嵌入式Linux操作系統(tǒng),通過幀緩沖設(shè)備的驅(qū)動方式,實(shí)現(xiàn)了TFT液晶顯示器的驅(qū)動程序。經(jīng)過測試字體的放大與旋轉(zhuǎn)及圖形的顯示,證明該驅(qū)動程序穩(wěn)定,具有很高的實(shí)用價(jià)值。
Abstract:
Key words :

摘  要: 以S3C6410處理器為核心,采用嵌入式Linux操作系統(tǒng),通過幀緩沖設(shè)備的驅(qū)動方式,實(shí)現(xiàn)了TFT液晶顯示器的驅(qū)動程序。經(jīng)過測試字體的放大與旋轉(zhuǎn)及圖形的顯示,證明該驅(qū)動程序穩(wěn)定,具有很高的實(shí)用價(jià)值。
關(guān)鍵詞: 嵌入式Linux;幀緩沖;TFT驅(qū)動程序;Platform機(jī)制

 由于Linux具有開放源碼、易于移植、內(nèi)核可裁剪、資源豐富、免費(fèi)等優(yōu)點(diǎn),使它在嵌入式領(lǐng)域越來越流行。隨著手持設(shè)備的與日俱增,人機(jī)交互界面向著更方便、更直觀的方向發(fā)展。觸摸屏的應(yīng)用讓數(shù)據(jù)的顯示和輸入結(jié)合為一體,使得人機(jī)交互界面更簡單、友好,已經(jīng)廣泛應(yīng)用于嵌入式系統(tǒng)當(dāng)中。在實(shí)際工程中,LCD驅(qū)動與觸摸驅(qū)動是分開的,所以本文只涉及LCD驅(qū)動。由于ARM核廣泛地應(yīng)用于嵌入式系統(tǒng)中,所以選用的是基于ARM1176JZF-S核的S3C6410 CPU,其頻率可以達(dá)到667 MHz,對多媒體的處理速度很快。
液晶顯示器相對于傳統(tǒng)的CRT有很多優(yōu)點(diǎn):輕薄、能耗低、輻射少等,市場的占有率也越來越大。LCD有多種類型,比如TN、STN、TFT、LTPS等[1]。TFT型LCD中,晶體管矩陣依顯示信號開啟或關(guān)閉液晶分子的電壓,使液晶分子軸轉(zhuǎn)向而成“亮”或“暗”的對比,避免了TN與STN依靠電場效應(yīng)而造成響應(yīng)時(shí)間長的缺點(diǎn),而且播放動畫或視頻不會有STN的拖影現(xiàn)象[2]。背光燈的應(yīng)用,使其在日光下也能顯示圖像,應(yīng)用廣泛。LTPS是TFT衍生的新一代技術(shù),價(jià)格較貴,所以這里是關(guān)于TFT液晶顯示器的驅(qū)動。
1 設(shè)備參數(shù)及硬件連接
1.1 TFT液晶顯示屏參數(shù)

 本文選用的是萬鑫的4.3寸的TFT真彩屏,分辨率為480×272,數(shù)據(jù)格式采用24 bit RGB接口模式,其主要參數(shù)如表1所示,參數(shù)名后面跟的英文字符串表示在程序中使用的宏。

1.2 TFT與S3C6410的硬件連接
 一塊LCD屏顯示圖像不僅需要LCD驅(qū)動器,還需要LCD控制器。通常LCD驅(qū)動器會與LCD玻璃基板制作在一起,而S3C6410內(nèi)部集成了一個(gè)LCD控制器,所以LCD與6410之間可以直接連接。圖1顯示了LCD液晶顯示器與LCD插槽之間的連接,圖2顯示了LCD插槽與S3C6410之間的連接。VD[0:23]是GRB數(shù)據(jù)線,VDD_LCD接的是LCD的電源,I2CSDA0背光,VDEN表示數(shù)據(jù)可以傳輸顏色數(shù)據(jù),其他的與表1的名稱對應(yīng),其中HSYNC、VSYNC、VDEN、VCLK分別與LHSYNC、LVSYNC、LVDEN、LVCLK連接。

2 Platform總線下的LCD驅(qū)動
2.1 Platform總線簡介

 在Linux2.6后的設(shè)備驅(qū)動模型中,總線、設(shè)備和驅(qū)動這三個(gè)實(shí)體稱之為總線設(shè)備驅(qū)動模型,即為同類的設(shè)備設(shè)計(jì)了一個(gè)框架,而框架的核心層實(shí)現(xiàn)了該類設(shè)備通用的一些功能,而不用程序員自己再去實(shí)現(xiàn);驅(qū)動與設(shè)備相分離,使得編寫主機(jī)控制器驅(qū)動和外設(shè)驅(qū)動并行,它們之間不再互相關(guān)聯(lián),實(shí)現(xiàn)分層與分離的思想,從而提高驅(qū)動的可移植性??偩€將設(shè)備和驅(qū)動綁定。在系統(tǒng)每注冊一個(gè)設(shè)備的時(shí)候,會尋找與之匹配的驅(qū)動;同樣地,在注冊一個(gè)驅(qū)動的時(shí)候,會尋找與之匹配的設(shè)備。匹配是通過比較設(shè)備和驅(qū)動中的名字來確定,匹配工作由總線完成;一般情況下,驅(qū)動會使用設(shè)備的資源,所以設(shè)備常常要比驅(qū)動先注冊,而且設(shè)備一般會放在板文件中。
 在嵌入式系統(tǒng)中,SoC系統(tǒng)中集成的獨(dú)立的外設(shè)控制器,掛接在SoC內(nèi)存空間的外設(shè)不依附于任何總線。在這種情形下,Linux發(fā)明了一種虛擬的總線,就是Platform總線,相應(yīng)的設(shè)備稱為platform_device,而驅(qū)動為platform_driver。而LCD正是掛接在Platform總線下的一個(gè)驅(qū)動,LCD驅(qū)動先構(gòu)造一個(gè)platform_device結(jié)構(gòu)體,然后注冊;接著LCD驅(qū)動構(gòu)造一個(gè)platform_driver結(jié)構(gòu)體,并注意platform_driver中的名字要與platform_device中的名字相同,這樣當(dāng)注冊platform_driver時(shí)就能夠匹配設(shè)備,Platform總線就會調(diào)用platform_driver中的probe函數(shù),這個(gè)函數(shù)是LCD驅(qū)動真正注冊的函數(shù);同理,卸載函數(shù)為platform_driver中的remove函數(shù)。
2.2 LCD驅(qū)動的platform_device與platform_driver的注冊
2.2.1 分配初始化一個(gè)platform_device結(jié)構(gòu)體

 使用platform_dev_lcd43=platform_device_alloc("lcd43",-1)分配并設(shè)置一個(gè)platform_device,第一個(gè)參數(shù)就是platform_device的名字,第二個(gè)參數(shù)為-1表示設(shè)備只有一個(gè)。在相應(yīng)的移除函數(shù)中調(diào)用platform_device_release(platform_dev_lcd43)函數(shù)釋放分配的內(nèi)存,這兩個(gè)函數(shù)都是內(nèi)核自帶的函數(shù)。下文中,如果沒有特別介紹,都是內(nèi)核自帶的函數(shù)。在這里,為了避免在platform_driver的注冊中引入太多的內(nèi)核函數(shù),并沒有把資源放入platform_device中,而是要用時(shí)再在platform_driver中實(shí)現(xiàn),這里只是簡單地演示實(shí)際工程中這種platform_device結(jié)構(gòu)的使用;實(shí)際工作中,只需把platform_driver的資源放入platform_device的resource中,然后調(diào)用platform_get_resource()函數(shù)來取得相應(yīng)的資源就可以了。
 接下來調(diào)用platform_device_register(platform_dev_ lcd43)注冊platform_device,在相應(yīng)的移除函數(shù)中調(diào)用platform_ device_unregister(platform_dev_lcd43)。
2.2.2 定義初始化一個(gè)platform_driver結(jié)構(gòu)體
 platform_driver結(jié)構(gòu)體就是LCD驅(qū)動的結(jié)構(gòu)體,把platform_driver中的成員初始化注冊后,內(nèi)核就可以調(diào)用這個(gè)驅(qū)動程序了。
 static struct platform_driver platform_lcd43_driver={
 .probe=lcd43_probe,
 .remove=lcd43_remove,
 .driver={
   .name="lcd43",
   .owner=THIS_MODULE,
 /*內(nèi)核的宏,表示當(dāng)前模塊*/
    }
 };
 接下來調(diào)用platform_driver_register(&platform_lcd43 _driver)注冊platform_driver結(jié)構(gòu)體。
相應(yīng)的移除函數(shù)為platform_driver_unregister(&platform_lcd43_driver)。當(dāng)調(diào)用platform_driver_register()函數(shù)后,內(nèi)核就自動的去找跟它相匹配的platform_device,如果找到,就調(diào)用platform_driver的probe函數(shù)。
3 LCD驅(qū)動的編寫
3.1 幀緩沖的概念

 幀緩沖是Linux系統(tǒng)為顯示設(shè)備提供的一個(gè)接口,它將顯示緩沖區(qū)進(jìn)行抽象,屏蔽了圖像硬件的底層差異,允許上層應(yīng)用程序在圖形模式下直接對緩沖區(qū)進(jìn)行讀寫。用戶只要在顯示緩沖區(qū)中與顯示點(diǎn)對應(yīng)的區(qū)域?qū)懭腩伾?,對?yīng)的顏色會自動在屏幕上以對應(yīng)的點(diǎn)顯示。幀緩沖驅(qū)動的應(yīng)用非常廣泛,Qt/Embedded、X Window、MiniGUI都是利用幀緩沖進(jìn)行圖像的繪制。LCD驅(qū)動程序的設(shè)計(jì)就是寫一個(gè)幀緩沖驅(qū)動程序,它的主設(shè)備號為29,當(dāng)注冊一個(gè)幀緩沖設(shè)備時(shí),會自動地在/dev目錄下創(chuàng)建一個(gè)fbn設(shè)備文件,n為注冊前系統(tǒng)幀緩沖設(shè)備的個(gè)數(shù),當(dāng)系統(tǒng)中沒有幀緩沖設(shè)備時(shí),n為0。
3.2 幀緩沖設(shè)備probe函數(shù)的編寫
3.2.1 幀緩沖結(jié)構(gòu)體的初始化

 幀緩沖設(shè)備驅(qū)動的編寫工作主要集中在platform_driver中的probe函數(shù)的編寫,probe()函數(shù)完成LCD驅(qū)動程序真正要做的事情,它需要完成:
 (1)申請一個(gè)struct fb_info結(jié)構(gòu)體,根據(jù)LCD屏參數(shù)并初始化。幀緩沖設(shè)備最關(guān)鍵的一個(gè)數(shù)據(jù)結(jié)構(gòu)體是fb_info結(jié)構(gòu)體,它包含幀緩沖設(shè)備屬性和操作的完整描述。
fb43_info=framebuffer_alloc(0,NULL);
/*申請一個(gè)struct fb_info結(jié)構(gòu)體*/
fb43_info->screen_size=HOZVAL*LINEVAL*4;
/*設(shè)置幀緩沖區(qū)的大小*/
fb_info結(jié)構(gòu)體成員fb_fix_screeninfo結(jié)構(gòu)的初始化,fb_fix_screeninfo記錄用戶不可修改的顯示器的參數(shù),如屏幕大小、緩沖區(qū)地址等。
strcpy(fix43->id,"lcd_4.3");/*設(shè)置名字*/
fix43->line_length=HOZVAL*4;
/*一行像素占的字節(jié),1像素4字節(jié),高8位丟棄*/
fix43->smem_len=LINEVAL*HOZVAL*4;
/*設(shè)置幀緩沖內(nèi)存的大小*/
fix43->type=FB_TYPE_PACKED_PIXELS;
/*設(shè)置LCD的型號*/
fix43->visual=FB_VISUAL_TRUECOLOR;
/*設(shè)置顯示為真彩色*/
fb_info結(jié)構(gòu)體成員fb_var_screeninfo結(jié)構(gòu)的初始化,fb_var_screeninfo記錄用戶可以更改的顯示器參數(shù),包括屏幕分辨率和每像素點(diǎn)的比特?cái)?shù)等。
var43->xres=HOZVAL;
/*x方向?qū)嶋H像素個(gè)數(shù),表1中的數(shù)值*/
var43->yres=LINEVAL;
/*y方向?qū)嶋H像素個(gè)數(shù),表1中的數(shù)值*/
var43->xres_virtual=HOZVAL;
/*x方向虛擬像素個(gè)數(shù),沒實(shí)現(xiàn)虛擬*/
var43->yres_virtual=LINEVAL;
/*y方向虛擬像素個(gè)數(shù),沒實(shí)現(xiàn)虛擬*/
var43->xoffset=0;/*x方向?qū)嶋H與虛擬間的偏移像素*/
var43->yoffset=0;/*y方向?qū)嶋H與虛擬間的偏移像素*/
var43->bits_per_pixel=32;
/*每像素32位表示,實(shí)際只是24位的RGB*/
var43->red.length=8;/*紅占8位*/
var43->red.offset=16;/*紅偏移16位*/
var43->green.length=8;/*綠占8位*/
var43->green.offset=8;/*綠偏移8位*/
var43->blue.length=8;/*藍(lán)占8位*/
var43->blue.offset=0;/*藍(lán)偏移0位*/
var43->hsync_len=HSPW;
/*以下都是表1中的數(shù)據(jù),硬件相關(guān)*/
var43->vsync_len=VSPW;
var43->right_margin=HBPD;
var43->left_margin=HFPD;
var43->lower_margin=VBPD;
var43->upper_margin=VFPD;
clk43=clk_get(NULL,"hclk");
hclkval=clk_get_rate(clk43);
/*獲得系統(tǒng)的HCLK,LCD的時(shí)鐘由它分頻得到*/
var43->pixclock=hclkval/(hclkval/9000000);
/*設(shè)置像素時(shí)鐘,注意C語言會對整型變量除法運(yùn)算*/
?。?)完成S3C6410的LCD控制器的初始化
 S3C6410自帶LCD控制器,只要把LCD控制寄存器設(shè)置合適的參數(shù),S3C6410會自動發(fā)出控制LCD的時(shí)序,而不需要程序員再去關(guān)心時(shí)序的問題。這部分的設(shè)置大都是硬件相關(guān),不同的CPU設(shè)置一般不會一樣。在Linux系統(tǒng)中,CPU不能直接使用物理地址,所以需要先經(jīng)過ioremap()函數(shù)建立新頁表,使物理地址映射到邏輯地址[3]。后文中出現(xiàn)的與具體寄存器相關(guān)的指針變量都是經(jīng)過ioremap()重新映射后的地址。
mifpcon=ioremap(0x7410800C,4);
spcon=ioremap(0x7F0081A0,4);
*mifpcon&=~(1<<3);
*spcon&=~(3);
*spcon|=1;
 根據(jù)S3C6410手冊要求的設(shè)置,要使用LCD控制器,必須把MOFPCON寄存器的第三位置零,SPCON[1:0]位要為“01”,表示為RGB模式。
 regs43->vidcon0=((hclkval/9000000-1)<<6)|(1<<4);
/*設(shè)置時(shí)鐘源及分頻系數(shù)*/
regs43->vidcon1=(1<<6)|(1<<5);
/*因?yàn)镾3C6410的行列同步脈沖與TFT屏的相反,所以要設(shè)置這兩個(gè)脈沖反轉(zhuǎn),要根據(jù)具體的TFT屏芯片手冊設(shè)置*/
/*設(shè)置垂直延時(shí)參數(shù),LCD硬件相關(guān),見表1內(nèi)容*/
regs43->vidtcon0=((VBPD-1)<<16)|((VFPD-1)<<8)|(VSPW-1)<<0;
/*設(shè)置垂直延時(shí)參數(shù),LCD硬件相關(guān),見表1內(nèi)容*/
regs43->vidtcon1=((HBPD-1)<<16)|((HFPD-1)<<8)|(HSPW-1)<<0;
regs43->vidtcon2=((LINEVAL-1)<<11)|((HOZVAL-1)<<0);/*設(shè)置行與列的分辨率*/
regs43->wincon0&=~(0xf<<2);
regs43->wincon0|=(0xb<<2);
/*設(shè)置為24位RGB格式*/
regs43->vidosd0a=(0<<11)|(0<<0);
/*設(shè)置左上角開始值,不留黑框*/
regs43->vidosd0b=((HOZVAL-1)<<11)|((LINEVAL-1)<<0);/*設(shè)置右下角位置*/
regs43->vidosd0c=HOZVAL*LINEVAL;
/*設(shè)置顯示框的大小,以字為單位*/
regs43->vidw00add0b0=fb43_info->fix.smem_start;
/*設(shè)置物理地址*/
regs43->vidw00add1b0=(fb43_info->fix.smem_start+LINEVAL*HOZVAL*4)&0xffffff;
/*設(shè)置幀緩沖內(nèi)存結(jié)束地址的低24位*/
?。?)分配幀緩沖區(qū)的內(nèi)存,同時(shí)設(shè)置幀緩沖區(qū)的虛擬及物理地址。
fb43_info->screen_base=dma_alloc_writecombine(NULL,fb43_info->screen_size,
&fb43_info->fix.smem_start,GFP_KERNEL);
/*設(shè)置幀緩沖區(qū)內(nèi)存,虛擬,物理地址*/
 分配幀緩沖內(nèi)存,設(shè)置固定參數(shù)的物理地址以及fb_nfo結(jié)構(gòu)的虛擬地址,第二個(gè)參數(shù)為幀緩沖區(qū)的大小,第三個(gè)參數(shù)會返回申請的幀緩沖區(qū)的開始物理地址,設(shè)置為在固定參數(shù)中沒有設(shè)置的物理地址,函數(shù)返回值為幀緩沖區(qū)的虛擬地址,最后的一個(gè)參數(shù)為分配內(nèi)存的標(biāo)志,設(shè)置為最常用的GFP_KERNEL。
 (4)開LCD時(shí)鐘、電源、背光,并注冊幀緩沖設(shè)備驅(qū)動。函數(shù)定義如下:
clk_enable_43lcd();
/*打開LCD的時(shí)鐘,因?yàn)镾3C6410可以關(guān)閉一些外設(shè)來省電*/
get_43lcd_ctr(fb43_info,hclkval);
/*完成S3C6410的LCD控制器的初始化*/
fb43_info->fbops=&fb43_ops;
/*設(shè)置幀緩沖的操作函數(shù)*/
fb43_info->pseudo_palette=&fb43_pseudo_palette;
/*設(shè)置調(diào)色板*/
enable_43lcd();/*開LCD電源、背光*/
3.2.2 設(shè)置LCD與S3C6410的連接設(shè)置

 


 從連接圖可以看出,LCD的數(shù)據(jù)線主要是在通用輸入輸出I口和J口[4]。由6410的芯片手冊如圖3所示,I口與J口基本一樣。只需要把它們的控制寄存器設(shè)置為LCD模式即可,這時(shí)S3C6410會根據(jù)LCD控制器的一些數(shù)據(jù),在恰當(dāng)?shù)臅r(shí)間發(fā)出顏色數(shù)據(jù)。設(shè)置如下:
 *gpicon=0xaaaaaaaa;
 *gpjcon=0xaaaaaaaa;

3.2.3 開LCD電源及背光,注冊幀緩沖設(shè)備
*gpbcon&=~(0xf<<24);
*gpbcon|=(0x1<<24);
/*設(shè)置GPB的第6位為輸出模式*/
*gpbdat|=(1<<6);/*開啟背光*/
*gpfcon&=~(3<<28);
*gpfcon|=(1<<28);
/*設(shè)置GPF的第14位為輸出模式*/
*gpfdat|=(1<<14);/*開啟LCD電源*/
regs43->vidcon0|=3;/*開LCD顯示*/
regs43->wincon0|=1;
/*開窗口0顯示,S3C6410可以同時(shí)顯示5個(gè)窗口,本文選擇的是窗口0*/
register_framebuffer(fb43_info);/*注冊幀緩沖設(shè)備*/
3.2.4 填充fb_ops結(jié)構(gòu)體
 struct fb_info結(jié)構(gòu)中的fb_ops結(jié)構(gòu)體是操縱幀緩沖設(shè)備的一些函數(shù)的集合,系統(tǒng)調(diào)用最后通過它們與LCD控制器硬件打交道。對于其中的fb_fillrect()、fb_copyarea()以及fb_imageblit()成員直接使用通用的cfb_fillrect()、cfb_copyarea()以及cfb_imageblit()就可以了。只需要設(shè)置以下幾個(gè)成員,其他的都用系統(tǒng)默認(rèn)的函數(shù),LCD驅(qū)動就能正確的工作了。
struct fb_ops fb43_ops={
  .owner=THIS_MODULE,/*表示為當(dāng)前模塊*/
  .fb_fillrect=cfb_fillrect,
  .fb_copyarea=cfb_copyarea,
  .fb_imageblit=cfb_imageblit,
  .fb_setcolreg=fb43_setcolreg,
};
 fb_setcolreg成員是實(shí)現(xiàn)偽顏色表和顏色表的填充,雖然使用24 bit RGB顏色格式不需要偽顏色表,但不實(shí)現(xiàn)它會出現(xiàn)一些問題。使用偽顏色表(也稱調(diào)色板)時(shí),發(fā)出的顏色值并不是真正要顯示的值,而是在一個(gè)顏色表中的索引。當(dāng)使用大于16 bit的顏色表時(shí),顏色表占據(jù)的內(nèi)存過大,所以使用它不劃算。它的設(shè)置如下,分別取出RGB的索引值,取其低16 bit,接著根據(jù)當(dāng)前顏色在RGB格式中的偏移值,把其他顏色位置零,最后再把取出的3種顏色合成一個(gè)顏色值。設(shè)置如下:
 colorg&=0xffff;
 colorg>>=16-bf->length;/*bf->length是顏色占據(jù)的長度,假如是16位RGB:5:6:5格式,紅色就占據(jù)最高的5位,所以低11位的值不要*/
 colorg<<bf->offset;
 /*如上,紅色占最高5位,所以要右移11位*/
 ……./*設(shè)置其他兩種顏色值*/
 return colorr|colorg|colorb;
4 LCD驅(qū)動的測試
 用arm-linux-gcc在交叉編譯平臺上編譯后,把生成的模塊文件lcd.ko用insmod加載到S3C6410開發(fā)板。把編譯好的測試文件也拷入到開發(fā)板的文件系統(tǒng)中,運(yùn)行測試文件[5],如圖4所示用freetype字形引擎顯示的放大及旋轉(zhuǎn)的文字,字體大小為40像素,旋轉(zhuǎn)45°角。如圖5所示為libjpeg庫顯示的圖片,顯示的圖片為圖4縮小到1/8時(shí)的情形(圖形上邊沒有顯示是因?yàn)閘ibjpeg庫只能把圖片縮小為1/2、1/4、1/8)??梢钥匆奓CD都能正常工作。

 本文詳述了一種TFT液晶顯示屏在嵌入式Linux平臺開發(fā)的方法,實(shí)現(xiàn)了其字體及圖形的穩(wěn)定顯示,證明了該方法能夠在一定條件下,LCD顯示性能的滿足。
參考文獻(xiàn)
[1] 杜春雷.ARM體系結(jié)構(gòu)與編程[M].北京:清華大學(xué)出版社,2003.
[2] 宋寶華.Linux設(shè)備驅(qū)動開發(fā)詳解(第2版)[M].北京:人民郵電出版社,2010.
[3] JONATHAN CORBET.Linux.設(shè)備驅(qū)動程序(第3版)[M].北京:中國電力出版社,2006.
[4] STEVENS W R.Unix環(huán)境高級編程(第2版)[M].北京:人民郵電出版社,2006.
[5] 韋東山.嵌入式Linux應(yīng)用開發(fā)完全手冊[M].北京:人民郵電出版社,2008.

此內(nèi)容為AET網(wǎng)站原創(chuàng),未經(jīng)授權(quán)禁止轉(zhuǎn)載。