當(dāng)前位置:首頁 > 單片機 > 單片機
[導(dǎo)讀]一 實驗內(nèi)容簡要描述1.實驗?zāi)康膶W(xué)會驅(qū)動程序的編寫方法,配置S3C2410的LCD驅(qū)動,以及在LCD屏上顯示包括bmp和jpeg兩種格式的圖片2.實驗內(nèi)容(1)分析S3c2410實驗箱LCD以及LCD控制器的硬件原理,據(jù)此找出相應(yīng)的硬件設(shè)

一 實驗內(nèi)容簡要描述

1.實驗?zāi)康?/p>

學(xué)會驅(qū)動程序的編寫方法,配置S3C2410的LCD驅(qū)動,以及在LCD屏上顯示包括bmp和jpeg兩種格式的圖片

2.實驗內(nèi)容

(1)分析S3c2410實驗箱LCD以及LCD控制器的硬件原理,據(jù)此找出相應(yīng)的硬件設(shè)置參數(shù),參考xcale實驗箱關(guān)于lcd的設(shè)置,完成s3c2410實驗箱LCD的設(shè)置

(2)在LCD上顯示一張BMP圖片或JPEG圖片

3.實驗條件(軟硬件環(huán)境)

PC機、S3C2410開發(fā)板、PXA255開發(fā)板

二 實驗原理

1. S3C2410內(nèi)置LCD控制器分析

1.1 S3C2410 LCD控制器

一 塊LCD屏顯示圖像,不但需要LCD驅(qū)動器,還需要有相應(yīng)的LCD控制器。通常LCD驅(qū)動器會以COF/COG的形式與LCD 玻璃基板制作在一起,而LCD控制器則由外部電路來實現(xiàn)。而S3C2410內(nèi)部已經(jīng)集成了LCD控制器,因此可以很方便地去控制各種類型的LCD屏,例如:STN和TFT屏。S3C2410 LCD控制器的特性如下:

(1)STN屏

支持3種掃描方式:4bit單掃、4位雙掃和8位單掃

支持單色、4級灰度和16級灰度屏

支持256色和4096色彩色STN屏(CSTN)

支持分辯率為640*480、320*240、160*160以及其它規(guī)格的多種LCD

(2)TFT屏

支持單色、4級灰度、256色的調(diào)色板顯示模式

支持64K和16M色非調(diào)色板顯示模式

支持分辯率為640*480,320*240及其它多種規(guī)格的LCD

對于控制TFT屏來說,除了要給它送視頻資料(VD[23:0])以外,還有以下一些信號是必不可少的,分別是:

VSYNC(VFRAME) :幀同步信號

HSYNC(VLINE) :行同步信號

VCLK :像數(shù)時鐘信號

VDEN(VM) :數(shù)據(jù)有效標(biāo)志信號

由于本項目所用的S3C2410上的LCD是TFT屏,并且TFT屏將是今后應(yīng)用的主流,因此接下來,重點圍繞TFT屏的控制來進行。

圖1.1是S3C2410內(nèi)部的LCD控制器的邏輯示意圖:

圖1.1

REGBANK 是LCD控制器的寄存器組,用來對LCD控制器的各項參數(shù)進行設(shè)置。而 LCDCDMA 則是LCD控制器專用的DMA信道,負責(zé)將視頻資料從系統(tǒng)總線(System Bus)上取來,通過 VIDPRCS 從VD[23:0]發(fā)送給LCD屏。同時 TIMEGEN 和 LPC3600 負責(zé)產(chǎn)生 LCD屏所需要的控制時序,例如VSYNC、HSYNC、VCLK、VDEN,然后從 VIDEO MUX 送給LCD屏。

1.2 TFT屏?xí)r序分析

圖 1.2是TFT屏的典型時序。其中VSYNC是幀同步信號,VSYNC每發(fā)出1個脈沖,都意味著新的1屏視頻資料開始發(fā)送。而HSYNC為行同步信號,每個HSYNC脈沖都表明新的1行視頻資料開始發(fā)送。而VDEN則用來標(biāo)明視頻資料的有效,VCLK是用來鎖存視頻資料的像數(shù)時鐘。

