一  实验内容简要描写叙述

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。

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

……

这样就完毕了驱动初始化到用户调用的整个过程。

3.  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;

图3.4  位图文件头的数据结构

(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.6  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

三  实验过程与结果

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,

},

};

在函数smdk2410_machine_init()函数中增加LCD的初始化代码,见下

static void __init smdk2410_machine_init(void){

s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg);

smdk_machine_init();

}

2.编译内核,产生zImage文件,放入tftp文件夹下。

3.在nfs的dev文件夹下建立FrameBuffer的设备节点,使用命令:

mknod fb0 c 29 0

4.启动开发板,载入内核和文件系统。

5.编写LCD的应用程序,程序见附录。

6.採用arm-linux-gcc 编译应用程序,产生可运行文件,放入nfs文件夹中。

7.在开发板上执行编译好的可执行文件,便可。

8.下图是BMP位图显示程序,在S3C2410上的执行结果。

四  实验心得体会

1. LCD驱动的主要问题是没有LCD屏的文档,我们找不到它的那些參数值,后来仅仅能參照Linux源代码里面的其它LCD屏的參数进行实验。

2. 在 驱动差错的过程中,我们採用跟踪打印的方法进行调试。刚開始的时候,内核打印出一行找不到LCD设备。我们定位到输出这行提示的代码处,进行反向跟踪。发 现传给函数的设备指针为空,于是往上排查,最终发现源码中未定义LCD的设备信息。于是驱动问题也就顺利攻克了。

3. 原来一直以为,仅仅要LCD驱动工作正常了,内核起来的时候,液晶屏会显示出Logo。当时搞了非常久一直没有,还以为是驱动的问题。后来随便写了一个LCD应用程序,居然能用。

4. 在调试过程应用程序中发现,在读取文件头的时候,假设直接定义一个bitmapfileheader为它动态分配内存:

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

然 后用fread((char*)bmph,sizeof(bitmapfileheader),1,f)把文件头一次性读出来,读出来的文件头是错误的, 经过调试发现原因是bitmapfileheader这个结构体中的type属性原本应该占2字节,可是被编译器在分配内存的时候进行了内存对齐的优化, 给他分配了4个字节的空间,造成读文件的错误。因此在编程中要特别注意内存对齐的影响。

typedef struct

{

WORD    type;(被优化)

DWORD bfsize;

DWORD reserved;

DWORD offbits;

}  bitmapfileheader;

5. 在 嵌入式应用程序的移植过程中,我们原来觉得ARM和PC机大小尾顺序是不同的,因此在应用程序中,也对这个区别进行了处理。当时,在调试过程中发现,PC 机程序能够直接移植到ARM上,不须要不论什么修改。可是我们的程序,的确存在会产生大小尾问题代码(在使用fread()读入时)。这到底是为什么?有人 说,ARM是能够设置大小尾顺序的。后来这个问题也没有深究下去。

