LINUX之LCD驅動

一  實驗內容簡要描述
1.實驗目的
學會驅動程序的編寫方法,配置S3C2410的LCD驅動,以及在LCD屏上顯示包括bmp和jpeg兩種格式的圖片
2.實驗內容
 (1)分析S3c2410實驗箱LCD以及LCD控制器的硬件原理,據此找出相應的硬件設置參數,參考xcale實驗箱關於lcd的設置,完成s3c2410實驗箱LCD的設置

 (2)在LCD上顯示一張BMP圖片或JPEG圖片
3.實驗條件(軟硬件環境)
PC機、S3C2410開發板、PXA255開發板

二  實驗原理
1.  S3C2410內置LCD控制器分析
1.1  S3C2410 LCD控制器
一塊LCD屏顯示圖像,不但需要LCD驅動器,還需要有相應的LCD控制器。通常LCD驅動器會以COF/COG的形式與LCD 玻璃基板製作在一起,而LCD控制器則由外部電路來實現。而S3C2410內部已經集成了LCD控制器,因此可以很方便地去控制各種類型的LCD屏,例如:STN和TFT屏。S3C2410 LCD控制器的特性如下:
(1)STN屏
支持3種掃描方式:4bit單掃、4位雙掃和8位單掃
支持單色、4級灰度和16級灰度屏
支持256色和4096色彩色STN屏(CSTN)
支持分辯率爲640*480、320*240、160*160以及其它規格的多種LCD
(2)TFT屏
支持單色、4級灰度、256色的調色板顯示模式
支持64K和16M色非調色板顯示模式
支持分辯率爲640*480,320*240及其它多種規格的LCD
對於控制TFT屏來說,除了要給它送視頻資料(VD[23:0])以外,還有以下一些信號是必不可少的,分別是:
VSYNC(VFRAME) :幀同步信號
HSYNC(VLINE) :行同步信號
VCLK :像數時鐘信號
VDEN(VM) :數據有效標誌信號
由於本項目所用的S3C2410上的LCD是TFT屏,並且TFT屏將是今後應用的主流,因此接下來,重點圍繞TFT屏的控制來進行。
圖1.1是S3C2410內部的LCD控制器的邏輯示意圖:
 
圖1.1
REGBANK 是LCD控制器的寄存器組,用來對LCD控制器的各項參數進行設置。而 LCDCDMA 則是LCD控制器專用的DMA信道,負責將視頻資料從系統總線(System Bus)上取來,通過 VIDPRCS 從VD[23:0]發送給LCD屏。同時 TIMEGEN 和 LPC3600 負責產生 LCD屏所需要的控制時序,例如VSYNC、HSYNC、VCLK、VDEN,然後從 VIDEO MUX 送給LCD屏。 

1.2  TFT屏時序分析
圖1.2是TFT屏的典型時序。其中VSYNC是幀同步信號,VSYNC每發出1個脈衝,都意味着新的1屏視頻資料開始發送。而HSYNC爲行同步信號,每個HSYNC脈衝都表明新的1行視頻資料開始發送。而VDEN則用來標明視頻資料的有效,VCLK是用來鎖存視頻資料的像數時鐘。
並且在幀同步以及行同步的頭尾都必須留有回掃時間,例如對於VSYNC來說前回掃時間就是(VSPW+1)+(VBPD+1),後回掃時間就是(VFPD +1);HSYNC亦類同。這樣的時序要求是當初CRT顯示器由於電子槍偏轉需要時間,但後來成了實際上的工業標準,乃至於後來出現的TFT屏爲了在時序上於CRT兼容,也採用了這樣的控制時序。
 
圖1.2
S3C2410實驗箱上的LCD是一款3.5寸TFT真彩LCD屏,分辯率爲240*320,下圖爲該屏的時序要求。
 
