一、開發(fā)環(huán)境1. LCD工作的硬件需求:
要使一塊LCD正常的顯示文字或圖像,不僅需要LCD驅(qū)動(dòng)器,而且還需要相應(yīng)的LCD控制器。在通常情況下,生產(chǎn)廠商把LCD驅(qū)動(dòng)器會(huì)以COF/COG的形式與LCD玻璃基板制作在一起,而LCD控制器則是由外部的電路來實(shí)現(xiàn),現(xiàn)在很多的MCU內(nèi)部都集成了LCD控制器,如S3C2410/2440等。通過LCD控制器就可以產(chǎn)生LCD驅(qū)動(dòng)器所需要的控制信號(hào)來控制STN/TFT屏了。
2. S3C2440內(nèi)部LCD控制器結(jié)構(gòu)圖:
我們根據(jù)數(shù)據(jù)手冊(cè)來描述一下這個(gè)集成在S3C2440內(nèi)部的LCD控制器:
a:LCD控制器由REGBANK、LCDCDMA、TIMEGEN、VIDPRCS寄存器組成;
b:REGBANK由17個(gè)可編程的寄存器組和一塊256*16的調(diào)色板內(nèi)存組成,它們用來配置LCD控制器的;
c:LCDCDMA是一個(gè)專用的DMA,它能自動(dòng)地把在偵內(nèi)存中的視頻數(shù)據(jù)傳送到LCD驅(qū)動(dòng)器,通過使用這個(gè)DMA通道,視頻數(shù)據(jù)在不需要CPU的干預(yù)的情況下顯示在LCD屏上;
d:VIDPRCS接收來自LCDCDMA的數(shù)據(jù),將數(shù)據(jù)轉(zhuǎn)換為合適的數(shù)據(jù)格式,比如說4/8位單掃,4位雙掃顯示模式,然后通過數(shù)據(jù)端口VD[23:0]傳送視頻數(shù)據(jù)到LCD驅(qū)動(dòng)器;
e:TIMEGEN由可編程的邏輯組成,他生成LCD驅(qū)動(dòng)器需要的控制信號(hào),比如VSYNC、HSYNC、VCLK和LEND等等,而這些控制信號(hào)又與REGBANK寄存器組中的LCDCON1/2/3/4/5的配置密切相關(guān),通過不同的配置,TIMEGEN就能產(chǎn)生這些信號(hào)的不同形態(tài),從而支持不同的LCD驅(qū)動(dòng)器(即不同的STN/TFT屏)。
3. 常見TFT屏工作時(shí)序分析:
LCD提供的外部接口信號(hào):
VSYNC/VFRAME/STV:垂直同步信號(hào)(TFT)/幀同步信號(hào)(STN)/SEC TFT信號(hào); HSYNC/VLINE/CPV:水平同步信號(hào)(TFT)/行同步脈沖信號(hào)(STN)/SEC TFT信號(hào); VCLK/LCD_HCLK:象素時(shí)鐘信號(hào)(TFT/STN)/SEC TFT信號(hào); VD[23:0]:LCD像素?cái)?shù)據(jù)輸出端口(TFT/STN/SEC TFT); VDEN/VM/TP:數(shù)據(jù)使能信號(hào)(TFT)/LCD驅(qū)動(dòng)交流偏置信號(hào)(STN)/SEC TFT 信號(hào); LEND/STH:行結(jié)束信號(hào)(TFT)/SEC TFT信號(hào); LCD_LPCOE:SEC TFT OE信號(hào); LCD_LPCREV:SEC TFT REV信號(hào); LCD_LPCREVB:SEC TFT REVB信號(hào)。 |
所有顯示器顯示圖像的原理都是從上到下,從左到右的。這是什么意思呢?這么說吧,一副圖像可以看做是一個(gè)矩形,由很多排列整齊的點(diǎn)一行一行組成,這些點(diǎn)稱之為像素。那么這幅圖在LCD上的顯示原理就是:
A:顯示指針從矩形左上角的第一行第一個(gè)點(diǎn)開始,一個(gè)點(diǎn)一個(gè)點(diǎn)的在LCD上顯示,在上面的時(shí)序圖上用時(shí)間線表示就為VCLK,我們稱之為像素時(shí)鐘信號(hào); B:當(dāng)顯示指針一直顯示到矩形的右邊就結(jié)束這一行,那么這一行的動(dòng)作在上面的時(shí)序圖中就稱之為1 Line; C:接下來顯示指針又回到矩形的左邊從第二行開始顯示,注意,顯示指針在從第一行的右邊回到第二行的左邊是需要一定的時(shí)間的,我們稱之為行切換; D:如此類推,顯示指針就這樣一行一行的顯示至矩形的右下角才把一副圖顯示完成。因此,這一行一行的顯示在時(shí)間線上看,就是時(shí)序圖上的HSYNC; E:然而,LCD的顯示并不是對(duì)一副圖像快速的顯示一下,為了持續(xù)和穩(wěn)定的在LCD上顯示,就需要切換到另一幅圖上(另一幅圖可以和上一副圖一樣或者不一樣,目的只是為了將圖像持續(xù)的顯示在LCD上)。那么這一副一副的圖像就稱之為幀,在時(shí)序圖上就表示為1 Frame,因此從時(shí)序圖上可以看出1 Line只是1 Frame中的一行; F:同樣的,在幀與幀切換之間也是需要一定的時(shí)間的,我們稱之為幀切換,那么LCD整個(gè)顯示的過程在時(shí)間線上看,就可表示為時(shí)序圖上的VSYNC。 |
上面時(shí)序圖上各時(shí)鐘延時(shí)參數(shù)的含義如下:(這些參數(shù)的值,LCD產(chǎn)生廠商會(huì)提供相應(yīng)的數(shù)據(jù)手冊(cè))
VBPD(vertical back porch):表示在一幀圖像開始時(shí),垂直同步信號(hào)以后的無效的行數(shù),對(duì)應(yīng)驅(qū)動(dòng)中的upper_margin; VFBD(vertical front porch):表示在一幀圖像結(jié)束后,垂直同步信號(hào)以前的無效的行數(shù),對(duì)應(yīng)驅(qū)動(dòng)中的lower_margin; VSPW(vertical sync pulse width):表示垂直同步脈沖的寬度,用行數(shù)計(jì)算,對(duì)應(yīng)驅(qū)動(dòng)中的vsync_len; HBPD(horizontal back porch):表示從水平同步信號(hào)開始到一行的有效數(shù)據(jù)開始之間的VCLK的個(gè)數(shù),對(duì)應(yīng)驅(qū)動(dòng)中的left_margin; HFPD(horizontal front porth):表示一行的有效數(shù)據(jù)結(jié)束到下一個(gè)水平同步信號(hào)開始之間的VCLK的個(gè)數(shù),對(duì)應(yīng)驅(qū)動(dòng)中的right_margin; HSPW(horizontal sync pulse width):表示水平同步信號(hào)的寬度,用VCLK計(jì)算,對(duì)應(yīng)驅(qū)動(dòng)中的hsync_len; |
對(duì)于以上這些參數(shù)的值將分別保存到REGBANK寄存器組中的LCDCON1/2/3/4/5寄存器中:(對(duì)寄存器的操作請(qǐng)查看S3c2440數(shù)據(jù)手冊(cè)LCD部分)
LCDCON1:17- 8位CLKVAL 6- 5位掃描模式(對(duì)于STN屏:4位單/雙掃、8位單掃) 4- 1位色位模式(1BPP、8BPP、16BPP等)
LCDCON2:31 - 24位VBPD 23 - 14位LINEVAL 13 - 6位VFPD 5 - 0位VSPW
LCDCON3:25 - 19位HBPD 18 - 8位HOZVAL 7 - 0位HFPD
LCDCON4: 7 - 0位HSPW
LCDCON5: |
4. 幀緩沖(FrameBuffer):
幀緩沖是Linux為顯示設(shè)備提供的一個(gè)接口,它把一些顯示設(shè)備描述成一個(gè)緩沖區(qū),允許應(yīng)用程序通過FrameBuffer定義好的接口訪問這些圖形設(shè)備,從而不用去關(guān)心具體的硬件細(xì)節(jié)。對(duì)于幀緩沖設(shè)備而言,只要在顯示緩沖區(qū)與顯示點(diǎn)對(duì)應(yīng)的區(qū)域?qū)懭腩伾?,?duì)應(yīng)的顏色就會(huì)自動(dòng)的在屏幕上顯示。下面來看一下在不同色位模式下緩沖區(qū)與顯示點(diǎn)的對(duì)應(yīng)關(guān)系:
三、幀緩沖(FrameBuffer)設(shè)備驅(qū)動(dòng)結(jié)構(gòu):
幀緩沖設(shè)備為標(biāo)準(zhǔn)的字符型設(shè)備,在Linux中主設(shè)備號(hào)29,定義在/include/linux/major.h中的FB_MAJOR,次設(shè)備號(hào)定義幀緩沖的個(gè)數(shù),最大允許有32個(gè)FrameBuffer,定義在/include/linux/fb.h中的FB_MAX,對(duì)應(yīng)于文件系統(tǒng)下/dev/fb%d設(shè)備文件。
1. 幀緩沖設(shè)備驅(qū)動(dòng)在Linux子系統(tǒng)中的結(jié)構(gòu)如下:
我們從上面這幅圖看,幀緩沖設(shè)備在Linux中也可以看做是一個(gè)完整的子系統(tǒng),大體由fbmem.c和xxxfb.c組成。向上給應(yīng)用程序提供完善的設(shè)備文件操作接口(即對(duì)FrameBuffer設(shè)備進(jìn)行read、write、ioctl等操作),接口在Linux提供的fbmem.c文件中實(shí)現(xiàn);向下提供了硬件操作的接口,只是這些接口Linux并沒有提供實(shí)現(xiàn),因?yàn)檫@要根據(jù)具體的LCD控制器硬件進(jìn)行設(shè)置,所以這就是我們要做的事情了(即xxxfb.c部分的實(shí)現(xiàn))。
2. 幀緩沖相關(guān)的重要數(shù)據(jù)結(jié)構(gòu):
從幀緩沖設(shè)備驅(qū)動(dòng)程序結(jié)構(gòu)看,該驅(qū)動(dòng)主要跟fb_info結(jié)構(gòu)體有關(guān),該結(jié)構(gòu)體記錄了幀緩沖設(shè)備的全部信息,包括設(shè)備的設(shè)置參數(shù)、狀態(tài)以及對(duì)底層硬件操作的函數(shù)指針。在Linux中,每一個(gè)幀緩沖設(shè)備都必須對(duì)應(yīng)一個(gè)fb_info,fb_info在/linux/fb.h中的定義如下:(只列出重要的一些)
structfb_info{ intnode; intflags; structfb_var_screeninfo var; structfb_fix_screeninfo fix; structfb_monspecs monspecs; structwork_structqueue; structfb_pixmap pixmap; structfb_pixmap sprite; structfb_cmap cmap; structfb_videomode*mode;
#ifdefCONFIG_FB_BACKLIGHT structbacklight_device*bl_dev; structmutex bl_curve_mutex; u8 bl_curve[FB_BACKLIGHT_LEVELS]; #endif #ifdefCONFIG_FB_DEFERRED_IO structdelayed_work deferred_work; structfb_deferred_io*fbdefio; #endif
structfb_ops*fbops; structdevice*device; structdevice*dev; intclass_flag; #ifdefCONFIG_FB_TILEBLITTING structfb_tile_ops*tileops; #endif char__iomem*screen_base; unsignedlongscreen_size; void*pseudo_palette; #defineFBINFO_STATE_RUNNING0 #defineFBINFO_STATE_SUSPENDED1 u32 state; void*fbcon_par; void*par; }; |
其中,比較重要的成員有structfb_var_screeninfo var、structfb_fix_screeninfo fix和structfb_ops*fbops,他們也都是結(jié)構(gòu)體。下面我們一個(gè)一個(gè)的來看。
fb_var_screeninfo結(jié)構(gòu)體主要記錄用戶可以修改的控制器的參數(shù),比如屏幕的分辨率和每個(gè)像素的比特?cái)?shù)等,該結(jié)構(gòu)體定義如下:
structfb_var_screeninfo{ __u32 xres; __u32 yres; __u32 xres_virtual; __u32 yres_virtual; __u32 xoffset; __u32 yoffset; __u32 bits_per_pixel; __u32 grayscale;
structfb_bitfield red; structfb_bitfield green; structfb_bitfield blue; structfb_bitfield transp;
__u32 nonstd; __u32 activate; __u32 height; __u32 width; __u32 accel_flags;
__u32 pixclock; __u32 left_margin; __u32 right_margin; __u32 upper_margin; __u32 lower_margin; __u32 hsync_len; __u32 vsync_len; __u32 sync; __u32 vmode; __u32rotate; __u32 reserved[5]; }; |
而fb_fix_screeninfo結(jié)構(gòu)體又主要記錄用戶不可以修改的控制器的參數(shù),比如屏幕緩沖區(qū)的物理地址和長(zhǎng)度等,該結(jié)構(gòu)體的定義如下:
structfb_fix_screeninfo{ charid[16]; unsignedlongsmem_start; __u32 smem_len; __u32 type; __u32 type_aux; __u32 visual; __u16 xpanstep; __u16 ypanstep; __u16 ywrapstep; __u32 line_length; unsignedlongmmio_start; __u32 mmio_len; __u32 accel; __u16 reserved[3]; }; |
fb_ops結(jié)構(gòu)體是對(duì)底層硬件操作的函數(shù)指針,該結(jié)構(gòu)體中定義了對(duì)硬件的操作有:(這里只列出了常用的操作)
structfb_ops{
structmodule*owner;
//檢查可變參數(shù)并進(jìn)行設(shè)置 int(*fb_check_var)(structfb_var_screeninfo*var,structfb_info*info);
//根據(jù)設(shè)置的值進(jìn)行更新,使之有效 int(*fb_set_par)(structfb_info*info);
//設(shè)置顏色寄存器 int(*fb_setcolreg)(unsignedregno,unsignedred,unsignedgreen, unsignedblue,unsignedtransp,structfb_info*info);
//顯示空白 int(*fb_blank)(intblank,structfb_info*info);
//矩形填充 void(*fb_fillrect)(structfb_info*info,conststructfb_fillrect*rect);
//復(fù)制數(shù)據(jù) void(*fb_copyarea)(structfb_info*info,conststructfb_copyarea*region);
//圖形填充 void(*fb_imageblit)(structfb_info*info,conststructfb_image*image); }; |
3. 幀緩沖設(shè)備作為平臺(tái)設(shè)備:
在S3C2440中,LCD控制器被集成在芯片的內(nèi)部作為一個(gè)相對(duì)獨(dú)立的單元,所以Linux把它看做是一個(gè)平臺(tái)設(shè)備,故在內(nèi)核代碼/arch/arm/plat-s3c24xx/devs.c中定義有LCD相關(guān)的平臺(tái)設(shè)備及資源,代碼如下:
//LCD控制器的資源信息 staticstructresource s3c_lcd_resource[]={ [0]={ .start=S3C24XX_PA_LCD,//控制器IO端口開始地址 .end=S3C24XX_PA_LCD+S3C24XX_SZ_LCD-1,//控制器IO端口結(jié)束地址 .flags=IORESOURCE_MEM,//標(biāo)識(shí)為L(zhǎng)CD控制器IO端口,在驅(qū)動(dòng)中引用這個(gè)就表示引用IO端口 }, [1]={ .start=IRQ_LCD,//LCD中斷 .end=IRQ_LCD, .flags=IORESOURCE_IRQ,//標(biāo)識(shí)為L(zhǎng)CD中斷 } };
staticu64 s3c_device_lcd_dmamask=0xffffffffUL;
structplatform_device s3c_device_lcd={ .name="s3c2410-lcd",//作為平臺(tái)設(shè)備的LCD設(shè)備名 .id=-1, .num_resources=ARRAY_SIZE(s3c_lcd_resource),//資源數(shù)量 .resource=s3c_lcd_resource,//引用上面定義的資源 .dev={ .dma_mask=&s3c_device_lcd_dmamask, .coherent_dma_mask=0xffffffffUL } };
EXPORT_SYMBOL(s3c_device_lcd);//導(dǎo)出定義的LCD平臺(tái)設(shè)備,好在mach-smdk2440.c的smdk2440_devices[]中添加到平臺(tái)設(shè)備列表中 |
除此之外,Linux還在/arch/arm/mach-s3c2410/include/mach/fb.h中為L(zhǎng)CD平臺(tái)設(shè)備定義了一個(gè)s3c2410fb_mach_info結(jié)構(gòu)體,該結(jié)構(gòu)體主要是記錄LCD的硬件參數(shù)信息(比如該結(jié)構(gòu)體的s3c2410fb_display成員結(jié)構(gòu)中就用于記錄LCD的屏幕尺寸、屏幕信息、可變的屏幕參數(shù)、LCD配置寄存器等),這樣在寫驅(qū)動(dòng)的時(shí)候就直接使用這個(gè)結(jié)構(gòu)體。下面,我們來看一下內(nèi)核是如果使用這個(gè)結(jié)構(gòu)體的。在/arch/arm/mach-s3c2440/mach-smdk2440.c中定義有:
//LCD硬件的配置信息,注意這里我使用的LCD是NEC 3.5寸TFT屏,這些參數(shù)要根據(jù)具體的LCD屏進(jìn)行設(shè)置 staticstructs3c2410fb_display smdk2440_lcd_cfg __initdata={ //這個(gè)地方的設(shè)置是配置LCD寄存器5,這些宏定義在regs-lcd.h中,計(jì)算后二進(jìn)制為:111111111111,然后對(duì)照數(shù)據(jù)手冊(cè)上LCDCON5的各位來看,注意是從右邊開始 .lcdcon5=S3C2410_LCDCON5_FRM565| S3C2410_LCDCON5_INVVLINE| S3C2410_LCDCON5_INVVFRAME| S3C2410_LCDCON5_PWREN| S3C2410_LCDCON5_HWSWP,
.type=S3C2410_LCDCON1_TFT,//TFT類型
.width=240,//屏幕寬度 .height=320,//屏幕高度 //以下一些參數(shù)在上面的時(shí)序圖分析中講到過,各參數(shù)的值請(qǐng)跟據(jù)具體的LCD屏數(shù)據(jù)手冊(cè)結(jié)合上面時(shí)序分析來設(shè)定 .pixclock=100000,//像素時(shí)鐘 .xres=240,//水平可見的有效像素 .yres=320,//垂直可見的有效像素 .bpp=16,//色位模式 .left_margin=19,//行切換,從同步到繪圖之間的延遲 .right_margin=36,//行切換,從繪圖到同步之間的延遲 .hsync_len=5,//水平同步的長(zhǎng)度 .upper_margin=1,//幀切換,從同步到繪圖之間的延遲 .lower_margin=5,//幀切換,從繪圖到同步之間的延遲 .vsync_len=1,//垂直同步的長(zhǎng)度 };
staticstructs3c2410fb_mach_info smdk2440_fb_info __initdata={ .displays=&smdk2440_lcd_cfg,//應(yīng)用上面定義的配置信息 .num_displays=1, .default_display=0,
.gpccon=0xaaaa555a,//將GPC0、GPC1配置成LEND和VCLK,將GPC8-15配置成VD0-7,其他配置成普通輸出IO口 .gpccon_mask=0xffffffff, .gpcup=0x0000ffff,//禁止GPIOC的上拉功能 .gpcup_mask=0xffffffff, .gpdcon=0xaaaaaaaa,//將GPD0-15配置成VD8-23 .gpdcon_mask=0xffffffff, .gpdup=0x0000ffff,//禁止GPIOD的上拉功能 .gpdup_mask=0xffffffff,
.lpcsel=0x0,//這個(gè)是三星TFT屏的參數(shù),這里不用 }; |
注意:可能有很多朋友不知道上面紅色部分的參數(shù)是做什么的,其值又是怎么設(shè)置的?其實(shí)它是跟你的開發(fā)板LCD控制器密切相關(guān)的,看了下面兩幅圖相信就大概知道他們是干什么用的:
上面第一幅圖是開發(fā)板原理圖的LCD控制器部分,第二幅圖是S3c2440數(shù)據(jù)手冊(cè)中IO端口C和IO端口D控制器部分。原理圖中使用了GPC8-15和GPD0-15來用做LCD控制器VD0-VD23的數(shù)據(jù)端口,又分別使用GPC0、GPC1端口用做LCD控制器的LEND和VCLK信號(hào),對(duì)于GPC2-7則是用做STN屏或者三星專業(yè)TFT屏的相關(guān)信號(hào)。然而,S3C2440的各個(gè)IO口并不是單一的功能,都是復(fù)用端口,要使用他們首先要對(duì)他們進(jìn)行配置。所以上面紅色部分的參數(shù)就是把GPC和GPD的部分端口配置成LCD控制功能模式。
從以上講述的內(nèi)容來看,要使LCD控制器支持其他的LCD屏,重要的是根據(jù)LCD的數(shù)據(jù)手冊(cè)修改以上這些參數(shù)的值。下面,我們?cè)倏匆幌略隍?qū)動(dòng)中是如果引用到s3c2410fb_mach_info結(jié)構(gòu)體的(注意上面講的是在內(nèi)核中如何使用的)。在mach-smdk2440.c中有:
//S3C2440初始化函數(shù) staticvoid__init smdk2440_machine_init(void) { //調(diào)用該函數(shù)將上面定義的LCD硬件信息保存到平臺(tái)數(shù)據(jù)中 s3c24xx_fb_set_platdata(&smdk2440_fb_info);
s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2440_devices,ARRAY_SIZE(smdk2440_devices)); smdk_machine_init(); } |
s3c24xx_fb_set_platdata定義在plat-s3c24xx/devs.c中:
void__init s3c24xx_fb_set_platdata(structs3c2410fb_mach_info*pd) { structs3c2410fb_mach_info*npd;
npd=kmalloc(sizeof(*npd),GFP_KERNEL); if(npd){ memcpy(npd,pd,sizeof(*npd)); //這里就是將內(nèi)核中定義的s3c2410fb_mach_info結(jié)構(gòu)體數(shù)據(jù)保存到LCD平臺(tái)數(shù)據(jù)中,所以在寫驅(qū)動(dòng)的時(shí)候就可以直接在平臺(tái)數(shù)據(jù)中獲取s3c2410fb_mach_info結(jié)構(gòu)體的數(shù)據(jù)(即LCD各種參數(shù)信息)進(jìn)行操作 s3c_device_lcd.dev.platform_data=npd; }else{ printk(KERN_ERR"no memory for LCD platform datan"); } } |
這里再講一個(gè)小知識(shí):不知大家有沒有留意,在平臺(tái)設(shè)備驅(qū)動(dòng)中,platform_data可以保存各自平臺(tái)設(shè)備實(shí)例的數(shù)據(jù),但這些數(shù)據(jù)的類型都是不同的,為什么都可以保存?這就要看看platform_data的定義,定義在/linux/device.h中,void *platform_data是一個(gè)void類型的指針,在Linux中void可保存任何數(shù)據(jù)類型。
評(píng)論