并且在幀同步以及行同步的頭尾都必須留有回掃時間,例如對于VSYNC來說前回掃時間就是(VSPW+1)+(VBPD+1),后回掃時間就是(VFPD +1);HSYNC亦類同。這樣的時序要求是當(dāng)初CRT顯示器由于電子槍偏轉(zhuǎn)需要時間,但后來成了實際上的工業(yè)標(biāo)準(zhǔn),乃至于后來出現(xiàn)的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

以上各參數(shù),除了LINVAL和HOZVAL直接和屏的分辯率有關(guān),其它的參數(shù)在實際操作過程中應(yīng)以上面的為參考,不應(yīng)偏差太多。

1.3 LCD控制器主要寄存器功能詳解

圖1.4

LINECNT :當(dāng)前行掃描計數(shù)器值,標(biāo)明當(dāng)前掃描到了多少行。

CLKVAL :決定VCLK的分頻比。LCD控制器輸出的VCLK是直接由系統(tǒng)總線(AHB)的工作頻率HCLK直接分頻得到的。做為240*320的TFT屏,應(yīng)保證得出的VCLK在5~10MHz之間。

MMODE :VM信號的觸發(fā)模式(僅對STN屏有效,對TFT屏無意義)。

PNRMODE :選擇當(dāng)前的顯示模式,對于TFT屏而言,應(yīng)選擇[11],即TFT LCD panel。

BPPMODE :選擇色彩模式,對于真彩顯示而言,選擇16bpp(64K色)即可滿足要求。

ENVID :使能LCD信號輸出。

圖1.5

VBPD , LINEVAL , VFPD , VSPW 的各項含義已經(jīng)在前面的時序圖中得到體現(xiàn)。

圖1.6

HBPD , HOZVAL , HFPD 的各項含義已經(jīng)在前面的時序圖中得到體現(xiàn)。

圖1.7

HSPW 的含義已經(jīng)在前面的時序圖中得到體現(xiàn)。

MVAL 只對 STN屏有效,對TFT屏無意義。

HSPW 的含義已經(jīng)在前面的時序圖中得到體現(xiàn),這里不再贅述。

MVAL 只對 STN屏有效,對TFT屏無意義。

圖1.8

VSTATUS :當(dāng)前VSYNC信號掃描狀態(tài),指明當(dāng)前VSYNC同步信號處于何種掃描階段。

HSTATUS :當(dāng)前HSYNC信號掃描狀態(tài),指明當(dāng)前HSYNC同步信號處于何種掃描階段。

BPP24BL :設(shè)定24bpp顯示模式時,視頻資料在顯示緩沖區(qū)中的排列順序(即低位有效還是高位有效)。對于16bpp的64K色顯示模式,該設(shè)置位無意義。

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 必須設(shè)為“1 ”,即選擇反相輸出。

INVVDEN , INVPWREN , INVLEND 的功能同前面的類似。

PWREN 為LCD電源使能控制。在CPU LCD控制器的輸出信號中,有一個電源使能管腳LCD_PWREN,用來做為LCD屏電源的開關(guān)信號。

ENLEND 對普通的TFT屏無效,可以不考慮。

BSWP 和 HWSWP 為字節(jié)(Byte)或半字(Half-Word)交換使能。由于不同的GUI對FrameBuffer(顯示緩沖區(qū))的管理不同,必要時需要通過調(diào)整 BSWP 和 HWSWP 來適應(yīng)GUI。

2. Linux 驅(qū)動

2.1 FrameBuffer

