一 實驗內容簡要描述
1.實驗目的
學會驅動程序的編寫方法,配置S3C2410的LCD驅動,以及在LCD屏上顯示包括bmp和jpeg兩種格式的圖片
2.實驗內容
(1)分析S3c2410實驗箱LCD以及LCD控制器的硬件原理,據此找出相應的硬件設置參數,參考xcale實驗箱關於lcd的設置,完成s3c2410實驗箱LCD的設置
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 FrameBufferLinux是工作在保護模式下,所以用戶態進程是無法像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