圖1.3
通過對比圖1.2和圖1.3,我們不難看出:
VSPW+1=2 -> VSPW=1
VBPD+1=2 -> VBPD=1
LINVAL+1=320-> LINVAL=319
VFPD+1=3 -> VFPD=2
HSPW+1=4 -> HSPW=3
HBPD+1=7 -> HBPW=6
HOZVAL+1=240-> HOZVAL=239
HFPD+1=31 -> HFPD=30
以上各參數,除了LINVAL和HOZVAL直接和屏的分辯率有關,其它的參數在實際操作過程中應以上面的爲參考,不應偏差太多。 

1.3  LCD控制器主要寄存器功能詳解
 
圖1.4
LINECNT :當前行掃描計數器值,標明當前掃描到了多少行。
CLKVAL :決定VCLK的分頻比。LCD控制器輸出的VCLK是直接由系統總線(AHB)的工作頻率HCLK直接分頻得到的。做爲240*320的TFT屏,應保證得出的VCLK在5~10MHz之間。
MMODE :VM信號的觸發模式(僅對STN屏有效,對TFT屏無意義)。
PNRMODE :選擇當前的顯示模式,對於TFT屏而言,應選擇[11],即TFT LCD panel。
BPPMODE :選擇色彩模式,對於真彩顯示而言,選擇16bpp(64K色)即可滿足要求。
ENVID :使能LCD信號輸出。
 
圖1.5
VBPD , LINEVAL , VFPD , VSPW 的各項含義已經在前面的時序圖中得到體現。
 
圖1.6
HBPD , HOZVAL , HFPD 的各項含義已經在前面的時序圖中得到體現。
 
圖1.7
HSPW 的含義已經在前面的時序圖中得到體現。
MVAL 只對 STN屏有效,對TFT屏無意義。
HSPW 的含義已經在前面的時序圖中得到體現,這裏不再贅述。
MVAL 只對 STN屏有效,對TFT屏無意義。 
 
 
圖1.8
VSTATUS :當前VSYNC信號掃描狀態,指明當前VSYNC同步信號處於何種掃描階段。
HSTATUS :當前HSYNC信號掃描狀態,指明當前HSYNC同步信號處於何種掃描階段。
BPP24BL :設定24bpp顯示模式時,視頻資料在顯示緩衝區中的排列順序(即低位有效還是高位有效)。對於16bpp的64K色顯示模式,該設置位無意義。
FRM565 :對於16bpp顯示模式,有2中形式,一種是RGB=5:5:5:1,另一種是5:6:5。後一種模式最爲常用,它的含義是表示64K種色彩的16bit RGB資料中,紅色(R)佔了5bit,綠色(G)佔了6bit,蘭色(B)佔了5bit
INVVCLK , INVLINE , INVFRAME , INVVD :通過前面的時序圖,我們知道,CPU的LCD控制器輸出的時序默認是正脈衝,而LCD需要VSYNC(VFRAME)、VLINE(HSYNC)均爲負脈衝,因此 INVLINE 和 INVFRAME 必須設爲“1 ”,即選擇反相輸出。
INVVDEN , INVPWREN , INVLEND 的功能同前面的類似。
PWREN 爲LCD電源使能控制。在CPU LCD控制器的輸出信號中,有一個電源使能管腳LCD_PWREN,用來做爲LCD屏電源的開關信號。
ENLEND 對普通的TFT屏無效,可以不考慮。
BSWP 和 HWSWP 爲字節(Byte)或半字(Half-Word)交換使能。由於不同的GUI對FrameBuffer(顯示緩衝區)的管理不同,必要時需要通過調整 BSWP 和 HWSWP 來適應GUI。

Linux 驅動

2.1  FrameBuffer
Linux是工作在保護模式下,所以用戶態進程是無法像DOS那樣使用顯卡BIOS裏提供的中斷調用來實現直接寫屏,Lin仿顯卡的功能,將顯ux抽象出FrameBuffer這個設備來供用戶態進程實現直接寫屏。Framebuffer機制模卡硬件結構抽象掉,可以通過Framebuffer的讀寫直接對顯存進行操作。用戶可以將Framebuffer看成是顯示內存的一個映像,將其映射到進程地址空間之後,就可以直接進行讀寫操作,而寫操作可以立即反應在屏幕上。這種操作是抽象的,統一的。用戶不必關心物理顯存的位置、換頁機制等等具體細節。這些都是由Framebuffer設備驅動來完成的。