Linux 是工作在保護模式下,所以用戶態(tài)進程是無法像DOS那樣使用顯卡BIOS里提供的中斷調(diào)用來實現(xiàn)直接寫屏,Lin仿顯卡的功能,將顯ux抽象出 FrameBuffer這個設(shè)備來供用戶態(tài)進程實現(xiàn)直接寫屏。Framebuffer機制??ㄓ布Y(jié)構(gòu)抽象掉,可以通過Framebuffer的讀寫直接對顯存進行操作。用戶可以將Framebuffer看成是顯示內(nèi)存的一個映像,將其映射到進程地址空間之后,就可以直接進行讀寫操作,而寫操作可以立即反應(yīng)在屏幕上。這種操作是抽象的,統(tǒng)一的。用戶不必關(guān)心物理顯存的位置、換頁機制等等具體細節(jié)。這些都是由Framebuffer設(shè)備驅(qū)動來完成的。

在 Linux系統(tǒng)下,F(xiàn)rameBuffer的主要的結(jié)構(gòu)如圖所示。Linux為了開發(fā)FrameBuffer程序的方便,使用了分層結(jié)構(gòu)。fbmem.c 處于Framebuffer設(shè)備驅(qū)動技術(shù)的中心位置。它為上層應(yīng)用程序提供系統(tǒng)調(diào)用,也為下一層的特定硬件驅(qū)動提供接口;那些底層硬件驅(qū)動需要用到這兒的接口來向系統(tǒng)內(nèi)核注冊它們自己。

fbmem.c 為所有支持FrameBuffer的設(shè)備驅(qū)動提供了通用的接口,避免重復(fù)工作。下將介紹fbmem.c主要的一些數(shù)據(jù)結(jié)構(gòu)。

2.2 數(shù)據(jù)結(jié)構(gòu)

2.2.1 Linux FrameBuffer的數(shù)據(jù)結(jié)構(gòu)

在FrameBuffer中,fb_info可以說是最重要的一個結(jié)構(gòu)體,它是Linux為幀緩沖設(shè)備定義的驅(qū)動層接口。它不僅包含了底層函數(shù),而且還有記錄設(shè)備狀態(tài)的數(shù)據(jù)。每個幀緩沖設(shè)備都與一個fb_info結(jié)構(gòu)相對應(yīng)。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成員域標(biāo)示了特定的FrameBuffer,實際上也就是一個FrameBuffer設(shè)備的次設(shè)備號。fb_var_screeninfo結(jié)構(gòu)體成員記錄用戶可修改的顯示控制器參數(shù),包括屏幕分辨率和每個像素點的比特數(shù)。fb_var_screeninfo中的xres定義屏幕一行有多少個點, yres定義屏幕一列有多少個點, bits_per_pixel定義每個點用多少個字節(jié)表示。其他域見以下代碼注釋。

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結(jié)構(gòu)體中,fb_fix_screeninfo中記錄用戶不能修改的顯示控制器的參數(shù),如屏幕緩沖區(qū)的物理地址,長度。當(dāng)對幀緩沖設(shè)備進行映射操作的時候,就是從fb_fix_screeninfo中取得緩沖區(qū)物理地址的。

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。它是提供給底層設(shè)備驅(qū)動的一個接口。通常我們編寫字符驅(qū)動的時候,要填寫一個file_operations結(jié)構(gòu)體,并使用register_chrdev()注冊之,以告訴Linux如何操控驅(qū)動。當(dāng)我們編寫一個FrameBuffer的時候,就要依照Linux FrameBuffer編程的套路,填寫fb_ops結(jié)構(gòu)體。這個fb_ops也就相當(dāng)于通常的file_operations結(jié)構(gòu)體。

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);

……………

}

上面的結(jié)構(gòu)體,根據(jù)函數(shù)的名字就可以看出它的作用,這里不在一一說明。下圖給出了Linux FrameBuffer的總體結(jié)構(gòu),作為這一部分的總結(jié)。

圖2.2

2.2.2 S3C2410中LCD的數(shù)據(jù)結(jié)構(gòu)

在S3C2410的LCD設(shè)備驅(qū)動中,定義了s3c2410fb_info來標(biāo)識一個LCD設(shè)備,結(jié)構(gòu)體如下:

struct s3c2410fb_info {

struct fb_info*fb;

struct device*dev;

struct s3c2410fb_mach_info *mach_info;

struct s3c2410fb_hwregs;/* LCD Hardware Regs */

dma_addr_tmap_dma;/* physical */

u_char *map_cpu;/* virtual */

u_intmap_size;

/* addresses of pieces placed in raw buffer */

u_char *screen_cpu;/* virtual address of buffer */

dma_addr_tscreen_dma;/* physical address of buffer */

…………

};

成 員變量fb指向我們上面所說明的fb_info結(jié)構(gòu)體,代表了一個FrameBuffer。dev則表示了這個LCD設(shè)備。 map_dma,map_cpu,map_size這三個域向了開辟給LCD DMA使用的內(nèi)存地址。screen_cpu,screen_dma指向了LCD控制器映射的內(nèi)存地址。另外regs標(biāo)識了LCD控制器的寄存器。

struct s3c2410fb_hw {

unsigned longlcdcon1;

unsigned longlcdcon2;

unsigned longlcdcon3;

unsigned longlcdcon4;

unsigned longlcdcon5;

};

這個寄存器和硬件的寄存器一一對應(yīng),主要作為實際寄存器的映像,以便程序使用。

這個s3c2410fb_info中還有一個s3c2410fb_mach_info成員域。它存放了和體系結(jié)構(gòu)相關(guān)的一些信息,如時鐘、LCD設(shè)備的GPIO口等等。這個結(jié)構(gòu)體定義為

struct s3c2410fb_mach_info {

unsigned charfixed_syncs;/* do not update sync/border */

inttype; /* LCD types */

intwidth; /* Screen size */

intheight;

struct s3c2410fb_val xres; /* Screen info */

struct s3c2410fb_val yres;

struct s3c2410fb_val bpp;

struct s3c2410fb_hw regs; /* lcd configuration registers */

/* GPIOs */

unsigned longgpcup;

unsigned longgpcup_mask;

unsigned longgpccon;

unsigned longgpccon_mask;

…………

};

圖2.3

上圖表示了S3C2410驅(qū)動的整體結(jié)構(gòu),反映了結(jié)構(gòu)體之間的相互關(guān)系

2.3 主要代碼結(jié)構(gòu)以及關(guān)鍵代碼分析

2.3.1 FrameBuffer驅(qū)動的統(tǒng)一管理

fbmem.c 實現(xiàn)了Linux FrameBuffer的中間層,任何一個FrameBuffer驅(qū)動,在系統(tǒng)初始化時,必須向fbmem.c注冊,即需要調(diào)用 register_framebuffer()函數(shù),在這個過程中,設(shè)備驅(qū)動的信息將會存放入名稱為registered_fb數(shù)組中,這個數(shù)組定義為

struct fb_info *registered_fb[FB_MAX];

int num_registered_fb;

它是類型為fb_info的數(shù)組,另外num_register_fb則存放了注冊過的設(shè)備數(shù)量。

我們分析一下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;/* 超過最大數(shù)量 */

num_registered_fb++;

for (i = 0 ; i < FB_MAX; i++)

if (!registered_fb[i])break;/* 找到空余的數(shù)組空間 */

fb_info->node = i;

fb_info->dev = device_create(fb_class, fb_info->device,

MKDEV(FB_MAJOR, i), "fb%d", i);/* 為設(shè)備建立設(shè)備節(jié)點 */

if (IS_ERR(fb_info->dev)) {

…………

} else{

fb_init_device(fb_info);/* 初始化改設(shè)備 */

}

…………

return 0;

}

從上面的代碼可知,當(dāng)FrameBuffer驅(qū)動進行注冊的時候,它將驅(qū)動的fb_info結(jié)構(gòu)體記錄到全局?jǐn)?shù)組registered_fb中,并動態(tài)建立設(shè)備節(jié)點,進行設(shè)備的初始化。注意,這里建立的設(shè)備節(jié)點的次設(shè)備號就是該驅(qū)動信息在registered_fb存放的位置,即數(shù)組下標(biāo)i 。在完成注冊之后,fbmem.c就記錄了驅(qū)動的fb_info。這樣我們就有可能實現(xiàn)fbmem.c對全部FrameBuffer驅(qū)動的統(tǒng)一處理。

2.3.2 實現(xiàn)消息的分派

fbmem.c實現(xiàn)了對系統(tǒng)全部FrameBuffer設(shè)備的統(tǒng)一管理。當(dāng)用戶嘗試使用一個特定的FrameBuffer時,fbmem.c怎么知道該調(diào)用那個特定的設(shè)備驅(qū)動呢?

我們知道,Linux是通過主設(shè)備號和次設(shè)備號,對設(shè)備進行唯一標(biāo)識。不同的FrameBuffer設(shè)備向fbmem.c注冊時,程序分配給它們的主設(shè)備號是一樣的,而次設(shè)備號是不一樣的。于是我們就可以通過用戶指明的次設(shè)備號,來覺得具體該調(diào)用哪一個FrameBuffer驅(qū)動。下面通過分析 fbmem.c的fb_open()函數(shù)來說明。(注:一般我們寫FrameBuffer驅(qū)動不需要實現(xiàn)open函數(shù),這里只是說明函數(shù)流程。)

static int fb_open(struct inode *inode, struct file *file){

int fbidx = iminor(inode);

struct fb_info *info;

int res;

/* 得到真正驅(qū)動的函數(shù)指針 */

if (!(info = registered_fb[fbidx])) return -ENODEV;

if (info->fbops->fb_open) {

res = info->fbops->fb_open(info,1);//調(diào)用驅(qū)動的open()

if (res) module_put(info->fbops->owner);

}

return res;

}

當(dāng)用戶打開一個FrameBuffer設(shè)備的時,將調(diào)用這里的fb_open()函數(shù)。傳進來的inode就是欲打開設(shè)備的設(shè)備號,包括主設(shè)備和次設(shè)備號。 fb_open函數(shù)首先通過iminor()函數(shù)取得次設(shè)備號,然后查全局?jǐn)?shù)組registered_fb得到設(shè)備的fb_info信息,而這里面存放了設(shè)備的操作函數(shù)集fb_ops。這樣,我們就可以調(diào)用具體驅(qū)動的fb_open() 函數(shù),實現(xiàn)open的操作。下面給出了一個LCD驅(qū)動的open() 函數(shù)的調(diào)用流程圖,用以說明上面的步驟。

圖2.4

2.3.3 開發(fā)板S3C2410 LCD驅(qū)動的流程

(1)在mach-smdk2410.c中,定義了初始的LCD參數(shù)。注意這是個全局變量。

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)內(nèi)核初始化時候調(diào)用s3c2410fb_probe函數(shù)。下面分析這個函數(shù)的做的工作。首先先動態(tài)分配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;

設(shè)置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;

……

該函數(shù)調(diào)用s3c2410fb_map_video_memory()申請DMA內(nèi)存,即顯存。

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;

…….

設(shè)置控制寄存器,設(shè)置硬件寄存器。

memcpy(&info->regs, &mach_info->regs,sizeof(info->regs));

info->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;

……….

調(diào)用函數(shù)s3c2410fb_init_registers(),把初始值寫入寄存器。

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

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

(3)當(dāng)用戶調(diào)用mmap()映射內(nèi)存的時候,F(xiàn)bmem.c把剛才設(shè)置好的顯存區(qū)域映射給用戶。

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);

……

這樣就完成了驅(qū)動初始化到用戶調(diào)用的整個過程。

3. BMP和JPEG圖形顯示程序

3.1 在LCD上顯示BMP或JPEG圖片的主流程圖

首先,在程序開始前。要在nfs/dev目錄下創(chuàng)建LCD的設(shè)備結(jié)點,設(shè)備名fb0,設(shè)備類型為字符設(shè)備,主設(shè)備號為29,次設(shè)備號為0。命令如下:

mknod fb0 c 29 0

在 LCD上顯示圖象的主流程圖如圖3.1所示。程序一開始要調(diào)用open函數(shù)打開設(shè)備,然后調(diào)用ioctl獲取設(shè)備相關(guān)信息,接下來就是讀取圖形文件數(shù)據(jù),把圖象的RGB值映射到顯存中,這部分是圖象顯示的核心。對于JPEG格式的圖片,要先經(jīng)過JPEG解碼才能得到RGB數(shù)據(jù),本項目中直接才用現(xiàn)成的 JPEG庫進行解碼。對于bmp格式的圖片,則可以直接從文件里面提取其RGB數(shù)據(jù)。要從一個bmp文件里面把圖片數(shù)據(jù)陣列提取出來,首先必須知道bmp 文件的格式。下面來詳細介紹bmp文件的格式。

圖3.1

3.2 bmp位圖格式分析

位圖文件可看成由四個部分組成:位圖文件頭、位圖信息頭、彩色表和定義位圖的字節(jié)陣列。如圖3.2所示。

圖3.2

文件頭中各個段的地址及其內(nèi)容如圖3.3。

圖3.3

位圖文件頭數(shù)據(jù)結(jié)構(gòu)包含BMP圖象文件的類型,顯示內(nèi)容等信息。它的數(shù)據(jù)結(jié)構(gòu)如下定義:

Typedef struct

{

int bfType;//表明位圖文件的類型,必須為BM

long bfSize;//表明位圖文件的大小,以字節(jié)為單位

int bfReserved1;//屬于保留字,必須為本0

int bfReserved2;//也是保留字,必須為本0

long bfOffBits;//位圖陣列的起始位置,以字節(jié)為單位

} BITMAPFILEHEADER;

圖3.4 位圖文件頭的數(shù)據(jù)結(jié)構(gòu)

(2)信息頭中各個段的地址及其內(nèi)容如圖3.5所示。

圖3.5

位圖信息頭的數(shù)據(jù)結(jié)構(gòu)包含了有關(guān)BMP圖象的寬,高,壓縮方法等信息,它的C語言數(shù)據(jù)結(jié)構(gòu)如圖3.6所示。

Typedef struct {

long biSize; //指出本數(shù)據(jù)結(jié)構(gòu)所需要的字節(jié)數(shù)

long biWidth;//以象素為單位,給出BMP圖象的寬度

long biHeight;//以象素為單位,給出BMP圖象的高度

int biPlanes;//輸出設(shè)備的位平面數(shù),必須置為1

int biBitCount;//給出每個象素的位數(shù)

long biCompress;//給出位圖的壓縮類型

long biSizeImage;//給出圖象字節(jié)數(shù)的多少

long biXPelsPerMeter;//圖像的水平分辨率

long biYPelsPerMeter;//圖象的垂直分辨率

long biClrUsed;//調(diào)色板中圖象實際使用的顏色素數(shù)

long biClrImportant;//給出重要顏色的索引值

} BITMAPINFOHEADER;

圖3.6 BITMAPINFOHEADER數(shù)據(jù)結(jié)構(gòu)

(3)對于象素小于或等于16位的圖片,都有一個顏色表用來給圖象數(shù)據(jù)陣列提供顏色索引,其中的每塊數(shù)據(jù)都以B、G、R的順序排列,還有一個是reserved保留位。而在圖形數(shù)據(jù)區(qū)域存放的是各個象素點的索引值。它的C語言結(jié)構(gòu)如圖3.7所示。

圖3.7 顏色表數(shù)據(jù)結(jié)構(gòu)

(4)對于24位和32位的圖片,沒有彩色表,他在圖象數(shù)據(jù)區(qū)里直接存放圖片的RGB數(shù)據(jù),其中的每個象素點的數(shù)據(jù)都以B、G、R的順序排列。每個象素點的數(shù)據(jù)結(jié)構(gòu)如圖3.8所示。

圖3.8 圖象數(shù)據(jù)陣列的數(shù)據(jù)結(jié)構(gòu)

(5)由于圖象數(shù)據(jù)陣列中的數(shù)據(jù)是從圖片的最后一行開始往上存放的,因此在顯示圖象時,是從圖象的左下角開始逐行掃描圖象,即從左到右,從下到上。

(6)對S3C2410或PXA255開發(fā)板上的LCD來說,他們每個象素點所占的位數(shù)為16位,這16位按B:G:R=5:6:5的方式分,其中B在最高位,R在最低位。而從bmp圖象得到的R、G、B數(shù)據(jù)則每個數(shù)據(jù)占8位,合起來一共24位,因此需要對該R、G、B數(shù)據(jù)進行移位組合成一個16位的數(shù)據(jù)。移位方法如下:

b >>= 3; g >>= 2; r >>= 3;

RGBValue = ( r<<11 | g << 5 | b);

基于以上分析,提取各種類型的bmp圖象的流程如圖3.9所示

圖 3.9

3.3 實現(xiàn)顯示任意大小的圖片

開發(fā)板上的LCD屏的大小是固定的,S3C2410上的LCD為:240*320,PXA255上的為:640*480。比屏幕小的圖片在屏上顯示當(dāng)然沒問題,但是如果圖片比屏幕大呢?這就要求我們通過某種算法對圖片進行縮放。

縮放的基本思想是將圖片分成若干個方塊,對每個方塊中的R、G、B數(shù)據(jù)進行取平均,得到一個新的R、G、B值,這個值就作為該方塊在LCD屏幕上的映射。

縮放的算法描述如下:

(1)、計算圖片大小與LCD屏大小的比例,以及方塊的大小。為了適應(yīng)各種屏幕大小,這里并不直接給lcd_width和lcd_height賦值為240和320。而是調(diào)用標(biāo)準(zhǔn)的接口來獲取有關(guān)屏幕的參數(shù)。具體如下:

// 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數(shù)值分別取平均,得到映射點的R、G、B值,把該點作為要在LCD上顯示的第(i , j)點存儲起來。

這部分的程序如下:

//-------------取平均--------