LCD与ARM,具体的更多相关文章

  1. 《Verilog数字系统设计教程(第2版).pdf》

    Verilog数字系统设计教程(第2版).pdf https://github.com/shigh1005/pdf_book <Linux设备驱动开发详解:基于最新的Linux 4.0内核.pd ...

  2. 【linux项目】lichee nano linux烧写

    目录 前言 参考: 安装交叉编译链 搭建 SPI FLASH 烧录环境 让芯片进入烧写模式 sunxi 烧写命令 u-boot 裁剪 拉取 u-boot 源码 配置 u-boot 检查 flash 驱 ...

  3. ARM LCD屏调试3--屏的应用编程

    2011-06-25 19:20:47 驱动自己写完了,应用函数自己就不写了,找了一点代码参考,移植并修改了一下,配合之前的定义的接口文档,我贴出部分代码.目录: 一,开发环境... 1 二,底层函数 ...

  4. ARM 之LCD和LCD控制器

    既然提到 了LCD那么我们首先必须要了解的就是他的种类,CD(liquid crystal  display), 即液晶显示器,是这一种采用了液晶控制透光度计数来实现色彩的显示器,他与传统的CRT显示 ...

  5. AM335x kernel4.4.12 LCD 时钟翻转设置记录

    TI AM335x kernel 4.4.12 LCD display 时钟翻转记录 因为公司硬件上已经确定LCD 转LVDS 转换芯片上确认以上升沿时钟为基准,所以只能在软件上调整相关东西. 入口在 ...

  6. linux(debian) arm-linux-g++ v4.5.1交叉编译 embedded arm 版本的QtWebkit (browser) 使用qt 4.8.6 版本

    最近需要做一个项目 在arm 架构的linux下 没有桌面环境的情况下拉起 有界面的浏览器使用. 考虑用qt 的界面和 qtwebikt 的库去实现这一系列操作. 本文参考: Qt移植到ARM Lin ...

  7. ARM嵌入式开发板

    iTOP-4412 ARM嵌入式开发板----主要特点 iTOP-4412开发平台是北京迅为电子研发设计的嵌入式开发板平台,核心板配备64位双通道2GB DDR3,16GBEMMC存储,三星原厂S5M ...

  8. ARM: STM32F7: hardfault caused by unaligned memory access

    ARM: STM32F7: hardfault caused by unaligned memory access ARM: STM32F7: 由未对齐的内存访问引起的hardfault异常 Info ...

  9. zju(9)LCD显示实验

    1.实验目的 1.学习和掌握linux下对IIC的操作方法以及驱动和应用程序的编写: 二.实验内容 1.编写EduKit-IV实验箱Linux操作系统下IIC的驱动,并编写应用程序实现将数据写入EEP ...

随机推荐

  1. Linux进程间通信——使用信号

    一.什么是信号 用过Windows的我们都知道,当我们无法正常结束一个程序时,可以用任务管理器强制结束这个进程,但这其实是怎么实现的呢?同样的功能在Linux上是通过生成信号和捕获信号来实现的,运行中 ...

  2. 字符编码介绍及java中的应用

    字符编码,就是对日常的控制符号.文字和常用符号的二进制表示.为了准确的表示如何编号,怎么生产八位字节流,Unicode Technical Report (UTR) #17提出现代编码模型的5个层次: ...

  3. Hadoop基础

    Hadoop组成 包括两个核心组成:HDFS:分布式文件系统,存储海量的数据MapReduce:并行处理框架,实现任务分解和调度 搭建大型数据仓库,PB级数据的存储.处理.分析.统计等业务(搜索引擎. ...

  4. 集合框架-TreeSet

    TreeSet是Set集合的常见子类. TreeSet:底层结构是 二叉树 元素是有排序的,但是不可以有重复元素. 相关代码演练: /* TreeSet ;元素是有序的,但是不可以元素重复. */ i ...

  5. (转)轻量级数据库 SQLite

    SQLite Expert – Personal Edition SQLite Expert 提供两个版本,分别是个人版和专业版.其中个人版是免费的,提供了大多数基本的管理功能. SQLite Exp ...

  6. Sublime 学习记录(四) Alignment 插件

    1)  Alignment 插件的安装 打开命令面板 输入pci 回车 输入Alignment 回车安装好即可 2)  Alignment 用处 用于代码对齐 3) Alignment 快捷键 默认为 ...

  7. 蜗牛爱课- CGAffineTransformMakeRotation 实现一张图片的自动旋转

    self.locationTimer =[NSTimer scheduledTimerWithTimeInterval: 0.01 target: self selector:@selector(tr ...

  8. oracle11g用户名密码不区分大小写

    oracle 11g 以前的版本的用户名和密码是不区分大小写的; oracle 11g 用户名和密码默认区分大小写,可更改alter system set sec_case_sensitive_log ...

  9. 泛型编程中的Concept, Model和Policy

    A crude explanation Concept A set of requirements on a type, e.g. a RandomAccessible concept require ...

  10. “如何稀释scroll事件”引出的问题

    背景:我在segmentfault提了个问题如何稀释onscroll事件,问题如下: 面试时问到这个问题,是这样的:    面试官问一个关于滚动到某个位置的时候出现一个顶部的导航栏,答完之后,她接着问 ...