嵌入式系統中LCD驅動的實現原理
From :http://www.laogu.com/wz_18338.htm
來源:今日電子
作者:四川大學 楊顯強 田遠富
詳細內容:
結合三星公司ARM9系列嵌入式處理器S3C2410,講解如何進行LCD驅動程式模組化編程及如何將驅動程式靜態載入進系統核心。
本文以三星公司ARM9核心晶片S3C2410的LCD介面為基礎,介紹了在Linux平台上開發嵌入式LCD驅動程式的一般方法。
本文硬體採用三星公司的S3C2410晶片的開發板,軟體採用Linux 2.4.19平台,編譯器為arm-linux-gcc的交叉編譯器,使用640×480解析度的TFT彩色LCD,通過對其Linux驅動程式進行改寫和調試,成功地實現了對該種屏的驅動和顯示。
嵌入式驅動的概念
裝置驅動程式是作業系統核心和機器硬體之間的介面,裝置驅動程式為應用程式屏蔽了硬體的細節,這樣在應用程式看來,硬體裝置只是一個裝置檔案,應用程式可以像操作普通檔案一樣對硬體裝置進行操作。裝置驅動程式是核心的一部分,它主要完成的功能有:對裝置進行初始化和釋放;把資料從核心傳送到硬體和從硬體讀取資料;讀取應用程式傳送給裝置檔案的資料、回送應用程式請求的資料以及檢測和處理裝置出現的錯誤。
Linux 將裝置分為最基本的兩大類:一類是字元裝置,另一類是塊裝置。字元裝置和塊裝置的主要區別是:在對字元裝置發出讀/寫請求時,實際的硬體I/O一般就緊接著發生了。字元裝置以單個位元組為單位進行順序讀寫操作,通常不使用緩衝技術;塊裝置則是以固定大小的資料區塊進行儲存和讀寫的,如硬碟、磁碟片等,並利用一塊系統記憶體作為緩衝區。為提高效率,系統對於塊裝置的讀寫提供了緩衝機制,由於涉及緩衝區管理、調度和同步等問題,實現起來比字元裝置複雜得多。LCD是以字元裝置方式加以訪問和管理的,Linux把顯示驅動看做字元裝置,把要顯示的資料一位元組一位元組地送往LCD磁碟機。
Linux 的裝置管理是和檔案系統緊密結合的,各種裝置都以檔案的形式存放在/dev目錄下,稱為裝置檔案。應用程式可以開啟、關閉和讀寫這些裝置檔案,完成對裝置的操作,就像操作普通的資料檔案一樣。為了管理這些裝置,系統為裝置編了號,每個裝置號又分為主裝置號和次裝置號。主裝置號用來區分不同種類的裝置,而次裝置號用來區分同一類型的多個裝置。對於常用裝置,Linux有約定俗成的編號,如硬碟的主裝置號是3。Linux為所有的裝置檔案都提供了統一的操作函數介面,方法是使用資料結構struct file_operations。這個資料結構中包括許多操作函數的指標,如open()、close()、read()和write()等,但由於外設的種類較多,操作方式各不相同。Struct file_operations結構體中的成員為一系列的介面函數,如用於讀/寫的read/write函數和用於控制的ioctl等。開啟一個檔案就是調用這個檔案file_operations中的open操作。不同類型的檔案有不同的file_operations成員函數,如普通的磁碟資料檔案, 介面函數完成磁碟資料區塊讀寫操作;而對於各種裝置檔案,則最終調用各自驅動程式中的I/O函數進行具體裝置的操作。這樣,應用程式根本不必考慮操作的是裝置還是普通檔案,可一律當作檔案處理,具有非常清晰統一的I/O介面。所以file_operations是檔案層次的I/O介面。
LCD控制器
LCD 控制器的功能是顯示驅動訊號,進而驅動LCD。使用者只需要通過讀寫一系列的寄存器,完成配置和顯示驅動。在驅動LCD設計的過程中首要的是配置LCD控制器,而在配置LCD控制器中最重要的一步則是框架緩衝區(FrameBuffer)的指定。使用者所要顯示的內容皆是從緩衝區中讀出,從而顯示到螢幕上的。框架緩衝區的大小由螢幕的解析度和顯示色彩數決定。驅動幀緩衝的實現是整個驅動開發過程的重點。S3C2410中的LCD控制器可支援STN和TFT兩種液晶。對於STN 液晶平板,該LCD控制器可支援4位雙掃描、4位單掃描和8位單掃描三種顯示類型,支援4級和16級灰階級單色顯示模式,支援256色和4096色顯示, 可接多種解析度的LCD,例如640×480、320×240和160×160等,在256色顯示模式時,最大可支援4096×1024、2048× 2048和1024×4096顯示。TFT液晶平板可支援1-2-4-8bpp(bits per pixel)調色盤顯示模式和16bpp非調色盤真彩顯示。
框架緩衝區是出現在Linux 2.2.xx及以後版本核心當中的一種驅動程式介面,這種介面將顯示裝置抽象為框架緩衝區裝置區。框架緩衝區為映像硬體裝置提供了一種抽象化處理,它代表了一些視頻硬體裝置,允許應用軟體通過定義明確的介面來訪問映像硬體裝置。這樣軟體無須瞭解任何涉及硬體底層驅動的東西(如硬體寄存器)。它允許上層應用程式在圖形模式下直接對顯示緩衝區進行讀寫和I/O控制等操作。通過專門的裝置節點可對該裝置進行訪問,如/dev/fb*。使用者可以將它看成是顯示記憶體的一個映像,將其映射到進程地址空間之後,就可以進行讀寫操作,而讀寫操作可以反映到LCD。
幀緩衝裝置對應的裝置檔案是/dev/fb*。如果系統有多個顯卡,Linux還支援多個幀緩衝裝置,最多可達32個,即/dev/fb0~/dev/fb31。而/dev/fb則指向當前的幀緩衝裝置,通常情況下,預設的幀緩衝裝置為/dev/fb0。
幀緩衝裝置也屬於字元裝置,採用“檔案層-驅動層”的介面方式。在檔案層為之定義了以下資料結構。
Static struct file_operations fb_fops={
ower: THIS_MODULE,
read: fb_read, /*讀操作*/
write: fb_write, /*寫操作*/
ioct1: fb_ioct1, /*I/O操作*/
mmap: fb_mmap, /*映射操作*/
open: fb_open, /*開啟操作*/
release: fb_release, /*關閉操作*/
}
其成員函數都在linux/driver/video/fbmem.c中定義,其中的函數對具體的硬體進行操作,對寄存器進行設定,對顯示緩衝進行映射。主要結構體還有以下幾個。
● Struct fb_fix_screeninfo:記錄了幀緩衝裝置和指定顯示模式的不可修改資訊。它包含了螢幕緩衝區的物理地址和長度。
● Struct fb_var_screeninfo:記錄了幀緩衝裝置和指定顯示模式的可修改資訊。它包括顯示螢幕的解析度、每個像素的位元數和一些時序變數。其中變數 xres定義了螢幕一行所佔的像素數,yres定義了螢幕一列所佔的像素數,bits_per_pixel定義了每個像素用多少個位來表示。
● Struct fb_info:Linux為幀緩衝裝置定義的驅動層介面。它不僅包含了底層函數,而且還有記錄裝置狀態的資料。每個幀緩衝裝置都與一個fb_info結構相對應。其中成員變數modename為裝置名稱,fontname為顯示字型,fbops為指向底層操作的函數的指標。
LCD驅動開發的主要工作
1 編寫初始化函數
初始化函數首先初始化LCD 控制器,通過寫寄存器設定顯示模式和顏色數,然後分配LCD顯示緩衝區。在Linux中可以用kmalloc()函數分配一段連續的空間。緩衝區大小為:點陣行數×點陣列數×用於表示一個像素的位元數/8。緩衝區通常分配在大容量的片外SDRAM中,起始地址儲存在LCD控制寄存器中。本文採用的LCD顯示方式為640×480,16位彩色,則需要分配的顯示緩衝區為640×480×2=600kb。最後是初始化一個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_ops在include/linux/fb.h中定義。這些函數都是用來設定/擷取fb_info結構中的成員變數的。當應用程式對裝置檔案進行 ioctl操作時候會調用它們。對於fb_get_fix(),應用程式傳入的是fb_fix_screeninfo結構,在函數中對其成員變數賦值,主要是smem_start(緩衝區起始地址)和smem_len(緩衝區長度),最終返回給應用程式。而fb_set_var()函數的傳入參數是 fb_var_screeninfo,函數中需要對xres、yres和bits_per_pixel賦值。
對於/dev/fb,對顯示裝置的操作主要有以下幾種。
● 讀/寫(read/write)/dev/fb:相當於讀/寫螢幕緩衝區。
● 映射(map)操作:由於Linux工作在保護模式,每個應用程式都有自己的虛擬位址空間,在應用程式中是不能直接存取物理緩衝區地址的。為此,Linux在檔案操作 file_operations結構中提供了mmap函數,可將檔案的內容映射到使用者空間。對於幀緩衝裝置,則可通過映射操作,可將螢幕緩衝區的物理地址映射到使用者空間的一段虛擬位址中,之後使用者就可以通過讀寫這段虛擬位址訪問螢幕緩衝區,在螢幕上繪圖了。
● I/O控制:對於幀緩衝裝置,對裝置檔案的ioctl操作可讀取/設定顯示裝置及螢幕的參數,如解析度、顯示顏色數和螢幕大小等。ioctl的操作是由底層的驅動程式來完成的。在應用程式中,操作/dev/fb的一般步驟如下:開啟/dev/fb裝置檔案;用ioctrl操作取得當前顯示螢幕的參數,如螢幕解析度和每個像素的位元數,根據螢幕參數可計算螢幕緩衝區的大小;將螢幕緩衝區映射到使用者空間;映射後即可直接讀寫螢幕緩衝區,進行繪圖和圖片顯示了。
LCD模組化驅動
在對S3C2410 的LCD編寫模組化驅動程式時,首先要從核心中去除LCD驅動。這裡需要做一些改動,系統調用被加在以下檔案中,需去除: /root/usr/src/arm/linux/kernel/sys.c;/root/usr/src/arm/linux/include/arm- arm下的unistd.h和lcd.h;/root/usr/src/arm/linux/arch/arm/kernel下的calls.s。
編寫模組化驅動程式,有以下幾個關鍵的函數。
● lcd_kernel_init(void)//當模組被載入時執行
● lcd_kernel_exit(void)//當模組被移出核心空間時被執行
● lcd_kernel1_ioctl(struct*inode, struct*file, unsigned int cmd, unsigned longarg) //其他功能
每當裝配裝置驅動程式時,系統自動調用初始化模組lcd_kernel_init(void)。
另一個必須提供的函數是lcd_kernel_exit(void),它在模組被卸載時調用,負責進行裝置驅動程式的工作。
執行insmod lcd.o命令即可將LCD驅動添加到核心中,執行rmmod lcd命令即可從核心中刪除LCD驅動。
靜態載入LCD驅動
將寫好的lcd 驅動程式lcd.c放到arm/linux/drivers/char目錄下,修改arm/linux/drivers/char/config.in檔案,加上一行:Bool''LCD driver support''CONFIG_LCD;修改arm/linux/drivers/char/Makefile檔案,加上一行:obj-$ (CONFIG_LCD)+=lcd.o。
這樣,當再進行make xconfig時,就會選擇是否將LCD驅動編譯進核心。同樣的辦法也可用在其他裝置上。
先說一下,我的板子使用CH7005將LCD訊號轉為VGA的, 2.4核心提供驅動,顯示效果小可。網上高人說只要把LCD驅動起來了,就可以顯示了,並且有人跟我一樣的,顯示成功了。
現在我的移植過程如下:arch/arm/mach-s3c2410/mach-smdk2410.c
static struct s3c2410fb_mach_info smdk2410_lcd_platdata = {
.fixed_syncs=0,
.width= 640,
.height= 480,
.xres = {
.defval= 640,
.min= 640,
.max= 640,
},
.yres = {
.defval= 480,
.min= 480,
.max= 480,
},
.bpp = {
.defval= 16,
.min= 16,
.max= 16,
},
.regs = {
.lcdcon1= S3C2410_LCDCON1_TFT16BPP | S3C2410_LCDCON1_TFT | S3C2410_LCDCON1_CLKVAL(1),
.lcdcon2= S3C2410_LCDCON2_VBPD(25) | S3C2410_LCDCON2_VFPD(5) | S3C2410_LCDCON2_VSPW(1),
.lcdcon3= S3C2410_LCDCON3_HBPD(67) | S3C2410_LCDCON3_HFPD(40),
.lcdcon4= S3C2410_LCDCON4_HSPW(31) | S3C2410_LCDCON4_MVAL(13),.lcdcon5= S3C2410_LCDCON5_FRM565 | S3C2410_LCDCON5_HWSWP | S3C2410_LCDCON5_PWREN|S3C2410_LCDCON5_INVVLINE|S3C2410_LCDCON5_INVVFRAME ,
},
.gpcup= 0xFFFFFFFF,
.gpcup_mask= 0xFFFFFFFF,.gpccon= 0xaaaaaaaa,
.gpccon_mask= 0xFFFFFFFF,.gpdup= 0xFFFFFFFF,
.gpdup_mask= 0xFFFFFFFF,.gpdcon= 0xaaaaaaaa,
//.gpdcon_mask= 0xFFFFFFFF,
.gpdcon_mask= 0,.lpcsel= 0x00,
};在smdk2410_map_io函數中加入了
set_s3c2410fb_info(&smdk2410_lcd_platdata);啟動之後,第一次
參數:
setenv bootargs console=ttySAC0 root=/dev/nfs nfsroot=59.69.74.87:/public/myroot_nfs ip=59.69.74.199:59.69.74.87:59.69.74.1:255.0.0.0:todaygood.cublog.cn:eth0:off
啟動後到
Console: switching to colour frame buffer device 80x30
fb0: s3c2410fb frame buffer device
fb1: Virtual frame buffer device, using 1024K of video memory 不動了。 重啟之後
第二次:VGA有閃爍的彩色線條顯示,但效果很不好,沒有小企鵝。
Initializing Cryptographic API
Console: switching to colour frame buffer device 80x30
fb0: s3c2410fb frame buffer device
fb1: Virtual frame buffer device, using 1024K of video memory
S3C2410 RTC, (c) 2004 Simtec ElectronicsRAMDISK driver initialized: 16 RAM disks of 8192K size 1024 blocksize
loop: loaded (max 8 devices)
nbd: registered device at major 43
usbcore: registered new driver ub
Cirrus Logic CS8900A driver for Linux (Modified for SMDK2410)
eth0: CS8900A rev E at 0xe0000300 irq=52, no eeprom , addr: 08: 0:3E:26:0A:5B
NFTL driver: nftlcore.c $Revision: 1.97 $, nftlmount.c $Revision: 1.40 $
usbmon: debugfs is not available
。。。。
[root@hjembed /]# ll /dev/fb/
crw-rw-rw- 1 root root 29, 0 Jan 1 00:00 0
crw-rw-rw- 1 root root 29, 1 Jan 1 00:00 1[root@hjembed /]# cat /proc/interrupts
CPU0
30: 68164 S3C2410 Timer Tick
32: 0 s3c2410-lcd
42: 0 ohci_hcd:usb1
52: 15333 eth0
70: 49 s3c2410-uart
71: 116 s3c2410-uart
Err: 0
[root@hjembed /]#移植過程中,寄存器的設定完全按2.4裡面套下來的,現在有些懷疑.gpcup= 0xFFFFFFFF,
.gpcup_mask= 0xFFFFFFFF,.gpccon= 0xaaaaaaaa,
.gpccon_mask= 0xFFFFFFFF,.gpdup= 0xFFFFFFFF,
.gpdup_mask= 0xFFFFFFFF,.gpdcon= 0xaaaaaaaa,
//.gpdcon_mask= 0xFFFFFFFF,
.gpdcon_mask= 0,.lpcsel= 0x00,這幾個參數的設定, 找了很久,沒有介紹,看代碼不知從何看起。還有,我看見論壇中的kmust兄 http://www.hhcn.com/cgi-bin/topic.cgi?forum=3&topic=553&show=25
在arch/arm/mach-s3c2410/common-smdk.c檔案的void __init smdk_machine_init(void)函數中加入GPIOC和GPIOD的初始化如下:
s3c2410_gpio_cfgpin(S3C2410_GPC0, S3C2410_GPC0_LEND);
s3c2410_gpio_cfgpin(S3C2410_GPC1, S3C2410_GPC1_VCLK);
....我看了一下硬體原理圖, 沒看見GPIOC, GPIOD 與LCD相關, 也有可能是我看漏了, 也有可能板子供應商沒有畫出來。前兩天準備搞個qt上面去試一下, 沒搞成,今天偶爾試了一下, 有兩次出現了小企鵝, 太興奮了,懷疑是我的錯覺, 然後我關掉電源,重啟,結果又沒有顯示了,再試了一下原來2.4的核心,
也沒有小企鵝了, 還是彩色的條紋一閃一閃的。 多作了幾次實驗,發現一個規律;
先用uboot下載vivi,切換至vivi,下載2.4核心, 啟動,可以顯示小企鵝,
然後在vivi下載uboot,下載2.6核心, 啟動,可以顯示小企鵝,現在,如果按下reset鍵,再次下載2.6核心, 啟動,也可以顯示小企鵝。
但是, 如果,你按下電源鍵,再開啟,不管你下載2.6核心還是2.4核心, 啟動,都不能顯示小企鵝,只顯示閃動的彩色條紋。由此得出,vivi中一定對某些東西作了初始化動作。 大家幫我分析分析!幸好提供的vivi源碼中有vga.c這個檔案,開啟看了一下,有對 24C08的讀寫操作,google一下,
原來是一個串列EEPROM,但是查板子原理圖, 又沒有這個器件,現在怎麼辦呢?2.6下的小企鵝,顯示效果挺好的,閃爍得沒有2.4下強烈。
--------------------------------------------------------------------------------
我的分析應該是正確的, 現在用vivi引導2.6的核心, 正常顯示小企鵝!
看來的確是作了初試化工作。 好在現在有一個介面可以顯示了,如何修改uboot,從長計議了。
參考資料: http://www.helloeda.com/Article/deembed/200612/55.html
| LCD驅動移植筆記 |
|
作者:tag 文章來源:csdn 點擊數:
37652 更新時間:2006-12-9 |
|
LCD驅動程式往2.6.11核心 的移植總結 硬體環境:SBC-2410X開發板(CPU:S3C2410X) 核心版本:2.6.11.1 運行環境:Debian2.6.8 交叉編譯環境:gcc-3.3.4-glibc-2.3.3 註:本驅動移植是基於s3c2400 framebuffer 的驅動。 一、從網上將Linux核心原始碼下載到本機上,並將其解壓: #tar jxf linux-2.6.11.1.tar.bz2 二、開啟核心頂層目錄中的Makefile檔案,這個檔案中需要修改的內容包括以下兩個方面。 (1)指定目標平台。 移植前: ARCH ?= $(SUBARCH) 移植後: ARCH :=arm (2)指定交叉編譯器。 移植前: CROSS_COMPILE ?= 移植後: CROSS_COMPILE :=/opt/crosstool/arm-s3c2410-linux-gnu/gcc-3.3.4-glibc-2.3.3/bin/arm-s3c2410-linux-gnu- 註:這裡假設編譯器就放在原生那個目錄下。 三、添加並修改驅動程式原始碼,這涉及到以下幾個方面。 (1)、將開發板配帶的LCD驅動程式s3c2400fb.c、s3c2400fb.h來源程式放到drivers/video/目錄下,並修改名字為s3c2410fb.c/s3c2400fb.h。 #cp s3c2400fb.c . drivers/video/s3c2410fb.c (2)、在s3c2410fb.c驅動程式裡面添加:sbc_gpio_con_set()、sbc_gpio_pullup_set()、 sbc_gpio_function_set()的聲明以及實現代碼用以替代2.4.18代碼中的write_gpio_bit()、 set_gpio_ctrl()函數,因為在2.4.18中這兩個函數都是用指標的方式對CPU寄存器進行設定,而在2.6.11的驅動程式裡面用了 __raw_writel()的方式對寄存器設定進行了封裝。 在驅動程式移植過程中由於是基於S3C2400的驅動,所以主要的修改工作就是根據所用開發板的硬體修改相應的寄存器的設定。 主要的修改有:s3c2410fb_mach_info結構,這個結果主要定義了所用顯示屏的一些資訊,如時鐘、大小等;修改 c2400fb_activate_var函數中關於寄存器的設定,這個函數涉及到了S3C2410 LCD控制器的有關設定,這些寄存器的設定要根據所用的螢幕(TFT/CSTN)來進行設定;修改 s3c2400fb_set_controller_regs和s3c2400fb_lcd_init函數,這個函數涉及到了CPU與LCD的物理串連,要根據LCD與CPU的具體串連來設定各個CPIO寄存器。 註:具體修改詳見驅動程式。 (3)、修改arch/arm/mach-s3c2410/s3c2410.c,在s3c2410_iodesc結構中添加:IODESC_ENT(LCD) 註:以上添加的語句就是為了將CPU的LCD寄存器的物理地址映射到所指向的虛擬位址上去,上面的結構還定義了虛擬位址所佔用的區間,並指定了該區間所指向的域(的屬性)。 (4)、修改drivers/video目錄下的Kconfig檔案,在最後添加如下內容: config FB_S3C2410 tristate "S3C2410 LCD support" depends on FB && ARM && ARCH_S3C2410 help This is a framebuffer device for the S3C2410 LCD Controller. If you plan to use the LCD display with your S3C2410 system, say Y here. (5)、修改drivers/video目錄下的Makefile檔案,在最後添加如下內容: obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o 四、配置、編譯核心。在核心頂層目錄當中鍵入: #make smdk2410_defconfig 由於2.6的核心預設就支援了S3C2410,所以就有一個預設的核心設定檔。裡面只是包括了一個簡單的配置,要使LCD驅動編譯進核心,還要進行手工配置。 #make menuconfig Graphics support ---> [*] Support for frame buffer devices [*] S3C2410 LCD support(BASED ON S3C2400) 將剛才添加的LCD驅動程式靜態添加到核心當中。 最後進行核心編譯。 #make 然後將鏡像下載到開發板中去. 而且在LCD顯示屏上的左上方會顯示一個小企鵝的表徵圖。查看裝置檔案。 [root@fa /]# ls -al /dev/fb/0 由此可見,LCD已經成功驅動,要測試驅動程式可以用 自己寫(見附件test.c)在顯示屏上顯示任意顏色的線條。 問題解析 在LCD驅動程式移植的過程中,出現的問題主要就是由於寄存器設定不正確而造成的問題。 在對驅動程式進行了函數替代以及改寫了一些函數之後,將驅動程式編譯進核心裡,核心可以正確的編譯串連並產生鏡像檔案,把鏡像檔案下載到開發板上,可以看到 drivers/video目錄下看到系統註冊的一個裝置檔案,但是在系統啟動之後就是無法看見小企鵝的表徵圖並且用測試程式去測試,LCD螢幕上也無法顯示任何有色的線條。通過多次查閱原始碼,才發現原來就是CPU有關LCD的8個寄存器的設定以及對GPC和GPD寄存器的設定不正確。後來對這幾個寄存器進行了正確的設定就可以在系統啟動之後看到小企鵝的表徵圖。由於螢幕的背景是藍色的,所以該表徵圖的顏色顯示不正確,但是用自己寫的測試程式去畫設定好的顏色的線條,在螢幕上總能正確的顯示出來,所以至於這個螢幕的顏色問題至今尚未解決。 |