for( i=0;i
{

for(j=0;j
{

color_sum_r=0;

color_sum_g=0;

color_sum_b=0;

for(m=i*heightScale;m
{

for(n=j*widthScale;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 圖片數(shù)據(jù)提取及顯示的總流程

通過以上的分析,整個圖片數(shù)據(jù)提取及顯示的總流程如圖3.10 所示。

圖 3.10

三 實驗過程與結(jié)果

1. Linux 源代碼的修改

首先修改arch/arm/mach-smdk2410.c文件,加入以下代碼。

static struct s3c2410fb_mach_info smdk2440_lcd_cfg __initdata = {

.regs= {

.lcdcon1= S3C2410_LCDCON1_TFT16BPP |

S3C2410_LCDCON1_TFT |

S3C2410_LCDCON1_CLKVAL(7),

.lcdcon2= S3C2410_LCDCON2_VBPD(4) |

S3C2410_LCDCON2_LINEVAL(319) |

S3C2410_LCDCON2_VFPD(1) |

S3C2410_LCDCON2_VSPW(1),

.lcdcon3= S3C2410_LCDCON3_HBPD(26) |

S3C2410_LCDCON3_HOZVAL(239) |

S3C2410_LCDCON3_HFPD(30),

.lcdcon4= S3C2410_LCDCON4_HSPW(13) |

S3C2410_LCDCON4_MVAL(13),

.lcdcon5= S3C2410_LCDCON5_FRM565 |

S3C2410_LCDCON5_INVVLINE |

S3C2410_LCDCON5_INVVFRAME |

S3C2410_LCDCON5_PWREN |

S3C2410_LCDCON5_HWSWP,

},

.lpcsel= ((0xCE6) & ~7) | 1<<4,

.width= 240,

.height= 320,

.xres= {

.min= 240,

.max= 240,

.defval= 240,

},

.yres= {

.min= 320,

.max= 320,

.defval = 320,

},

.bpp= {

.min= 16,

.max= 16,

.defval = 16,

},

};

在函數(shù)smdk2410_machine_init()函數(shù)中加入LCD的初始化代碼,見下

static void __init smdk2410_machine_init(void){

s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg);

smdk_machine_init();

}

2.編譯內(nèi)核,產(chǎn)生zImage文件,放入tftp目錄下。

3.在nfs的dev目錄下建立FrameBuffer的設(shè)備節(jié)點,使用命令:

mknod fb0 c 29 0

4.啟動開發(fā)板,加載內(nèi)核和文件系統(tǒng)。

5.編寫LCD的應(yīng)用程序,程序見附錄。

6.采用arm-linux-gcc 編譯應(yīng)用程序,產(chǎn)生可執(zhí)行文件,放入nfs目錄中。

7.在開發(fā)板上運行編譯好的可執(zhí)行文件,便可。

8.下圖是BMP位圖顯示程序,在S3C2410上的運行結(jié)果。

四 實驗心得體會

1.LCD驅(qū)動的主要問題是沒有LCD屏的文檔,我們找不到它的那些參數(shù)值,后來只能參照Linux源碼里面的其他LCD屏的參數(shù)進行實驗。

2.在驅(qū)動差錯的過程中,我們采用跟蹤打印的方法進行調(diào)試。剛開始的時候,內(nèi)核打印出一行找不到LCD設(shè)備。我們定位到輸出這行提示的代碼處,進行反向跟蹤。發(fā)現(xiàn)傳給函數(shù)的設(shè)備指針為空,于是往上排查,終于發(fā)現(xiàn)源代碼中沒有定義LCD的設(shè)備信息。于是驅(qū)動問題也就順利解決了。

3.原來一直以為,只要LCD驅(qū)動工作正常了,內(nèi)核起來的時候,液晶屏?xí)@示出Logo。當(dāng)時搞了很久一直沒有,還以為是驅(qū)動的問題。后來隨便寫了一個LCD應(yīng)用程序,竟然能用。

4.在調(diào)試過程應(yīng)用程序中發(fā)現(xiàn),在讀取文件頭的時候,如果直接定義一個bitmapfileheader為它動態(tài)分配內(nèi)存:

*bmph=(bitmapfileheader*)malloc(sizeof(bitmapfileheader));

然后用fread((char*)bmph,sizeof(bitmapfileheader),1,f)把文件頭一次性讀出來,讀出來的文件頭是錯誤的,經(jīng)過調(diào)試發(fā)現(xiàn)原因是bitmapfileheader這個結(jié)構(gòu)體中的type屬性原本應(yīng)該占2字節(jié),但是被編譯器在分配內(nèi)存的時候進行了內(nèi)存對齊的優(yōu)化,給他分配了4個字節(jié)的空間,造成讀文件的錯誤。因此在編程中要特別注意內(nèi)存對齊的影響。

typedef struct

{

WORD type;(被優(yōu)化)

DWORD bfsize;

DWORD reserved;

DWORD offbits;

} bitmapfileheader;

5.在嵌入式應(yīng)用程序的移植過程中,我們原來認為ARM和PC機大小尾順序是不同的,因此在應(yīng)用程序中,也對這個差別進行了處理。當(dāng)時,在調(diào)試過程中發(fā)現(xiàn),PC 機程序可以直接移植到ARM上,不需要任何改動。但是我們的程序,的確存在會產(chǎn)生大小尾問題代碼(在使用fread()讀入時)。這究竟是為什么?有人說,ARM是可以設(shè)置大小尾順序的。后來這個問題也沒有深究下去。

五 參考文獻
(1)嵌入式系統(tǒng)設(shè)計與應(yīng)用開發(fā):鄭靈翔. 北京:北京航天航空大學(xué)出版社 2006


本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