S3c2410 LCD驱动学习心得

基于ARM9处理器S3C2410的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。

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是可以设置大小尾顺序的。后来这个问题也没有深究下去。

五  参考文献
(1)嵌入式系统设计与应用开发:郑灵翔. 北京:北京航天航空大学出版社 2006



Samsung S3C2410 LCD 初始化与开发指南 本文还有配套的精品资源,点击获取 简介:针对Samsung S3C2410处理器的LCD屏幕初始化程序,是嵌入式系统如便携设备和数字媒体播放器中用户交互界面的关键组成部分。本文档详细描述了初始化LCD控制器的必要步骤,包括配置时序、寄存器初始化、背光设置和显示开启,并通过分析相关源文件如 lcd.c 和 lcdlib.c ,提供了对LCD编程的深... 阅读详情

相关推荐

S3C2410平台GPIO驱动开发与应用教程

本文还有配套的精品资源,点击获取 简介:本文详细介绍在S3C2410微处理器平台上的GPIO驱动源代码实现,包括初始化、配置、中断处理及用户空间接口等方面。教程旨在帮助开发者理解并掌握GPIO驱动的设计原理和实际应用技巧,涉及的资料包含图文教程和源代码。 1. GPIO驱动基本原理介绍 1.1 GPIO概述 通用输入输出端口(GPIO)是微控制器和处理...

weixin_35754676的博客 1327

S3C2410的LCD驱动

<br />一  实验内容简要描述<br />1.实验目的<br />学会驱动程序的编写方法,配置S3C2410的LCD驱动,以及在LCD屏上显示包括bmp和jpeg两种格式的图片<br />2.实验内容<br /> (1)分析S3c2410实验箱LCD以及LCD控制器的硬件原理,据此找出相应的硬件设置参数,参考xcale实验箱关于lcd的设置,完成s3c2410实验箱LCD的设置<br />(2)在LCD上显示一张BMP图片或JPEG图片<br />3.实验条件(软硬件环境)<br />PC机、S3C24

351

倍福Beckhoff运动控制功能块实战解析:从MC_ReadActualPosition到MC_GearInVelo

本文深入解析倍福(Beckhoff)TwinCAT运动控制功能块,从基础的MC_ReadActualPosition、MC_Power等轴状态读取与使能操作,到MC_MoveAbsolute、MC_MoveRelative等单轴定位指令,并重点剖析了用于动态速度同步的核心功能块MC_GearInVelo在飞锯等场景的应用。文章结合实战经验,详细介绍了齿轮耦合、电子凸轮(MC_CamIn)等高级同步功能的参数配置、调试技巧及常见问题,旨在帮助工程师快速掌握倍福运动控制工具箱,实现复杂的同步工艺。

transformer2023的博客 981

TFTLCD屏幕实验

了解TFTLCD屏幕和FSMC的驱动原理,通过STM32F4的FSMC接口来控制TFTLCD的显示。

xhj1021的博客 1万+

ComfyUI中一个超好用的变量管理类节点-KJNodes

在AI绘图中,ComfyUI的热度开始慢慢起来一些,但是这个软件在使用的过程中,需要很多连线,仅仅一个最简单的动画生成,就需要连接这么多线条,而且有些线会飞的很远,看起来就很不美观,尤其是一些复杂工作流,连接到最后自己可能已经分不太清楚到底连接好了没有。今天在研究C站的大佬们的工作流的时候,发现一个有意思的工作流,看起来大概是这种感觉:干净整洁。他用到了KJNodes这么个节点来管理变量,git上星标也不算很多,所以我想很多朋友可能还不知道,所以这里简单介绍下用法。

聚梦小课堂 7521

MediaCreationToolW11:Windows11系统升级与修复利器

在日常使用Windows11系统的过程中,我们可能会遇到一些棘手的问题,比如系统更新时出现异常导致无法成功更新,或者系统本身出现异常需要进行无损修复。此外,当我们的电脑尚未升级到Windows11,而我们又希望进行升级时,一款高效且可靠的工具就显得尤为重要。接下来,MediaCreationToolW11将开始下载Windows11的安装文件,并创建Windows11介质。在“选择你要执行的操作”界面中,选择“ISO文件”。此时,你可以使用你的账户登录系统,并开始使用全新的Windows11系统了。

nntxthml的博客 2408

实验(四):LCD1602显示实验

《单片机原理及应用》实验报告(四)【zzuli】

心之所向 1万+

基于步科(Kinco HMI 2.5)的组态软件使用

本篇介绍Kinco HMI 2.5的组态软件使用,图标如下 目录   一、新建工程并配置连接 1.1 根据工程选择PLC与HMI型号 1.2 配置PLC连接与选项 二、组态画面编辑 2.1 打开画面窗口 2.2 编辑画面 2.3 组态变量与PLC程序变量连接 三、组态程序下载到HMI 3.1 连接 3.2 编译 3.3 下载 一、新建工程并配置连接 1.1 根据工...

ACanswer的专栏 1万+

(42)STM32——LCD显示屏实验笔记

今天我们要讲解的是有关LCD显示屏的知识,其实这部分知识之前就学习过了,但是因为没有LCD显示屏,就没有做实验,所以我们今天就当是来复习一下之前的知识点了。主要的内容就是关于LCD的原理,与学会使用LCD。

weixin_66578482的博客 4641

