LCD底层驱动分析

根据分析的框架,自己写一个LCD驱动程序

1分析LCD硬件原理图

Von和Voff接的是一个电源电路,通过LCD_POWER接的是GPG4来控制LCD电源,高电平表示开启LCD电源

VM接的是CPU的VM:VDEN /GPC4为数据使能信号,

VLINE接的CPU:HSYNC/GPC2,HSYNC信号有效时,表示一行数据的开始;

VFRAME接的CPU:VSYNC/GPC3,VSYNC信号有效时,表示一帧数据的开始

VCLK接的CPU:VCLK/GPC1 表示像素时钟信号,每个VCLK信号表示正在传输一个像素的数据;

LED-和LED+接到背光电路上,背光电路是由GPB0控制;高电平表示开启背光

VD[3~7],VD[10~15],VD[19~23]代表的是数据接口,CPU通过LCD专用的DMA来给传输像素的数据,

2 配置LCD用到GPIO引脚

根据原理图分析用的CPU 引脚,需要进行一一的配置

2.1先配置数据引脚VD;

*gpccon = 0xaaaaaaaa; 
  *gpdcon = 0xaaaaaaaa;

2.2配置控制引脚
   /*3.注册配置GPIO,用于LCD*/
   /*GPC4为配置为VDEN
   *GPC2配置为HSYNC
  *GPC3配置为VSYNC
  *GPC1配置为VCLK
  */
  *gpccon =(2<<2)|(2<<4) |(2<<6) |(2<<8);

3 配置S3C2440对应的寄存器的值

3.1 详细分析LCD时序

打开S3C2440中的LCD数据手册

S3C2440数据手册中的LCD控制时序图                              这是LCD数据手册中的时序图

那么S3C2440手册中的时序图的配置需要根据LCD数据手册的时序时间来进行配置,只有这样才能对这个LCD正确操作;

根据时序图就可以分析出LCD是怎么传输数据的

3.1.1一帧数据的传输

一帧数据的传输的时钟基准是VCLK,它代表每个VCLK信号都代表一个正在传输的像素数据;对比两个数据手册可以得出这个VCLK时钟信号时间为5~12MHz

(1)INT_FrSyn是帧同步信号,如果有图像显示,就会发出一个中断信号,产生一个同步信号

(2)VSYNC是一帧的同步信号,一帧数据开始传输;通过对比可知,VSYNC脉冲宽度为Tvp,而且和S3C2440中的信号互为反向;

(3)VSPW表示VSYNC脉冲的宽度为(VDPW+1)个HSYNC信号周期=Tvp:表示VSPW+1行数据无效

(4)VBPD表示表示VSYNC信号后,还需要经过(VBPD+1)个HSYNC信号周期=Tvb;需要经过VBPD+1行无效数据后,才会出现有效数据

(5)VDEN表示有效数据开始传输;开始连续传输LINEVAL+1行数数据;

传输完后,VDEN变为低电平,还需要经过VFPD+1=Tvf个无效行数据,一帧数据才能结束;然后会进行下一帧数据传输;

3.1.2一帧数据中一行数据是怎么传输的呢

根据时序图可知;一帧数据的传输,是通过一行行数据才完成的;

(1)当HSYNC发出一个高脉冲时;一行数据开始传输;

(2)HSPW+1代表HSYNC脉冲宽度;需要经过HSPW+1个VCLK个周期=Thp;也就是需要经过HSPW+1个无效像素;

(3)HBPD+1代表还需要经过HBPD+1个VCLK时钟=Thb;也就是需要经过HBPD+1个无效像素,才可以出现有效像素。

(4)从HSYNC产生开始;在出现有效像素之前,一共需要经过HSPW+HBPD+2个无效像素,才能出现有效像素;

(5)VDEN代表数据使能信号;然后会连续传输HOZVAL+1个像素;当VDEN无效时,还需要经过HFDP+1个像素才能表示一行像素传输完成;

(6)HFPD+1代表一行有效数据传输完后,还需要经过HFPD+1个VCLK才能表示一行像素传输完毕;

3.2 详细解析LCD控制的配置方法

/*4.设置硬件相关的设置*/
/*设置LCDCON1
*设置CLKVAL[17:8]像素时钟,这个值要参考LCD手册VCLK的范围5~12MHz
*设置扫描模式TFT[6:5]=11
*设置像素模式为16BPP
*禁止LCD ENVID[0]控制信号
*/
lcd_reg->LCDCON1=(4<<8) |(0x03<<5)|(0x0c<<1);