在Linux系統下,FrameBuffer的主要的結構如圖所示。Linux爲了開發FrameBuffer程序的方便,使用了分層結構。fbmem.c處於Framebuffer設備驅動技術的中心位置。它爲上層應用程序提供系統調用,也爲下一層的特定硬件驅動提供接口;那些底層硬件驅動需要用到這兒的接口來向系統內核註冊它們自己。

fbmem.c 爲所有支持FrameBuffer的設備驅動提供了通用的接口,避免重複工作。下將介紹fbmem.c主要的一些數據結構。

2.2  數據結構
2.2.1  Linux FrameBuffer的數據結構
在FrameBuffer中,fb_info可以說是最重要的一個結構體,它是Linux爲幀緩衝設備定義的驅動層接口。它不僅包含了底層函數,而且還有記錄設備狀態的數據。每個幀緩衝設備都與一個fb_info結構相對應。fb_info的主要成員如下
struct fb_info {
 int node;
 struct fb_var_screeninfo var; /* Current var */
 struct fb_fix_screeninfo fix;  /* Current fix */
 struct fb_videomode *mode; /* current mode */

 struct fb_ops *fbops;
 struct device *device;   /* This is the parent */
 struct device *dev;   /* This is this fb device */

 char __iomem *screen_base; /* Virtual address */
 unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */ 
 …………
};
其中node成員域標示了特定的FrameBuffer,實際上也就是一個FrameBuffer設備的次設備號。fb_var_screeninfo結構體成員記錄用戶可修改的顯示控制器參數,包括屏幕分辨率和每個像素點的比特數。fb_var_screeninfo中的xres定義屏幕一行有多少個點, yres定義屏幕一列有多少個點, bits_per_pixel定義每個點用多少個字節表示。其他域見以下代碼註釋。
struct fb_var_screeninfo {
 __u32 xres;   /* visible resolution */
 __u32 yres;
 __u32 xoffset;  /* offset from virtual to visible */
 __u32 yoffset;  /* resolution */
 __u32 bits_per_pixel; /* bits/pixel */
 __u32 pixclock;  /* pixel clock in ps (pico seconds) */
 __u32 left_margin; /* time from sync to picture */
 __u32 right_margin; /* time from picture to sync */
 __u32 hsync_len;  /* length of horizontal sync */
 __u32 vsync_len;  /* length of vertical sync */
 …………
};
在fb_info結構體中,fb_fix_screeninfo中記錄用戶不能修改的顯示控制器的參數,如屏幕緩衝區的物理地址,長度。當對幀緩衝設備進行映射操作的時候,就是從fb_fix_screeninfo中取得緩衝區物理地址的。
struct fb_fix_screeninfo {
 char id[16];        /* identification string eg "TT Builtin" */
 unsigned long smem_start;    /* Start of frame buffer mem (physical address) */
 __u32 smem_len;        /* Length of frame buffer mem */
 unsigned long mmio_start;    /* Start of Mem Mapped I/O(physical address) */
 __u32 mmio_len;      /* Length of Memory Mapped I/O  */
 …………
};
fb_info還有一個很重要的域就是fb_ops。它是提供給底層設備驅動的一個接口。通常我們編寫字符驅動的時候,要填寫一個file_operations結構體,並使用register_chrdev()註冊之,以告訴Linux如何操控驅動。當我們編寫一個FrameBuffer的時候,就要依照Linux FrameBuffer編程的套路,填寫fb_ops結構體。這個fb_ops也就相當於通常的file_operations結構體。
struct fb_ops {
int (*fb_open)(struct fb_info *info, int user);
int (*fb_release)(struct fb_info *info, int user);
ssize_t (*fb_read)(struct file *file, char __user *buf, size_t count, loff_t *ppos);
ssize_t (*fb_write)(struct file *file, const char __user *buf, size_t count,
loff_t *ppos);
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_setcmap)(struct fb_cmap *cmap, struct fb_info *info)
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
……………
}
上面的結構體,根據函數的名字就可以看出它的作用,這裏不在一一說明。下圖給出了Linux FrameBuffer的總體結構,作爲這一部分的總結。
 