教你搞懂RT-Thread的移植和配置ENV工具环境

RT-Thread的移植和配置ENV工具环境

Troubadour~的博客 8264

uniapp实战:mui-player插件如何完美适配m3u8/flv直播流(附完整代码)

本文详细介绍了如何在Uniapp中使用mui-player插件完美适配m3u8和flv直播流,提供从基础配置到高级优化的全流程解决方案。通过动态加载策略、响应式布局和内存管理,实现跨平台稳定播放,适用于企业级直播应用开发。

weixin_29001655的博客 237

基于S3C2440的嵌入式Linux驱动——SPI子系统解读(一)

本文将介绍SPI子系统。内核版本为2.6.30。如有错误欢迎指正。 预备知识要求:1.SPI总线                        2. platfrom平台                        3. sysfs子系统                        4. 阅读过LDD3第3,5,6,7,9,10,11章的内容。 NOTE:如果

RopenYuan的专栏 2713

Windows/Linux双平台实战:QtCreator构建套件配置全攻略(含GCC/G++/GDB/CMake)

本文详细介绍了在Windows和Linux双平台下配置QtCreator构建套件的完整流程,涵盖GCC/G++、GDB和CMake等核心工具链的设置技巧。通过分步指导和实用示例,帮助开发者解决跨平台开发中的配置难题,提升Qt应用开发效率。特别针对QtCreator新手和需要在多平台切换的开发者提供了全面的配置指南。

weixin_28836507的博客 471

S3C2410 LCD驱动学习心得

2.  Linux 驱动 2.1  FrameBuffer Linux是工作在保护模式下,所以用户态进程是无法像DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Lin仿显卡的功能,将显ux抽象出FrameBuffer这个设备来供用户态进程实现直接写屏。Framebuffer机制模卡硬件结构抽象掉,可以通过Framebuffer的读写直接对显存进行操作。用户可以将Framebuffer

zy_11162311的专栏 4597

Android 13.0 framework层系统手势增加上滑手势home事件功能(相当于Home键)

在13.0的定制化开发系统手势功能的时候,客户需求要求在上滑手势的时候,在底部上滑时候进入系统桌面,也就是增加 home键功能,所以就需要分析相关的系统手势上滑事件,然后添加home事件这样 就可以实现这个功能了

安卓兼职framework和app工程师的博客 1133

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

S3C2410 LCD驱动学习心得(一)

一  实验内容简要描述 1.实验目的 学会驱动程序的编写方法,配置S3C2410的LCD驱动,以及在LCD屏上显示包括bmp和jpeg两种格式的图片 2.实验内容  (1)分析S3c2410实验箱LCD以及LCD控制器的硬件原理,据此找出相应的硬件设置参数,参考xcale实验箱关于lcd的设置,完成s3c2410实验箱LCD的设置  (2)在LCD

bianchengji的博客 933

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

S3C2410 LCD驱动学习心得(三)

http://www.linuxidc.com/Linux/2011-08/41673p2.htm 3.  BMP和JPEG图形显示程序 3.1  在LCD上显示BMP或JPEG图片的主流程图 首先,在程序开始前。要在nfs/dev目录下创建LCD的设备结点,设备名fb0,设备类型为字符设备,主设备号为29,次设备号为0。命令如下: mknod fb0 c 29 0 在LCD上显示图

bianchengji的博客 546

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

S3c2410 LCD驱动学习心得 (转)

2.  Linux 驱动 2.1  FrameBuffer Linux是工作在保护模式下,所以用户态进程是无法像DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Lin仿显卡的功能,将显ux抽象出FrameBuffer这个设备来供用户态进程实现直接写屏。Framebuffer机制模卡硬件结构抽象掉,可以通过Framebuffer的读写直接对显存进行操作。用户可以将Framebuffer

gate0088的专栏 717

S3C2410 LCD驱动学习心得(二)

http://www.linuxidc.com/Linux/2011-08/41673p2.htm 2.  Linux 驱动 2.1  FrameBuffer Linux是工作在保护模式下,所以用户态进程是无法像DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Lin仿显卡的功能,将显ux抽象出FrameBuffer这个设备来供用户态进程实现直接写屏。Framebuffer机

bianchengji的博客 519

开发板的lcd 显示

http://hi.baidu.com/guoggn/blog/item/018ed7f28932261eb07ec59a.html 转】S3c2410 LCD驱动学习心得 2010-07-26 14:51 转载自 583893280 最终编辑 guoggn 一  实验内容简要描述 1.实验目的 学会驱动程序的编写方法,配置S3C2410的LC

yhc1991的专栏 5053

S3C2410 LCD 驱动程序移植及GUI程序编写

2007-12-25 10:13:34 1. 为了不让大家觉枯燥,让朋友们更好的理解,我以一个实例来叙述 S3C2410 下一个驱动程序的编写(本文的初始化源码以华恒公司提供的 s3c2410fb.c 为基础)及简单的 GUI程序的编写。 2. 拿到一块 LCD,首先要将 LCD的各个控制线与 S3C2410 的 LCD控制信号相接,当然,电源也一定要接入了,否则不亮可

mybirdsky的专栏 1285
上一篇: Linux 2.6.30 内核文件文件系统剖析
下一篇: CRC校验源码分析
ADC0809608
博客等级 码龄17年 44粉丝 10原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值