/*设置垂直方向
*设置VBPD参考LCD手册时序图
* 设置VBPD+1=Tvb[31:24] =2; VBPD=1
*设置VSPW+1=Tvp[13:6]=10; VSPW=9;
*设置VFPD+1=Tvf[5:0]=2 VFPD=1;
*LINEVAL垂直长度为272-1=271[23:14]
*/
lcd_reg->LCDCON2=(1<<24) |(271<<14)|(1<<6) |(9<<0);

/*设置水平方向
*
*HBPD[25:9] >>2clk HBPD+1>2;HBPD=1;
*HOZVAL[18:9] 水平方向的480-1=479
*HFPD [7:0] HFPD+1 =Thf >=2;HFPD=1;
*/
lcd_reg->LCDCON3=(1<<19) |(479<<8) |(1<<0);

/*HSPW[7:0]Thp=41;HSPW+1=41;*/

   lcd_reg->LCDCON4=(40<<0);

/*
*FRM565[11]=1
*INVVCLK[10]=1 来读取数据的触发方=1
*INVVLINE [9]判断HSYNC极性为反转1
*INVVFRAME[8]控制VSYNC极性反转=1
*INVVDEN[6]判断VD极性,不反转=0
*POWER[3] =0
*HWSWP=1
*BSWP=0
*/
lcd_reg->LCDCON5=(1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<1);
/*把缓冲区的地址写到寄存器中*/
lcd_reg->LCDSADDR1= (s3c_lcd->fix.smem_start >> 1) &~(3<<30);
lcd_reg->LCDSADDR2=((s3c_lcd->fix.smem_start+ s3c_lcd->screen_size)>>1) &0x1FFFFF;
lcd_reg->LCDSADDR3=(480*16/16);

4 编写LCD驱动框架

根据之前对LCD硬件的分析,现在开始编写代码

源码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>

#include <asm/mach/map.h>
#include <asm/arch/regs-lcd.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/fb.h>

static struct fb_info *s3c_lcd;
static u32 pseudo_palette[16];
/*定义LCD配置寄存器*/
static struct s3c_reg {
   u32 LCDCON1 ;
   u32 LCDCON2 ;
   u32 LCDCON3 ;
   u32 LCDCON4 ;
   u32 LCDCON5 ;
   u32 LCDSADDR1;
   u32 LCDSADDR2;
   u32 LCDSADDR3;
   u32 REDLUT;
   u32 GREENLUT;
   u32 BLUELUT;
   u32 resver[9];
   u32 DITHMODE;
   u32 TPAL;
   u32 LCDINTPND;
   u32 LCDSRCPND;
   u32 LCDINTMSK;
   u32 TCONSEL;
   };

static volatile unsigned int *gpccon;
static volatile unsigned int *gpcdat;
static volatile unsigned int *gpdcon;
static volatile unsigned int *gpbcon;
static volatile unsigned int *gpgcon;
static volatile unsigned int *gpbdat;
static volatile struct s3c_reg *lcd_reg;

static inline unsigned int chan_to_field(unsigned int chan,
                     struct fb_bitfield *bf)
{
    chan &= 0xffff;
    chan >>= 16 - bf->length;
    return chan << bf->offset;
}
static int lcdfb_setcolreg(unsigned regno,
                   unsigned red, unsigned green, unsigned blue,
                   unsigned transp, struct fb_info *info)
{  unsigned int val;
     if(regno >16)
           return 1;
            val  = chan_to_field(red,   &info->var.red);
            val |= chan_to_field(green, &info->var.green);
            val |= chan_to_field(blue,  &info->var.blue);
            pseudo_palette[regno] = val;
            return 0;
}

static struct fb_ops s3c_lcd_ops = {
    .owner        = THIS_MODULE,
    .fb_setcolreg    = lcdfb_setcolreg,
    .fb_fillrect    = cfb_fillrect,
    .fb_copyarea    = cfb_copyarea,
    .fb_imageblit    = cfb_imageblit,
};
static int __init lcd_init(void)
{
/*1.分配一个fb_info结构体*/
  s3c_lcd = framebuffer_alloc(0,NULL);
  gpgcon=(volatile unsigned int *)ioremap(0x56000060,8);
  gpccon = (volatile unsigned int *)ioremap(0x56000020,8);
  gpcdat=gpccon+1;
  gpdcon =(volatile unsigned int *)ioremap(0x56000030,8);
  gpbcon=(volatile unsigned int *)ioremap(0x56000010,8);
  gpbdat=gpbcon+1;

    /*2.设置*/
   lcd_reg =ioremap(0x4d000000,sizeof(struct s3c_reg));
    /*2.2 设置可变参数  */
   s3c_lcd->var.xres                 = 480;//X轴的实际像素
   s3c_lcd->var.yres                 =272;//y轴实际像素
   s3c_lcd->var.xres_virtual      =480;//虚拟像素设置和实际像素一样
   s3c_lcd->var.yres_virtual      =272;
   s3c_lcd->var.xoffset             =0;//实际像素和虚拟像素偏移值为0
   s3c_lcd->var.yoffset             =0;
   s3c_lcd->var.bits_per_pixel   =16;//每个像素点有16个位组成
   s3c_lcd->var.red.offset         =11;//red在16位域中偏移值为11
   s3c_lcd->var.red.length         =5;
   s3c_lcd->var.red.msb_right   =0;
   s3c_lcd->var.green.offset      =5;//red在16位域中偏移值为11
   s3c_lcd->var.green.length      =6;
   s3c_lcd->var.green.msb_right=0;
   s3c_lcd->var.blue.offset       =0;//red在16位域中偏移值为11
   s3c_lcd->var.blue.length       =5;
   s3c_lcd->var.blue.msb_right =0;
   s3c_lcd->var.activate =FB_ACTIVATE_NOW;
   /*2.3 设置固定参数*/
   strcpy(s3c_lcd->fix.id, "mylcd");
   s3c_lcd->fix.smem_len        =480*272*16/8;//缓冲区大小
   s3c_lcd->fix.type                 =FB_TYPE_PACKED_PIXELS;
   s3c_lcd->fix.visual                =FB_VISUAL_TRUECOLOR;
   s3c_lcd->fix.line_length        =480*16/8;
   /*2.4 设置其他设置*/
   s3c_lcd->pseudo_palette = pseudo_palette;
   s3c_lcd->fbops                    =&s3c_lcd_ops;
   s3c_lcd->screen_size   = 480*272*16/8;//屏幕像素的个数
   s3c_lcd->screen_base =dma_alloc_writecombine(NULL,  s3c_lcd->screen_size , &s3c_lcd->fix.smem_start,
                          GFP_KERNEL);
   /*
   *配置GPC引脚为数据引脚
   *VD3~VD7
   *gpccon = (0x10<<22)|(0x10<<24)|(0x10<<26)|(0x10<<28)|(0x10<<30);
   /*VD10~VD15 VD19~VD23
     gpdcon =(0x10<<4) |(0x10<<6) |(0x10<<8) |(0x10<<10) |(0x10<<12)|(0x10<<14) |    (0x10<<22) |(0x10<<24) |(0x10<<26) |(0x10<<28) |(0x10<<23);
   */
 *gpccon  = 0xaaaaaaaa;   /* GPIO管脚用于VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */
 *gpdcon  = 0xaaaaaaaa;   /* GPIO管脚用于VD[23:8] */

   /*GPG4为LCD_POWER*/
     *gpgcon |=(3<<8);
   /*GPB0为输出,控制LCD背光*/
     *gpbcon &=~(3<<0);
     *gpbcon |=1;
     *gpbdat &=~(1<<0);
     /*
      *配置GPD
     */
      /*3.注册配置GPIO,用于LCD*/
     /*GPC4为配置为VDEN
       *GPC2配置为HSYNC
       *GPC3配置为VSYNC
       *GPC1配置为VCLK
       */
     *gpccon =(2<<2)|(2<<4) |(2<<6) |(2<<8);
     /*4.设置硬件相关的设置*/
    /*设置LCDCON1
      *设置CLKVAL[17:8]像素时钟,这个值要参考LCD手册CLOCK CYCLE
      *设置MMODE为[7]=0
      *设置扫描模式TFT[6:5]=11
      *设置像素模式为16BPP
      *禁止LCD控制信号
      */

     lcd_reg->LCDCON1=(4<<8) |(0x03<<5)|(0x0c<<1);

       /*设置垂直方向
      *设置VBPD参考LCD手册时序图
      *  设置VBPD+1=Tvb[31:24] =2; VBPD=1
      *设置VSPW+1=Tvp[13:6]=10;   VSPW=9;
      *设置VFPD+1=Tvf[5:0]=2           VFPD=1;
      *LINEVAL垂直长度为272-1=271[23:14]
     */
    lcd_reg->LCDCON2=(1<<24) |(271<<14)|(1<<6) |(9<<0);
    /*设置水平方向
    *
    *HBPD[25:9] >>2clk  HBPD+1>2;HBPD=1;
        *HOZVAL[18:9]  水平方向的480-1=479
        *HFPD  [7:0] HFPD+1 =Thf >=2;HFPD=1;
        *
    */
    lcd_reg->LCDCON3=(1<<19) |(479<<8) |(1<<0);
         /*
         * HSPW[7:0]Thp=41;HSPW+1=41;
         *
         */
         lcd_reg->LCDCON4=(40<<0);
        /*
        *CON5
        *FRM565[11]=1
        *INVVCLK[10]=1 来读取数据的触发方=1
        *为巍⑽仙厥剑?
        *INVVLINE [9]判断HSYNC极性为反转1
        *INVVFRAME[8]控制VSYNC极性反转=1
        *INVVDEN[6]判断VD极性,不反转=0
            *POWER[3]  =0
            *HWSWP=1
            *BSWP=0
        */
   lcd_reg->LCDCON5=(1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<1);
 /*把缓冲区的地址写到寄存器中*/
   lcd_reg->LCDSADDR1=(s3c_lcd->fix.smem_start >> 1)  &~(3<<30);
   lcd_reg->LCDSADDR2=((s3c_lcd->fix.smem_start+ s3c_lcd->screen_size)>>1) &0x1FFFFF;
   lcd_reg->LCDSADDR3=(480*16/16);
   /*开启LCD控制信号*/
   lcd_reg->LCDCON1 |=(1<<0);
  *gpbdat |= 1; /* 输出高电平, 使能背光 */
  /*POWER输出*/
    lcd_reg->LCDCON5 |=(1<<3);
  /*根据LCD手册设置LCD控制器,比如VCLK频率*/
   /*分配frambuffer,并把地址告诉LCD控制器,*/
register_framebuffer(s3c_lcd);
return 0;
}
static void lcd_exit(void)
{
unregister_framebuffer(s3c_lcd);
framebuffer_release(s3c_lcd);
iounmap(gpgcon);
iounmap(gpccon);
iounmap(gpbcon);
iounmap(gpdcon);
dma_free_writecombine(NULL,  s3c_lcd->screen_size , &s3c_lcd->fix.smem_start,
                          GFP_KERNEL);
 lcd_reg->LCDCON1&= ~(1<<0); /* 关闭LCD本身 */

*gpbdat &= ~1;     /* 关闭背光 */
}
module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");

重新编译内核,要把原来的内核配置中的驱动自带的LCD驱动变成模块;然后make uImage 重新生成一个内核,然后再make module 生成一个模块;因为要用到

.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,这三个函数,所以要把关于这三个函数的.c文件编译成模块,

然后在编译新编的LCD驱动,

insmod cfbfillrect.ko

insmod cfbcopyarea.ko

insmod cfbimageblit.ko

insmod LCD.ko

这样就把新编译的LCD驱动加载到内核中;

echo hello > /dev/tty1命令,则hello就会显示在LCD屏上

echo LCD.ko > /dev/fb0 则LCD屏会花屏;

    显示结果

时间: 2024-10-21 08:34:24

LCD底层驱动分析的相关文章

LCD驱动分析【转】

转自:http://blog.csdn.net/hanmengaidudu/article/details/21559153 1.S3C2440上LCD驱动 (FrameBuffer)实例开发讲解 其中的代码也可直接参考:drivers/video/s3c2410fb.c 以下为转载文章,文章原地址:http://blog.csdn.net/jianyun123/archive/2010/04/24/5524427.aspx S3C2440上LCD驱动 (FrameBuffer)实例开发讲解 一

S3C6410 LCD驱动分析(转)

一. 理论分析1. 几个概念:FIMC :    Fully Interactive Mobile Camera (完全交互式移动摄像机)FIMD:     Fully Interactive Mobile Display (完全交互式移动显示设备)2. 设置VCLK在VIDCON0中bit[3:2]-->Select the Video Clock source =00 --> HCLK=133MHZbit[13:6] --> CLKVAL_F = 13  (这个值是在驱动中计算出来的

LInux LCD驱动分析

一.让LCD显示可爱的小企鹅还是先说说环境吧,处理器为S3C2410,linux的版本当然是2.6.20的.下面先说说怎样让LCD上显示出可爱的小企鹅.最直接的步骤如下(记住不要问为什么哈-_-,一步一步跟着走就行了):1.       添加s3c2410处理器的LCD控制寄存器的初始值,具体做法为在文件arch/arm/mach-s3c2410/mach-smdk2410.c中添加struct s3c2410fb_mach_info类型的寄存器描述讯息,如下所示:static struct s

星云精准测试对安卓底层驱动代码的测试案例分析

Android原生底层驱动应用面极广,但一直没有很好的办法进行质量追踪.本文借助星云精准测试的高可靠性的测试技术手段,针对Android原生底层驱动进行分析.插桩.编译.采集数据.数据分析等,逐步讲解精准测试是如何实现android原生底层驱动的对接. 在本文中,我们可以清晰地查看到如何进行技术对接的每一步,比如如何使用星云精准测试进行代码插桩.实现测试用例与采集底层驱动运行代码的数据追溯.对最终采集的数据进行一系列分析等. 一.安卓源码精准测试流程概述 经分析android源码的编译主要依靠A

LCD设备驱动

一.LCD硬件原理 利用液晶制成的显示器LCD,依据驱动方式可分为静态驱动.简单矩阵驱动以及主动矩阵驱动3中.其中,简单矩阵型又可再区分扭转向列型(TN)和超扭转式向列型(STN)两种,而主动矩阵型则以薄膜式晶体管型(TFT)为主流. 一块LCD屏显示图像不但需要LCD驱动器,还需要有相应的LCD控制器.通常LCD驱动器会议COF/COG与LCD玻璃基板制作在一起,而LCD控制则由外部电路来实现.许多MCU北部直接集成了LCD控制,通过LCD控制器可以方便地控制STN和TFT屏. 下图给出了LC

10. LCD驱动程序 ——框架分析

引言: 由LCD的硬件原理及操作(可参看韦哥博客:第017课 LCD原理详解及裸机程序分析) 我们知道只要LCD控制器的相关寄存器正确配置好,就可以在LCD面板上显示framebuffer中的内容. 若应用程序需要在LCD屏幕上显示文字或图像时,只需要把相应的显示内容以正确的格式写到Framebuffer中即可. (Framebuffer,中文名字是帧缓冲,这个帧也就是一副图像所需要的数据.因此,帧缓冲其实就是LCD设备的驱动程序) 一.LCD驱动程序框架 根据上述思路,Linux LCD 驱动

linux串口驱动分析——发送数据

一.应用程序中write函数到底层驱动历程 和前文提到的一样,首先先注册串口,使用uart_register_driver函数,依次分别为tty_register_driver,cdev_init函数,找到使用的file_operations,即应用程序与tty架构的统一接口.步骤不再赘述. static const struct file_operations tty_fops = { .llseek = no_llseek, .read = tty_read, .write = tty_wr

20150311 NandFlash驱动分析

20150311 NandFlash驱动分析 2015-03-11 李海沿 一.结构体详解 MTD体系结构: 在linux中提供了MTD(Memory Technology Device,内存技术设备)系统来建立Flash针对linux的统一.抽象的接口 引入MTD后,linux系统中的Flash设备驱动及接口可分为4层: 设备节点 MTD设备层 MTD原始设备层 硬件驱动层 硬件驱动层:Flash硬件驱动层负责底层硬件设备实际的读.写.擦除,Linux MTD设备的NAND型Flash驱动位于

linux串口驱动分析

linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作.UART 使用系统时钟能够支持最高 115.2Kbps 的波特率.每一个 UART 通道对于接收器和发送器包含了 2 个 64 位的 FIFO. 寄存器 名称 地址 在linux中的描写叙述 (2410 和 2440 处理器对内存地址映射关系同样) UART 线性控制寄存器(ULCONn) ULC