圖2.2

2.2.2  S3C2410中LCD的數據結構
 在S3C2410的LCD設備驅動中,定義了s3c2410fb_info來標識一個LCD設備,結構體如下:
struct s3c2410fb_info {
 struct fb_info  *fb;
 struct device  *dev;
 struct s3c2410fb_mach_info *mach_info;
 struct s3c2410fb_hw regs;  /* LCD Hardware Regs */
 dma_addr_t  map_dma;  /* physical */
 u_char *   map_cpu;  /* virtual */
 u_int   map_size;
 /* addresses of pieces placed in raw buffer */
 u_char *   screen_cpu;  /* virtual address of buffer */
 dma_addr_t  screen_dma;  /* physical address of buffer */
 …………
};
成員變量fb指向我們上面所說明的fb_info結構體,代表了一個FrameBuffer。dev則表示了這個LCD設備。map_dma,map_cpu,map_size這三個域向了開闢給LCD DMA使用的內存地址。screen_cpu,screen_dma指向了LCD控制器映射的內存地址。另外regs標識了LCD控制器的寄存器。
struct s3c2410fb_hw {
 unsigned long lcdcon1;
 unsigned long lcdcon2;
 unsigned long lcdcon3;
 unsigned long lcdcon4;
 unsigned long lcdcon5;
};
這個寄存器和硬件的寄存器一一對應,主要作爲實際寄存器的映像,以便程序使用。
 這個s3c2410fb_info中還有一個s3c2410fb_mach_info成員域。它存放了和體系結構相關的一些信息,如時鐘、LCD設備的GPIO口等等。這個結構體定義爲
struct s3c2410fb_mach_info {
 unsigned char fixed_syncs; /* do not update sync/border */
 int type;      /* LCD types */
 int width;      /* Screen size */
 int height;
 struct s3c2410fb_val xres;  /* Screen info */
 struct s3c2410fb_val yres;
 struct s3c2410fb_val bpp;
 struct s3c2410fb_hw  regs;  /* lcd configuration registers */
 /* GPIOs */
 unsigned long gpcup;
 unsigned long gpcup_mask;
 unsigned long gpccon;
 unsigned long gpccon_mask;
 ………… 
}; 
  
圖2.3
上圖表示了S3C2410驅動的整體結構,反映了結構體之間的相互關係

2.3  主要代碼結構以及關鍵代碼分析
2.3.1  FrameBuffer驅動的統一管理
 fbmem.c實現了Linux FrameBuffer的中間層,任何一個FrameBuffer驅動,在系統初始化時,必須向fbmem.c註冊,即需要調用register_framebuffer()函數,在這個過程中,設備驅動的信息將會存放入名稱爲registered_fb數組中,這個數組定義爲
struct fb_info *registered_fb[FB_MAX];
int num_registered_fb;
它是類型爲fb_info的數組,另外num_register_fb則存放了註冊過的設備數量。
 我們分析一下register_framebuffer的代碼。
int register_framebuffer(struct fb_info *fb_info)
{
 int i;
 struct fb_event event;
 struct fb_videomode mode;

 if (num_registered_fb == FB_MAX) return -ENXIO; /* 超過最大數量 */
 num_registered_fb++;
 for (i = 0 ; i < FB_MAX; i++)
  if (!registered_fb[i]) break;     /* 找到空餘的數組空間 */
 fb_info->node = i;

 fb_info->dev = device_create(fb_class, fb_info->device,
     MKDEV(FB_MAJOR, i), "fb%d", i);  /* 爲設備建立設備節點 */
 if (IS_ERR(fb_info->dev)) {
  …………
 } else{
  fb_init_device(fb_info);      /* 初始化改設備 */
 }
 …………
 return 0;
}
從上面的代碼可知,當FrameBuffer驅動進行註冊的時候,它將驅動的fb_info結構體記錄到全局數組registered_fb中,並動態建立設備節點,進行設備的初始化。注意,這裏建立的設備節點的次設備號就是該驅動信息在registered_fb存放的位置,即數組下標i 。在完成註冊之後,fbmem.c就記錄了驅動的fb_info。這樣我們就有可能實現fbmem.c對全部FrameBuffer驅動的統一處理。

2.3.2  實現消息的分派
fbmem.c實現了對系統全部FrameBuffer設備的統一管理。當用戶嘗試使用一個特定的FrameBuffer時,fbmem.c怎麼知道該調用那個特定的設備驅動呢?
我們知道,Linux是通過主設備號和次設備號,對設備進行唯一標識。不同的FrameBuffer設備向fbmem.c註冊時,程序分配給它們的主設備號是一樣的,而次設備號是不一樣的。於是我們就可以通過用戶指明的次設備號,來覺得具體該調用哪一個FrameBuffer驅動。下面通過分析fbmem.c的fb_open()函數來說明。(注:一般我們寫FrameBuffer驅動不需要實現open函數,這裏只是說明函數流程。)


static int fb_open(struct inode *inode, struct file *file){
 int fbidx = iminor(inode);
 struct fb_info *info;
 int res;
    /* 得到真正驅動的函數指針 */
 if (!(info = registered_fb[fbidx])) return -ENODEV;  
 if (info->fbops->fb_open) {
  res = info->fbops->fb_open(info,1); //調用驅動的open()
  if (res)  module_put(info->fbops->owner);
 }
 return res;
}
當用戶打開一個FrameBuffer設備的時,將調用這裏的fb_open()函數。傳進來的inode就是欲打開設備的設備號,包括主設備和次設備號。fb_open函數首先通過iminor()函數取得次設備號,然後查全局數組registered_fb得到設備的fb_info信息,而這裏面存放了設備的操作函數集fb_ops。這樣,我們就可以調用具體驅動的fb_open() 函數,實現open的操作。下面給出了一個LCD驅動的open() 函數的調用流程圖,用以說明上面的步驟。
 
 圖2.4

2.3.3  開發板S3C2410 LCD驅動的流程
(1)在mach-smdk2410.c中,定義了初始的LCD參數。注意這是個全局變量。
static struct s3c2410fb_mach_info smdk2410_lcd_cfg = {
 .regs= {
  .lcdcon1 = S3C2410_LCDCON1_TFT16BPP |
   S3C2410_LCDCON1_TFT|
   S3C2410_LCDCON1_CLKVAL(7),
  ......
 },
 .width  = 240,   .height = 320,
 .xres = {.min = 240,.max= 240,.defval = 240},
 .bpp   = {.min = 16,  .max= 16,  .defval = 16},
 ......
};
(2)內核初始化時候調用s3c2410fb_probe函數。下面分析這個函數的做的工作。首先先動態分配s3c2410fb_info空間。
   fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info),&pdev->dev);
把域mach_info指向mach-smdk2410.c中的smdk2410_lcd_cfg 。
info->mach_info = pdev->dev.platform_data;
設置fb_info域的fix,var,fops字段。

fbinfo->fix.type  =  FB_TYPE_PACKED_PIXELS;
fbinfo->fix.type_aux     = 0;
fbinfo->fix.xpanstep     = 0;

fbinfo->var.nonstd     = 0;
fbinfo->var.activate   = FB_ACTIVATE_NOW;
fbinfo->var.height     = mach_info->height;
fbinfo->var.width     = mach_info->width;

fbinfo->fbops      = &s3c2410fb_ops;
……
該函數調用s3c2410fb_map_video_memory()申請DMA內存,即顯存。

fbi->map_size = PAGE_ALIGN(fbi->fb->fix.smem_len + PAGE_SIZE);
fbi->map_cpu  = dma_alloc_writecombine(fbi->dev, fbi->map_size,
          &fbi->map_dma, GFP_KERNEL);

fbi->map_size = fbi->fb->fix.smem_len;
…….
設置控制寄存器,設置硬件寄存器。

memcpy(&info->regs, &mach_info->regs,sizeof(info->regs));
info->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;
……….
調用函數s3c2410fb_init_registers(),把初始值寫入寄存器。

writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);
   writel(fbi->regs.lcdcon2, S3C2410_LCDCON2);

(3)當用戶調用mmap()映射內存的時候,Fbmem.c把剛纔設置好的顯存區域映射給用戶。
  start = info->fix.smem_start;
  len = PAGE_ALIGN( (start & ~PAGE_MASK) + info->fix.smem_len);
  io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,vma->vm_page_prot);
  ……
這樣就完成了驅動初始化到用戶調用的整個過程。

BMP和JPEG圖形顯示程序

3.1  在LCD上顯示BMP或JPEG圖片的主流程圖
首先,在程序開始前。要在nfs/dev目錄下創建LCD的設備結點,設備名fb0,設備類型爲字符設備,主設備號爲29,次設備號爲0。命令如下:
mknod fb0 c 29 0
在LCD上顯示圖象的主流程圖如圖3.1所示。程序一開始要調用open函數打開設備,然後調用ioctl獲取設備相關信息,接下來就是讀取圖形文件數據,把圖象的RGB值映射到顯存中,這部分是圖象顯示的核心。對於JPEG格式的圖片,要先經過JPEG解碼才能得到RGB數據,本項目中直接才用現成的JPEG庫進行解碼。對於bmp格式的圖片,則可以直接從文件裏面提取其RGB數據。要從一個bmp文件裏面把圖片數據陣列提取出來,首先必須知道bmp文件的格式。下面來詳細介紹bmp文件的格式。
 
圖3.1

3.2  bmp位圖格式分析
位圖文件可看成由四個部分組成:位圖文件頭、位圖信息頭、彩色表和定義位圖的字節陣列。如圖3.2所示。
 
圖3.2
文件頭中各個段的地址及其內容如圖3.3。
 
圖3.3
位圖文件頭數據結構包含BMP圖象文件的類型,顯示內容等信息。它的數據結構如下定義:
Typedef struct
{  
int  bfType;//表明位圖文件的類型,必須爲BM
long bfSize;//表明位圖文件的大小,以字節爲單位
int  bfReserved1;//屬於保留字,必須爲本0
int  bfReserved2;//也是保留字,必須爲本0
long bfOffBits;//位圖陣列的起始位置,以字節爲單位
}  BITMAPFILEHEADER;

(2)信息頭中各個段的地址及其內容如圖3.5所示。
 
圖3.5
位圖信息頭的數據結構包含了有關BMP圖象的寬,高,壓縮方法等信息,它的C語言數據結構如圖3.6所示。
Typedef struct {
long  biSize; //指出本數據結構所需要的字節數
long  biWidth;//以象素爲單位,給出BMP圖象的寬度
long  biHeight;//以象素爲單位,給出BMP圖象的高度
int    biPlanes;//輸出設備的位平面數,必須置爲1
int    biBitCount;//給出每個象素的位數
long  biCompress;//給出位圖的壓縮類型
long  biSizeImage;//給出圖象字節數的多少
long  biXPelsPerMeter;//圖像的水平分辨率
long  biYPelsPerMeter;//圖象的垂直分辨率
long  biClrUsed;//調色板中圖象實際使用的顏色素數
long  biClrImportant;//給出重要顏色的索引值
} BITMAPINFOHEADER;


(3)對於象素小於或等於16位的圖片,都有一個顏色表用來給圖象數據陣列提供顏色索引,其中的每塊數據都以B、G、R的順序排列,還有一個是reserved保留位。而在圖形數據區域存放的是各個象素點的索引值。它的C語言結構如圖3.7所示。
 
圖3.7  顏色表數據結構
(4)對於24位和32位的圖片,沒有彩色表,他在圖象數據區裏直接存放圖片的RGB數據,其中的每個象素點的數據都以B、G、R的順序排列。每個象素點的數據結構如圖3.8所示。
 
圖3.8  圖象數據陣列的數據結構
(5)由於圖象數據陣列中的數據是從圖片的最後一行開始往上存放的,因此在顯示圖象時,是從圖象的左下角開始逐行掃描圖象,即從左到右,從下到上。
(6)對S3C2410或PXA255開發板上的LCD來說,他們每個象素點所佔的位數爲16位,這16位按B:G:R=5:6:5的方式分,其中B在最高位,R在最低位。而從bmp圖象得到的R、G、B數據則每個數據佔8位,合起來一共24位,因此需要對該R、G、B數據進行移位組合成一個16位的數據。移位方法如下:
b >>= 3; g >>= 2; r >>= 3;
RGBValue = ( r<<11 | g << 5 | b);
基於以上分析,提取各種類型的bmp圖象的流程如圖3.9所示
 

圖 3.9

3.3  實現顯示任意大小的圖片
開發板上的LCD屏的大小是固定的,S3C2410上的LCD爲:240*320,PXA255上的爲:640*480。比屏幕小的圖片在屏上顯示當然沒問題,但是如果圖片比屏幕大呢?這就要求我們通過某種算法對圖片進行縮放。
縮放的基本思想是將圖片分成若干個方塊,對每個方塊中的R、G、B數據進行取平均,得到一個新的R、G、B值,這個值就作爲該方塊在LCD屏幕上的映射。


縮放的算法描述如下:
(1)、計算圖片大小與LCD屏大小的比例,以及方塊的大小。爲了適應各種屏幕大小,這裏並不直接給lcd_width和lcd_height賦值爲240和320。而是調用標準的接口來獲取有關屏幕的參數。具體如下:
    // Get variable screen information
    if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
        printf("Error reading variable information. ");
        exit(3);
    }
unsigned int lcd_width=vinfo.xres;
 unsigned int lcd_height=vinfo.yres;

計算比例:
widthScale=bmpi->width/lcd_width;
heightScale=bmpi->height/lcd_height;
本程序中方塊的大小以如下的方式確定:
unsigned int paneWidth= 
 unsigned int paneHeight= ;
符號 代表向上取整。
(2)、從圖片的左上角開始,以(i* widthScale,j* heightScale)位起始點,以寬paneWidth 高paneHeight爲一個小方塊,對該方塊的R、G、B數值分別取平均,得到映射點的R、G、B值,把該點作爲要在LCD上顯示的第(i , j)點存儲起來。
這部分的程序如下:
 //-------------取平均--------
 for( i=0;i<now_height;i++)
 {
  for(j=0;j<now_width;j++)
  {   
   color_sum_r=0;
   color_sum_g=0;
   color_sum_b=0;
   for(m=i*heightScale;m<i*heightScale+paneHeight;m++)
   {
    for(n=j*widthScale;n<j*widthScale+paneWidth;n++)
    {
     color_sum_r+=pointvalue[m][n].r;
     color_sum_g+=pointvalue[m][n].g;
     color_sum_b+=pointvalue[m][n].b;
    }
   }
   RGBvalue_256->r=div_round(color_sum_r,paneHeight*paneWidth);
   RGBvalue_256->g=div_round(color_sum_g,paneHeight*paneWidth);
   RGBvalue_256->b=div_round(color_sum_b,paneHeight*paneWidth);
  }
 }
3.4  圖片數據提取及顯示的總流程
通過以上的分析,整個圖片數據提取及顯示的總流程如圖3.10 所示。 
 
圖 3.10


轉載地址http://www.linuxidc.com/Linux/2011-08/41673.htm

 

發佈了51 篇原創文章 · 獲贊 18 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章