1. 背景知識 在多媒體的推動下,彩色LCD越來越多地應用到嵌入式系統中,PDA和手機等大多都採用LCD作為顯示器材,因此學習LCD的應用很有實際意義! LCD工作的硬體需求:要使一塊LCD正常的顯示文字或圖像,不僅需要LCD驅動器,而且還需要相應的LCD控制器。在通常情況下,生產廠商把LCD驅動器
1. 背景知識
- 在多媒體的推動下,彩色LCD越來越多地應用到嵌入式系統中,PDA和手機等大多都採用LCD作為顯示器材,因此學習LCD的應用很有實際意義!
- LCD工作的硬體需求:要使一塊LCD正常的顯示文字或圖像,不僅需要LCD驅動器,而且還需要相應的LCD控制器。在通常情況下,生產廠商把LCD驅動器會以COF/COG的形式與LCD玻璃基板製作在一起,而LCD控制器則是由外部的電路來實現,現在很多的MCU內部都集成了LCD控制器,如S3C2410/2440等。TQ2440是採用了S3C2440,S3C2410通過LCD控制器就可以產生LCD驅動器所需要的控制信號來控制STN/TFT屏了。
- S3C2440內部LCD控制器結構圖
根據數據手冊來描述一下這個集成在S3C2440內部的LCD控制器:
a:LCD控制器由REGBANK、LCDCDMA、TIMEGEN、VIDPRCS寄存器組成;
b:REGBANK由17個可編程的寄存器組和一塊256*16的調色板記憶體組成,它們用來配置LCD控制器的;
c:LCDCDMA是一個專用的DMA,它能自動地把在偵記憶體中的視頻數據傳送到LCD驅動器,通過使用這個DMA通道,視頻數據在不需要CPU的干預的情況下顯示在LCD屏上;
d:VIDPRCS接收來自LCDCDMA的數據,將數據轉換為合適的數據格式,比如說4/8位單掃,4位雙掃顯示模式,然後通過數據埠VD[23:0]傳送視頻數據到LCD驅動器;
e:TIMEGEN由可編程的邏輯組成,他生成LCD驅動器需要的控制信號,比如VSYNC、HSYNC、VCLK和LEND等等,而這些控制 信號又與REGBANK寄存器組中的LCDCON1/2/3/4/5的配置密切相關,通過不同的配置,TIMEGEN就能產生這些信號的不同形態,從而支 持不同的LCD驅動器(即不同的STN/TFT屏)
- 常見TFT屏工作時序分析:
LCD提供的外部介面信號:
VSYNC/VFRAME/STV:垂直同步信號(TFT)/幀同步信號(STN)/SEC TFT信號;
HSYNC/VLINE/CPV:水平同步信號(TFT)/行同步脈衝信號(STN)/SEC TFT信號;
VCLK/LCD_HCLK:象素時鐘信號(TFT/STN)/SEC TFT信號;
VD[23:0]:LCD
像素數據輸出埠(TFT/STN/SEC TFT);
VDEN/VM/TP:數據使能信號(TFT)/LCD驅動交流偏置信號(STN)/SEC TFT 信號;
LEND/STH:行結束信號(TFT)/SEC TFT信號;
LCD_LPCOE:SEC TFT OE
信號;
LCD_LPCREV:SEC TFT REV
信號;
LCD_LPCREVB:SEC TFT REVB信
號。
VBPD(vertical back porch):
表示在一幀圖像開始時,垂直同步信號以後的無效的行數,對應驅動中的upper_margin;
VFBD(vertical front porch):表示在一幀圖像結束後,垂直同步信號以前的無效的行數,對應驅動中的lower_margin;
VSPW(vertical sync pulse width):表示垂直同步脈衝的寬度,用行數計算,對應驅動中的vsync_len;
HBPD(horizontal back porch):表示從水平同步信號開始到一行的有效數據開始之間的VCLK的個數,對應驅動中的left_margin;
HFPD(horizontal front porth):表示一行的有效數據結束到下一個水平同步信號開始之間的VCLK的個數,對應驅動中的right_margin;
HSPW(horizontal sync pulse width):表示水平同步信號的寬度,用VCLK計算,對應驅動中的hsync_len;
作為幀同步信號的VSYNC,每發出一個脈衝,都意味著新的一屏圖像數據開始發送。而作為行同步信號的HSYNC,每發出一脈衝意味著新的一行圖像資料開始發送。在幀同步和行同步的頭尾留有回掃時間,這樣的時序安排起源於CRT顯示器電子槍偏轉所需要的時間,但後來成為實際上的工業標準,因此TFT屏也包含了回掃時間。
所有顯示器顯示圖像的原理都是從上到下,從左到右的。這是什麼意思呢?這麼說吧,一副圖像可以看做是一個矩形,由很多排列整齊的一行一行點組成,這些點稱之為像素。那麼這幅圖在LCD上的顯示原理就是:
A:顯示指針從矩形左上角的第一行第一個點開始,一個點一個點的在LCD上顯示,在上面的時序圖上用時間線表示就為VCLK,我們稱之為像素時鐘信號;
B:當顯示指針一直顯示到矩形的右邊就結束這一行,那麼這一行的動作在上面的時序圖中就稱之為1 Line;
C:接下來顯示指針又回到矩形的左邊從第二行開始顯示,註意,顯示指針在從第一行的右邊回到第二行的左邊是需要一定的時間的,我們稱之為行切換;
D:如此類推,顯示指針就這樣一行一行的顯示至矩形的右下角才把一副圖顯示完成。因此,這一行一行的顯示在時間線上看,就是時序圖上的HSYNC;
E:然 而,LCD的顯示並不是對一副圖像快速的顯示一下,為了持續和穩定的在LCD上顯示,就需要切換到另一幅圖上(另一幅圖可以和上一副圖一樣或者不一樣,目的只是為了將圖像持續的顯示在LCD上)。那麼這一副一副的圖像就稱之為幀,在時序圖上就表示為1 Frame,因此從時序圖上可以看出1 Line只是1 Frame中的一行;
F:同樣的,在幀與幀切換之間也是需要一定的時間的,我們稱之為幀切換,那麼LCD整個顯示的過程在時間線上看,就可表示為時序圖上的VSYNC
LCD控制器時序參數可以用下圖形象的表示出來:
- 幀緩衝
幀緩衝是Linux為顯示設備提供的一個介面,它把一些顯示設備描述成一個緩衝區,允許應用程式通過 FrameBuffer定義好的介面訪問這些圖形設備,從而不用去關心具體的硬體細節。對於幀緩衝設備而言,只要在顯示緩衝區與顯示點對應的區域寫入顏色 值,對應的顏色就會自動的在屏幕上顯示。
通過幀緩衝顯示漢字點陣,成為linux漢化的唯一可能性,幀緩衝設備是標準字元設備,主設備號為29,對應於/dev/fbn設備文件。
2. 幀緩衝(FrameBuffer)設備驅動結構
幀緩衝設備為標準的字元型設備,在Linux中主設備號29,定義在/include/linux/major.h,但FB_MAJOR是在include\linux\fb.h中定義,次設備號定義幀緩衝的個數,最大允許有32個FrameBuffer,定義在/include/linux/fb.h中的FB_MAX,也就是最大支持顯示器的個數。對應於文件系統下/dev /fb%d設備文件
3. 幀緩衝設備驅動在Linux子系統中的結構如下:
幀緩衝設備提供給用戶空間的file_operations結構體由fbmem.c的file_operation提供,而特定幀緩衝設備結構體的註冊、註銷以及其中成員的維護,尤其是fb_ops中成員函數的實現則由xxxfb.c文件實現,fb_ops中成員函數最終會操作LCD控制器硬體寄存器。
4. 幀緩衝相關的重要數據結構
從幀緩衝設備驅動程式結構 看,該驅動主要跟fb_info結構體有關,該結構體記錄了幀緩衝設備的全部信息,包括設備的設置參數、狀態以及對底層硬體操作的函數指針。在Linux 中,幀緩衝設備最關鍵的一個數據結構體是fb_info(為了便於記憶,我們把它簡稱為“FBI”),FBI中包括了關於幀緩衝設備屬性和操作的完整描述。每一個幀緩衝設備都必須對應一個fb_info,fb_info在/linux/fb.h中的定義如下:(只列出重要的一些)
4.1 fb_info結構體
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
struct fb_info {
int node;
int flags;
struct mutex lock ; /* 用於open/release/ioctl的鎖*/
struct fb_var_screeninfo var ; /*LCD可變參數*/
struct fb_fix_screeninfo fix; /*LCD固定參數*/
struct fb_monspecs monspecs; /*LCD顯示器標準*/
struct work_struct queue; /*幀緩衝事件隊列*/
struct fb_pixmap pixmap; /*圖像硬體mapper*/
struct fb_pixmap sprite; /*游標硬體mapper*/
struct fb_cmap cmap; /*當前的顏色表*/
struct fb_videomode *mode; /*當前的顯示模式*/
#ifdef CONFIG_FB_BACKLIGHT
struct backlight_device *bl_dev; /*對應的背光設備*/
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS]; /*背光調整*/
#endif
#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif
struct fb_ops *fbops; /*對底層硬體操作的函數指針*/
struct device *device;
struct device *dev; /*fb設備*/
int class_flag;
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /*圖塊Blitting*/
#endif
char __iomem *screen_base; /*虛擬基地址*/
unsigned long screen_size; /*LCD IO映射的虛擬記憶體大小*/
void *pseudo_palette; /*偽16色顏色表*/
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /*LCD的掛起或恢復狀態*/
void *fbcon_par;
void *par;
};
|
其中,比較重要的成員有struct fb_var_screeninfo var、struct fb_fix_screeninfo fix和struct fb_ops *fbops,他們也都是結構體,下麵我們一個一個的來看。
4.2 struct fb_var_screeninfo
fb_var_screeninfo結構體主要記錄用戶可以修改的控制器的參數,比如屏幕的解析度和每個像素的比特數等。例如:fb_var_screeninfo中的xres定義屏幕一行有多少個點,yres定義一屏幕一列有多少個點,bits_per_pixel定義每個點用多少個自己表示。該結構體定義如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
struct fb_var_screeninfo {
__u32 xres; /*可見屏幕一行有多少個像素點*/
__u32 yres; /*可見屏幕一列有多少個像素點*/
__u32 xres_virtual; /*虛擬屏幕一行有多少個像素點*/
__u32 yres_virtual; /*虛擬屏幕一列有多少個像素點*/
__u32 xoffset; /*虛擬到可見屏幕之間的行偏移*/
__u32 yoffset; /*虛擬到可見屏幕之間的列偏移*/
__u32 bits_per_pixel; /*每個像素的位數即BPP*/
__u32 grayscale; /*非0時,指的是灰度*/
struct fb_bitfield red; /*fb緩存的R位域*/
struct fb_bitfield green; /*fb緩存的G位域*/
struct fb_bitfield blue; /*fb緩存的B位域*/
struct fb_bitfield transp; /*透明度*/
__u32 nonstd; /* != 0 非標準像素格式*/
__u32 activate;
__u32 height; /*高度*/
__u32 width; /*寬度*/
__u32 accel_flags;
/*定時:除了pixclock本身外,其他的都以像素時鐘為單位*/
__u32 pixclock; /*像素時鐘(皮秒)*/
__u32 left_margin; /*行切換,從同步到繪圖之間的延遲*/
__u32 right_margin; /*行切換,從繪圖到同步之間的延遲*/
__u32 upper_margin; /*幀切換,從同步到繪圖之間的延遲*/
__u32 lower_margin; /*幀切換,從繪圖到同步之間的延遲*/
__u32 hsync_len; /*水平同步的長度*/
__u32 vsync_len; /*垂直同步的長度*/
__u32 sync;
__u32 vmode;
__u32 rotate;
__u32 reserved[5]; /*保留*/
};
|
4.3 fb_fix_screeninfo結構體
fb_fix_screeninfo結構體又主要記錄用戶不可以修改的控制器的參數,比如屏幕緩衝區的物理地址和長度等。當幀緩衝設備進行映射操作的時候,就是從fb_fix_screeninfo中取得緩衝區物理地址。該結構體的定義如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
struct fb_fix_screeninfo {
char id[16]; /*字元串形式的標示符 */
unsigned long smem_start; /*fb緩存的開始位置 */
__u32 smem_len; /*fb緩存的長度 */
__u32 type; /*看FB_TYPE_* */
__u32 type_aux; /*分界*/
__u32 visual; /*看FB_VISUAL_* */
__u16 xpanstep; /*如果沒有硬體panning就賦值為0 */
__u16 ypanstep; /*如果沒有硬體panning就賦值為0 */
__u16 ywrapstep; /*如果沒有硬體ywrap就賦值為0 */
__u32 line_length; /*一行的位元組數 */
unsigned long mmio_start; /*記憶體映射IO的開始位置*/
__u32 mmio_len; /*記憶體映射IO的長度*/
__u32 accel;
__u16 reserved[3]; /*保留*/
};
|
4.4 fb_ops結構體
fb_ops結構體是對底層硬體操作的函數指針,該結構體中定義了對硬體的操作有:(這裡只列出了常用的操作)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
struct fb_ops {
struct module *owner;
//檢查可變參數併進行設置
int (*fb_check_var)( struct fb_var_screeninfo * var , struct fb_info *info);
//根據設置的值進行更新,使之有效
int (*fb_set_par)( struct fb_info *info);
//設置顏色寄存器
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info);
//顯示空白
int (*fb_blank)( int blank, struct fb_info *info);
//矩形填充
void (*fb_fillrect) ( struct fb_info *info, const struct fb_fillrect *rect);
//複製數據
void (*fb_copyarea) ( struct fb_info *info, const struct fb_copyarea *region);
//圖形填充
void (*fb_imageblit) ( struct fb_info *info, const struct fb_image *image);
};
|
5. 幀緩衝設備作為平臺設備
S3C2440的LCD控制器被集成在晶元的內部作為一個相對獨立的單元,所以Linux把它看做是一個平臺設備,故在內核代碼/arch/arm/plat-s3c24xx/devs.c中定義有LCD相關的平臺設備及資源,代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
/* LCD Controller */
//LCD控制器的資源信息
static struct resource s3c_lcd_resource[] = {
[0] = {
.start = S3C24XX_PA_LCD, //控制器IO埠開始地址
.end = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1, //控制器IO埠結束地址
.flags = IORESOURCE_MEM, //標識為LCD控制器IO埠,在驅動中引用這個就表示引用IO埠
},
[1] = {
.start = IRQ_LCD, //LCD中斷
.end = IRQ_LCD,
.flags = IORESOURCE_IRQ, //標識為LCD中斷
}
};
static u64 s3c_device_lcd_dmamask = 0xffffffffUL;
struct platform_device s3c_device_lcd = {
.name = "s3c2410-lcd" , //作為平臺設備的LCD設備名
.id = -1,
.num_resources = ARRAY_SIZE(s3c_lcd_resource), //資源數量
.resource = s3c_lcd_resource, //引用上面定義的資源
.dev = {
.dma_mask = &s3c_device_lcd_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
EXPORT_SYMBOL(s3c_device_lcd); //導出定義的LCD平臺設備,好在mach-smdk2440.c的smdk2440_devices[]中添加到平臺設備列表中
|
除此之外,Linux還在/arch/arm/mach-s3c2410/include/mach/fb.h中為LCD平臺設備定義了一個 s3c2410fb_mach_info結構體,該結構體主要是記錄LCD的硬體參數信息(比如該結構體的s3c2410fb_display成員結構中 就用於記錄LCD的屏幕尺寸、屏幕信息、可變的屏幕參數、LCD配置寄存器等),這樣在寫驅動的時候就直接使用這個結構體。下麵,我們來看一下內核是如果使用這個結構體的。在/arch/arm/mach-s3c2440/mach-smdk2440.c中定義有: